treeselectjs 0.2.5 → 0.2.8
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 +25 -19
- package/dist/input.js +1 -1
- package/dist/list.css +2 -2
- package/dist/list.js +1 -1
- package/dist/treeselect-js.css +4 -4
- package/dist/treeselect-js.js +1 -1
- package/package.json +1 -1
- package/src/input.js +6 -6
- package/src/list.css +4 -0
- package/src/list.js +44 -15
- package/src/treeselect-js.css +6 -2
- package/src/treeselect-js.js +78 -65
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,57 +75,61 @@ 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
|
|
101
|
+
parentHtmlContainer | HTMLElement (required!) | It should be a HTML element (div), it will be changed to the list container.
|
|
99
102
|
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.
|
|
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.
|
|
101
104
|
openLevel | Number (0) | All groups will be opened to this level.
|
|
102
105
|
appendToBody | Boolean (false) | List will be appended to the body instead of the input container.
|
|
103
|
-
alwaysOpen | Boolean (false) | List will be always opened.
|
|
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`.
|
|
104
107
|
showTags | Boolean (true) | Selected values look like tags. The false value shows results as '{count} elements selected'.
|
|
105
108
|
clearable | Boolean (true) | Clear icon is available.
|
|
106
109
|
searchable | Boolean (true) | Search is available.
|
|
107
110
|
placeholder | String ('Search...') | Placeholder text.
|
|
108
|
-
grouped | Boolean (true) | Show groups in the input and group
|
|
111
|
+
grouped | Boolean (true) | Show groups in the input and group leafs if all group selected.
|
|
109
112
|
listSlotHtmlComponent | HTMLElement (null) | It should be a HTML element, it will be append to the end of the list.
|
|
110
113
|
disabled | Boolean (false) | List will be disabled.
|
|
111
114
|
emptyText | String ('No results found...') | A empty list text.
|
|
115
|
+
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
116
|
|
|
113
117
|
### Emits
|
|
114
|
-
Name | Return Type |
|
|
118
|
+
Name | Return Type | Description
|
|
115
119
|
------------- | ------------- | -------------
|
|
116
120
|
input | Array[String] | Returns selected ids without groups, only leafs.
|
|
117
121
|
|
|
118
122
|
### Methods
|
|
119
|
-
Name | Params |
|
|
123
|
+
Name | Params | Description
|
|
120
124
|
------------- | ------------- | -------------
|
|
121
125
|
updateValue | Array[String] | Update selected values.
|
|
122
|
-
mount | None | Helps to remount and update settings.
|
|
123
|
-
destroy | None | Deletes elements from the DOM
|
|
126
|
+
mount | None | Helps to remount and update settings. Change settings that you need (treeselect.appendToBody = true), then call mount().
|
|
127
|
+
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)**.
|
|
124
128
|
|
|
125
129
|
### Notes
|
|
126
130
|
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**.
|
|
127
131
|
2) If you want to update props, set props to the entity of the class and then call **mount()** method.
|
|
128
132
|
3) Use **updateValue()** method to update only the value.
|
|
129
|
-
4) If you need to delete List from the DOM when you don't need treeselect anymore - call **destroy()**.
|
|
133
|
+
4) If you need to delete List from the DOM when you don't need treeselect anymore - call **destroy()**.
|
|
134
|
+
5) Do not use **duplicated** values for the options. You will see a error with duplicated values.
|
|
135
|
+
6) **Value** inside the **options** prop should be a **String**.
|
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: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.#htmlTagsSection.innerHTML="",this.showTags?this.#htmlTagsSection.append(...this.#createTags()):this.#htmlTagsSection.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.#htmlTagsSection=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.#htmlTagsSection,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;
|
package/dist/list.css
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.treeselect-list{width:100%;box-sizing:border-box;border:1px solid #d7dde4;overflow-y:auto;background-color:#fff;max-height:300px}
|
|
2
2
|
.treeselect-list__group-container{box-sizing:border-box}.treeselect-list__item{display:flex;align-items:center;box-sizing:border-box;cursor:pointer;height:30px}
|
|
3
|
-
.treeselect-list__item--focused{background-color:#f0ffff !important}
|
|
4
|
-
.treeselect-list__item-icon{display:flex;align-items:center;cursor:pointer;height:20px;width:20px;min-width:20px}
|
|
3
|
+
.treeselect-list__item:focus{outline:0}.treeselect-list__item--focused{background-color:#f0ffff !important}
|
|
4
|
+
.treeselect-list__item--hidden{display:none}.treeselect-list__item-icon{display:flex;align-items:center;cursor:pointer;height:20px;width:20px;min-width:20px}
|
|
5
5
|
.treeselect-list__item-icon svg{pointer-events:none;width:100%;height:100%;stroke:#c5c7cb}
|
|
6
6
|
.treeselect-list__item-icon:hover svg{stroke:#838790}.treeselect-list__item-checkbox-container{width:20px;height:20px;min-width:20px;border:1px solid #d7dde4;border-radius:3px;position:relative;background-color:#fff;pointer-events:none;box-sizing:border-box}
|
|
7
7
|
.treeselect-list__item-checkbox-container svg{position:absolute;height:100%;width:100%}
|
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);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};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)}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={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){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 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.#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={ids:getCheckedValues(this.flattedOptions),groupedIds: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,8 +1,8 @@
|
|
|
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
|
-
.treeselect-list--bottom,.treeselect-list--bottom-to-body{border-top-color
|
|
7
|
+
.treeselect-list--bottom,.treeselect-list--bottom-to-body{border-top-color:#d7dde4;border-top-left-radius:0;border-top-right-radius:0}
|
|
8
8
|
.treeselect-list--top{left:0;bottom:100%}.treeselect-list--bottom{left:0;top:100%}
|
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;#
|
|
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!")};class Treeselect{#htmlContainer=null;#treeselectList=null;#treeselectInput=null;#containerResizer=null;#scrollEvent=null;#resizeEvent=null;#focusEvent=null;#blurEvent=null;#isListOpened=!1;constructor({parentHtmlContainer:e,value:t,options:s,openLevel:i,appendToBody:n,alwaysOpen:l,showTags:r,clearable:o,searchable:a,placeholder:c,grouped:d,listSlotHtmlComponent:h,disabled:p,emptyText:u,staticList:m}){validateProps({parentHtmlContainer:e,staticList:m,appendToBody:n}),this.parentHtmlContainer=e,this.value=t??[],this.options=s??[],this.openLevel=i??0,this.appendToBody=n??!0,this.alwaysOpen=l&&!p,this.showTags=r??!0,this.clearable=o??!0,this.searchable=a??!0,this.placeholder=c??"Search...",this.grouped=d??!0,this.listSlotHtmlComponent=h??null,this.disabled=p??!1,this.emptyText=u??"No results found...",this.staticList=m&&!this.appendToBody,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{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 n=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()})),n.srcElement.addEventListener("input",e=>{e=e.detail.map(({id:e})=>e);this.value=e,t.updateValue(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=>{const{groupedIds:t,ids:s}=e.detail;e=this.grouped?t:s;n.updateValue(e),this.value=s.map(({id:e})=>e),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(),a=window.innerHeight-l-r,a=a<l&&i<=l&&a<i,i=(this.appendToBody&&(e.style.transform=a?`translateY(${l-s-i}px)`:`translateY(${l+r-s}px)`,e.style.width=o+"px",e.style.left=n+window.scrollX+"px"),a?"top":"bottom");e.getAttribute("direction")!==i&&(e.setAttribute("direction",i),this.#updateDirectionClasses(a,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
|
|
@@ -77,12 +77,12 @@ class TreeselectInput {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
#updateTags () {
|
|
80
|
-
this.#
|
|
80
|
+
this.#htmlTagsSection.innerHTML = ''
|
|
81
81
|
|
|
82
82
|
if (this.showTags) {
|
|
83
|
-
this.#
|
|
83
|
+
this.#htmlTagsSection.append(...this.#createTags())
|
|
84
84
|
} else {
|
|
85
|
-
this.#
|
|
85
|
+
this.#htmlTagsSection.appendChild(this.#createCountElement())
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -142,7 +142,7 @@ class TreeselectInput {
|
|
|
142
142
|
container.classList.add('treeselect-input')
|
|
143
143
|
container.setAttribute('tabindex', '-1')
|
|
144
144
|
|
|
145
|
-
this.#
|
|
145
|
+
this.#htmlTagsSection = this.#createTagsSection()
|
|
146
146
|
this.#htmlEditControl = this.#createControl()
|
|
147
147
|
this.#htmlOperators = this.#createOperators()
|
|
148
148
|
|
|
@@ -156,7 +156,7 @@ class TreeselectInput {
|
|
|
156
156
|
this.focus()
|
|
157
157
|
})
|
|
158
158
|
|
|
159
|
-
container.append(this.#
|
|
159
|
+
container.append(this.#htmlTagsSection, this.#htmlEditControl, this.#htmlOperators)
|
|
160
160
|
|
|
161
161
|
return container
|
|
162
162
|
}
|
package/src/list.css
CHANGED
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)
|
|
@@ -67,7 +68,10 @@ const checkInput = ({ id, isGroup, childOf, checked }, flatOptions) => {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
const updateValue = (newValue, flatOptions, srcElement) => {
|
|
70
|
-
flatOptions.forEach(option =>
|
|
71
|
+
flatOptions.forEach(option => {
|
|
72
|
+
option.checked = false
|
|
73
|
+
option.isPartialChecked = false
|
|
74
|
+
})
|
|
71
75
|
const toCheck = flatOptions.filter(option => newValue.includes(option.id))
|
|
72
76
|
toCheck.forEach(option => {
|
|
73
77
|
option.checked = true
|
|
@@ -187,13 +191,13 @@ const getAllFlattedChildren = (childOf, flattedOption) => {
|
|
|
187
191
|
}, [])
|
|
188
192
|
}
|
|
189
193
|
|
|
190
|
-
const
|
|
194
|
+
const getAllFlattenParents = (childOf, flatOptions) => {
|
|
191
195
|
return flatOptions.reduce((acc, curr) => {
|
|
192
196
|
if (curr.id === childOf) {
|
|
193
197
|
acc.push(curr)
|
|
194
198
|
|
|
195
199
|
if (curr.childOf) {
|
|
196
|
-
acc.push(...
|
|
200
|
+
acc.push(...getAllFlattenParents(curr.childOf, flatOptions))
|
|
197
201
|
}
|
|
198
202
|
}
|
|
199
203
|
|
|
@@ -233,6 +237,26 @@ const getListItemByCheckbox = (checkbox) => {
|
|
|
233
237
|
return listItem
|
|
234
238
|
}
|
|
235
239
|
|
|
240
|
+
const validateOptions = (flattedOption) => {
|
|
241
|
+
const { duplications } = flattedOption.reduce((acc, curr) => {
|
|
242
|
+
if (acc.allItems.includes(curr.id)) {
|
|
243
|
+
acc.duplications.push(curr.id)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
acc.allItems.push(curr.id)
|
|
247
|
+
|
|
248
|
+
return acc
|
|
249
|
+
}, {
|
|
250
|
+
duplications: [],
|
|
251
|
+
allItems: []
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
if (duplications.length) {
|
|
256
|
+
console.error(`Validation: You have duplicated values: ${duplications.join(', ')}! You should use unique values.`)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
236
260
|
class TreeselectList {
|
|
237
261
|
#lastFocusedItem = null
|
|
238
262
|
#isMouseActionsAvailable = true
|
|
@@ -251,12 +275,13 @@ class TreeselectList {
|
|
|
251
275
|
this.listSlotHtmlComponent = listSlotHtmlComponent
|
|
252
276
|
this.emptyText = emptyText ?? 'No results found...'
|
|
253
277
|
|
|
254
|
-
this.flattedOptions =
|
|
278
|
+
this.flattedOptions = getFlatOptions(this.options, this.openLevel)
|
|
255
279
|
this.flattedOptionsBeforeSearch = this.flattedOptions
|
|
256
280
|
this.selectedNodes = { ids: [], groupedIds: [] }
|
|
257
281
|
this.srcElement = this.#createList()
|
|
258
282
|
|
|
259
283
|
this.updateValue(this.value)
|
|
284
|
+
validateOptions(this.flattedOptions)
|
|
260
285
|
}
|
|
261
286
|
|
|
262
287
|
// Public methods
|
|
@@ -266,6 +291,10 @@ class TreeselectList {
|
|
|
266
291
|
}
|
|
267
292
|
|
|
268
293
|
updateSearchValue (searchText) {
|
|
294
|
+
if (searchText === this.searchText) {
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
|
|
269
298
|
const isStartOfSearching = this.searchText === '' && searchText !== ''
|
|
270
299
|
this.searchText = searchText
|
|
271
300
|
|
|
@@ -291,9 +320,9 @@ class TreeselectList {
|
|
|
291
320
|
}
|
|
292
321
|
|
|
293
322
|
const allOptions = this.flattedOptions.reduce((acc, curr) => {
|
|
294
|
-
const
|
|
323
|
+
const isSearched = curr.name.toLowerCase().includes(searchText.toLowerCase())
|
|
295
324
|
|
|
296
|
-
if (
|
|
325
|
+
if (isSearched) {
|
|
297
326
|
acc.push(curr)
|
|
298
327
|
|
|
299
328
|
if (curr.isGroup) {
|
|
@@ -302,7 +331,7 @@ class TreeselectList {
|
|
|
302
331
|
}
|
|
303
332
|
|
|
304
333
|
if (curr.childOf) {
|
|
305
|
-
const flattedParents =
|
|
334
|
+
const flattedParents = getAllFlattenParents(curr.childOf, this.flattedOptions)
|
|
306
335
|
acc.push(...flattedParents)
|
|
307
336
|
}
|
|
308
337
|
}
|
|
@@ -408,8 +437,8 @@ class TreeselectList {
|
|
|
408
437
|
}
|
|
409
438
|
|
|
410
439
|
focusFirstListElement () {
|
|
411
|
-
const
|
|
412
|
-
const itemFocused = this.srcElement.querySelector(`.${
|
|
440
|
+
const focusedClass = 'treeselect-list__item--focused'
|
|
441
|
+
const itemFocused = this.srcElement.querySelector(`.${focusedClass}`)
|
|
413
442
|
const allCheckboxes = Array.from(this.srcElement.querySelectorAll('.treeselect-list__item-checkbox'))
|
|
414
443
|
.filter(checkbox => window.getComputedStyle(getListItemByCheckbox(checkbox)).display !== 'none')
|
|
415
444
|
|
|
@@ -418,11 +447,11 @@ class TreeselectList {
|
|
|
418
447
|
}
|
|
419
448
|
|
|
420
449
|
if (itemFocused) {
|
|
421
|
-
itemFocused.classList.remove(
|
|
450
|
+
itemFocused.classList.remove(focusedClass)
|
|
422
451
|
}
|
|
423
452
|
|
|
424
453
|
const firstItem = getListItemByCheckbox(allCheckboxes[0])
|
|
425
|
-
firstItem.classList.add(
|
|
454
|
+
firstItem.classList.add(focusedClass)
|
|
426
455
|
}
|
|
427
456
|
|
|
428
457
|
// Private methods
|
|
@@ -604,7 +633,7 @@ class TreeselectList {
|
|
|
604
633
|
hideShowChildren(this.flattedOptions, flattedOption)
|
|
605
634
|
updateDOM(this.flattedOptions, this.srcElement)
|
|
606
635
|
|
|
607
|
-
this.#
|
|
636
|
+
this.#emitArrowClick()
|
|
608
637
|
}
|
|
609
638
|
|
|
610
639
|
#groupMouseAction (isMouseOver, itemElement) {
|
|
@@ -631,7 +660,7 @@ class TreeselectList {
|
|
|
631
660
|
}
|
|
632
661
|
|
|
633
662
|
// Emits
|
|
634
|
-
#
|
|
663
|
+
#emitArrowClick () {
|
|
635
664
|
this.srcElement.dispatchEvent(new CustomEvent('arrow-click'))
|
|
636
665
|
}
|
|
637
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
|
}
|
|
@@ -52,7 +56,7 @@
|
|
|
52
56
|
|
|
53
57
|
.treeselect-list--bottom,
|
|
54
58
|
.treeselect-list--bottom-to-body {
|
|
55
|
-
border-top-color:
|
|
59
|
+
border-top-color: #d7dde4;
|
|
56
60
|
border-top-left-radius: 0;
|
|
57
61
|
border-top-right-radius: 0;
|
|
58
62
|
}
|
package/src/treeselect-js.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
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
|
+
|
|
4
15
|
class Treeselect {
|
|
5
16
|
// Components
|
|
6
17
|
#htmlContainer = null
|
|
@@ -8,16 +19,17 @@ class Treeselect {
|
|
|
8
19
|
#treeselectInput = null
|
|
9
20
|
|
|
10
21
|
// Resize props
|
|
11
|
-
#transform = { top: null, bottom: null }
|
|
12
|
-
#treeselectInitPosition = null
|
|
13
22
|
#containerResizer = null
|
|
14
|
-
#containerWidth = 0
|
|
15
23
|
|
|
16
24
|
// Outside listeners
|
|
17
25
|
#scrollEvent = null
|
|
26
|
+
#resizeEvent = null
|
|
18
27
|
#focusEvent = null
|
|
19
28
|
#blurEvent = null
|
|
20
29
|
|
|
30
|
+
// State
|
|
31
|
+
#isListOpened = false
|
|
32
|
+
|
|
21
33
|
constructor ({
|
|
22
34
|
parentHtmlContainer,
|
|
23
35
|
value,
|
|
@@ -32,8 +44,15 @@ class Treeselect {
|
|
|
32
44
|
grouped,
|
|
33
45
|
listSlotHtmlComponent,
|
|
34
46
|
disabled,
|
|
35
|
-
emptyText
|
|
47
|
+
emptyText,
|
|
48
|
+
staticList
|
|
36
49
|
}) {
|
|
50
|
+
validateProps({
|
|
51
|
+
parentHtmlContainer,
|
|
52
|
+
staticList,
|
|
53
|
+
appendToBody
|
|
54
|
+
})
|
|
55
|
+
|
|
37
56
|
this.parentHtmlContainer = parentHtmlContainer
|
|
38
57
|
this.value = value ?? []
|
|
39
58
|
this.options = options ?? []
|
|
@@ -48,6 +67,7 @@ class Treeselect {
|
|
|
48
67
|
this.listSlotHtmlComponent = listSlotHtmlComponent ?? null
|
|
49
68
|
this.disabled = disabled ?? false
|
|
50
69
|
this.emptyText = emptyText ?? 'No results found...'
|
|
70
|
+
this.staticList = staticList && !this.appendToBody
|
|
51
71
|
|
|
52
72
|
this.srcElement = null
|
|
53
73
|
|
|
@@ -56,16 +76,12 @@ class Treeselect {
|
|
|
56
76
|
|
|
57
77
|
// Public methods
|
|
58
78
|
mount () {
|
|
59
|
-
|
|
60
|
-
this.#closeList()
|
|
61
|
-
this.srcElement.innerHTML = ''
|
|
62
|
-
this.srcElement = null
|
|
63
|
-
this.#removeOutsideListeners()
|
|
64
|
-
}
|
|
79
|
+
this.destroy()
|
|
65
80
|
|
|
66
81
|
this.srcElement = this.#createTreeselect()
|
|
67
82
|
|
|
68
83
|
this.#scrollEvent = this.scrollWindowHandler.bind(this)
|
|
84
|
+
this.#resizeEvent = this.scrollWindowHandler.bind(this)
|
|
69
85
|
this.#focusEvent = this.focusWindowHandler.bind(this)
|
|
70
86
|
this.#blurEvent = this.blurWindowHandler.bind(this)
|
|
71
87
|
|
|
@@ -91,7 +107,7 @@ class Treeselect {
|
|
|
91
107
|
this.#closeList()
|
|
92
108
|
this.srcElement.innerHTML = ''
|
|
93
109
|
this.srcElement = null
|
|
94
|
-
this.#removeOutsideListeners()
|
|
110
|
+
this.#removeOutsideListeners(true)
|
|
95
111
|
}
|
|
96
112
|
}
|
|
97
113
|
|
|
@@ -120,9 +136,7 @@ class Treeselect {
|
|
|
120
136
|
|
|
121
137
|
if (this.appendToBody) {
|
|
122
138
|
this.#containerResizer = new ResizeObserver(() => {
|
|
123
|
-
|
|
124
|
-
this.#containerWidth = width
|
|
125
|
-
this.updateListPosition(container, list.srcElement, true)
|
|
139
|
+
this.updateListPosition()
|
|
126
140
|
})
|
|
127
141
|
}
|
|
128
142
|
|
|
@@ -134,10 +148,14 @@ class Treeselect {
|
|
|
134
148
|
this.#emitInput()
|
|
135
149
|
})
|
|
136
150
|
input.srcElement.addEventListener('open', () => this.#openList())
|
|
137
|
-
input.srcElement.addEventListener('keydown', (e) =>
|
|
151
|
+
input.srcElement.addEventListener('keydown', (e) => {
|
|
152
|
+
if (this.#isListOpened) {
|
|
153
|
+
list.callKeyAction(e.key)
|
|
154
|
+
}
|
|
155
|
+
})
|
|
138
156
|
input.srcElement.addEventListener('search', (e) => {
|
|
139
157
|
list.updateSearchValue(e.detail)
|
|
140
|
-
this.updateListPosition(
|
|
158
|
+
this.updateListPosition()
|
|
141
159
|
})
|
|
142
160
|
input.srcElement.addEventListener('focus', () => {
|
|
143
161
|
this.#updateFocusClasses(true)
|
|
@@ -166,7 +184,7 @@ class Treeselect {
|
|
|
166
184
|
})
|
|
167
185
|
list.srcElement.addEventListener('arrow-click', () => {
|
|
168
186
|
input.focus()
|
|
169
|
-
this.updateListPosition(
|
|
187
|
+
this.updateListPosition()
|
|
170
188
|
})
|
|
171
189
|
|
|
172
190
|
this.#htmlContainer = container
|
|
@@ -179,7 +197,10 @@ class Treeselect {
|
|
|
179
197
|
}
|
|
180
198
|
|
|
181
199
|
#openList () {
|
|
200
|
+
this.#isListOpened = true
|
|
201
|
+
|
|
182
202
|
window.addEventListener('scroll', this.#scrollEvent, true)
|
|
203
|
+
window.addEventListener('resize', this.#resizeEvent)
|
|
183
204
|
|
|
184
205
|
if (this.appendToBody) {
|
|
185
206
|
document.body.appendChild(this.#treeselectList.srcElement)
|
|
@@ -188,13 +209,16 @@ class Treeselect {
|
|
|
188
209
|
this.#htmlContainer.appendChild(this.#treeselectList.srcElement)
|
|
189
210
|
}
|
|
190
211
|
|
|
191
|
-
this.updateListPosition(
|
|
212
|
+
this.updateListPosition()
|
|
192
213
|
this.#updateOpenCloseClasses(true)
|
|
193
214
|
this.#treeselectList.focusFirstListElement()
|
|
194
215
|
}
|
|
195
216
|
|
|
196
217
|
#closeList () {
|
|
218
|
+
this.#isListOpened = false
|
|
219
|
+
|
|
197
220
|
window.removeEventListener('scroll', this.#scrollEvent, true)
|
|
221
|
+
window.removeEventListener('resize', this.#resizeEvent)
|
|
198
222
|
const isElementExist = this.appendToBody
|
|
199
223
|
? document.body.contains(this.#treeselectList.srcElement)
|
|
200
224
|
: this.#htmlContainer.contains(this.#treeselectList.srcElement)
|
|
@@ -246,10 +270,19 @@ class Treeselect {
|
|
|
246
270
|
} else {
|
|
247
271
|
this.#treeselectInput.srcElement.classList.remove('treeselect-input--opened')
|
|
248
272
|
}
|
|
273
|
+
|
|
274
|
+
if (this.staticList) {
|
|
275
|
+
this.#treeselectList.srcElement.classList.add('treeselect-list--static')
|
|
276
|
+
} else {
|
|
277
|
+
this.#treeselectList.srcElement.classList.remove('treeselect-list--static')
|
|
278
|
+
}
|
|
249
279
|
}
|
|
250
280
|
|
|
251
|
-
#removeOutsideListeners () {
|
|
252
|
-
|
|
281
|
+
#removeOutsideListeners (isDestroy) {
|
|
282
|
+
if (!this.alwaysOpen || isDestroy) {
|
|
283
|
+
window.removeEventListener('scroll', this.#scrollEvent, true)
|
|
284
|
+
window.removeEventListener('resize', this.#resizeEvent)
|
|
285
|
+
}
|
|
253
286
|
|
|
254
287
|
document.removeEventListener('click', this.#focusEvent, true)
|
|
255
288
|
document.removeEventListener('focus', this.#focusEvent, true)
|
|
@@ -258,7 +291,7 @@ class Treeselect {
|
|
|
258
291
|
|
|
259
292
|
// Outside Listeners
|
|
260
293
|
scrollWindowHandler () {
|
|
261
|
-
this.updateListPosition(
|
|
294
|
+
this.updateListPosition()
|
|
262
295
|
}
|
|
263
296
|
|
|
264
297
|
focusWindowHandler (e) {
|
|
@@ -277,56 +310,36 @@ class Treeselect {
|
|
|
277
310
|
this.#updateFocusClasses(false)
|
|
278
311
|
}
|
|
279
312
|
|
|
280
|
-
// Update direction of the list. Support appendToBody and
|
|
281
|
-
updateListPosition (
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const
|
|
286
|
-
const isTopDirection = spaceTop > spaceBottom && window.innerHeight - spaceTop < listHeight + spaceDelta
|
|
287
|
-
const attributeToAdd = isTopDirection ? 'top' : 'buttom'
|
|
288
|
-
const currentAttr = list.getAttribute('direction')
|
|
289
|
-
|
|
290
|
-
this.#htmlContainer.setAttribute('direction', attributeToAdd)
|
|
291
|
-
|
|
292
|
-
// Standart class handler handler with absolute position
|
|
293
|
-
if (!this.appendToBody) {
|
|
294
|
-
const isNoNeedToUpdate = currentAttr === attributeToAdd
|
|
295
|
-
|
|
296
|
-
if (isNoNeedToUpdate) {
|
|
297
|
-
return
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
this.#updateDirectionClasses(isTopDirection, false)
|
|
301
|
-
|
|
302
|
-
return
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Append to body handler
|
|
306
|
-
if (!this.#treeselectInitPosition || isNeedForceUpdate) {
|
|
307
|
-
list.style.transform = null
|
|
313
|
+
// Update direction of the list. Support appendToBody and standard mode with absolute
|
|
314
|
+
updateListPosition () {
|
|
315
|
+
const list = this.#treeselectList.srcElement
|
|
316
|
+
// We need to reset position
|
|
317
|
+
list.style.transform = null
|
|
318
|
+
const container = this.#htmlContainer
|
|
308
319
|
|
|
309
|
-
|
|
310
|
-
|
|
320
|
+
const { y: listY, height: listHeight } = list.getBoundingClientRect()
|
|
321
|
+
const { x: containerX, y: containerY, height: containerHeight, width: containerWidth } = container.getBoundingClientRect()
|
|
322
|
+
const windowHeight = window.innerHeight
|
|
311
323
|
|
|
312
|
-
|
|
324
|
+
const spaceTop = containerY
|
|
325
|
+
const spaceBottom = windowHeight - containerY - containerHeight
|
|
326
|
+
const isTopDirection = spaceTop > spaceBottom && spaceTop >= listHeight && spaceBottom < listHeight
|
|
327
|
+
|
|
328
|
+
if (this.appendToBody) {
|
|
329
|
+
list.style.transform = isTopDirection
|
|
330
|
+
? `translateY(${containerY - listY - listHeight}px)`
|
|
331
|
+
: `translateY(${containerY + containerHeight - listY}px)`
|
|
332
|
+
list.style.width = `${containerWidth}px`
|
|
333
|
+
list.style.left = `${containerX + window.scrollX}px`
|
|
313
334
|
}
|
|
314
335
|
|
|
315
|
-
const
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
// TODO you should use css max-height
|
|
319
|
-
// list.style.maxHeight = `${window.innerHeight - containerHeight}px`
|
|
336
|
+
const attributeToAdd = isTopDirection ? 'top' : 'bottom'
|
|
337
|
+
const currentAttr = list.getAttribute('direction')
|
|
320
338
|
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
this.#
|
|
339
|
+
if (currentAttr !== attributeToAdd) {
|
|
340
|
+
list.setAttribute('direction', attributeToAdd)
|
|
341
|
+
this.#updateDirectionClasses(isTopDirection, this.appendToBody)
|
|
324
342
|
}
|
|
325
|
-
|
|
326
|
-
list.style.transform = isTopDirection ? this.#transform.top : this.#transform.bottom
|
|
327
|
-
this.#updateDirectionClasses(isTopDirection, true)
|
|
328
|
-
list.style.width = `${this.#containerWidth}px`
|
|
329
|
-
list.style.left = `${containerX}px`
|
|
330
343
|
}
|
|
331
344
|
|
|
332
345
|
// Emits
|