multiSelect.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. window.makeMultiselect = (items => {
  2. class MultiSelect {
  3. #htmlSelectElement;
  4. #htmlRootElement;
  5. #htmlInput;
  6. #htmlInputList;
  7. #htmlWrapper;
  8. #htmlDropdown;
  9. #ignoreMouseOut = false;
  10. #options = [];
  11. #eventHandlers = [];
  12. constructor(htmlElement) {
  13. this.#htmlSelectElement = htmlElement;
  14. this.#htmlRootElement = htmlElement.parentElement;
  15. for (let i of this.#htmlSelectElement.querySelectorAll("option"))
  16. this.#options.push({
  17. value: i.textContent,
  18. checked: !!i.selected,
  19. indeterminate: !!i.indeterminate,
  20. htmlListItem: null
  21. });
  22. }
  23. addEventListener(fncHandler) {
  24. this.#eventHandlers.push(fncHandler);
  25. }
  26. #onUpdate() {
  27. for (let i of this.#eventHandlers)
  28. i(this.#options);
  29. }
  30. #updateTextInputValue() {
  31. this.#htmlInputList.textContent = "";
  32. this.#htmlInputList.appendChild(this.#htmlInput);
  33. for (let i in this.#options) {
  34. if (this.#options[i].checked) {
  35. let li = document.createElement("li");
  36. let closeBt = document.createElement("a");
  37. closeBt.href = "#";
  38. closeBt.textContent = "x";
  39. closeBt.addEventListener("click", e => {
  40. this.#options[i].checked = false;
  41. this.#options[i].indeterminate = false;
  42. this.#rebuildDropdownContent();
  43. this.#updateTextInputValue();
  44. this.#onUpdate();
  45. e.preventDefault();
  46. });
  47. li.textContent = this.#options[i].value;
  48. li.className = "checked";
  49. li.appendChild(closeBt);
  50. if (this.#options[i].indeterminate)
  51. li.classList.add("indeterminate");
  52. this.#htmlInputList.insertBefore(li, this.#htmlInput);
  53. }
  54. }
  55. }
  56. #rebuildDropdownContent() {
  57. this.#htmlDropdown.textContent = "";
  58. for (let i in this.#options) {
  59. let wrapper = document.createElement("li");
  60. let lbl = document.createElement("label");
  61. let chk = document.createElement("input");
  62. let span = document.createElement("span");
  63. span.textContent = this.#options[i].value;
  64. chk.type = "checkbox";
  65. chk.checked = !!this.#options[i].checked;
  66. chk.indeterminate = !!this.#options[i].indeterminate;
  67. chk.index = i;
  68. lbl.appendChild(chk);
  69. lbl.appendChild(span);
  70. wrapper.appendChild(lbl);
  71. this.#htmlDropdown.appendChild(wrapper);
  72. this.#options[i].htmlListItem = wrapper;
  73. chk.addEventListener("change", e => {
  74. let state = this.#options[e.currentTarget.index];
  75. if (!state.checked) {
  76. e.currentTarget.checked = true;
  77. e.currentTarget.indeterminate = false;
  78. } else if (!state.indeterminate) {
  79. e.currentTarget.checked = true;
  80. e.currentTarget.indeterminate = true;
  81. } else {
  82. e.currentTarget.checked = false;
  83. e.currentTarget.indeterminate = false;
  84. }
  85. state.checked = e.currentTarget.checked;
  86. state.indeterminate = e.currentTarget.indeterminate;
  87. e.stopImmediatePropagation();
  88. this.#updateTextInputValue();
  89. this.#onUpdate();
  90. });
  91. }
  92. }
  93. render() {
  94. this.#htmlSelectElement.style.display = "none";
  95. if (!this.#htmlInput) {
  96. this.#htmlInput = document.createElement("input");
  97. this.#htmlInput.className = "multiselect-input";
  98. this.#htmlInput.type = "text";
  99. this.#htmlInput.addEventListener("input", e => {
  100. let input = e.currentTarget.value.toLocaleLowerCase();
  101. for (let i of this.#options) {
  102. if (i.value.toLocaleLowerCase().indexOf(input) >= 0) {
  103. i.htmlListItem.classList.remove("hidden");
  104. } else {
  105. i.htmlListItem.classList.add("hidden");
  106. }
  107. }
  108. });
  109. this.#htmlInputList && this.#htmlInputList.appendChild(this.#htmlInput);
  110. }
  111. if (!this.#htmlInputList) {
  112. this.#htmlInputList = document.createElement("ul");
  113. this.#htmlInputList.className = "multiselect-inputlist";
  114. this.#htmlRootElement && this.#htmlRootElement.appendChild(this.#htmlInputList);
  115. this.#htmlInputList.appendChild(this.#htmlInput);
  116. }
  117. if (!this.#htmlDropdown) {
  118. this.#htmlDropdown = document.createElement("ul");
  119. this.#htmlDropdown.className = "multiselect-dropdown";
  120. this.#htmlDropdown.classList.add("hidden");
  121. this.#htmlRootElement && this.#htmlRootElement.appendChild(this.#htmlDropdown);
  122. }
  123. if (!this.#htmlWrapper) {
  124. this.#htmlWrapper = document.createElement("div");
  125. this.#htmlWrapper.className = "multiselect-wrapper";
  126. this.#htmlWrapper.appendChild(this.#htmlInputList);
  127. this.#htmlWrapper.appendChild(this.#htmlDropdown);
  128. this.#htmlRootElement.appendChild(this.#htmlWrapper);
  129. this.#htmlRootElement.addEventListener("mousedown", e => {
  130. this.#ignoreMouseOut = setTimeout(() => { this.#ignoreMouseOut = false; }, 500);
  131. });
  132. this.#htmlWrapper.addEventListener("focusin", e => {
  133. this.#htmlDropdown.classList.remove("hidden");
  134. });
  135. this.#htmlWrapper.addEventListener("focusout", e => {
  136. this.#ignoreMouseOut === false && this.#htmlDropdown.classList.add("hidden");
  137. });
  138. document.body.addEventListener("click", e => {
  139. this.#ignoreMouseOut === false && this.#htmlDropdown.classList.add("hidden");
  140. });
  141. }
  142. this.#rebuildDropdownContent();
  143. this.#updateTextInputValue();
  144. }
  145. }
  146. _makeMultiSelect = htmlElement => {
  147. let select = new MultiSelect(htmlElement);
  148. select.render();
  149. return select;
  150. };
  151. if (items instanceof HTMLElement)
  152. return _makeMultiSelect(items);
  153. else if (items.length)
  154. return Array.from(items).map(i => window.makeMultiselect(i));
  155. });