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 +37 -30
- package/dist/input.js +1 -1
- package/dist/list.js +1 -1
- package/dist/treeselect-js.css +3 -3
- package/dist/treeselect-js.js +1 -1
- package/package.json +1 -1
- package/src/input.js +9 -7
- package/src/list.js +23 -18
- package/src/treeselect-js.css +5 -1
- package/src/treeselect-js.js +74 -15
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
|

|
|
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 '
|
|
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="
|
|
78
|
+
slot.innerHTML='<a class="treeselect-demo__slot" href="">Click!</a>'
|
|
77
79
|
|
|
78
|
-
const domElement = document.querySelector('.treeselect-
|
|
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
|
-
|
|
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) |
|
|
99
|
+
Name | Type (default) | Description
|
|
97
100
|
------------- | ------------- | -------------
|
|
98
|
-
parentHtmlContainer | HTMLElement | It
|
|
99
|
-
value | Array[String] ([]) |
|
|
100
|
-
options | Array[Object] ([]) | It is an array of objects { name: String, value: String, children: [] }
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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 |
|
|
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 |
|
|
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
|
|
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{#
|
|
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;
|
package/dist/treeselect-js.css
CHANGED
|
@@ -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:
|
|
3
|
-
.treeselect .treeselect-list{position:absolute}.treeselect-
|
|
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}
|
package/dist/treeselect-js.js
CHANGED
|
@@ -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:
|
|
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
package/src/input.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import svg from './svgIcons.js'
|
|
2
2
|
|
|
3
3
|
class TreeselectInput {
|
|
4
|
-
#
|
|
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.#
|
|
82
|
+
this.#htmlTagsSection.innerHTML = ''
|
|
81
83
|
|
|
82
84
|
if (this.showTags) {
|
|
83
|
-
this.#
|
|
85
|
+
this.#htmlTagsSection.append(...this.#createTags())
|
|
84
86
|
} else {
|
|
85
|
-
this.#
|
|
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.#
|
|
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.#
|
|
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}
|
|
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
|
|
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 =
|
|
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
|
|
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(...
|
|
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 =
|
|
278
|
+
this.flattedOptions = getFlatOptions(this.options, this.openLevel)
|
|
278
279
|
this.flattedOptionsBeforeSearch = this.flattedOptions
|
|
279
|
-
this.selectedNodes = {
|
|
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
|
|
323
|
+
const isSearched = curr.name.toLowerCase().includes(searchText.toLowerCase())
|
|
319
324
|
|
|
320
|
-
if (
|
|
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 =
|
|
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
|
|
436
|
-
const itemFocused = this.srcElement.querySelector(`.${
|
|
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(
|
|
450
|
+
itemFocused.classList.remove(focusedClass)
|
|
446
451
|
}
|
|
447
452
|
|
|
448
453
|
const firstItem = getListItemByCheckbox(allCheckboxes[0])
|
|
449
|
-
firstItem.classList.add(
|
|
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.#
|
|
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
|
-
|
|
653
|
-
|
|
657
|
+
nodes: getCheckedValues(this.flattedOptions),
|
|
658
|
+
groupedNodes: getGroupedValues(this.flattedOptions)
|
|
654
659
|
}
|
|
655
660
|
}
|
|
656
661
|
|
|
657
662
|
// Emits
|
|
658
|
-
#
|
|
663
|
+
#emitArrowClick () {
|
|
659
664
|
this.srcElement.dispatchEvent(new CustomEvent('arrow-click'))
|
|
660
665
|
}
|
|
661
666
|
|
package/src/treeselect-js.css
CHANGED
|
@@ -16,13 +16,17 @@
|
|
|
16
16
|
left: 0;
|
|
17
17
|
border-radius: 4px;
|
|
18
18
|
box-sizing: border-box;
|
|
19
|
-
z-index:
|
|
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
|
}
|
package/src/treeselect-js.js
CHANGED
|
@@ -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 {
|
|
77
|
-
const inputNewValue = this.grouped ?
|
|
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 {
|
|
142
|
+
const {groupedNodes, nodes } = list.selectedNodes
|
|
103
143
|
const input = new TreeselectInput({
|
|
104
|
-
value: this.grouped ?
|
|
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
|
|
122
|
-
|
|
123
|
-
list.
|
|
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) =>
|
|
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 {
|
|
151
|
-
const inputIds = this.grouped ?
|
|
196
|
+
const {groupedNodes, nodes } = e.detail
|
|
197
|
+
const inputIds = this.grouped ? groupedNodes : nodes
|
|
152
198
|
input.updateValue(inputIds)
|
|
153
|
-
this.value =
|
|
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
|
|
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' : '
|
|
354
|
+
const attributeToAdd = isTopDirection ? 'top' : 'bottom'
|
|
296
355
|
const currentAttr = list.getAttribute('direction')
|
|
297
356
|
|
|
298
357
|
if (currentAttr !== attributeToAdd) {
|