treeselectjs 0.2.6 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,6 +5,8 @@ A multi-select js component with nested options.
5
5
  - Full key support (ArrowUp, ArrowDown, Space, ArrowLeft, ArrowRight, Enter)
6
6
  - Screen sensitive direction
7
7
 
8
+ **Live Demo:** https://dipson88.github.io/treeselectjs/
9
+
8
10
  ![Example img](https://github.com/dipson88/treeselectjs/blob/main/treeselectjs.png?raw=true)
9
11
 
10
12
  ### Getting Started
@@ -24,7 +26,7 @@ and css file with styles
24
26
 
25
27
  Example
26
28
  ```
27
- import Treeselect from 'treeselectjs'
29
+ import Treeselect from '../dist/treeselect-js.js';
28
30
 
29
31
  const options = [
30
32
  {
@@ -73,54 +75,59 @@ const options = [
73
75
  ]
74
76
 
75
77
  const slot = document.createElement('div')
76
- slot.innerHTML='<a class="test" href="">Slot example text</a>'
78
+ slot.innerHTML='<a class="treeselect-demo__slot" href="">Click!</a>'
77
79
 
78
- const domElement = document.querySelector('.treeselect-test')
80
+ const domElement = document.querySelector('.treeselect-demo')
79
81
  const treeselect = new Treeselect({
80
82
  parentHtmlContainer: domElement,
81
83
  value: ['West End', 'Paris', 'Lyon'],
82
84
  options: options,
83
- alwaysOpen: true,
84
- appendToBody: true,
85
- listSlotHtmlComponent: slot,
86
- disabled: false,
87
- emptyText: 'No data text'
85
+ listSlotHtmlComponent: slot
88
86
  })
89
87
 
90
88
  treeselect.srcElement.addEventListener('input', (e) => {
91
- console.log(e.detail)
89
+ console.log('Selected value:', e.detail)
90
+ })
91
+
92
+ slot.addEventListener('click', (e) => {
93
+ e.preventDefault()
94
+ alert('Slot click!')
92
95
  })
93
96
  ```
94
97
 
95
98
  ### Props
96
- Name | Type (default) | Discription
99
+ Name | Type (default) | Description
97
100
  ------------- | ------------- | -------------
98
- parentHtmlContainer | HTMLElement | It sould be a HTML element (div), it will be changed to the list container.
99
- value | Array[String] ([]) | It is an array with ids.
100
- options | Array[Object] ([]) | It is an array of objects { name: String, value: String, children: [] }, where children are the same array of objects. Do not use duplicated values.
101
- openLevel | Number (0) | All groups will be opened to this level.
102
- appendToBody | Boolean (false) | List will be appended to the body instead of the input container.
103
- alwaysOpen | Boolean (false) | List will be always opened.
104
- showTags | Boolean (true) | Selected values look like tags. The false value shows results as '{count} elements selected'.
105
- clearable | Boolean (true) | Clear icon is available.
106
- searchable | Boolean (true) | Search is available.
107
- placeholder | String ('Search...') | Placeholder text.
108
- grouped | Boolean (true) | Show groups in the input and group lefs if all group selected.
109
- listSlotHtmlComponent | HTMLElement (null) | It should be a HTML element, it will be append to the end of the list.
110
- disabled | Boolean (false) | List will be disabled.
111
- emptyText | String ('No results found...') | A empty list text.
101
+ **parentHtmlContainer** | HTMLElement (required!) | It should be a HTML element (div), it will be changed to the list container.
102
+ **value** | Array[String] ([]) | An array of `value` from `options` prop. This value will be selected on load of the treeselect. You can call `updateValue` to update prop or set value `treeselect.value` and call `mount`. The `value` changes if you check/uncheck checkboxes or remove tags from the input.
103
+ **options** | Array[Object] ([]) | It is an array of objects ```{ name: String, value: String, children: [] }```, where children are the same array of objects. Do not use duplicated values.
104
+ **openLevel** | Number (0) | All groups will be opened to this level.
105
+ **appendToBody** | Boolean (false) | List will be appended to the body instead of the input container.
106
+ **alwaysOpen** | Boolean (false) | List will be always opened. You can use it for comfortable style changing. If you what to use it as an opened list, turn `staticList` to `true`.
107
+ **showTags** | Boolean (true) | Selected values look like tags. The false value shows results as '{count} elements selected'. You can change text if you use `tagsCountText` prop. For one selected element, you will see a name of this element.
108
+ **tagsCountText** | String ('elements selected') | This text will be shown if you use 'showTags'. This text will be inserted after the count of the selected elements - ```'{count} {tagsCountText}'```.
109
+ **clearable** | Boolean (true) | Clear icon is available.
110
+ **searchable** | Boolean (true) | Search is available.
111
+ **placeholder** | String ('Search...') | Placeholder text.
112
+ **grouped** | Boolean (true) | Show groups in the input and group leafs if all group selected.
113
+ **listSlotHtmlComponent** | HTMLElement (null) | It should be a HTML element, it will be append to the end of the list.
114
+ **disabled** | Boolean (false) | List will be disabled.
115
+ **emptyText** | String ('No results found...') | A empty list text.
116
+ **staticList** | Boolean (false) | Add the list as a static DOM element. List doesn't overlap content. This prop will be ignored if you use `appendToBody`.
112
117
 
113
118
  ### Emits
114
- Name | Return Type | Discription
119
+ Name | Return Type | Description
115
120
  ------------- | ------------- | -------------
116
- input | Array[String] | Returns selected ids without groups, only leafs.
121
+ **input** | Array[String] | Returns selected ids without groups, only leafs.
117
122
 
118
123
  ### Methods
119
- Name | Params | Discription
124
+ Name | Params | Description
120
125
  ------------- | ------------- | -------------
121
- updateValue | Array[String] | Update selected values.
122
- mount | None | Helps to remount and update settings. Change settings that you need (treeselect.appendToBody = true), then call mount().
123
- destroy | None | Deletes elements from the DOM. Call mount() to add treeselect to the DOM with previously saved internal data. If you need to recreate treeselect with default params - call **new Treeselect(options)**.
126
+ **updateValue** | Array[String] | Update selected values.
127
+ **mount** | None | Helps to remount and update settings. Change settings that you need (treeselect.appendToBody = true), then call mount().
128
+ **destroy** | None | Deletes elements from the DOM. Call mount() to add treeselect to the DOM with previously saved internal data. If you need to recreate treeselect with default params - call ```new Treeselect(options)```.
129
+ **focus** | None | Focuses treeselect input without open/close state changes.
130
+ **toggleOpenClose** | None | Open or close treeselect list and focus treeselect input.
124
131
 
125
132
  ### Notes
126
133
  1) If you want to change the padding of the element you can use CSS selector. I've added **'group'** and **'level'** attributes, but you have to use **!important**.
package/dist/input.js CHANGED
@@ -1 +1 @@
1
- import svg from"./svgIcons.js";class TreeselectInput{#htmlTagsSecton=null;#htmlEditControl=null;#htmlOperators=null;#htmlArrow=null;#openEvent=new CustomEvent("open");#closeEvent=new CustomEvent("close");constructor({value:e,showTags:t,clearable:s,isAlwaysOpened:a,searchable:i,placeholder:n,disabled:r}){this.value=e,this.showTags=t??!0,this.searchable=i??!0,this.placeholder=n??"Search...",this.clearable=s??!0,this.isAlwaysOpened=a??!1,this.disabled=r??!1,this.isOpened=!1,this.searchText="",this.srcElement=this.#createTreeselectInput(),this.#updateDOM()}focus(){this.#htmlEditControl.focus()}blur(){this.isOpened&&this.#updateOpenClose()}updateValue(e){this.value=e,this.#updateTags(),this.#updateEditControl()}removeItem(t){this.value=this.value.filter(e=>e.id!==t),this.#emitInput(),this.#updateTags(),this.#updateEditControl()}clear(){this.value=[],this.searchText="",this.#emitSearch(""),this.#emitInput(),this.#updateTags(),this.#updateEditControl()}openClose(){this.#updateOpenClose()}#updateDOM(){this.#updateTags(),this.#updateEditControl(),this.#updateOperators()}#updateTags(){this.#htmlTagsSecton.innerHTML="",this.showTags?this.#htmlTagsSecton.append(...this.#createTags()):this.#htmlTagsSecton.appendChild(this.#createCountElement())}#updateOperators(){const e=[];this.#htmlOperators.innerHTML="",this.clearable&&e.push(this.#createClearButton()),this.isAlwaysOpened||e.push(this.#createInputArrow(this.isOpened)),e.length&&this.#htmlOperators.append(...e)}#updateArrowDirection(){var e;this.isAlwaysOpened||(e=this.isOpened?svg.arrowUp:svg.arrowDown,this.#htmlArrow.innerHTML=e)}#updateEditControl(){this.value?.length?this.#htmlEditControl.removeAttribute("placeholder"):this.#htmlEditControl.setAttribute("placeholder",this.placeholder),this.searchable?this.srcElement.classList.remove("treeselect-input--unsearchable"):this.srcElement.classList.add("treeselect-input--unsearchable"),this.#htmlEditControl.value=this.searchText}#updateOpenClose(){this.isOpened=!this.isOpened,this.#updateArrowDirection(),this.isOpened?this.#emitOpen():this.#emitClose()}#createTreeselectInput(){const e=document.createElement("div");return e.classList.add("treeselect-input"),e.setAttribute("tabindex","-1"),this.#htmlTagsSecton=this.#createTagsSection(),this.#htmlEditControl=this.#createControl(),this.#htmlOperators=this.#createOperators(),e.addEventListener("mousedown",e=>{e.preventDefault(),this.isOpened||this.#updateOpenClose(),this.focus()}),e.append(this.#htmlTagsSecton,this.#htmlEditControl,this.#htmlOperators),e}#createTagsSection(){const e=document.createElement("div");return e.classList.add("treeselect-input__tags"),e}#createTags(){return this.value.map(t=>{const e=document.createElement("div");e.classList.add("treeselect-input__tags-element"),e.setAttribute("tabindex","-1"),e.setAttribute("tag-id",t.id),e.setAttribute("title",t.name);var s=this.#createTagName(t.name),a=this.#createTagCross();return e.addEventListener("mousedown",e=>{e.preventDefault(),e.stopPropagation(),this.focus(),this.removeItem(t.id)}),e.append(s,a),e})}#createTagName(e){const t=document.createElement("span");return t.classList.add("treeselect-input__tags-name"),t.innerHTML=e,t}#createTagCross(){const e=document.createElement("span");return e.classList.add("treeselect-input__tags-cross"),e.innerHTML=svg.cross,e}#createCountElement(){const e=document.createElement("span");return e.classList.add("treeselect-input__tags-count"),this.value.length?e.innerHTML=1===this.value.length?this.value[0].name:this.value.length+" elements selected":e.innerHTML="",e}#createControl(){const a=document.createElement("input");return a.classList.add("treeselect-input__edit"),this.disabled&&a.setAttribute("tabindex","-1"),a.addEventListener("keydown",e=>{"Backspace"!==e.key||this.searchText.length||!this.value.length||this.showTags||this.clear(),"Backspace"===e.key&&!this.searchText.length&&this.value.length&&this.removeItem(this.value[this.value.length-1].id),"Space"!==e.code||this.searchText&&this.searchable||this.#updateOpenClose()}),a.addEventListener("input",e=>{e.stopPropagation();var t=this.searchText,s=a.value.trim();0===t.length&&0===s.length?a.value="":(this.searchable?(this.#emitSearch(e.target.value),this.isOpened||this.#updateOpenClose()):a.value="",this.searchText=a.value)}),a}#createOperators(){const e=document.createElement("div");return e.classList.add("treeselect-input__operators"),e}#createClearButton(){const e=document.createElement("span");return e.classList.add("treeselect-input__clear"),e.setAttribute("tabindex","-1"),e.innerHTML=svg.clear,e.addEventListener("mousedown",e=>{e.preventDefault(),e.stopPropagation(),this.#htmlEditControl.focus(),(this.searchText.length||this.value.length)&&this.clear()}),e}#createInputArrow(e){return this.#htmlArrow=document.createElement("span"),this.#htmlArrow.classList.add("treeselect-input__arrow"),this.#htmlArrow.innerHTML=e?svg.arrowUp:svg.arrowDown,this.#htmlArrow.addEventListener("mousedown",e=>{e.stopPropagation(),e.preventDefault(),this.focus(),this.#updateOpenClose()}),this.#htmlArrow}#emitInput(){this.srcElement.dispatchEvent(new CustomEvent("input",{detail:this.value}))}#emitSearch(e){this.srcElement.dispatchEvent(new CustomEvent("search",{detail:e}))}#emitOpen(){this.srcElement.dispatchEvent(this.#openEvent)}#emitClose(){this.srcElement.dispatchEvent(this.#closeEvent)}}export default TreeselectInput;
1
+ import svg from"./svgIcons.js";class TreeselectInput{#htmlTagsSection=null;#htmlEditControl=null;#htmlOperators=null;#htmlArrow=null;#openEvent=new CustomEvent("open");#closeEvent=new CustomEvent("close");constructor({value:t,showTags:e,tagsCountText:s,clearable:a,isAlwaysOpened:i,searchable:n,placeholder:r,disabled:h}){this.value=t,this.showTags=e??!0,this.tagsCountText=s??"elements selected",this.searchable=n??!0,this.placeholder=r??"Search...",this.clearable=a??!0,this.isAlwaysOpened=i??!1,this.disabled=h??!1,this.isOpened=!1,this.searchText="",this.srcElement=this.#createTreeselectInput(),this.#updateDOM()}focus(){this.#htmlEditControl.focus()}blur(){this.isOpened&&this.#updateOpenClose()}updateValue(t){this.value=t,this.#updateTags(),this.#updateEditControl()}removeItem(e){this.value=this.value.filter(t=>t.id!==e),this.#emitInput(),this.#updateTags(),this.#updateEditControl()}clear(){this.value=[],this.searchText="",this.#emitSearch(""),this.#emitInput(),this.#updateTags(),this.#updateEditControl()}openClose(){this.#updateOpenClose()}#updateDOM(){this.#updateTags(),this.#updateEditControl(),this.#updateOperators()}#updateTags(){this.#htmlTagsSection.innerHTML="",this.showTags?this.#htmlTagsSection.append(...this.#createTags()):this.#htmlTagsSection.appendChild(this.#createCountElement())}#updateOperators(){const t=[];this.#htmlOperators.innerHTML="",this.clearable&&t.push(this.#createClearButton()),this.isAlwaysOpened||t.push(this.#createInputArrow(this.isOpened)),t.length&&this.#htmlOperators.append(...t)}#updateArrowDirection(){var t;this.isAlwaysOpened||(t=this.isOpened?svg.arrowUp:svg.arrowDown,this.#htmlArrow.innerHTML=t)}#updateEditControl(){this.value?.length?this.#htmlEditControl.removeAttribute("placeholder"):this.#htmlEditControl.setAttribute("placeholder",this.placeholder),this.searchable?this.srcElement.classList.remove("treeselect-input--unsearchable"):this.srcElement.classList.add("treeselect-input--unsearchable"),this.#htmlEditControl.value=this.searchText}#updateOpenClose(){this.isOpened=!this.isOpened,this.#updateArrowDirection(),this.isOpened?this.#emitOpen():this.#emitClose()}#createTreeselectInput(){const t=document.createElement("div");return t.classList.add("treeselect-input"),t.setAttribute("tabindex","-1"),this.#htmlTagsSection=this.#createTagsSection(),this.#htmlEditControl=this.#createControl(),this.#htmlOperators=this.#createOperators(),t.addEventListener("mousedown",t=>{t.preventDefault(),this.isOpened||this.#updateOpenClose(),this.focus()}),t.append(this.#htmlTagsSection,this.#htmlEditControl,this.#htmlOperators),t}#createTagsSection(){const t=document.createElement("div");return t.classList.add("treeselect-input__tags"),t}#createTags(){return this.value.map(e=>{const t=document.createElement("div");t.classList.add("treeselect-input__tags-element"),t.setAttribute("tabindex","-1"),t.setAttribute("tag-id",e.id),t.setAttribute("title",e.name);var s=this.#createTagName(e.name),a=this.#createTagCross();return t.addEventListener("mousedown",t=>{t.preventDefault(),t.stopPropagation(),this.focus(),this.removeItem(e.id)}),t.append(s,a),t})}#createTagName(t){const e=document.createElement("span");return e.classList.add("treeselect-input__tags-name"),e.innerHTML=t,e}#createTagCross(){const t=document.createElement("span");return t.classList.add("treeselect-input__tags-cross"),t.innerHTML=svg.cross,t}#createCountElement(){const t=document.createElement("span");return t.classList.add("treeselect-input__tags-count"),this.value.length?t.innerHTML=1===this.value.length?this.value[0].name:this.value.length+" "+this.tagsCountText:t.innerHTML="",t}#createControl(){const a=document.createElement("input");return a.classList.add("treeselect-input__edit"),this.disabled&&a.setAttribute("tabindex","-1"),a.addEventListener("keydown",t=>{"Backspace"!==t.key||this.searchText.length||!this.value.length||this.showTags||this.clear(),"Backspace"===t.key&&!this.searchText.length&&this.value.length&&this.removeItem(this.value[this.value.length-1].id),"Space"!==t.code||this.searchText&&this.searchable||this.#updateOpenClose()}),a.addEventListener("input",t=>{t.stopPropagation();var e=this.searchText,s=a.value.trim();0===e.length&&0===s.length?a.value="":(this.searchable?(this.#emitSearch(t.target.value),this.isOpened||this.#updateOpenClose()):a.value="",this.searchText=a.value)}),a}#createOperators(){const t=document.createElement("div");return t.classList.add("treeselect-input__operators"),t}#createClearButton(){const t=document.createElement("span");return t.classList.add("treeselect-input__clear"),t.setAttribute("tabindex","-1"),t.innerHTML=svg.clear,t.addEventListener("mousedown",t=>{t.preventDefault(),t.stopPropagation(),this.#htmlEditControl.focus(),(this.searchText.length||this.value.length)&&this.clear()}),t}#createInputArrow(t){return this.#htmlArrow=document.createElement("span"),this.#htmlArrow.classList.add("treeselect-input__arrow"),this.#htmlArrow.innerHTML=t?svg.arrowUp:svg.arrowDown,this.#htmlArrow.addEventListener("mousedown",t=>{t.stopPropagation(),t.preventDefault(),this.focus(),this.#updateOpenClose()}),this.#htmlArrow}#emitInput(){this.srcElement.dispatchEvent(new CustomEvent("input",{detail:this.value}))}#emitSearch(t){this.srcElement.dispatchEvent(new CustomEvent("search",{detail:t}))}#emitOpen(){this.srcElement.dispatchEvent(this.#openEvent)}#emitClose(){this.srcElement.dispatchEvent(this.#closeEvent)}}export default TreeselectInput;
package/dist/list.js CHANGED
@@ -1 +1 @@
1
- import svg from"./svgIcons.js";const getFlatOptons=(e,i,l=0,c=0)=>e.reduce((e,t)=>{var s=!!t.children?.length;return e.push({id:t.value,name:t.name,childOf:l,isGroup:s,checked:!1,level:c,isClosed:i<=c&&s,hidden:i<c}),s&&(s=getFlatOptons(t.children,i,t.value,c+1),e.push(...s)),e},[]),checkAllChildrenInputs=({id:t,checked:s},i)=>{i.forEach(e=>{e.childOf===t&&(e.checked=s,e.isGroup&&checkAllChildrenInputs(e,i))})},checkAllParentInputs=(t,e)=>{const s=e.find(e=>e.id===t),i=e.filter(e=>e.childOf===s.id);var l=i.every(e=>e.checked),c=i.some(e=>e.isPartialChecked||e.checked)&&!l,r=!l&&!c;l&&(s.checked=!0,s.isPartialChecked=!1),c&&(s.checked=!1,s.isPartialChecked=!0),r&&(s.checked=!1,s.isPartialChecked=!1),s.childOf&&checkAllParentInputs(s.childOf,e)},checkInput=({id:e,isGroup:t,childOf:s,checked:i},l)=>{t&&checkAllChildrenInputs({id:e,checked:i},l),s&&checkAllParentInputs(s,l)},updateValue=(t,s,e)=>{s.forEach(e=>{e.checked=!1,e.isPartialChecked=!1});const i=s.filter(e=>t.includes(e.id));i.forEach(e=>{e.checked=!0,e.isPartialChecked=!1,checkInput(e,s)}),updateDOM(s,e)},hideShowChildren=(t,{id:s,isClosed:i})=>{const e=t.filter(e=>e.childOf===s);e.forEach(e=>{e.hidden=i,e.isGroup&&!e.isClosed&&hideShowChildren(t,{id:e.id,isClosed:i})})},updateDOM=(l,c)=>{l.forEach(e=>{const t=c.querySelector(`[input-id="${e.id}"]`),s=getListItemByCheckbox(t);if(t.checked=e.checked,e.checked?s.classList.add("treeselect-list__item--checked"):s.classList.remove("treeselect-list__item--checked"),e.isPartialChecked?s.classList.add("treeselect-list__item--partial-checked"):s.classList.remove("treeselect-list__item--partial-checked"),e.isGroup){const i=s.querySelector(".treeselect-list__item-icon");e.isClosed?(s.classList.add("treeselect-list__item--closed"),i.innerHTML=svg.arrowRight):(s.classList.remove("treeselect-list__item--closed"),i.innerHTML=svg.arrowDown)}e.hidden?s.classList.add("treeselect-list__item--hidden"):s.classList.remove("treeselect-list__item--hidden"),updateLeftPaddingItems(e,s,l),updateCheckboxClasses(e,t)});var e=l.some(e=>!e.hidden);const t=c.querySelector(".treeselect-list__empty");e?t.classList.add("treeselect-list__empty--hidden"):t.classList.remove("treeselect-list__empty--hidden")},updateLeftPaddingItems=(t,e,s)=>{0===t.level?(s=s.some(e=>e.isGroup&&e.level===t.level),s=!t.isGroup&&s?"20px":"5px",e.style.paddingLeft=t.isGroup?"0":s):e.style.paddingLeft=t.isGroup?20*t.level+"px":20*t.level+20+"px",e.setAttribute("level",t.level),e.setAttribute("group",t.isGroup)},updateCheckboxClasses=(e,t)=>{const s=t.parentNode,i=s.querySelector(".treeselect-list__item-checkbox-icon");e.checked?i.innerHTML=svg.check:e.isPartialChecked?i.innerHTML=svg.partialCheck:i.innerHTML=""},getAllFlattedChildren=(s,i)=>i.reduce((e,t)=>(t.childOf===s&&(e.push(t),t.isGroup&&e.push(...getAllFlattedChildren(t.id,i))),e),[]),getAllFlattendParents=(s,i)=>i.reduce((e,t)=>(t.id===s&&(e.push(t),t.childOf&&e.push(...getAllFlattendParents(t.childOf,i))),e),[]),getGroupedValues=e=>{const{onlyGroupsIds:t,allItems:s}=e.reduce((e,t)=>(t.checked&&(t.isGroup&&e.onlyGroupsIds.push(t.id),e.allItems.push(t)),e),{onlyGroupsIds:[],allItems:[]});return s.filter(e=>!t.includes(e.childOf))},getCheckedValues=e=>e.filter(e=>e.checked&&!e.isGroup),getListItemByCheckbox=e=>{return e.parentNode.parentNode},validateOptions=e=>{const t=e.reduce((e,t)=>(e.allItems.includes(t.id)&&e.duplications.push(t.id),e.allItems.push(t.id),e),{duplications:[],allItems:[]})["duplications"];t.length&&console.error(`You have duplicated values: ${t.join(", ")}! You should use unique values.`)};class TreeselectList{#lastFocusedItem=null;#isMouseActionsAvailable=!0;constructor({options:e,value:t,openLevel:s,listSlotHtmlComponent:i,emptyText:l}){this.options=e,this.value=t,this.searchText="",this.openLevel=s??0,this.listSlotHtmlComponent=i,this.emptyText=l??"No results found...",this.flattedOptions=getFlatOptons(this.options,this.openLevel),this.flattedOptionsBeforeSearch=this.flattedOptions,this.selectedNodes={ids:[],groupedIds:[]},this.srcElement=this.#createList(),this.updateValue(this.value),validateOptions(this.flattedOptions)}updateValue(e){updateValue(e,this.flattedOptions,this.srcElement),this.#updateSelectedNodes()}updateSearchValue(i){var e=""===this.searchText&&""!==i;if(this.searchText=i,e&&(this.flattedOptionsBeforeSearch=JSON.parse(JSON.stringify(this.flattedOptions))),""===this.searchText)return this.flattedOptions=this.flattedOptionsBeforeSearch.map(t=>{const e=this.flattedOptions.find(e=>e.id===t.id);return e.isClosed=t.isClosed,e.hidden=t.hidden,e}),this.flattedOptionsBeforeSearch=[],updateDOM(this.flattedOptions,this.srcElement),void this.focusFirstListElement();const s=this.flattedOptions.reduce((e,t)=>{var s;return t.name.toLowerCase().includes(i.toLowerCase())&&(e.push(t),t.isGroup&&(s=getAllFlattedChildren(t.id,this.flattedOptions),e.push(...s)),t.childOf&&(s=getAllFlattendParents(t.childOf,this.flattedOptions),e.push(...s))),e},[]);this.flattedOptions.forEach(t=>{s.some(e=>e.id===t.id)?(t.isGroup&&(t.isClosed=!1,hideShowChildren(this.flattedOptions,t)),t.hidden=!1):t.hidden=!0}),updateDOM(this.flattedOptions,this.srcElement),this.focusFirstListElement()}callKeyAction(e){this.#isMouseActionsAvailable=!1;const t=this.srcElement.querySelector(".treeselect-list__item--focused");if("Enter"===e&&t&&t.dispatchEvent(new Event("mousedown")),"ArrowLeft"===e||"ArrowRight"===e){if(!t)return;const c=t.querySelector(".treeselect-list__item-checkbox"),r=c.getAttribute("input-id");var s=this.flattedOptions.find(e=>e.id===r);const o=t.querySelector(".treeselect-list__item-icon");"ArrowLeft"!==e||s.isClosed||o.dispatchEvent(new Event("mousedown")),"ArrowRight"===e&&s.isClosed&&o.dispatchEvent(new Event("mousedown"))}if("ArrowDown"===e||"ArrowUp"===e){const d=Array.from(this.srcElement.querySelectorAll(".treeselect-list__item-checkbox")).filter(e=>"none"!==window.getComputedStyle(getListItemByCheckbox(e)).display);if(d.length)if(t){s=d.findIndex(e=>getListItemByCheckbox(e).classList.contains("treeselect-list__item--focused"));const n=getListItemByCheckbox(d[s]);n.classList.remove("treeselect-list__item--focused");var s="ArrowDown"===e?s+1:s-1,i="ArrowDown"===e?0:d.length-1,i=d[s]??d[i],s=!d[s];const a=getListItemByCheckbox(i);a.classList.add("treeselect-list__item--focused");var i=this.srcElement.getBoundingClientRect(),l=a.getBoundingClientRect();s&&"ArrowDown"===e?this.srcElement.scroll(0,0):s&&"ArrowUp"===e?this.srcElement.scroll(0,this.srcElement.scrollHeight):i.y+i.height<l.y+l.height?this.srcElement.scroll(0,this.srcElement.scrollTop+l.height):i.y>l.y&&this.srcElement.scroll(0,this.srcElement.scrollTop-l.height)}else{const h=getListItemByCheckbox(d[0]);h.classList.add("treeselect-list__item--focused")}}}focusFirstListElement(){var e="treeselect-list__item--focused";const t=this.srcElement.querySelector("."+e);var s=Array.from(this.srcElement.querySelectorAll(".treeselect-list__item-checkbox")).filter(e=>"none"!==window.getComputedStyle(getListItemByCheckbox(e)).display);if(s.length){t&&t.classList.remove(e);const i=getListItemByCheckbox(s[0]);i.classList.add(e)}}#createList(){const e=[],t=document.createElement("div");t.classList.add("treeselect-list");var s=this.#getListHTML(this.options);if(e.push(...s),this.listSlotHtmlComponent){const i=document.createElement("div");i.classList.add("treeselect-list__slot"),i.appendChild(this.listSlotHtmlComponent),e.push(i)}s=this.#createEmptyList();return e.push(s),t.addEventListener("mouseout",e=>{e.stopPropagation(),this.#lastFocusedItem&&this.#isMouseActionsAvailable&&this.#lastFocusedItem.classList.add("treeselect-list__item--focused")}),t.addEventListener("mousemove",()=>{this.#isMouseActionsAvailable=!0}),t.append(...e),t}#getListHTML(e){return e.reduce((e,t)=>{if(t.children?.length){const i=this.#createGroupContainer(t);var s=this.#getListHTML(t.children);return i.append(...s),e.push(i),e}s=this.#createGroupItem(t,!1);return e.push(s),e},[])}#createGroupContainer(e){const t=document.createElement("div");t.setAttribute("group-container-id",e.value),t.classList.add("treeselect-list__group-container");e=this.#createGroupItem(e,!0);return t.appendChild(e),t}#createGroupItem(s,e){const t=document.createElement("div");t.setAttribute("tabindex","-1"),t.setAttribute("title",s.name),t.classList.add("treeselect-list__item"),e&&(e=this.#createArrow(),t.appendChild(e)),t.addEventListener("mouseover",()=>{this.#isMouseActionsAvailable&&this.#groupMouseAction(!0,t)},!0),t.addEventListener("mouseout",()=>{this.#isMouseActionsAvailable&&(this.#groupMouseAction(!1,t),this.#lastFocusedItem=t)},!0),t.addEventListener("mousedown",e=>{e.stopPropagation();const t=e.target.querySelector(".treeselect-list__item-checkbox");t.checked=!t.checked,this.#checkboxClickEvent(t,s)});var e=this.#createCheckbox(s),i=this.#createCheckboxLabel(s);return t.append(e,i),t}#createArrow(){const e=document.createElement("span");return e.setAttribute("tabindex","-1"),e.classList.add("treeselect-list__item-icon"),e.innerHTML=svg.arrowDown,e.addEventListener("mousedown",e=>{e.stopPropagation(),this.#arrowClickEvent(e)}),e}#createCheckbox(e){const t=document.createElement("div"),s=(t.classList.add("treeselect-list__item-checkbox-container"),document.createElement("span")),i=(s.classList.add("treeselect-list__item-checkbox-icon"),s.innerHTML="",document.createElement("input"));return i.setAttribute("tabindex","-1"),i.setAttribute("type","checkbox"),i.setAttribute("input-id",e.value),i.classList.add("treeselect-list__item-checkbox"),t.append(s,i),t}#createCheckboxLabel(e){const t=document.createElement("label");return t.innerHTML=e.name,t.classList.add("treeselect-list__item-label"),t}#createEmptyList(){const e=document.createElement("div"),t=(e.classList.add("treeselect-list__empty"),e.setAttribute("title",this.emptyText),document.createElement("span")),s=(t.classList.add("treeselect-list__empty-icon"),t.innerHTML=svg.attention,document.createElement("span"));return s.classList.add("treeselect-list__empty-text"),s.innerHTML=this.emptyText,e.append(t,s),e}#checkboxClickEvent(e,t){const s=this.flattedOptions.find(e=>e.id===t.value);s.checked=e.checked,s.isPartialChecked=!1,checkInput(s,this.flattedOptions),updateDOM(this.flattedOptions,this.srcElement),this.#emitInput()}#arrowClickEvent(e){const t=e.target.parentNode.querySelector("[input-id]"),s=t.getAttribute("input-id"),i=this.flattedOptions.find(e=>e.id===s);i.isClosed=!i.isClosed,hideShowChildren(this.flattedOptions,i),updateDOM(this.flattedOptions,this.srcElement),this.#emitArrrowClick()}#groupMouseAction(e,t){const s="treeselect-list__item--focused";if(e){const i=Array.from(this.srcElement.querySelectorAll("."+s));i.length&&i.forEach(e=>e.classList.remove(s)),t.classList.add(s)}else t.classList.remove(s)}#updateSelectedNodes(){this.selectedNodes={ids:getCheckedValues(this.flattedOptions),groupedIds:getGroupedValues(this.flattedOptions)}}#emitArrrowClick(){this.srcElement.dispatchEvent(new CustomEvent("arrow-click"))}#emitInput(){this.#updateSelectedNodes(),this.srcElement.dispatchEvent(new CustomEvent("input",{detail:this.selectedNodes}))}}export default TreeselectList;
1
+ import svg from"./svgIcons.js";const getFlatOptions=(e,i,l=0,c=0)=>e.reduce((e,t)=>{var s=!!t.children?.length;return e.push({id:t.value,name:t.name,childOf:l,isGroup:s,checked:!1,level:c,isClosed:i<=c&&s,hidden:i<c}),s&&(s=getFlatOptions(t.children,i,t.value,c+1),e.push(...s)),e},[]),checkAllChildrenInputs=({id:t,checked:s},i)=>{i.forEach(e=>{e.childOf===t&&(e.checked=s,e.isPartialChecked=!1,e.isGroup&&checkAllChildrenInputs(e,i))})},checkAllParentInputs=(t,e)=>{const s=e.find(e=>e.id===t),i=e.filter(e=>e.childOf===s.id);var l=i.every(e=>e.checked),c=i.some(e=>e.isPartialChecked||e.checked)&&!l,r=!l&&!c;l&&(s.checked=!0,s.isPartialChecked=!1),c&&(s.checked=!1,s.isPartialChecked=!0),r&&(s.checked=!1,s.isPartialChecked=!1),s.childOf&&checkAllParentInputs(s.childOf,e)},checkInput=({id:e,isGroup:t,childOf:s,checked:i},l)=>{t&&checkAllChildrenInputs({id:e,checked:i},l),s&&checkAllParentInputs(s,l)},updateValue=(t,s,e)=>{s.forEach(e=>{e.checked=!1,e.isPartialChecked=!1});const i=s.filter(e=>t.includes(e.id));i.forEach(e=>{e.checked=!0,e.isPartialChecked=!1,checkInput(e,s)}),updateDOM(s,e)},hideShowChildren=(t,{id:s,isClosed:i})=>{const e=t.filter(e=>e.childOf===s);e.forEach(e=>{e.hidden=i,e.isGroup&&!e.isClosed&&hideShowChildren(t,{id:e.id,isClosed:i})})},updateDOM=(l,c)=>{l.forEach(e=>{const t=c.querySelector(`[input-id="${e.id}"]`),s=getListItemByCheckbox(t);if(t.checked=e.checked,e.checked?s.classList.add("treeselect-list__item--checked"):s.classList.remove("treeselect-list__item--checked"),e.isPartialChecked?s.classList.add("treeselect-list__item--partial-checked"):s.classList.remove("treeselect-list__item--partial-checked"),e.isGroup){const i=s.querySelector(".treeselect-list__item-icon");e.isClosed?(s.classList.add("treeselect-list__item--closed"),i.innerHTML=svg.arrowRight):(s.classList.remove("treeselect-list__item--closed"),i.innerHTML=svg.arrowDown)}e.hidden?s.classList.add("treeselect-list__item--hidden"):s.classList.remove("treeselect-list__item--hidden"),updateLeftPaddingItems(e,s,l),updateCheckboxClasses(e,t)});var e=l.some(e=>!e.hidden);const t=c.querySelector(".treeselect-list__empty");e?t.classList.add("treeselect-list__empty--hidden"):t.classList.remove("treeselect-list__empty--hidden")},updateLeftPaddingItems=(t,e,s)=>{0===t.level?(s=s.some(e=>e.isGroup&&e.level===t.level),s=!t.isGroup&&s?"20px":"5px",e.style.paddingLeft=t.isGroup?"0":s):e.style.paddingLeft=t.isGroup?20*t.level+"px":20*t.level+20+"px",e.setAttribute("level",t.level),e.setAttribute("group",t.isGroup)},updateCheckboxClasses=(e,t)=>{const s=t.parentNode,i=s.querySelector(".treeselect-list__item-checkbox-icon");e.checked?i.innerHTML=svg.check:e.isPartialChecked?i.innerHTML=svg.partialCheck:i.innerHTML=""},getAllFlattedChildren=(s,i)=>i.reduce((e,t)=>(t.childOf===s&&(e.push(t),t.isGroup&&e.push(...getAllFlattedChildren(t.id,i))),e),[]),getAllFlattenParents=(s,i)=>i.reduce((e,t)=>(t.id===s&&(e.push(t),t.childOf&&e.push(...getAllFlattenParents(t.childOf,i))),e),[]),getGroupedValues=e=>{const{onlyGroupsIds:t,allItems:s}=e.reduce((e,t)=>(t.checked&&(t.isGroup&&e.onlyGroupsIds.push(t.id),e.allItems.push(t)),e),{onlyGroupsIds:[],allItems:[]});return s.filter(e=>!t.includes(e.childOf))},getCheckedValues=e=>e.filter(e=>e.checked&&!e.isGroup),getListItemByCheckbox=e=>{return e.parentNode.parentNode},validateOptions=e=>{const t=e.reduce((e,t)=>(e.allItems.includes(t.id)&&e.duplications.push(t.id),e.allItems.push(t.id),e),{duplications:[],allItems:[]})["duplications"];t.length&&console.error(`Validation: You have duplicated values: ${t.join(", ")}! You should use unique values.`)};class TreeselectList{#lastFocusedItem=null;#isMouseActionsAvailable=!0;constructor({options:e,value:t,openLevel:s,listSlotHtmlComponent:i,emptyText:l}){this.options=e,this.value=t,this.searchText="",this.openLevel=s??0,this.listSlotHtmlComponent=i,this.emptyText=l??"No results found...",this.flattedOptions=getFlatOptions(this.options,this.openLevel),this.flattedOptionsBeforeSearch=this.flattedOptions,this.selectedNodes={nodes:[],groupedNodes:[]},this.srcElement=this.#createList(),this.updateValue(this.value),validateOptions(this.flattedOptions)}updateValue(e){updateValue(e,this.flattedOptions,this.srcElement),this.#updateSelectedNodes()}updateSearchValue(i){if(i!==this.searchText){var e=""===this.searchText&&""!==i;if(this.searchText=i,e&&(this.flattedOptionsBeforeSearch=JSON.parse(JSON.stringify(this.flattedOptions))),""===this.searchText)return this.flattedOptions=this.flattedOptionsBeforeSearch.map(t=>{const e=this.flattedOptions.find(e=>e.id===t.id);return e.isClosed=t.isClosed,e.hidden=t.hidden,e}),this.flattedOptionsBeforeSearch=[],updateDOM(this.flattedOptions,this.srcElement),void this.focusFirstListElement();const s=this.flattedOptions.reduce((e,t)=>{var s;return t.name.toLowerCase().includes(i.toLowerCase())&&(e.push(t),t.isGroup&&(s=getAllFlattedChildren(t.id,this.flattedOptions),e.push(...s)),t.childOf&&(s=getAllFlattenParents(t.childOf,this.flattedOptions),e.push(...s))),e},[]);this.flattedOptions.forEach(t=>{s.some(e=>e.id===t.id)?(t.isGroup&&(t.isClosed=!1,hideShowChildren(this.flattedOptions,t)),t.hidden=!1):t.hidden=!0}),updateDOM(this.flattedOptions,this.srcElement),this.focusFirstListElement()}}callKeyAction(e){this.#isMouseActionsAvailable=!1;const t=this.srcElement.querySelector(".treeselect-list__item--focused");if("Enter"===e&&t&&t.dispatchEvent(new Event("mousedown")),"ArrowLeft"===e||"ArrowRight"===e){if(!t)return;const c=t.querySelector(".treeselect-list__item-checkbox"),r=c.getAttribute("input-id");var s=this.flattedOptions.find(e=>e.id===r);const o=t.querySelector(".treeselect-list__item-icon");"ArrowLeft"!==e||s.isClosed||o.dispatchEvent(new Event("mousedown")),"ArrowRight"===e&&s.isClosed&&o.dispatchEvent(new Event("mousedown"))}if("ArrowDown"===e||"ArrowUp"===e){const n=Array.from(this.srcElement.querySelectorAll(".treeselect-list__item-checkbox")).filter(e=>"none"!==window.getComputedStyle(getListItemByCheckbox(e)).display);if(n.length)if(t){s=n.findIndex(e=>getListItemByCheckbox(e).classList.contains("treeselect-list__item--focused"));const d=getListItemByCheckbox(n[s]);d.classList.remove("treeselect-list__item--focused");var s="ArrowDown"===e?s+1:s-1,i="ArrowDown"===e?0:n.length-1,i=n[s]??n[i],s=!n[s];const a=getListItemByCheckbox(i);a.classList.add("treeselect-list__item--focused");var i=this.srcElement.getBoundingClientRect(),l=a.getBoundingClientRect();s&&"ArrowDown"===e?this.srcElement.scroll(0,0):s&&"ArrowUp"===e?this.srcElement.scroll(0,this.srcElement.scrollHeight):i.y+i.height<l.y+l.height?this.srcElement.scroll(0,this.srcElement.scrollTop+l.height):i.y>l.y&&this.srcElement.scroll(0,this.srcElement.scrollTop-l.height)}else{const h=getListItemByCheckbox(n[0]);h.classList.add("treeselect-list__item--focused")}}}focusFirstListElement(){var e="treeselect-list__item--focused";const t=this.srcElement.querySelector("."+e);var s=Array.from(this.srcElement.querySelectorAll(".treeselect-list__item-checkbox")).filter(e=>"none"!==window.getComputedStyle(getListItemByCheckbox(e)).display);if(s.length){t&&t.classList.remove(e);const i=getListItemByCheckbox(s[0]);i.classList.add(e)}}#createList(){const e=[],t=document.createElement("div");t.classList.add("treeselect-list");var s=this.#getListHTML(this.options);if(e.push(...s),this.listSlotHtmlComponent){const i=document.createElement("div");i.classList.add("treeselect-list__slot"),i.appendChild(this.listSlotHtmlComponent),e.push(i)}s=this.#createEmptyList();return e.push(s),t.addEventListener("mouseout",e=>{e.stopPropagation(),this.#lastFocusedItem&&this.#isMouseActionsAvailable&&this.#lastFocusedItem.classList.add("treeselect-list__item--focused")}),t.addEventListener("mousemove",()=>{this.#isMouseActionsAvailable=!0}),t.append(...e),t}#getListHTML(e){return e.reduce((e,t)=>{if(t.children?.length){const i=this.#createGroupContainer(t);var s=this.#getListHTML(t.children);return i.append(...s),e.push(i),e}s=this.#createGroupItem(t,!1);return e.push(s),e},[])}#createGroupContainer(e){const t=document.createElement("div");t.setAttribute("group-container-id",e.value),t.classList.add("treeselect-list__group-container");e=this.#createGroupItem(e,!0);return t.appendChild(e),t}#createGroupItem(s,e){const t=document.createElement("div");t.setAttribute("tabindex","-1"),t.setAttribute("title",s.name),t.classList.add("treeselect-list__item"),e&&(e=this.#createArrow(),t.appendChild(e)),t.addEventListener("mouseover",()=>{this.#isMouseActionsAvailable&&this.#groupMouseAction(!0,t)},!0),t.addEventListener("mouseout",()=>{this.#isMouseActionsAvailable&&(this.#groupMouseAction(!1,t),this.#lastFocusedItem=t)},!0),t.addEventListener("mousedown",e=>{e.stopPropagation();const t=e.target.querySelector(".treeselect-list__item-checkbox");t.checked=!t.checked,this.#checkboxClickEvent(t,s)});var e=this.#createCheckbox(s),i=this.#createCheckboxLabel(s);return t.append(e,i),t}#createArrow(){const e=document.createElement("span");return e.setAttribute("tabindex","-1"),e.classList.add("treeselect-list__item-icon"),e.innerHTML=svg.arrowDown,e.addEventListener("mousedown",e=>{e.stopPropagation(),this.#arrowClickEvent(e)}),e}#createCheckbox(e){const t=document.createElement("div"),s=(t.classList.add("treeselect-list__item-checkbox-container"),document.createElement("span")),i=(s.classList.add("treeselect-list__item-checkbox-icon"),s.innerHTML="",document.createElement("input"));return i.setAttribute("tabindex","-1"),i.setAttribute("type","checkbox"),i.setAttribute("input-id",e.value),i.classList.add("treeselect-list__item-checkbox"),t.append(s,i),t}#createCheckboxLabel(e){const t=document.createElement("label");return t.innerHTML=e.name,t.classList.add("treeselect-list__item-label"),t}#createEmptyList(){const e=document.createElement("div"),t=(e.classList.add("treeselect-list__empty"),e.setAttribute("title",this.emptyText),document.createElement("span")),s=(t.classList.add("treeselect-list__empty-icon"),t.innerHTML=svg.attention,document.createElement("span"));return s.classList.add("treeselect-list__empty-text"),s.innerHTML=this.emptyText,e.append(t,s),e}#checkboxClickEvent(e,t){const s=this.flattedOptions.find(e=>e.id===t.value);s.checked=e.checked,s.isPartialChecked=!1,checkInput(s,this.flattedOptions),updateDOM(this.flattedOptions,this.srcElement),this.#emitInput()}#arrowClickEvent(e){const t=e.target.parentNode.querySelector("[input-id]"),s=t.getAttribute("input-id"),i=this.flattedOptions.find(e=>e.id===s);i.isClosed=!i.isClosed,hideShowChildren(this.flattedOptions,i),updateDOM(this.flattedOptions,this.srcElement),this.#emitArrowClick()}#groupMouseAction(e,t){const s="treeselect-list__item--focused";if(e){const i=Array.from(this.srcElement.querySelectorAll("."+s));i.length&&i.forEach(e=>e.classList.remove(s)),t.classList.add(s)}else t.classList.remove(s)}#updateSelectedNodes(){this.selectedNodes={nodes:getCheckedValues(this.flattedOptions),groupedNodes:getGroupedValues(this.flattedOptions)}}#emitArrowClick(){this.srcElement.dispatchEvent(new CustomEvent("arrow-click"))}#emitInput(){this.#updateSelectedNodes(),this.srcElement.dispatchEvent(new CustomEvent("input",{detail:this.selectedNodes}))}}export default TreeselectList;
@@ -1,7 +1,7 @@
1
1
  @import './input.css';@import './list.css';.treeselect{width:100%;position:relative;box-sizing:border-box}
2
- .treeselect--disabled{pointer-events:none}.treeselect-list{position:absolute;left:0;border-radius:4px;box-sizing:border-box;z-index:1050}
3
- .treeselect .treeselect-list{position:absolute}.treeselect-input--focused{border-color:#101010}
4
- .treeselect-input--opened.treeselect-input--top{border-top-color:transparent;border-top-left-radius:0;border-top-right-radius:0}
2
+ .treeselect--disabled{pointer-events:none}.treeselect-list{position:absolute;left:0;border-radius:4px;box-sizing:border-box;z-index:1000}
3
+ .treeselect .treeselect-list{position:absolute}.treeselect .treeselect-list--static{position:static}
4
+ .treeselect-input--focused{border-color:#101010}.treeselect-input--opened.treeselect-input--top{border-top-color:transparent;border-top-left-radius:0;border-top-right-radius:0}
5
5
  .treeselect-input--opened.treeselect-input--bottom{border-bottom-color:transparent;border-bottom-left-radius:0;border-bottom-right-radius:0}
6
6
  .treeselect-list--focused{border-color:#101010}.treeselect-list--top,.treeselect-list--top-to-body{border-bottom-color:#d7dde4;border-bottom-left-radius:0;border-bottom-right-radius:0}
7
7
  .treeselect-list--bottom,.treeselect-list--bottom-to-body{border-top-color:#d7dde4;border-top-left-radius:0;border-top-right-radius:0}
@@ -1 +1 @@
1
- import TreeselectInput from"./input.js";import TreeselectList from"./list.js";class Treeselect{#htmlContainer=null;#treeselectList=null;#treeselectInput=null;#containerResizer=null;#scrollEvent=null;#focusEvent=null;#blurEvent=null;constructor({parentHtmlContainer:e,value:t,options:s,openLevel:i,appendToBody:l,alwaysOpen:n,showTags:r,clearable:o,searchable:c,placeholder:a,grouped:d,listSlotHtmlComponent:h,disabled:u,emptyText:p}){this.parentHtmlContainer=e,this.value=t??[],this.options=s??[],this.openLevel=i??0,this.appendToBody=l??!0,this.alwaysOpen=n&&!u,this.showTags=r??!0,this.clearable=o??!0,this.searchable=c??!0,this.placeholder=a??"Search...",this.grouped=d??!0,this.listSlotHtmlComponent=h??null,this.disabled=u??!1,this.emptyText=p??"No results found...",this.srcElement=null,this.mount()}mount(){this.destroy(),this.srcElement=this.#createTreeselect(),this.#scrollEvent=this.scrollWindowHandler.bind(this),this.#focusEvent=this.focusWindowHandler.bind(this),this.#blurEvent=this.blurWindowHandler.bind(this),this.alwaysOpen&&this.#treeselectInput.openClose(),this.disabled&&this.srcElement.classList.add("treeselect--disabled")}updateValue(e){const t=this.#treeselectList;t.updateValue(e);var{groupedIds:e,ids:s}=t.selectedNodes,e=this.grouped?e:s;this.#treeselectInput.updateValue(e)}destroy(){this.srcElement&&(this.#closeList(),this.srcElement.innerHTML="",this.srcElement=null,this.#removeOutsideListeners(!0))}#createTreeselect(){const e=this.parentHtmlContainer,t=(e.classList.add("treeselect"),new TreeselectList({options:this.options,value:this.value,openLevel:this.openLevel,listSlotHtmlComponent:this.listSlotHtmlComponent,emptyText:this.emptyText}));var{groupedIds:s,ids:i}=t.selectedNodes;const l=new TreeselectInput({value:this.grouped?s:i,showTags:this.showTags,clearable:this.clearable,isAlwaysOpened:this.alwaysOpen,searchable:this.searchable,placeholder:this.placeholder,disabled:this.disabled});return this.appendToBody&&(this.#containerResizer=new ResizeObserver(()=>{this.updateListPosition()})),l.srcElement.addEventListener("input",e=>{e=e.detail.map(({id:e})=>e);this.value=e,t.updateValue(e),this.#emitInput()}),l.srcElement.addEventListener("open",()=>this.#openList()),l.srcElement.addEventListener("keydown",e=>t.callKeyAction(e.key)),l.srcElement.addEventListener("search",e=>{t.updateSearchValue(e.detail),this.updateListPosition()}),l.srcElement.addEventListener("focus",()=>{this.#updateFocusClasses(!0),document.addEventListener("mousedown",this.#focusEvent,!0),document.addEventListener("focus",this.#focusEvent,!0),window.addEventListener("blur",this.#blurEvent)},!0),this.alwaysOpen||l.srcElement.addEventListener("close",()=>{this.#closeList()}),t.srcElement.addEventListener("mouseup",()=>{l.focus()},!0),t.srcElement.addEventListener("input",e=>{const{groupedIds:t,ids:s}=e.detail;e=this.grouped?t:s;l.updateValue(e),this.value=s.map(({id:e})=>e),l.focus(),this.#emitInput()}),t.srcElement.addEventListener("arrow-click",()=>{l.focus(),this.updateListPosition()}),this.#htmlContainer=e,this.#treeselectList=t,this.#treeselectInput=l,e.append(l.srcElement),e}#openList(){window.addEventListener("scroll",this.#scrollEvent,!0),this.appendToBody?(document.body.appendChild(this.#treeselectList.srcElement),this.#containerResizer.observe(this.#htmlContainer)):this.#htmlContainer.appendChild(this.#treeselectList.srcElement),this.updateListPosition(),this.#updateOpenCloseClasses(!0),this.#treeselectList.focusFirstListElement()}#closeList(){window.removeEventListener("scroll",this.#scrollEvent,!0),(this.appendToBody?document.body:this.#htmlContainer).contains(this.#treeselectList.srcElement)&&(this.appendToBody?(document.body.removeChild(this.#treeselectList.srcElement),this.#containerResizer?.disconnect()):this.#htmlContainer.removeChild(this.#treeselectList.srcElement),this.#updateOpenCloseClasses(!1))}#updateDirectionClasses(e,t){var s=t?"treeselect-list--top-to-body":"treeselect-list--top",t=t?"treeselect-list--bottom-to-body":"treeselect-list--bottom";e?(this.#treeselectList.srcElement.classList.add(s),this.#treeselectList.srcElement.classList.remove(t),this.#treeselectInput.srcElement.classList.add("treeselect-input--top"),this.#treeselectInput.srcElement.classList.remove("treeselect-input--bottom")):(this.#treeselectList.srcElement.classList.remove(s),this.#treeselectList.srcElement.classList.add(t),this.#treeselectInput.srcElement.classList.remove("treeselect-input--top"),this.#treeselectInput.srcElement.classList.add("treeselect-input--bottom"))}#updateFocusClasses(e){e?(this.#treeselectInput.srcElement.classList.add("treeselect-input--focused"),this.#treeselectList.srcElement.classList.add("treeselect-list--focused")):(this.#treeselectInput.srcElement.classList.remove("treeselect-input--focused"),this.#treeselectList.srcElement.classList.remove("treeselect-list--focused"))}#updateOpenCloseClasses(e){e?this.#treeselectInput.srcElement.classList.add("treeselect-input--opened"):this.#treeselectInput.srcElement.classList.remove("treeselect-input--opened")}#removeOutsideListeners(e){this.alwaysOpen&&!e||window.removeEventListener("scroll",this.#scrollEvent,!0),document.removeEventListener("click",this.#focusEvent,!0),document.removeEventListener("focus",this.#focusEvent,!0),window.removeEventListener("blur",this.#blurEvent)}scrollWindowHandler(){this.updateListPosition()}focusWindowHandler(e){this.#htmlContainer.contains(e.target)||this.#treeselectList.srcElement.contains(e.target)||(this.#treeselectInput.blur(),this.#removeOutsideListeners(),this.#updateFocusClasses(!1))}blurWindowHandler(){this.#treeselectInput.blur(),this.#removeOutsideListeners(),this.#updateFocusClasses(!1)}updateListPosition(){const e=this.#treeselectList.srcElement,t=(e.style.transform=null,this.#htmlContainer);var{y:s,height:i}=e.getBoundingClientRect(),{x:l,y:n,height:r,width:o}=t.getBoundingClientRect(),c=window.innerHeight-n-r,c=c<n&&i<=n&&c<i,i=(this.appendToBody&&(e.style.transform=c?`translateY(${n-s-i}px)`:`translateY(${n+r-s}px)`,e.style.width=o+"px",e.style.left=l+"px"),c?"top":"buttom");e.getAttribute("direction")!==i&&(e.setAttribute("direction",i),this.#updateDirectionClasses(c,this.appendToBody))}#emitInput(){this.srcElement.dispatchEvent(new CustomEvent("input",{detail:this.value}))}}export default Treeselect;
1
+ import TreeselectInput from"./input.js";import TreeselectList from"./list.js";const validateProps=({parentHtmlContainer:e,staticList:t,appendToBody:s})=>{e||console.error("Validation: parentHtmlContainer prop is required!"),t&&s&&console.error("Validation: You should set staticList to false if you use appendToBody!")},getOnlyIds=e=>e.map(e=>e.id);class Treeselect{#htmlContainer=null;#treeselectList=null;#treeselectInput=null;#containerResizer=null;#scrollEvent=null;#resizeEvent=null;#focusEvent=null;#blurEvent=null;constructor({parentHtmlContainer:e,value:t,options:s,openLevel:i,appendToBody:n,alwaysOpen:l,showTags:r,tagsCountText:o,clearable:c,searchable:a,placeholder:d,grouped:h,listSlotHtmlComponent:p,disabled:u,emptyText:m,staticList:L}){validateProps({parentHtmlContainer:e,staticList:L,appendToBody:n}),this.parentHtmlContainer=e,this.value=t??[],this.options=s??[],this.openLevel=i??0,this.appendToBody=n??!0,this.alwaysOpen=l&&!u,this.showTags=r??!0,this.tagsCountText=o??"elements selected",this.clearable=c??!0,this.searchable=a??!0,this.placeholder=d??"Search...",this.grouped=h??!0,this.listSlotHtmlComponent=p??null,this.disabled=u??!1,this.emptyText=m??"No results found...",this.staticList=L&&!this.appendToBody,this.isListOpened=!1,this.srcElement=null,this.mount()}mount(){this.destroy(),this.srcElement=this.#createTreeselect(),this.#scrollEvent=this.scrollWindowHandler.bind(this),this.#resizeEvent=this.scrollWindowHandler.bind(this),this.#focusEvent=this.focusWindowHandler.bind(this),this.#blurEvent=this.blurWindowHandler.bind(this),this.alwaysOpen&&this.#treeselectInput.openClose(),this.disabled&&this.srcElement.classList.add("treeselect--disabled")}updateValue(e){const t=this.#treeselectList;t.updateValue(e);var{groupedNodes:e,nodes:s}=t.selectedNodes,e=this.grouped?e:s;this.#treeselectInput.updateValue(e)}destroy(){this.srcElement&&(this.#closeList(),this.srcElement.innerHTML="",this.srcElement=null,this.#removeOutsideListeners(!0))}focus(){this.#treeselectInput&&this.#treeselectInput.focus()}toggleOpenClose(){this.#treeselectInput&&(this.#treeselectInput.openClose(),this.#treeselectInput.focus())}#createTreeselect(){const e=this.parentHtmlContainer,t=(e.classList.add("treeselect"),new TreeselectList({options:this.options,value:this.value,openLevel:this.openLevel,listSlotHtmlComponent:this.listSlotHtmlComponent,emptyText:this.emptyText}));var{groupedNodes:s,nodes:i}=t.selectedNodes;const n=new TreeselectInput({value:this.grouped?s:i,showTags:this.showTags,tagsCountText:this.tagsCountText,clearable:this.clearable,isAlwaysOpened:this.alwaysOpen,searchable:this.searchable,placeholder:this.placeholder,disabled:this.disabled});return this.appendToBody&&(this.#containerResizer=new ResizeObserver(()=>{this.updateListPosition()})),n.srcElement.addEventListener("input",e=>{e=getOnlyIds(e.detail),t.updateValue(e),e=t.selectedNodes.nodes;this.value=getOnlyIds(e),this.#emitInput()}),n.srcElement.addEventListener("open",()=>this.#openList()),n.srcElement.addEventListener("keydown",e=>{this.isListOpened&&t.callKeyAction(e.key)}),n.srcElement.addEventListener("search",e=>{t.updateSearchValue(e.detail),this.updateListPosition()}),n.srcElement.addEventListener("focus",()=>{this.#updateFocusClasses(!0),document.addEventListener("mousedown",this.#focusEvent,!0),document.addEventListener("focus",this.#focusEvent,!0),window.addEventListener("blur",this.#blurEvent)},!0),this.alwaysOpen||n.srcElement.addEventListener("close",()=>{this.#closeList()}),t.srcElement.addEventListener("mouseup",()=>{n.focus()},!0),t.srcElement.addEventListener("input",e=>{var{groupedNodes:e,nodes:t}=e.detail,e=this.grouped?e:t;n.updateValue(e),this.value=getOnlyIds(t),n.focus(),this.#emitInput()}),t.srcElement.addEventListener("arrow-click",()=>{n.focus(),this.updateListPosition()}),this.#htmlContainer=e,this.#treeselectList=t,this.#treeselectInput=n,e.append(n.srcElement),e}#openList(){this.isListOpened=!0,window.addEventListener("scroll",this.#scrollEvent,!0),window.addEventListener("resize",this.#resizeEvent),this.appendToBody?(document.body.appendChild(this.#treeselectList.srcElement),this.#containerResizer.observe(this.#htmlContainer)):this.#htmlContainer.appendChild(this.#treeselectList.srcElement),this.updateListPosition(),this.#updateOpenCloseClasses(!0),this.#treeselectList.focusFirstListElement()}#closeList(){this.isListOpened=!1,window.removeEventListener("scroll",this.#scrollEvent,!0),window.removeEventListener("resize",this.#resizeEvent),(this.appendToBody?document.body:this.#htmlContainer).contains(this.#treeselectList.srcElement)&&(this.appendToBody?(document.body.removeChild(this.#treeselectList.srcElement),this.#containerResizer?.disconnect()):this.#htmlContainer.removeChild(this.#treeselectList.srcElement),this.#updateOpenCloseClasses(!1))}#updateDirectionClasses(e,t){var s=t?"treeselect-list--top-to-body":"treeselect-list--top",t=t?"treeselect-list--bottom-to-body":"treeselect-list--bottom";e?(this.#treeselectList.srcElement.classList.add(s),this.#treeselectList.srcElement.classList.remove(t),this.#treeselectInput.srcElement.classList.add("treeselect-input--top"),this.#treeselectInput.srcElement.classList.remove("treeselect-input--bottom")):(this.#treeselectList.srcElement.classList.remove(s),this.#treeselectList.srcElement.classList.add(t),this.#treeselectInput.srcElement.classList.remove("treeselect-input--top"),this.#treeselectInput.srcElement.classList.add("treeselect-input--bottom"))}#updateFocusClasses(e){e?(this.#treeselectInput.srcElement.classList.add("treeselect-input--focused"),this.#treeselectList.srcElement.classList.add("treeselect-list--focused")):(this.#treeselectInput.srcElement.classList.remove("treeselect-input--focused"),this.#treeselectList.srcElement.classList.remove("treeselect-list--focused"))}#updateOpenCloseClasses(e){e?this.#treeselectInput.srcElement.classList.add("treeselect-input--opened"):this.#treeselectInput.srcElement.classList.remove("treeselect-input--opened"),this.staticList?this.#treeselectList.srcElement.classList.add("treeselect-list--static"):this.#treeselectList.srcElement.classList.remove("treeselect-list--static")}#removeOutsideListeners(e){this.alwaysOpen&&!e||(window.removeEventListener("scroll",this.#scrollEvent,!0),window.removeEventListener("resize",this.#resizeEvent)),document.removeEventListener("click",this.#focusEvent,!0),document.removeEventListener("focus",this.#focusEvent,!0),window.removeEventListener("blur",this.#blurEvent)}scrollWindowHandler(){this.updateListPosition()}focusWindowHandler(e){this.#htmlContainer.contains(e.target)||this.#treeselectList.srcElement.contains(e.target)||(this.#treeselectInput.blur(),this.#removeOutsideListeners(),this.#updateFocusClasses(!1))}blurWindowHandler(){this.#treeselectInput.blur(),this.#removeOutsideListeners(),this.#updateFocusClasses(!1)}updateListPosition(){const e=this.#treeselectList.srcElement,t=(e.style.transform=null,this.#htmlContainer);var{y:s,height:i}=e.getBoundingClientRect(),{x:n,y:l,height:r,width:o}=t.getBoundingClientRect(),c=window.innerHeight-l-r,c=c<l&&i<=l&&c<i,i=(this.appendToBody&&(e.style.transform=c?`translateY(${l-s-i}px)`:`translateY(${l+r-s}px)`,e.style.width=o+"px",e.style.left=n+window.scrollX+"px"),c?"top":"bottom");e.getAttribute("direction")!==i&&(e.setAttribute("direction",i),this.#updateDirectionClasses(c,this.appendToBody))}#emitInput(){this.srcElement.dispatchEvent(new CustomEvent("input",{detail:this.value}))}}export default Treeselect;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "treeselectjs",
3
- "version": "0.2.6",
3
+ "version": "0.2.9",
4
4
  "description": "Treeselect JS",
5
5
  "main": "dist/treeselect-js.js",
6
6
  "repository": {
package/src/input.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import svg from './svgIcons.js'
2
2
 
3
3
  class TreeselectInput {
4
- #htmlTagsSecton = null
4
+ #htmlTagsSection = null
5
5
  #htmlEditControl = null
6
6
  #htmlOperators = null
7
7
  #htmlArrow = null
@@ -11,6 +11,7 @@ class TreeselectInput {
11
11
  constructor ({
12
12
  value,
13
13
  showTags,
14
+ tagsCountText,
14
15
  clearable,
15
16
  isAlwaysOpened,
16
17
  searchable,
@@ -20,6 +21,7 @@ class TreeselectInput {
20
21
  this.value = value
21
22
 
22
23
  this.showTags = showTags ?? true
24
+ this.tagsCountText = tagsCountText ?? 'elements selected'
23
25
  this.searchable = searchable ?? true
24
26
  this.placeholder = placeholder ?? 'Search...'
25
27
  this.clearable = clearable ?? true
@@ -77,12 +79,12 @@ class TreeselectInput {
77
79
  }
78
80
 
79
81
  #updateTags () {
80
- this.#htmlTagsSecton.innerHTML = ''
82
+ this.#htmlTagsSection.innerHTML = ''
81
83
 
82
84
  if (this.showTags) {
83
- this.#htmlTagsSecton.append(...this.#createTags())
85
+ this.#htmlTagsSection.append(...this.#createTags())
84
86
  } else {
85
- this.#htmlTagsSecton.appendChild(this.#createCountElement())
87
+ this.#htmlTagsSection.appendChild(this.#createCountElement())
86
88
  }
87
89
  }
88
90
 
@@ -142,7 +144,7 @@ class TreeselectInput {
142
144
  container.classList.add('treeselect-input')
143
145
  container.setAttribute('tabindex', '-1')
144
146
 
145
- this.#htmlTagsSecton = this.#createTagsSection()
147
+ this.#htmlTagsSection = this.#createTagsSection()
146
148
  this.#htmlEditControl = this.#createControl()
147
149
  this.#htmlOperators = this.#createOperators()
148
150
 
@@ -156,7 +158,7 @@ class TreeselectInput {
156
158
  this.focus()
157
159
  })
158
160
 
159
- container.append(this.#htmlTagsSecton, this.#htmlEditControl, this.#htmlOperators)
161
+ container.append(this.#htmlTagsSection, this.#htmlEditControl, this.#htmlOperators)
160
162
 
161
163
  return container
162
164
  }
@@ -221,7 +223,7 @@ class TreeselectInput {
221
223
 
222
224
  countEl.innerHTML = this.value.length === 1
223
225
  ? this.value[0].name
224
- : `${this.value.length} elements selected`
226
+ : `${this.value.length} ${this.tagsCountText}`
225
227
 
226
228
  return countEl
227
229
  }
package/src/list.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import svg from './svgIcons.js'
2
2
 
3
- const getFlatOptons = (options, openLevel, groupId = 0, level = 0) => {
3
+ const getFlatOptions = (options, openLevel, groupId = 0, level = 0) => {
4
4
  return options.reduce((acc, curr) => {
5
5
  const isGroup = !!curr.children?.length
6
6
  const isClosed = level >= openLevel && isGroup
@@ -8,7 +8,7 @@ const getFlatOptons = (options, openLevel, groupId = 0, level = 0) => {
8
8
  acc.push({ id: curr.value, name: curr.name, childOf: groupId, isGroup, checked: false, level, isClosed, hidden })
9
9
 
10
10
  if (isGroup) {
11
- const children = getFlatOptons(curr.children, openLevel, curr.value, level + 1)
11
+ const children = getFlatOptions(curr.children, openLevel, curr.value, level + 1)
12
12
  acc.push(...children)
13
13
  }
14
14
 
@@ -20,6 +20,7 @@ const checkAllChildrenInputs = ({ id, checked }, flatOptions) => {
20
20
  flatOptions.forEach(option => {
21
21
  if (option.childOf === id) {
22
22
  option.checked = checked
23
+ option.isPartialChecked = false
23
24
 
24
25
  if (option.isGroup) {
25
26
  checkAllChildrenInputs(option, flatOptions)
@@ -190,13 +191,13 @@ const getAllFlattedChildren = (childOf, flattedOption) => {
190
191
  }, [])
191
192
  }
192
193
 
193
- const getAllFlattendParents = (childOf, flatOptions) => {
194
+ const getAllFlattenParents = (childOf, flatOptions) => {
194
195
  return flatOptions.reduce((acc, curr) => {
195
196
  if (curr.id === childOf) {
196
197
  acc.push(curr)
197
198
 
198
199
  if (curr.childOf) {
199
- acc.push(...getAllFlattendParents(curr.childOf, flatOptions))
200
+ acc.push(...getAllFlattenParents(curr.childOf, flatOptions))
200
201
  }
201
202
  }
202
203
 
@@ -252,7 +253,7 @@ const validateOptions = (flattedOption) => {
252
253
 
253
254
 
254
255
  if (duplications.length) {
255
- console.error(`You have duplicated values: ${duplications.join(', ')}! You should use unique values.`)
256
+ console.error(`Validation: You have duplicated values: ${duplications.join(', ')}! You should use unique values.`)
256
257
  }
257
258
  }
258
259
 
@@ -274,9 +275,9 @@ class TreeselectList {
274
275
  this.listSlotHtmlComponent = listSlotHtmlComponent
275
276
  this.emptyText = emptyText ?? 'No results found...'
276
277
 
277
- this.flattedOptions = getFlatOptons(this.options, this.openLevel)
278
+ this.flattedOptions = getFlatOptions(this.options, this.openLevel)
278
279
  this.flattedOptionsBeforeSearch = this.flattedOptions
279
- this.selectedNodes = { ids: [], groupedIds: [] }
280
+ this.selectedNodes = { nodes: [], groupedNodes: [] }
280
281
  this.srcElement = this.#createList()
281
282
 
282
283
  this.updateValue(this.value)
@@ -290,6 +291,10 @@ class TreeselectList {
290
291
  }
291
292
 
292
293
  updateSearchValue (searchText) {
294
+ if (searchText === this.searchText) {
295
+ return
296
+ }
297
+
293
298
  const isStartOfSearching = this.searchText === '' && searchText !== ''
294
299
  this.searchText = searchText
295
300
 
@@ -315,9 +320,9 @@ class TreeselectList {
315
320
  }
316
321
 
317
322
  const allOptions = this.flattedOptions.reduce((acc, curr) => {
318
- const isSerched = curr.name.toLowerCase().includes(searchText.toLowerCase())
323
+ const isSearched = curr.name.toLowerCase().includes(searchText.toLowerCase())
319
324
 
320
- if (isSerched) {
325
+ if (isSearched) {
321
326
  acc.push(curr)
322
327
 
323
328
  if (curr.isGroup) {
@@ -326,7 +331,7 @@ class TreeselectList {
326
331
  }
327
332
 
328
333
  if (curr.childOf) {
329
- const flattedParents = getAllFlattendParents(curr.childOf, this.flattedOptions)
334
+ const flattedParents = getAllFlattenParents(curr.childOf, this.flattedOptions)
330
335
  acc.push(...flattedParents)
331
336
  }
332
337
  }
@@ -432,8 +437,8 @@ class TreeselectList {
432
437
  }
433
438
 
434
439
  focusFirstListElement () {
435
- const focusedCalss = 'treeselect-list__item--focused'
436
- const itemFocused = this.srcElement.querySelector(`.${focusedCalss}`)
440
+ const focusedClass = 'treeselect-list__item--focused'
441
+ const itemFocused = this.srcElement.querySelector(`.${focusedClass}`)
437
442
  const allCheckboxes = Array.from(this.srcElement.querySelectorAll('.treeselect-list__item-checkbox'))
438
443
  .filter(checkbox => window.getComputedStyle(getListItemByCheckbox(checkbox)).display !== 'none')
439
444
 
@@ -442,11 +447,11 @@ class TreeselectList {
442
447
  }
443
448
 
444
449
  if (itemFocused) {
445
- itemFocused.classList.remove(focusedCalss)
450
+ itemFocused.classList.remove(focusedClass)
446
451
  }
447
452
 
448
453
  const firstItem = getListItemByCheckbox(allCheckboxes[0])
449
- firstItem.classList.add(focusedCalss)
454
+ firstItem.classList.add(focusedClass)
450
455
  }
451
456
 
452
457
  // Private methods
@@ -628,7 +633,7 @@ class TreeselectList {
628
633
  hideShowChildren(this.flattedOptions, flattedOption)
629
634
  updateDOM(this.flattedOptions, this.srcElement)
630
635
 
631
- this.#emitArrrowClick()
636
+ this.#emitArrowClick()
632
637
  }
633
638
 
634
639
  #groupMouseAction (isMouseOver, itemElement) {
@@ -649,13 +654,13 @@ class TreeselectList {
649
654
 
650
655
  #updateSelectedNodes () {
651
656
  this.selectedNodes = {
652
- ids: getCheckedValues(this.flattedOptions),
653
- groupedIds: getGroupedValues(this.flattedOptions)
657
+ nodes: getCheckedValues(this.flattedOptions),
658
+ groupedNodes: getGroupedValues(this.flattedOptions)
654
659
  }
655
660
  }
656
661
 
657
662
  // Emits
658
- #emitArrrowClick () {
663
+ #emitArrowClick () {
659
664
  this.srcElement.dispatchEvent(new CustomEvent('arrow-click'))
660
665
  }
661
666
 
@@ -16,13 +16,17 @@
16
16
  left: 0;
17
17
  border-radius: 4px;
18
18
  box-sizing: border-box;
19
- z-index: 1050;
19
+ z-index: 1000;
20
20
  }
21
21
 
22
22
  .treeselect .treeselect-list {
23
23
  position: absolute;
24
24
  }
25
25
 
26
+ .treeselect .treeselect-list--static {
27
+ position: static;
28
+ }
29
+
26
30
  .treeselect-input--focused {
27
31
  border-color: #101010;
28
32
  }
@@ -1,6 +1,19 @@
1
1
  import TreeselectInput from "./input.js"
2
2
  import TreeselectList from "./list.js"
3
3
 
4
+
5
+ const validateProps = ({ parentHtmlContainer, staticList, appendToBody }) => {
6
+ if (!parentHtmlContainer) {
7
+ console.error('Validation: parentHtmlContainer prop is required!')
8
+ }
9
+
10
+ if (staticList && appendToBody) {
11
+ console.error('Validation: You should set staticList to false if you use appendToBody!')
12
+ }
13
+ }
14
+
15
+ const getOnlyIds = (nodes) => nodes.map(node => node.id)
16
+
4
17
  class Treeselect {
5
18
  // Components
6
19
  #htmlContainer = null
@@ -12,6 +25,7 @@ class Treeselect {
12
25
 
13
26
  // Outside listeners
14
27
  #scrollEvent = null
28
+ #resizeEvent = null
15
29
  #focusEvent = null
16
30
  #blurEvent = null
17
31
 
@@ -23,14 +37,22 @@ class Treeselect {
23
37
  appendToBody,
24
38
  alwaysOpen,
25
39
  showTags,
40
+ tagsCountText,
26
41
  clearable,
27
42
  searchable,
28
43
  placeholder,
29
44
  grouped,
30
45
  listSlotHtmlComponent,
31
46
  disabled,
32
- emptyText
47
+ emptyText,
48
+ staticList
33
49
  }) {
50
+ validateProps({
51
+ parentHtmlContainer,
52
+ staticList,
53
+ appendToBody
54
+ })
55
+
34
56
  this.parentHtmlContainer = parentHtmlContainer
35
57
  this.value = value ?? []
36
58
  this.options = options ?? []
@@ -38,6 +60,7 @@ class Treeselect {
38
60
  this.appendToBody = appendToBody ?? true
39
61
  this.alwaysOpen = alwaysOpen && !disabled
40
62
  this.showTags = showTags ?? true
63
+ this.tagsCountText = tagsCountText ?? 'elements selected'
41
64
  this.clearable = clearable ?? true
42
65
  this.searchable = searchable ?? true
43
66
  this.placeholder = placeholder ?? 'Search...'
@@ -45,7 +68,10 @@ class Treeselect {
45
68
  this.listSlotHtmlComponent = listSlotHtmlComponent ?? null
46
69
  this.disabled = disabled ?? false
47
70
  this.emptyText = emptyText ?? 'No results found...'
71
+ this.staticList = staticList && !this.appendToBody
48
72
 
73
+ // State
74
+ this.isListOpened = false
49
75
  this.srcElement = null
50
76
 
51
77
  this.mount()
@@ -58,6 +84,7 @@ class Treeselect {
58
84
  this.srcElement = this.#createTreeselect()
59
85
 
60
86
  this.#scrollEvent = this.scrollWindowHandler.bind(this)
87
+ this.#resizeEvent = this.scrollWindowHandler.bind(this)
61
88
  this.#focusEvent = this.focusWindowHandler.bind(this)
62
89
  this.#blurEvent = this.blurWindowHandler.bind(this)
63
90
 
@@ -73,8 +100,8 @@ class Treeselect {
73
100
  updateValue (newValue) {
74
101
  const list = this.#treeselectList
75
102
  list.updateValue(newValue)
76
- const {groupedIds, ids } = list.selectedNodes
77
- const inputNewValue = this.grouped ? groupedIds : ids
103
+ const {groupedNodes, nodes } = list.selectedNodes
104
+ const inputNewValue = this.grouped ? groupedNodes : nodes
78
105
  this.#treeselectInput.updateValue(inputNewValue)
79
106
  }
80
107
 
@@ -87,6 +114,19 @@ class Treeselect {
87
114
  }
88
115
  }
89
116
 
117
+ focus () {
118
+ if (this.#treeselectInput) {
119
+ this.#treeselectInput.focus()
120
+ }
121
+ }
122
+
123
+ toggleOpenClose () {
124
+ if (this.#treeselectInput) {
125
+ this.#treeselectInput.openClose()
126
+ this.#treeselectInput.focus()
127
+ }
128
+ }
129
+
90
130
  #createTreeselect () {
91
131
  const container = this.parentHtmlContainer
92
132
  container.classList.add('treeselect')
@@ -99,10 +139,11 @@ class Treeselect {
99
139
  emptyText: this.emptyText
100
140
  })
101
141
 
102
- const {groupedIds, ids } = list.selectedNodes
142
+ const {groupedNodes, nodes } = list.selectedNodes
103
143
  const input = new TreeselectInput({
104
- value: this.grouped ? groupedIds : ids,
144
+ value: this.grouped ? groupedNodes : nodes,
105
145
  showTags: this.showTags,
146
+ tagsCountText: this.tagsCountText,
106
147
  clearable: this.clearable,
107
148
  isAlwaysOpened: this.alwaysOpen,
108
149
  searchable: this.searchable,
@@ -118,13 +159,18 @@ class Treeselect {
118
159
 
119
160
  // Input events
120
161
  input.srcElement.addEventListener('input', (e) => {
121
- const ids = e.detail.map(({ id }) => id)
122
- this.value = ids
123
- list.updateValue(ids)
162
+ const inputIds = getOnlyIds(e.detail)
163
+ list.updateValue(inputIds)
164
+ const { nodes } = list.selectedNodes
165
+ this.value = getOnlyIds(nodes)
124
166
  this.#emitInput()
125
167
  })
126
168
  input.srcElement.addEventListener('open', () => this.#openList())
127
- input.srcElement.addEventListener('keydown', (e) => list.callKeyAction(e.key))
169
+ input.srcElement.addEventListener('keydown', (e) => {
170
+ if (this.isListOpened) {
171
+ list.callKeyAction(e.key)
172
+ }
173
+ })
128
174
  input.srcElement.addEventListener('search', (e) => {
129
175
  list.updateSearchValue(e.detail)
130
176
  this.updateListPosition()
@@ -147,10 +193,10 @@ class Treeselect {
147
193
  input.focus()
148
194
  }, true)
149
195
  list.srcElement.addEventListener('input', (e) => {
150
- const {groupedIds, ids } = e.detail
151
- const inputIds = this.grouped ? groupedIds : ids
196
+ const {groupedNodes, nodes } = e.detail
197
+ const inputIds = this.grouped ? groupedNodes : nodes
152
198
  input.updateValue(inputIds)
153
- this.value = ids.map(({ id }) => id)
199
+ this.value = getOnlyIds(nodes)
154
200
  input.focus()
155
201
  this.#emitInput()
156
202
  })
@@ -169,7 +215,10 @@ class Treeselect {
169
215
  }
170
216
 
171
217
  #openList () {
218
+ this.isListOpened = true
219
+
172
220
  window.addEventListener('scroll', this.#scrollEvent, true)
221
+ window.addEventListener('resize', this.#resizeEvent)
173
222
 
174
223
  if (this.appendToBody) {
175
224
  document.body.appendChild(this.#treeselectList.srcElement)
@@ -184,7 +233,10 @@ class Treeselect {
184
233
  }
185
234
 
186
235
  #closeList () {
236
+ this.isListOpened = false
237
+
187
238
  window.removeEventListener('scroll', this.#scrollEvent, true)
239
+ window.removeEventListener('resize', this.#resizeEvent)
188
240
  const isElementExist = this.appendToBody
189
241
  ? document.body.contains(this.#treeselectList.srcElement)
190
242
  : this.#htmlContainer.contains(this.#treeselectList.srcElement)
@@ -236,11 +288,18 @@ class Treeselect {
236
288
  } else {
237
289
  this.#treeselectInput.srcElement.classList.remove('treeselect-input--opened')
238
290
  }
291
+
292
+ if (this.staticList) {
293
+ this.#treeselectList.srcElement.classList.add('treeselect-list--static')
294
+ } else {
295
+ this.#treeselectList.srcElement.classList.remove('treeselect-list--static')
296
+ }
239
297
  }
240
298
 
241
299
  #removeOutsideListeners (isDestroy) {
242
300
  if (!this.alwaysOpen || isDestroy) {
243
301
  window.removeEventListener('scroll', this.#scrollEvent, true)
302
+ window.removeEventListener('resize', this.#resizeEvent)
244
303
  }
245
304
 
246
305
  document.removeEventListener('click', this.#focusEvent, true)
@@ -269,7 +328,7 @@ class Treeselect {
269
328
  this.#updateFocusClasses(false)
270
329
  }
271
330
 
272
- // Update direction of the list. Support appendToBody and standart mode with absolute
331
+ // Update direction of the list. Support appendToBody and standard mode with absolute
273
332
  updateListPosition () {
274
333
  const list = this.#treeselectList.srcElement
275
334
  // We need to reset position
@@ -289,10 +348,10 @@ class Treeselect {
289
348
  ? `translateY(${containerY - listY - listHeight}px)`
290
349
  : `translateY(${containerY + containerHeight - listY}px)`
291
350
  list.style.width = `${containerWidth}px`
292
- list.style.left = `${containerX}px`
351
+ list.style.left = `${containerX + window.scrollX}px`
293
352
  }
294
353
 
295
- const attributeToAdd = isTopDirection ? 'top' : 'buttom'
354
+ const attributeToAdd = isTopDirection ? 'top' : 'bottom'
296
355
  const currentAttr = list.getAttribute('direction')
297
356
 
298
357
  if (currentAttr !== attributeToAdd) {