@nova-design-system/nova-webcomponents 3.0.0-beta.28 → 3.0.0-beta.30
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/dist/blazor-docs.json +1345 -174
- package/dist/cjs/index-c50face0.js +14 -14
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/native.cjs.js +1 -1
- package/dist/cjs/{nv-badge.cjs.entry.js → nv-badge_2.cjs.entry.js} +44 -4
- package/dist/cjs/nv-badge_2.cjs.entry.js.map +1 -0
- package/dist/cjs/nv-fieldmultiselect.cjs.entry.js +1038 -0
- package/dist/cjs/nv-fieldmultiselect.cjs.entry.js.map +1 -0
- package/dist/cjs/nv-fieldnumber.cjs.entry.js +3 -3
- package/dist/cjs/nv-fieldpassword.cjs.entry.js +3 -3
- package/dist/cjs/nv-fieldradio.cjs.entry.js +3 -3
- package/dist/cjs/nv-fieldselect.cjs.entry.js +5 -5
- package/dist/cjs/nv-fieldtext.cjs.entry.js +3 -3
- package/dist/cjs/nv-fieldtextarea.cjs.entry.js +3 -3
- package/dist/cjs/nv-fieldtoggle.cjs.entry.js +2 -2
- package/dist/cjs/nv-icon.cjs.entry.js +1 -1
- package/dist/cjs/{nv-iconbutton.cjs.entry.js → nv-iconbutton_2.cjs.entry.js} +22 -2
- package/dist/cjs/nv-iconbutton_2.cjs.entry.js.map +1 -0
- package/dist/cjs/nv-menu.cjs.entry.js +1 -1
- package/dist/cjs/nv-popover.cjs.entry.js +1 -1
- package/dist/cjs/nv-row.cjs.entry.js +1 -1
- package/dist/cjs/nv-stack.cjs.entry.js +1 -1
- package/dist/cjs/nv-table.cjs.entry.js +2 -2
- package/dist/cjs/nv-tablebody.cjs.entry.js +1 -1
- package/dist/cjs/nv-tablecolumn.cjs.entry.js +1 -1
- package/dist/cjs/nv-tabledatacell.cjs.entry.js +1 -1
- package/dist/cjs/nv-tablehead.cjs.entry.js +1 -1
- package/dist/cjs/nv-tablerow.cjs.entry.js +1 -1
- package/dist/cjs/nv-tooltip.cjs.entry.js +1 -1
- package/dist/collection/collection-manifest.json +2 -0
- package/dist/collection/components/nv-badge/nv-badge.css +70 -0
- package/dist/collection/components/nv-badge/nv-badge.docs.js +1 -1
- package/dist/collection/components/nv-badge/nv-badge.docs.js.map +1 -1
- package/dist/collection/components/nv-badge/nv-badge.js +3 -3
- package/dist/collection/components/nv-badge/nv-badge.js.map +1 -1
- package/dist/collection/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.css +12 -0
- package/dist/collection/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.docs.js +6 -0
- package/dist/collection/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.docs.js.map +1 -0
- package/dist/collection/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.js +171 -0
- package/dist/collection/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.js.map +1 -0
- package/dist/collection/components/nv-fieldmultiselect/nv-fieldmultiselect.css +201 -0
- package/dist/collection/components/nv-fieldmultiselect/nv-fieldmultiselect.docs.js +257 -0
- package/dist/collection/components/nv-fieldmultiselect/nv-fieldmultiselect.docs.js.map +1 -0
- package/dist/collection/components/nv-fieldmultiselect/nv-fieldmultiselect.js +1578 -0
- package/dist/collection/components/nv-fieldmultiselect/nv-fieldmultiselect.js.map +1 -0
- package/dist/collection/components/nv-fieldnumber/nv-fieldnumber.js +3 -3
- package/dist/collection/components/nv-fieldpassword/nv-fieldpassword.js +3 -3
- package/dist/collection/components/nv-fieldradio/nv-fieldradio.js +3 -3
- package/dist/collection/components/nv-fieldselect/nv-fieldselect.js +5 -5
- package/dist/collection/components/nv-fieldtext/nv-fieldtext.js +3 -3
- package/dist/collection/components/nv-fieldtextarea/nv-fieldtextarea.js +3 -3
- package/dist/collection/components/nv-fieldtoggle/nv-fieldtoggle.js +2 -2
- package/dist/collection/components/nv-icon/nv-icon.js +1 -1
- package/dist/collection/components/nv-iconbutton/nv-iconbutton.js +1 -1
- package/dist/collection/components/nv-loader/nv-loader.js +1 -1
- package/dist/collection/components/nv-menu/nv-menu.js +1 -1
- package/dist/collection/components/nv-popover/nv-popover.js +1 -1
- package/dist/collection/components/nv-row/nv-row.js +1 -1
- package/dist/collection/components/nv-stack/nv-stack.js +1 -1
- package/dist/collection/components/nv-table/nv-table.js +2 -2
- package/dist/collection/components/nv-tablebody/nv-tablebody.js +1 -1
- package/dist/collection/components/nv-tablecolumn/nv-tablecolumn.js +1 -1
- package/dist/collection/components/nv-tabledatacell/nv-tabledatacell.js +1 -1
- package/dist/collection/components/nv-tablehead/nv-tablehead.js +1 -1
- package/dist/collection/components/nv-tablerow/nv-tablerow.js +1 -1
- package/dist/collection/components/nv-tooltip/nv-tooltip.js +1 -1
- package/dist/collection/dev/dev-components.js +1 -1
- package/dist/collection/dev/dev-components.js.map +1 -1
- package/dist/components/nv-alert.js +1 -1
- package/dist/components/nv-avatar.js +1 -1
- package/dist/components/nv-badge.js +1 -147
- package/dist/components/nv-badge.js.map +1 -1
- package/dist/components/nv-button.js +1 -1
- package/dist/components/nv-fieldcheckbox.js +1 -121
- package/dist/components/nv-fieldcheckbox.js.map +1 -1
- package/dist/components/nv-fielddropdown.js +5 -5
- package/dist/components/nv-fielddropdownitem.js +1 -1
- package/dist/components/nv-fielddropdownitemcheck.d.ts +11 -0
- package/dist/components/nv-fielddropdownitemcheck.js +8 -0
- package/dist/components/nv-fielddropdownitemcheck.js.map +1 -0
- package/dist/components/nv-fieldmultiselect.d.ts +11 -0
- package/dist/components/nv-fieldmultiselect.js +1129 -0
- package/dist/components/nv-fieldmultiselect.js.map +1 -0
- package/dist/components/nv-fieldnumber.js +4 -4
- package/dist/components/nv-fieldpassword.js +6 -6
- package/dist/components/nv-fieldradio.js +3 -3
- package/dist/components/nv-fieldselect.js +6 -6
- package/dist/components/nv-fieldtext.js +4 -4
- package/dist/components/nv-fieldtextarea.js +3 -3
- package/dist/components/nv-fieldtoggle.js +2 -2
- package/dist/components/nv-icon.js +1 -1
- package/dist/components/nv-iconbutton.js +1 -1
- package/dist/components/nv-loader.js +1 -1
- package/dist/components/nv-menu.js +2 -2
- package/dist/components/nv-menuitem.js +1 -1
- package/dist/components/nv-popover.js +1 -1
- package/dist/components/nv-row.js +1 -1
- package/dist/components/nv-stack.js +1 -1
- package/dist/components/nv-table.js +2 -2
- package/dist/components/nv-tablebody.js +1 -1
- package/dist/components/nv-tablecolumn.js +1 -1
- package/dist/components/nv-tabledatacell.js +1 -1
- package/dist/components/nv-tablehead.js +1 -1
- package/dist/components/nv-tablerow.js +1 -1
- package/dist/components/nv-tooltip.js +2 -2
- package/dist/components/p-5ce661bd.js +125 -0
- package/dist/components/p-5ce661bd.js.map +1 -0
- package/dist/components/{p-45c63143.js → p-8bec002e.js} +4 -4
- package/dist/components/{p-45c63143.js.map → p-8bec002e.js.map} +1 -1
- package/dist/components/{p-8c8cf8ea.js → p-a1aa8970.js} +2 -2
- package/dist/components/{p-8c8cf8ea.js.map → p-a1aa8970.js.map} +1 -1
- package/dist/{esm/nv-badge.entry.js → components/p-a4802979.js} +51 -15
- package/dist/components/p-a4802979.js.map +1 -0
- package/dist/components/{p-45405075.js → p-b12abc99.js} +2 -2
- package/dist/components/{p-45405075.js.map → p-b12abc99.js.map} +1 -1
- package/dist/components/{p-89fb6636.js → p-d5dd3def.js} +2 -2
- package/dist/components/{p-89fb6636.js.map → p-d5dd3def.js.map} +1 -1
- package/dist/components/{p-689a7b38.js → p-d70df149.js} +2 -2
- package/dist/components/{p-689a7b38.js.map → p-d70df149.js.map} +1 -1
- package/dist/components/p-e4641c41.js +72 -0
- package/dist/components/p-e4641c41.js.map +1 -0
- package/dist/docs.json +1326 -171
- package/dist/esm/index-e7b35c14.js +14 -14
- package/dist/esm/loader.js +1 -1
- package/dist/esm/native.js +1 -1
- package/dist/esm/nv-badge_2.entry.js +154 -0
- package/dist/esm/nv-badge_2.entry.js.map +1 -0
- package/dist/esm/nv-fieldmultiselect.entry.js +1034 -0
- package/dist/esm/nv-fieldmultiselect.entry.js.map +1 -0
- package/dist/esm/nv-fieldnumber.entry.js +3 -3
- package/dist/esm/nv-fieldpassword.entry.js +3 -3
- package/dist/esm/nv-fieldradio.entry.js +3 -3
- package/dist/esm/nv-fieldselect.entry.js +5 -5
- package/dist/esm/nv-fieldtext.entry.js +3 -3
- package/dist/esm/nv-fieldtextarea.entry.js +3 -3
- package/dist/esm/nv-fieldtoggle.entry.js +2 -2
- package/dist/esm/nv-icon.entry.js +1 -1
- package/dist/esm/{nv-iconbutton.entry.js → nv-iconbutton_2.entry.js} +22 -3
- package/dist/esm/nv-iconbutton_2.entry.js.map +1 -0
- package/dist/esm/nv-menu.entry.js +1 -1
- package/dist/esm/nv-popover.entry.js +1 -1
- package/dist/esm/nv-row.entry.js +1 -1
- package/dist/esm/nv-stack.entry.js +1 -1
- package/dist/esm/nv-table.entry.js +2 -2
- package/dist/esm/nv-tablebody.entry.js +1 -1
- package/dist/esm/nv-tablecolumn.entry.js +1 -1
- package/dist/esm/nv-tabledatacell.entry.js +1 -1
- package/dist/esm/nv-tablehead.entry.js +1 -1
- package/dist/esm/nv-tablerow.entry.js +1 -1
- package/dist/esm/nv-tooltip.entry.js +1 -1
- package/dist/native/native.css +1 -1
- package/dist/native/native.esm.js +1 -1
- package/dist/native/native.esm.js.map +1 -1
- package/dist/native/{p-dbae3920.entry.js → p-0fbb962b.entry.js} +2 -2
- package/dist/native/{p-81256924.entry.js → p-12eaebd6.entry.js} +2 -2
- package/dist/native/p-1c689ec7.entry.js +2 -0
- package/dist/native/{p-bdab3562.entry.js → p-305951e4.entry.js} +2 -2
- package/dist/native/p-306d1f04.entry.js +2 -0
- package/dist/native/p-306d1f04.entry.js.map +1 -0
- package/dist/native/{p-de6e0e1f.entry.js → p-3f912745.entry.js} +2 -2
- package/dist/native/p-589eb477.entry.js +2 -0
- package/dist/native/{p-e2df46e2.entry.js → p-5b4bdbe2.entry.js} +2 -2
- package/dist/native/{p-75b5fdce.entry.js → p-65ad60eb.entry.js} +2 -2
- package/dist/native/{p-ea4092a7.entry.js → p-7b2d8b8c.entry.js} +2 -2
- package/dist/native/{p-c1c661a0.entry.js → p-7b3a4cbf.entry.js} +2 -2
- package/dist/native/{p-38817aa3.entry.js → p-91d30cd7.entry.js} +2 -2
- package/dist/native/{p-c4f7e36d.entry.js → p-92e3e334.entry.js} +2 -2
- package/dist/native/{p-1edb76e1.entry.js → p-a03df637.entry.js} +2 -2
- package/dist/native/p-a5582014.entry.js +2 -0
- package/dist/native/p-a5582014.entry.js.map +1 -0
- package/dist/native/p-a7a3c45f.entry.js +2 -0
- package/dist/native/p-a7be7540.entry.js +2 -0
- package/dist/native/p-a7be7540.entry.js.map +1 -0
- package/dist/native/p-a8c0fefa.entry.js +2 -0
- package/dist/native/{p-99850272.entry.js.map → p-a8c0fefa.entry.js.map} +1 -1
- package/dist/native/{p-808cf007.entry.js → p-c8f36510.entry.js} +2 -2
- package/dist/native/p-d3d682a7.entry.js +2 -0
- package/dist/native/p-d4c0a1e0.entry.js +2 -0
- package/dist/native/{p-844f4878.entry.js → p-dbade286.entry.js} +2 -2
- package/dist/types/components/nv-badge/nv-badge.d.ts +1 -1
- package/dist/types/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.d.ts +52 -0
- package/dist/types/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.docs.d.ts +4 -0
- package/dist/types/components/nv-fieldmultiselect/nv-fieldmultiselect.d.ts +405 -0
- package/dist/types/components/nv-fieldmultiselect/nv-fieldmultiselect.docs.d.ts +4 -0
- package/dist/types/components.d.ts +331 -2
- package/dist/vscode-data.json +325 -1
- package/hydrate/index.js +1171 -40
- package/hydrate/index.mjs +1171 -40
- package/package.json +7 -2
- package/readme.md +5 -1
- package/dist/cjs/nv-badge.cjs.entry.js.map +0 -1
- package/dist/cjs/nv-iconbutton.cjs.entry.js.map +0 -1
- package/dist/cjs/nv-loader.cjs.entry.js +0 -29
- package/dist/cjs/nv-loader.cjs.entry.js.map +0 -1
- package/dist/esm/nv-badge.entry.js.map +0 -1
- package/dist/esm/nv-iconbutton.entry.js.map +0 -1
- package/dist/esm/nv-loader.entry.js +0 -25
- package/dist/esm/nv-loader.entry.js.map +0 -1
- package/dist/native/p-01be802d.entry.js +0 -2
- package/dist/native/p-0cfe2048.entry.js +0 -2
- package/dist/native/p-0cfe2048.entry.js.map +0 -1
- package/dist/native/p-28cf537b.entry.js +0 -2
- package/dist/native/p-4b926563.entry.js +0 -2
- package/dist/native/p-861f91d3.entry.js +0 -2
- package/dist/native/p-9221f72a.entry.js +0 -2
- package/dist/native/p-99850272.entry.js +0 -2
- package/dist/native/p-dba9c57c.entry.js +0 -2
- package/dist/native/p-dba9c57c.entry.js.map +0 -1
- package/dist/native/p-ed893068.entry.js +0 -2
- package/dist/native/p-ed893068.entry.js.map +0 -1
- /package/dist/native/{p-dbae3920.entry.js.map → p-0fbb962b.entry.js.map} +0 -0
- /package/dist/native/{p-81256924.entry.js.map → p-12eaebd6.entry.js.map} +0 -0
- /package/dist/native/{p-4b926563.entry.js.map → p-1c689ec7.entry.js.map} +0 -0
- /package/dist/native/{p-bdab3562.entry.js.map → p-305951e4.entry.js.map} +0 -0
- /package/dist/native/{p-de6e0e1f.entry.js.map → p-3f912745.entry.js.map} +0 -0
- /package/dist/native/{p-9221f72a.entry.js.map → p-589eb477.entry.js.map} +0 -0
- /package/dist/native/{p-e2df46e2.entry.js.map → p-5b4bdbe2.entry.js.map} +0 -0
- /package/dist/native/{p-75b5fdce.entry.js.map → p-65ad60eb.entry.js.map} +0 -0
- /package/dist/native/{p-ea4092a7.entry.js.map → p-7b2d8b8c.entry.js.map} +0 -0
- /package/dist/native/{p-c1c661a0.entry.js.map → p-7b3a4cbf.entry.js.map} +0 -0
- /package/dist/native/{p-38817aa3.entry.js.map → p-91d30cd7.entry.js.map} +0 -0
- /package/dist/native/{p-c4f7e36d.entry.js.map → p-92e3e334.entry.js.map} +0 -0
- /package/dist/native/{p-1edb76e1.entry.js.map → p-a03df637.entry.js.map} +0 -0
- /package/dist/native/{p-01be802d.entry.js.map → p-a7a3c45f.entry.js.map} +0 -0
- /package/dist/native/{p-808cf007.entry.js.map → p-c8f36510.entry.js.map} +0 -0
- /package/dist/native/{p-28cf537b.entry.js.map → p-d3d682a7.entry.js.map} +0 -0
- /package/dist/native/{p-861f91d3.entry.js.map → p-d4c0a1e0.entry.js.map} +0 -0
- /package/dist/native/{p-844f4878.entry.js.map → p-dbade286.entry.js.map} +0 -0
|
@@ -0,0 +1,1038 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
const index = require('./index-c50face0.js');
|
|
6
|
+
const v4 = require('./v4-7014b8b0.js');
|
|
7
|
+
|
|
8
|
+
const nvFieldmultiselectCss = "nv-fieldmultiselect{--nv-field-border-default:var(--components-form-field-border-default);--nv-field-border-hover:var(--components-form-field-border-hover);--nv-field-border-focus:var(--components-form-field-border-focus);--nv-field-border-disabled:var(--components-form-field-border-default);--nv-field-border-readonly:var(--components-form-field-border-default);--nv-field-focus-box-shadow:var(--color-focus-brand);--nv-field-background:var(--components-form-field-background-default);display:flex;flex-direction:column;align-items:flex-start;gap:var(--form-gap-y);box-sizing:border-box}nv-fieldmultiselect[readonly]:not([readonly=false]){--nv-field-border-default:var(--components-form-field-border-readonly);--nv-field-border-hover:var(--nv-field-border-default);--nv-field-border-focus:var(--components-form-field-border-focus);--nv-field-border-disabled:var(--nv-field-border-default);--nv-field-border-readonly:var(--nv-field-border-default);--nv-field-background:var(--components-form-field-background-readonly)}nv-fieldmultiselect[error]:not([error=false]){--nv-field-border-default:var(--components-form-field-border-error);--nv-field-border-hover:var(--nv-field-border-default);--nv-field-border-focus:var(--nv-field-border-default);--nv-field-border-disabled:var(--nv-field-border-default);--nv-field-border-readonly:var(--nv-field-border-default);--nv-field-focus-box-shadow:var(--color-focus-destructive)}nv-fieldmultiselect[required]:not([required=false])>label::after{content:\"*\";color:var(--components-form-text-required);font-weight:700}nv-fieldmultiselect label{display:flex;align-items:center;gap:var(--form-label-gap);align-self:stretch;color:var(--components-form-text-label-default);font-family:\"TT Norms Pro\", sans-serif;font-size:var(--form-label-font-size);font-style:normal;font-weight:500;line-height:var(--form-label-line-height)}nv-fieldmultiselect nv-popover{width:100%;display:block}nv-fieldmultiselect .input-wrapper-multiselect{display:flex;flex-wrap:wrap;gap:var(--form-gap-x);align-items:stretch;align-self:stretch;width:100%}nv-fieldmultiselect .input-container-multiselect{display:flex;flex-grow:1;padding:calc(var(--form-field-padding-y) - 1px) var(--form-field-padding-x);justify-content:center;align-items:center;gap:var(--form-field-gap);align-self:stretch;border-radius:var(--form-field-radius);border-width:1px;border-style:solid;border-color:var(--nv-field-border-default);opacity:var(--components-form-opacity-default);background:var(--nv-field-background);transition:all 150ms ease-out;display:flex;align-items:center;position:relative;width:100%;min-height:40px}nv-fieldmultiselect .input-container-multiselect:hover{border-color:var(--nv-field-border-hover)}nv-fieldmultiselect .input-container-multiselect:focus-within{border-color:var(--nv-field-border-focus);box-shadow:0px 0px 0px var(--focus-field-stroke) var(--nv-field-focus-box-shadow)}nv-fieldmultiselect .input-container-multiselect:has(input:read-only){opacity:0.5;background-color:var(--components-form-field-background-readonly);border-color:var(--nv-field-border-readonly)}nv-fieldmultiselect .input-container-multiselect:has(input:disabled){opacity:0.5;background-color:var(--components-form-field-background-disabled);border-color:var(--nv-field-border-disabled)}nv-fieldmultiselect .input-container-multiselect input,nv-fieldmultiselect .input-container-multiselect p.non-filterable-text{display:flex;align-items:center;flex:1 0 0;overflow:hidden;background-color:transparent;color:var(--components-form-field-content-text);text-overflow:ellipsis;font-size:var(--form-field-font-size);font-style:normal;font-weight:500;line-height:var(--form-field-line-height);width:100%;padding-right:2rem;flex-grow:1;margin:0;min-height:100%;box-sizing:border-box}nv-fieldmultiselect .input-container-multiselect input:focus,nv-fieldmultiselect .input-container-multiselect p.non-filterable-text:focus{outline:none}nv-fieldmultiselect .input-container-multiselect input::placeholder,nv-fieldmultiselect .input-container-multiselect p.non-filterable-text::placeholder{overflow:hidden;color:var(--components-form-field-content-placeholder);text-overflow:ellipsis;font-family:\"TT Norms Pro\", sans-serif;font-size:var(--form-field-font-size);font-style:normal;font-weight:400;line-height:var(--form-field-line-height)}nv-fieldmultiselect .input-container-multiselect .toggle-dropdown-icon{position:absolute;right:0;top:50%;transform:translateY(-50%);z-index:2}nv-fieldmultiselect .input-container-multiselect nv-icon.validation{color:var(--nv-field-border-default);position:absolute;right:50px}nv-fieldmultiselect .input-container-multiselect.focus-within,nv-fieldmultiselect .input-container-multiselect:hover{border-color:var(--nv-field-border-focus);box-shadow:0px 0px 0px var(--focus-field-stroke) var(--nv-field-focus-box-shadow)}nv-fieldmultiselect .non-filterable-text{display:block;border-radius:var(--form-field-radius);background-color:var(--nv-field-background);color:var(--components-form-field-content-text);font-size:var(--form-field-font-size);font-weight:500;line-height:var(--form-field-line-height);box-sizing:border-box;cursor:pointer;height:100%;min-height:40px}nv-fieldmultiselect .non-filterable-text span{display:inline-block;width:100%;overflow:hidden;text-overflow:ellipsis}nv-fieldmultiselect .description{align-self:stretch;color:var(--components-form-text-description-default);font-family:\"TT Norms Pro\", sans-serif;font-size:var(--form-description-font-size);font-style:normal;line-height:var(--form-description-line-height)}nv-fieldmultiselect .error-description{align-self:stretch;color:var(--components-form-text-description-default);font-family:\"TT Norms Pro\", sans-serif;font-size:var(--form-description-font-size);font-style:normal;line-height:var(--form-description-line-height);color:var(--components-form-text-description-error)}nv-fieldmultiselect hr{border:none;border-top:1px solid var(--dropdown-divider-color, #ccc);margin:0.5rem 0}.input-container-multiselect.focus-within,.input-container-multiselect:hover{border-color:var(--nv-field-border-focus);box-shadow:0px 0px 0px var(--focus-field-stroke) var(--nv-field-focus-box-shadow)}.no-results-message{text-align:center;padding:10px;color:var(--nv-field-error-text, #999)}";
|
|
9
|
+
const NvFieldmultiselectStyle0 = nvFieldmultiselectCss;
|
|
10
|
+
|
|
11
|
+
const NvFieldmultiselect = class {
|
|
12
|
+
constructor(hostRef) {
|
|
13
|
+
index.registerInstance(this, hostRef);
|
|
14
|
+
this.dropdownItemSelected = index.createEvent(this, "dropdownItemSelected", 7);
|
|
15
|
+
this.valueChanged = index.createEvent(this, "valueChanged", 7);
|
|
16
|
+
this.multiselectChange = index.createEvent(this, "multiselectChange", 7);
|
|
17
|
+
/**
|
|
18
|
+
* Flag to prevent concurrent reordering operations.
|
|
19
|
+
* Private property preferred over @State as it:
|
|
20
|
+
* - Manages internal component logic without affecting UI
|
|
21
|
+
* - Avoids unnecessary re-renders
|
|
22
|
+
* - Provides better performance for rapid state changes
|
|
23
|
+
* - Only needs class-level scope
|
|
24
|
+
*/
|
|
25
|
+
this.isReordering = false;
|
|
26
|
+
/**
|
|
27
|
+
* Handle badge close for options mode.
|
|
28
|
+
*/
|
|
29
|
+
this.handleBadgeCloseOptions = () => {
|
|
30
|
+
this.selectedValues = [];
|
|
31
|
+
this.multiselectChange.emit(this.selectedValues);
|
|
32
|
+
// Uncheck all elements
|
|
33
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
34
|
+
items.forEach(item => {
|
|
35
|
+
item.checked = false;
|
|
36
|
+
item.style.display = '';
|
|
37
|
+
});
|
|
38
|
+
// Reorder options without the divider since there are no selected elements
|
|
39
|
+
this.parsedOptions = this.parsedOptions.filter(option => !option.isDivider);
|
|
40
|
+
// Reorder options without the divider since there are no selected elements
|
|
41
|
+
this.reorderOptionsContent();
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Handle badge close for slots mode.
|
|
45
|
+
*/
|
|
46
|
+
this.handleBadgeCloseSlots = () => {
|
|
47
|
+
this.selectedValues = [];
|
|
48
|
+
this.multiselectChange.emit(this.selectedValues);
|
|
49
|
+
// Uncheck all elements
|
|
50
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
51
|
+
items.forEach(item => {
|
|
52
|
+
item.checked = false;
|
|
53
|
+
item.style.display = '';
|
|
54
|
+
});
|
|
55
|
+
// Reorder slot content
|
|
56
|
+
this.reorderSlotContent();
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Handle popover close
|
|
60
|
+
*/
|
|
61
|
+
this.handlePopoverClose = () => {
|
|
62
|
+
this.open = false;
|
|
63
|
+
this.filterText = '';
|
|
64
|
+
if (this.modeState === 'options') {
|
|
65
|
+
this.reorderOptionsContent();
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.reorderSlotContent();
|
|
69
|
+
}
|
|
70
|
+
// Reset filter if needed
|
|
71
|
+
if (this.filterable) {
|
|
72
|
+
this.resetFilter();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Handle keyboard events for options mode.
|
|
77
|
+
* @param {KeyboardEvent} event - The keyboard event.
|
|
78
|
+
*/
|
|
79
|
+
this.handleKeyDownOptions = (event) => {
|
|
80
|
+
if (!this.open) {
|
|
81
|
+
if (event.key === 'ArrowDown') {
|
|
82
|
+
this.open = true;
|
|
83
|
+
this.popoverElement.show();
|
|
84
|
+
if (!this.filterable) {
|
|
85
|
+
this.focusFirstItem();
|
|
86
|
+
}
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
93
|
+
if (items.length === 0) {
|
|
94
|
+
console.warn('No dropdown items found to navigate');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let currentIndex = items.findIndex(item => item.classList.contains('highlighted'));
|
|
98
|
+
if (event.key === 'ArrowDown') {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
currentIndex = (currentIndex + 1) % items.length;
|
|
101
|
+
this.updateHighlightedItem(items, currentIndex);
|
|
102
|
+
}
|
|
103
|
+
else if (event.key === 'ArrowUp') {
|
|
104
|
+
event.preventDefault();
|
|
105
|
+
currentIndex = (currentIndex - 1 + items.length) % items.length;
|
|
106
|
+
this.updateHighlightedItem(items, currentIndex);
|
|
107
|
+
}
|
|
108
|
+
else if (event.key === 'Enter' && currentIndex >= 0) {
|
|
109
|
+
event.preventDefault();
|
|
110
|
+
items[currentIndex].click();
|
|
111
|
+
}
|
|
112
|
+
else if (event.key === 'Escape') {
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
this.handlePopoverClose();
|
|
115
|
+
if (this.inputElement) {
|
|
116
|
+
this.inputElement.blur();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Handle input blur for options mode.
|
|
122
|
+
*/
|
|
123
|
+
this.handleInputBlurOptions = () => {
|
|
124
|
+
setTimeout(() => {
|
|
125
|
+
if (!this.el.contains(document.activeElement)) {
|
|
126
|
+
this.handlePopoverClose();
|
|
127
|
+
}
|
|
128
|
+
}, 150);
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Handle click on the input container for options mode.
|
|
132
|
+
* @param {MouseEvent} event - The click event.
|
|
133
|
+
*/
|
|
134
|
+
this.handleInputContainerClickOptions = (event) => {
|
|
135
|
+
if (this.disabled || this.readonly) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const target = event.target;
|
|
139
|
+
if (target.tagName === 'P' || target.tagName === 'SPAN') {
|
|
140
|
+
this.open = true;
|
|
141
|
+
this.popoverElement.show();
|
|
142
|
+
const inputContainer = this.el.querySelector('.input-container');
|
|
143
|
+
if (inputContainer) {
|
|
144
|
+
inputContainer.classList.add('focus-within');
|
|
145
|
+
const removeFocusWithin = () => {
|
|
146
|
+
inputContainer.classList.remove('focus-within');
|
|
147
|
+
};
|
|
148
|
+
this.popoverElement.addEventListener('hide', removeFocusWithin);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Handle click on the input container for slots mode.
|
|
154
|
+
* @param {MouseEvent} event - The click event.
|
|
155
|
+
*/
|
|
156
|
+
this.handleInputContainerClickSlots = (event) => {
|
|
157
|
+
if (this.disabled || this.readonly) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const target = event.target;
|
|
161
|
+
if (target.tagName === 'P' || target.tagName === 'SPAN') {
|
|
162
|
+
this.open = true;
|
|
163
|
+
this.popoverElement.show();
|
|
164
|
+
const inputContainer = this.el.querySelector('.input-container');
|
|
165
|
+
if (inputContainer) {
|
|
166
|
+
inputContainer.classList.add('focus-within');
|
|
167
|
+
const removeFocusWithin = () => {
|
|
168
|
+
inputContainer.classList.remove('focus-within');
|
|
169
|
+
};
|
|
170
|
+
this.popoverElement.addEventListener('hide', removeFocusWithin);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Handle input change for options mode.
|
|
176
|
+
* @param {Event} event - The input event.
|
|
177
|
+
*/
|
|
178
|
+
this.handleInputOptions = (event) => {
|
|
179
|
+
if (!this.filterable)
|
|
180
|
+
return;
|
|
181
|
+
if (this.disabled || this.readonly) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const input = event.target;
|
|
185
|
+
this.value = input.value;
|
|
186
|
+
this.valueChanged.emit(this.value);
|
|
187
|
+
// Clear any existing timer
|
|
188
|
+
if (this.debounceTimer) {
|
|
189
|
+
window.clearTimeout(this.debounceTimer);
|
|
190
|
+
}
|
|
191
|
+
// Set a new timer for filtering
|
|
192
|
+
this.debounceTimer = window.setTimeout(() => {
|
|
193
|
+
this.filterText = input.value.toLowerCase();
|
|
194
|
+
this.filterOptionsItems();
|
|
195
|
+
}, this.debounceDelay);
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Handle input change for slots mode
|
|
199
|
+
* @param {Event} event - The input event.
|
|
200
|
+
*/
|
|
201
|
+
this.handleInputSlots = (event) => {
|
|
202
|
+
if (!this.filterable)
|
|
203
|
+
return;
|
|
204
|
+
if (this.disabled || this.readonly)
|
|
205
|
+
return;
|
|
206
|
+
const input = event.target;
|
|
207
|
+
this.value = input.value;
|
|
208
|
+
this.valueChanged.emit(this.value);
|
|
209
|
+
// Clear any existing timer
|
|
210
|
+
if (this.debounceTimer) {
|
|
211
|
+
window.clearTimeout(this.debounceTimer);
|
|
212
|
+
}
|
|
213
|
+
// Set a new timer for filtering
|
|
214
|
+
this.debounceTimer = window.setTimeout(() => {
|
|
215
|
+
this.filterText = input.value.toLowerCase();
|
|
216
|
+
this.filterSlotsItems();
|
|
217
|
+
}, this.debounceDelay);
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Handle input focus for options mode.
|
|
221
|
+
*/
|
|
222
|
+
this.handleInputFocusOptions = () => {
|
|
223
|
+
if (this.disabled || this.readonly) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
this.open = true;
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* Handle input focus for slots mode.
|
|
230
|
+
*/
|
|
231
|
+
this.handleInputFocusSlots = () => {
|
|
232
|
+
if (this.disabled || this.readonly) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.open = true;
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Handle input blur for slots mode.
|
|
239
|
+
*/
|
|
240
|
+
this.handleInputBlurSlots = () => {
|
|
241
|
+
setTimeout(() => {
|
|
242
|
+
if (!this.el.contains(document.activeElement)) {
|
|
243
|
+
this.handlePopoverClose();
|
|
244
|
+
}
|
|
245
|
+
}, 150);
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* Toggle the multiselect popover for options mode.
|
|
249
|
+
*/
|
|
250
|
+
this.togglePopoverOptions = () => {
|
|
251
|
+
if (this.disabled || this.readonly) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (this.open) {
|
|
255
|
+
this.handlePopoverClose();
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
this.open = true;
|
|
259
|
+
this.popoverElement.show();
|
|
260
|
+
if (!this.filterable) {
|
|
261
|
+
this.focusFirstItem();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
/**
|
|
266
|
+
* Toggle the multiselect popover for slots mode.
|
|
267
|
+
*/
|
|
268
|
+
this.togglePopoverSlots = () => {
|
|
269
|
+
if (this.disabled || this.readonly) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
this.open = !this.open;
|
|
273
|
+
if (this.open) {
|
|
274
|
+
this.popoverElement.show();
|
|
275
|
+
if (!this.filterable) {
|
|
276
|
+
this.focusFirstItem();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
this.popoverElement.hide();
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
/**
|
|
284
|
+
* Handle keyboard events for slots mode.
|
|
285
|
+
* @param {KeyboardEvent} event - The keyboard event.
|
|
286
|
+
*/
|
|
287
|
+
this.handleKeyDownSlots = (event) => {
|
|
288
|
+
if (!this.open) {
|
|
289
|
+
if (event.key === 'ArrowDown') {
|
|
290
|
+
this.open = true;
|
|
291
|
+
this.popoverElement.show();
|
|
292
|
+
if (!this.filterable) {
|
|
293
|
+
this.focusFirstItem();
|
|
294
|
+
}
|
|
295
|
+
event.preventDefault();
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
301
|
+
if (items.length === 0) {
|
|
302
|
+
console.warn('No dropdown items found to navigate');
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
let currentIndex = items.findIndex(item => item.classList.contains('highlighted'));
|
|
306
|
+
if (event.key === 'ArrowDown') {
|
|
307
|
+
event.preventDefault();
|
|
308
|
+
currentIndex = (currentIndex + 1) % items.length;
|
|
309
|
+
this.updateHighlightedItem(items, currentIndex);
|
|
310
|
+
}
|
|
311
|
+
else if (event.key === 'ArrowUp') {
|
|
312
|
+
event.preventDefault();
|
|
313
|
+
currentIndex = (currentIndex - 1 + items.length) % items.length;
|
|
314
|
+
this.updateHighlightedItem(items, currentIndex);
|
|
315
|
+
}
|
|
316
|
+
else if (event.key === 'Enter' && currentIndex >= 0) {
|
|
317
|
+
event.preventDefault();
|
|
318
|
+
items[currentIndex].click();
|
|
319
|
+
}
|
|
320
|
+
else if (event.key === 'Escape') {
|
|
321
|
+
event.preventDefault();
|
|
322
|
+
this.handlePopoverClose();
|
|
323
|
+
if (this.inputElement) {
|
|
324
|
+
this.inputElement.blur();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
this.inputId = v4.v4();
|
|
329
|
+
this.name = undefined;
|
|
330
|
+
this.label = undefined;
|
|
331
|
+
this.description = undefined;
|
|
332
|
+
this.placeholder = undefined;
|
|
333
|
+
this.maxHeight = '200px';
|
|
334
|
+
this.badgeLabel = '';
|
|
335
|
+
this.emptyresult = 'No results found';
|
|
336
|
+
this.required = false;
|
|
337
|
+
this.error = false;
|
|
338
|
+
this.errorDescription = undefined;
|
|
339
|
+
this.readonly = false;
|
|
340
|
+
this.disabled = false;
|
|
341
|
+
this.autocomplete = 'off';
|
|
342
|
+
this.filterable = false;
|
|
343
|
+
this.value = undefined;
|
|
344
|
+
this.open = false;
|
|
345
|
+
this.mode = undefined;
|
|
346
|
+
this.options = undefined;
|
|
347
|
+
this.parsedOptions = [];
|
|
348
|
+
this.selectedValues = [];
|
|
349
|
+
this.sortedOptions = [];
|
|
350
|
+
this.filterText = '';
|
|
351
|
+
this.isFilterable = this.filterable;
|
|
352
|
+
this.debounceDelay = 300;
|
|
353
|
+
this.modeState = 'options';
|
|
354
|
+
}
|
|
355
|
+
//#endregion PROPERTIES
|
|
356
|
+
/****************************************************************************/
|
|
357
|
+
//#region EVENTS
|
|
358
|
+
/**
|
|
359
|
+
* Subscribe to click outside event.
|
|
360
|
+
*/
|
|
361
|
+
connectedCallback() {
|
|
362
|
+
this.modeState = this.mode;
|
|
363
|
+
this.handleOptionsChange(this.options);
|
|
364
|
+
document.addEventListener('click', this.handleClickOutside.bind(this));
|
|
365
|
+
// If we don't have parsed options, initialize with slot elements
|
|
366
|
+
if (!this.parsedOptions || this.parsedOptions.length === 0) {
|
|
367
|
+
// Wait for the slot to be available
|
|
368
|
+
requestAnimationFrame(() => {
|
|
369
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
370
|
+
// Initialize selectedValues with checked elements
|
|
371
|
+
this.selectedValues = items
|
|
372
|
+
.filter(item => item.hasAttribute('checked'))
|
|
373
|
+
.map(item => item.getAttribute('value') || '');
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
// Initialize the sorted options array with the parsed options for initial rendering
|
|
377
|
+
this.sortedOptions = [...this.parsedOptions];
|
|
378
|
+
// Handle pre-selection during component initialization
|
|
379
|
+
this.setInitialSelection();
|
|
380
|
+
// Apply filtering if the multiselect is filterable and there is a value
|
|
381
|
+
if (this.filterable && this.value) {
|
|
382
|
+
this.filterText = this.value.toLowerCase();
|
|
383
|
+
this.filterItems();
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
// Reset visibility state of all dropdown items
|
|
387
|
+
this.resetFilter();
|
|
388
|
+
}
|
|
389
|
+
const slot = this.el.querySelector('slot[name="content"]');
|
|
390
|
+
if (slot) {
|
|
391
|
+
const observer = new MutationObserver(() => {
|
|
392
|
+
this.reorderSlotContent();
|
|
393
|
+
});
|
|
394
|
+
observer.observe(slot, {
|
|
395
|
+
childList: true,
|
|
396
|
+
subtree: true,
|
|
397
|
+
});
|
|
398
|
+
this.reorderSlotContent();
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Unsubscribe from click outside event.
|
|
403
|
+
*/
|
|
404
|
+
disconnectedCallback() {
|
|
405
|
+
document.removeEventListener('click', this.handleClickOutside.bind(this));
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Emitted when the input loses focus.
|
|
409
|
+
* @param {CustomEvent<boolean>} event - The event object containing the focus state.
|
|
410
|
+
*/
|
|
411
|
+
handleOpenChanged(event) {
|
|
412
|
+
// Update the open state of popover
|
|
413
|
+
this.open = event.detail;
|
|
414
|
+
if (this.open) {
|
|
415
|
+
if (this.filterText) {
|
|
416
|
+
this.filterItems();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
this.reorderSlotContent();
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Listen for the `itemChecked` event emitted by child items.
|
|
425
|
+
* @param {CustomEvent<{ value: string; checked: boolean }>} event - The event object containing the selected value and its checked state.
|
|
426
|
+
*/
|
|
427
|
+
handleItemChecked(event) {
|
|
428
|
+
if (this.disabled || this.readonly)
|
|
429
|
+
return;
|
|
430
|
+
const { value, checked } = event.detail;
|
|
431
|
+
if (value !== undefined && value !== null) {
|
|
432
|
+
const newSelectedValues = [...this.selectedValues];
|
|
433
|
+
if (checked && !newSelectedValues.includes(value)) {
|
|
434
|
+
newSelectedValues.push(value);
|
|
435
|
+
}
|
|
436
|
+
else if (!checked) {
|
|
437
|
+
const index = newSelectedValues.indexOf(value);
|
|
438
|
+
if (index > -1) {
|
|
439
|
+
newSelectedValues.splice(index, 1);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
this.selectedValues = newSelectedValues;
|
|
443
|
+
this.multiselectChange.emit(this.selectedValues);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Emitted when the options change.
|
|
448
|
+
* @param {string | Array<{label: string, value: string, isDivider?: boolean, disabled?: boolean, description?: string, checked?: boolean}>} newValue - The new value of the options.
|
|
449
|
+
*/
|
|
450
|
+
handleOptionsChange(newValue) {
|
|
451
|
+
if (typeof newValue === 'string') {
|
|
452
|
+
try {
|
|
453
|
+
const parsedOpts = JSON.parse(newValue);
|
|
454
|
+
// Process options: initialize selections, reorder items, and add dividers where needed
|
|
455
|
+
this.selectedValues = parsedOpts
|
|
456
|
+
.filter(option => option.checked)
|
|
457
|
+
.map(option => option.value);
|
|
458
|
+
const checkedItems = parsedOpts.filter(option => this.selectedValues.includes(option.value));
|
|
459
|
+
const uncheckedItems = parsedOpts.filter(option => !this.selectedValues.includes(option.value));
|
|
460
|
+
this.parsedOptions = [
|
|
461
|
+
...checkedItems,
|
|
462
|
+
...(checkedItems.length > 0 && uncheckedItems.length > 0
|
|
463
|
+
? [{ isDivider: true, label: '', value: '' }]
|
|
464
|
+
: []),
|
|
465
|
+
...uncheckedItems,
|
|
466
|
+
];
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
console.error('Error parsing options:', error);
|
|
470
|
+
this.parsedOptions = [];
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
else if (Array.isArray(newValue)) {
|
|
474
|
+
this.selectedValues = newValue
|
|
475
|
+
.filter(option => option.checked)
|
|
476
|
+
.map(option => option.value);
|
|
477
|
+
this.parsedOptions = [...newValue];
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
this.parsedOptions = [];
|
|
481
|
+
}
|
|
482
|
+
this.reorderSlotContent();
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Emitted when the value changes.
|
|
486
|
+
*/
|
|
487
|
+
watchValueHandler() {
|
|
488
|
+
// Handle value change and update the corresponding multiselect item if it exists
|
|
489
|
+
this.setInitialSelection();
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Emitted when the filterable property changes.
|
|
493
|
+
* @param {boolean} newValue - The new value of the filterable property.
|
|
494
|
+
*/
|
|
495
|
+
watchFilterableHandler(newValue) {
|
|
496
|
+
this.isFilterable = newValue;
|
|
497
|
+
this.filterable = newValue;
|
|
498
|
+
}
|
|
499
|
+
// Add a watcher for the slot content
|
|
500
|
+
handleSlotChange(event) {
|
|
501
|
+
const slot = event.target;
|
|
502
|
+
if (slot.name === 'content') {
|
|
503
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
504
|
+
// Update selectedValues when slot content changes
|
|
505
|
+
this.selectedValues = items
|
|
506
|
+
.filter(item => item.hasAttribute('checked'))
|
|
507
|
+
.map(item => item.getAttribute('value') || '');
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
//#endregion EVENTS
|
|
511
|
+
/****************************************************************************/
|
|
512
|
+
//#region METHODS
|
|
513
|
+
/**
|
|
514
|
+
* Retrieves the current filter text entered by the user.
|
|
515
|
+
* @returns {string} The filter text.
|
|
516
|
+
*/
|
|
517
|
+
async getFilterText() {
|
|
518
|
+
return this.filterText;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Set the initial selection based on the current value and update the inputElement value.
|
|
522
|
+
*/
|
|
523
|
+
setInitialSelection() {
|
|
524
|
+
var _a;
|
|
525
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitem'));
|
|
526
|
+
const selectedItem = items.find(item => {
|
|
527
|
+
var _a;
|
|
528
|
+
return item.getAttribute('label') === this.value ||
|
|
529
|
+
item.getAttribute('value') === this.value ||
|
|
530
|
+
((_a = item.textContent) === null || _a === void 0 ? void 0 : _a.trim()) === this.value;
|
|
531
|
+
});
|
|
532
|
+
// Remove 'selected' from all items first to reset the state
|
|
533
|
+
items.forEach(item => {
|
|
534
|
+
item.removeAttribute('selected');
|
|
535
|
+
item.classList.remove('selected');
|
|
536
|
+
});
|
|
537
|
+
if (selectedItem) {
|
|
538
|
+
// Add the `selected` attribute and `selected` class for visual styling
|
|
539
|
+
selectedItem.setAttribute('selected', 'true');
|
|
540
|
+
selectedItem.classList.add('selected');
|
|
541
|
+
// Update the value and inputElement value to reflect the pre-selected item
|
|
542
|
+
this.value =
|
|
543
|
+
selectedItem.getAttribute('label') ||
|
|
544
|
+
selectedItem.getAttribute('value') ||
|
|
545
|
+
((_a = selectedItem.textContent) === null || _a === void 0 ? void 0 : _a.trim()) ||
|
|
546
|
+
'';
|
|
547
|
+
if (this.inputElement) {
|
|
548
|
+
this.inputElement.value = this.value;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Reset the filter and make all items visible.
|
|
554
|
+
*/
|
|
555
|
+
async resetFilter() {
|
|
556
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
557
|
+
// Reset visibility state of all dropdown items
|
|
558
|
+
items.forEach(item => {
|
|
559
|
+
item.style.display = '';
|
|
560
|
+
});
|
|
561
|
+
// Clean up filter-related UI elements
|
|
562
|
+
const ul = this.el.querySelector('ul');
|
|
563
|
+
if (ul) {
|
|
564
|
+
const emptyMessage = ul.querySelector('[data-empty]');
|
|
565
|
+
const divider = ul.querySelector('hr.dropdown-divider');
|
|
566
|
+
if (emptyMessage)
|
|
567
|
+
emptyMessage.remove();
|
|
568
|
+
if (divider)
|
|
569
|
+
divider.remove();
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Returns the list of selected values.
|
|
574
|
+
* @returns {string[]} The selected values.
|
|
575
|
+
*/
|
|
576
|
+
async getSelectedValues() {
|
|
577
|
+
return this.selectedValues;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Reorder the content of the slot.
|
|
581
|
+
*/
|
|
582
|
+
reorderSlotContent() {
|
|
583
|
+
if (this.modeState === 'options')
|
|
584
|
+
return;
|
|
585
|
+
const ul = this.el.querySelector('ul');
|
|
586
|
+
if (!ul)
|
|
587
|
+
return;
|
|
588
|
+
// First remove all existing dividers
|
|
589
|
+
ul.querySelectorAll('hr.dropdown-divider').forEach(divider => divider.remove());
|
|
590
|
+
// Get all visible items (not hidden)
|
|
591
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck')).filter(item => item.style.display !== 'none');
|
|
592
|
+
// Check if we have an empty message
|
|
593
|
+
const hasEmptyMessage = ul.querySelector('[data-empty]');
|
|
594
|
+
if (hasEmptyMessage) {
|
|
595
|
+
// If we have an empty message, don't reorder
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
// Separate selected and unselected items
|
|
599
|
+
const selectedItems = items.filter(item => this.selectedValues.includes(item.getAttribute('value') || ''));
|
|
600
|
+
const unselectedItems = items.filter(item => !this.selectedValues.includes(item.getAttribute('value') || ''));
|
|
601
|
+
// Empty the list
|
|
602
|
+
while (ul.firstChild) {
|
|
603
|
+
ul.removeChild(ul.firstChild);
|
|
604
|
+
}
|
|
605
|
+
// Reorder items
|
|
606
|
+
selectedItems.forEach(item => ul.appendChild(item));
|
|
607
|
+
// Add divider only if we have both selected AND unselected items
|
|
608
|
+
// AND if visible items are different
|
|
609
|
+
if (selectedItems.length > 0 &&
|
|
610
|
+
unselectedItems.length > 0 &&
|
|
611
|
+
items.length > 1) {
|
|
612
|
+
const divider = document.createElement('hr');
|
|
613
|
+
divider.className = 'dropdown-divider';
|
|
614
|
+
ul.appendChild(divider);
|
|
615
|
+
}
|
|
616
|
+
// Add unselected items
|
|
617
|
+
unselectedItems.forEach(item => ul.appendChild(item));
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Reorder the content for options mode with async handling
|
|
621
|
+
*/
|
|
622
|
+
async reorderOptionsContent() {
|
|
623
|
+
if (this.isReordering ||
|
|
624
|
+
!this.parsedOptions ||
|
|
625
|
+
this.parsedOptions.length === 0 ||
|
|
626
|
+
this.modeState !== 'options') {
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
try {
|
|
630
|
+
this.isReordering = true;
|
|
631
|
+
// Update parsedOptions first
|
|
632
|
+
const optionsWithoutDivider = this.parsedOptions.filter(opt => !opt.isDivider);
|
|
633
|
+
const selectedOptions = optionsWithoutDivider.filter(opt => this.selectedValues.includes(opt.value));
|
|
634
|
+
const unselectedOptions = optionsWithoutDivider.filter(opt => !this.selectedValues.includes(opt.value));
|
|
635
|
+
// Update parsedOptions with new divider
|
|
636
|
+
this.parsedOptions = [
|
|
637
|
+
...selectedOptions,
|
|
638
|
+
...(selectedOptions.length > 0 && unselectedOptions.length > 0
|
|
639
|
+
? [{ isDivider: true, label: '', value: '' }]
|
|
640
|
+
: []),
|
|
641
|
+
...unselectedOptions,
|
|
642
|
+
];
|
|
643
|
+
// Then update DOM
|
|
644
|
+
await new Promise(resolve => requestAnimationFrame(resolve));
|
|
645
|
+
const ul = this.el.querySelector('ul');
|
|
646
|
+
if (!ul)
|
|
647
|
+
return;
|
|
648
|
+
// Get all items
|
|
649
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
650
|
+
// Remove all existing dividers
|
|
651
|
+
ul.querySelectorAll('hr.dropdown-divider').forEach(divider => divider.remove());
|
|
652
|
+
// Separate selected and unselected items
|
|
653
|
+
const selectedItems = items.filter(item => this.selectedValues.includes(item.getAttribute('value') || ''));
|
|
654
|
+
const unselectedItems = items.filter(item => !this.selectedValues.includes(item.getAttribute('value') || ''));
|
|
655
|
+
// Empty the list
|
|
656
|
+
while (ul.firstChild) {
|
|
657
|
+
ul.removeChild(ul.firstChild);
|
|
658
|
+
}
|
|
659
|
+
// Reorder items
|
|
660
|
+
selectedItems.forEach(item => ul.appendChild(item));
|
|
661
|
+
// Add divider only if there are both selected AND unselected items
|
|
662
|
+
if (selectedItems.length > 0 && unselectedItems.length > 0) {
|
|
663
|
+
const divider = document.createElement('hr');
|
|
664
|
+
divider.className = 'dropdown-divider';
|
|
665
|
+
ul.appendChild(divider);
|
|
666
|
+
}
|
|
667
|
+
// Add unselected items
|
|
668
|
+
unselectedItems.forEach(item => ul.appendChild(item));
|
|
669
|
+
}
|
|
670
|
+
finally {
|
|
671
|
+
this.isReordering = false;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Filter multiselect items based on the text entered by the user.
|
|
676
|
+
*/
|
|
677
|
+
filterItems() {
|
|
678
|
+
const ul = this.el.querySelector('ul');
|
|
679
|
+
if (!ul)
|
|
680
|
+
return;
|
|
681
|
+
// Remove existing empty message if any
|
|
682
|
+
const existingEmptyMessage = ul.querySelector('[data-empty]');
|
|
683
|
+
if (existingEmptyMessage) {
|
|
684
|
+
existingEmptyMessage.remove();
|
|
685
|
+
}
|
|
686
|
+
if (!this.filterText.trim()) {
|
|
687
|
+
if (this.parsedOptions && this.parsedOptions.length > 0) {
|
|
688
|
+
// Reset options display
|
|
689
|
+
const items = Array.from(ul.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
690
|
+
items.forEach(item => {
|
|
691
|
+
item.style.display = '';
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
else {
|
|
695
|
+
// Reset slot items display
|
|
696
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
697
|
+
items.forEach(item => {
|
|
698
|
+
item.style.display = '';
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
this.reorderSlotContent();
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
const normalizedFilter = this.normalizeText(this.filterText);
|
|
705
|
+
let hasVisibleItems = false;
|
|
706
|
+
if (this.parsedOptions && this.parsedOptions.length > 0) {
|
|
707
|
+
// Filter options mode
|
|
708
|
+
const items = Array.from(ul.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
709
|
+
items.forEach(item => {
|
|
710
|
+
const option = this.parsedOptions.find(opt => opt.value === item.getAttribute('value'));
|
|
711
|
+
if (option && !option.isDivider) {
|
|
712
|
+
const matchesFilter = this.normalizeText(option.label).includes(normalizedFilter) ||
|
|
713
|
+
this.normalizeText(option.value).includes(normalizedFilter);
|
|
714
|
+
item.style.display = matchesFilter ? '' : 'none';
|
|
715
|
+
if (matchesFilter)
|
|
716
|
+
hasVisibleItems = true;
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
// Hide dividers if needed
|
|
720
|
+
const dividers = ul.querySelectorAll('hr.dropdown-divider');
|
|
721
|
+
dividers.forEach(divider => {
|
|
722
|
+
divider.style.display = hasVisibleItems ? '' : 'none';
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
else {
|
|
726
|
+
// Filter slot items mode
|
|
727
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
728
|
+
items.forEach(item => {
|
|
729
|
+
const label = item.getAttribute('label') || '';
|
|
730
|
+
const value = item.getAttribute('value') || '';
|
|
731
|
+
const textContent = item.textContent || '';
|
|
732
|
+
const matchesFilter = this.normalizeText(label).includes(normalizedFilter) ||
|
|
733
|
+
this.normalizeText(value).includes(normalizedFilter) ||
|
|
734
|
+
this.normalizeText(textContent).includes(normalizedFilter);
|
|
735
|
+
item.style.display = matchesFilter ? '' : 'none';
|
|
736
|
+
if (matchesFilter)
|
|
737
|
+
hasVisibleItems = true;
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
// Add empty message if no items match the filter
|
|
741
|
+
if (!hasVisibleItems) {
|
|
742
|
+
const emptyMessage = document.createElement('li');
|
|
743
|
+
emptyMessage.setAttribute('data-empty', 'true');
|
|
744
|
+
emptyMessage.textContent = this.emptyresult;
|
|
745
|
+
emptyMessage.classList.add('no-results-message');
|
|
746
|
+
ul.appendChild(emptyMessage);
|
|
747
|
+
}
|
|
748
|
+
this.reorderSlotContent();
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Filter multiselect items in options mode.
|
|
752
|
+
*/
|
|
753
|
+
filterOptionsItems() {
|
|
754
|
+
const ul = this.el.querySelector('ul');
|
|
755
|
+
if (!ul)
|
|
756
|
+
return;
|
|
757
|
+
// Remove existing empty message if any
|
|
758
|
+
const existingEmptyMessage = ul.querySelector('[data-empty]');
|
|
759
|
+
if (existingEmptyMessage) {
|
|
760
|
+
existingEmptyMessage.remove();
|
|
761
|
+
}
|
|
762
|
+
if (!this.filterText.trim()) {
|
|
763
|
+
// Reset options display
|
|
764
|
+
const items = Array.from(ul.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
765
|
+
items.forEach(item => {
|
|
766
|
+
item.style.display = '';
|
|
767
|
+
});
|
|
768
|
+
this.reorderOptionsContent();
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
const normalizedFilter = this.normalizeText(this.filterText);
|
|
772
|
+
let hasVisibleItems = false;
|
|
773
|
+
// Filter options mode
|
|
774
|
+
const items = Array.from(ul.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
775
|
+
items.forEach(item => {
|
|
776
|
+
const option = this.parsedOptions.find(opt => opt.value === item.getAttribute('value'));
|
|
777
|
+
if (option && !option.isDivider) {
|
|
778
|
+
const matchesFilter = this.normalizeText(option.label).includes(normalizedFilter) ||
|
|
779
|
+
this.normalizeText(option.value).includes(normalizedFilter);
|
|
780
|
+
item.style.display = matchesFilter ? '' : 'none';
|
|
781
|
+
if (matchesFilter)
|
|
782
|
+
hasVisibleItems = true;
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
// Get visible items after filtering
|
|
786
|
+
const visibleItems = items.filter(item => item.style.display !== 'none');
|
|
787
|
+
const visibleSelectedItems = visibleItems.filter(item => this.selectedValues.includes(item.getAttribute('value') || ''));
|
|
788
|
+
// Hide divider if all visible items are either all selected or all unselected
|
|
789
|
+
const divider = ul.querySelector('hr.dropdown-divider');
|
|
790
|
+
if (divider) {
|
|
791
|
+
const shouldShowDivider = visibleSelectedItems.length > 0 &&
|
|
792
|
+
visibleSelectedItems.length < visibleItems.length;
|
|
793
|
+
divider.style.display = shouldShowDivider ? '' : 'none';
|
|
794
|
+
}
|
|
795
|
+
// Add empty message if no items match the filter
|
|
796
|
+
if (!hasVisibleItems) {
|
|
797
|
+
const emptyMessage = document.createElement('li');
|
|
798
|
+
emptyMessage.setAttribute('data-empty', 'true');
|
|
799
|
+
emptyMessage.textContent = this.emptyresult;
|
|
800
|
+
emptyMessage.classList.add('no-results-message');
|
|
801
|
+
ul.appendChild(emptyMessage);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Filter multiselect items in slots mode
|
|
806
|
+
*/
|
|
807
|
+
filterSlotsItems() {
|
|
808
|
+
if (this.modeState === 'options')
|
|
809
|
+
return;
|
|
810
|
+
const ul = this.el.querySelector('ul');
|
|
811
|
+
if (!ul)
|
|
812
|
+
return;
|
|
813
|
+
// Remove existing empty message if any
|
|
814
|
+
const existingEmptyMessage = ul.querySelector('[data-empty]');
|
|
815
|
+
if (existingEmptyMessage) {
|
|
816
|
+
existingEmptyMessage.remove();
|
|
817
|
+
}
|
|
818
|
+
// If filter text is empty, reset all items visibility
|
|
819
|
+
if (!this.filterText.trim()) {
|
|
820
|
+
this.resetFilter();
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
const normalizedFilter = this.normalizeText(this.filterText);
|
|
824
|
+
let hasVisibleItems = false;
|
|
825
|
+
// Get all items and preserve them in the DOM
|
|
826
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
827
|
+
// Instead of removing/reordering, just hide/show items
|
|
828
|
+
items.forEach(item => {
|
|
829
|
+
var _a;
|
|
830
|
+
const label = item.getAttribute('label') || '';
|
|
831
|
+
const value = item.getAttribute('value') || '';
|
|
832
|
+
const textContent = ((_a = item.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || '';
|
|
833
|
+
const matchesFilter = this.normalizeText(label).includes(normalizedFilter) ||
|
|
834
|
+
this.normalizeText(value).includes(normalizedFilter) ||
|
|
835
|
+
this.normalizeText(textContent).includes(normalizedFilter);
|
|
836
|
+
item.style.display = matchesFilter ? '' : 'none';
|
|
837
|
+
if (matchesFilter)
|
|
838
|
+
hasVisibleItems = true;
|
|
839
|
+
});
|
|
840
|
+
// Get visible items after filtering
|
|
841
|
+
const visibleItems = items.filter(item => item.style.display !== 'none');
|
|
842
|
+
const visibleSelectedItems = visibleItems.filter(item => this.selectedValues.includes(item.getAttribute('value') || ''));
|
|
843
|
+
// Hide divider if all visible items are either all selected or all unselected
|
|
844
|
+
const divider = ul.querySelector('hr.dropdown-divider');
|
|
845
|
+
if (divider) {
|
|
846
|
+
const shouldShowDivider = visibleSelectedItems.length > 0 &&
|
|
847
|
+
visibleSelectedItems.length < visibleItems.length;
|
|
848
|
+
divider.style.display = shouldShowDivider ? '' : 'none';
|
|
849
|
+
}
|
|
850
|
+
// Add empty message if no items match the filter
|
|
851
|
+
if (!hasVisibleItems) {
|
|
852
|
+
const emptyMessage = document.createElement('li');
|
|
853
|
+
emptyMessage.setAttribute('data-empty', 'true');
|
|
854
|
+
emptyMessage.textContent = this.emptyresult;
|
|
855
|
+
emptyMessage.classList.add('no-results-message');
|
|
856
|
+
ul.appendChild(emptyMessage);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Normalizes text by removing accents and converting to lowercase
|
|
861
|
+
* @param {string} text - The text to normalize
|
|
862
|
+
* @returns {string} The normalized text
|
|
863
|
+
*
|
|
864
|
+
* @example
|
|
865
|
+
* normalizeText("Café Latte") => "cafe latte"
|
|
866
|
+
*
|
|
867
|
+
* @description
|
|
868
|
+
* This function performs text normalization in three steps:
|
|
869
|
+
* 1. Decomposes characters into their base form and combining characters (NFD)
|
|
870
|
+
* 2. Removes all diacritical marks (accents, umlauts, etc.)
|
|
871
|
+
* 3. Converts to lowercase and trims whitespace
|
|
872
|
+
*
|
|
873
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize} - MDN documentation on String.normalize()
|
|
874
|
+
* @see {@link https://en.wikipedia.org/wiki/Unicode_equivalence#Normal_forms} - More info about Unicode normalization forms
|
|
875
|
+
*/
|
|
876
|
+
normalizeText(text) {
|
|
877
|
+
return text
|
|
878
|
+
.normalize('NFD') // Decompose characters into base + combining characters (e.g., é => e + ´)
|
|
879
|
+
.replace(/[\u0300-\u036f]/g, '') // Remove all diacritical marks (Unicode range for combining characters)
|
|
880
|
+
.toLowerCase() // Convert to lowercase
|
|
881
|
+
.trim(); // Remove leading and trailing whitespace
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Handle click outside the component.
|
|
885
|
+
* @param {MouseEvent} event - The click event.
|
|
886
|
+
*/
|
|
887
|
+
handleClickOutside(event) {
|
|
888
|
+
if (this.el.contains(event.target) ||
|
|
889
|
+
(this.inputElement && this.inputElement.contains(event.target))) {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
// Force le même comportement que la fermeture par iconbutton
|
|
893
|
+
if (this.modeState === 'options') {
|
|
894
|
+
this.handlePopoverClose();
|
|
895
|
+
}
|
|
896
|
+
else {
|
|
897
|
+
this.popoverElement.hide();
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Handle keyboard events & arrow key navigation.
|
|
902
|
+
* If the multiselect is not open, opens it and focuses on the first item if the list is not filterable.
|
|
903
|
+
* If the multiselect is open, handles arrow key navigation and closes it if the focus is outside the component.
|
|
904
|
+
* @param {KeyboardEvent} event - The keyboard event.
|
|
905
|
+
*/
|
|
906
|
+
handleKeyDown(event) {
|
|
907
|
+
if (!this.open) {
|
|
908
|
+
if (event.key === 'ArrowDown') {
|
|
909
|
+
this.open = true;
|
|
910
|
+
this.popoverElement.show();
|
|
911
|
+
// Focus on the first item if the list is not filterable
|
|
912
|
+
if (!this.filterable) {
|
|
913
|
+
this.focusFirstItem();
|
|
914
|
+
}
|
|
915
|
+
event.preventDefault();
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
const items = Array.from(this.el.querySelectorAll('nv-fielddropdownitemcheck'));
|
|
921
|
+
if (items.length === 0) {
|
|
922
|
+
console.warn('No dropdown items found to navigate');
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
let currentIndex = items.findIndex(item => item.classList.contains('highlighted'));
|
|
926
|
+
if (event.key === 'ArrowDown') {
|
|
927
|
+
event.preventDefault();
|
|
928
|
+
currentIndex = (currentIndex + 1) % items.length;
|
|
929
|
+
this.updateHighlightedItem(items, currentIndex);
|
|
930
|
+
}
|
|
931
|
+
else if (event.key === 'ArrowUp') {
|
|
932
|
+
event.preventDefault();
|
|
933
|
+
currentIndex = (currentIndex - 1 + items.length) % items.length;
|
|
934
|
+
this.updateHighlightedItem(items, currentIndex);
|
|
935
|
+
}
|
|
936
|
+
else if (event.key === 'Enter' && currentIndex >= 0) {
|
|
937
|
+
event.preventDefault();
|
|
938
|
+
items[currentIndex].click();
|
|
939
|
+
}
|
|
940
|
+
else if (event.key === 'Escape') {
|
|
941
|
+
event.preventDefault();
|
|
942
|
+
this.open = false;
|
|
943
|
+
this.popoverElement.hide();
|
|
944
|
+
if (this.inputElement) {
|
|
945
|
+
this.inputElement.blur();
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Updates the highlighted item in the dropdown list.
|
|
951
|
+
*
|
|
952
|
+
* @param {(HTMLNvFielddropdownitemElement | HTMLNvFielddropdownitemcheckElement)[]} items - The items to update.
|
|
953
|
+
* @param {number} index - The index of the item to highlight.
|
|
954
|
+
*/
|
|
955
|
+
updateHighlightedItem(items, index) {
|
|
956
|
+
items.forEach((item, i) => {
|
|
957
|
+
if (i === index) {
|
|
958
|
+
item.classList.add('highlighted');
|
|
959
|
+
item.setAttribute('tabindex', '0');
|
|
960
|
+
item.focus();
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
item.classList.remove('highlighted');
|
|
964
|
+
item.setAttribute('tabindex', '-1');
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Focus on the first item in the dropdown list.
|
|
970
|
+
*/
|
|
971
|
+
focusFirstItem() {
|
|
972
|
+
const firstItem = this.el.querySelector('nv-fielddropdownitemcheck');
|
|
973
|
+
if (firstItem) {
|
|
974
|
+
firstItem.setAttribute('tabindex', '0');
|
|
975
|
+
firstItem.classList.add('highlighted');
|
|
976
|
+
firstItem.focus();
|
|
977
|
+
}
|
|
978
|
+
else {
|
|
979
|
+
console.warn('No first item found to focus');
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
983
|
+
* Watch for changes in the mode prop and update modeState accordingly
|
|
984
|
+
* @param {string} newValue - The new mode value
|
|
985
|
+
*/
|
|
986
|
+
handleModeChange(newValue) {
|
|
987
|
+
this.modeState = newValue;
|
|
988
|
+
}
|
|
989
|
+
//#endregion METHODS
|
|
990
|
+
/****************************************************************************/
|
|
991
|
+
//#region RENDER
|
|
992
|
+
/**
|
|
993
|
+
* Renders the component in options mode
|
|
994
|
+
* @returns {any} The JSX for options mode
|
|
995
|
+
*/
|
|
996
|
+
renderOptionsMode() {
|
|
997
|
+
return (index.h(index.Host, null, (this.label || this.el.querySelector('[slot="label"]')) && (index.h("label", { htmlFor: this.inputId }, index.h("slot", { name: "label" }, this.label))), index.h("nv-popover", { ref: el => (this.popoverElement = el), triggerMode: "controlled", placement: "bottom-start", open: this.open }, index.h("div", { class: "input-wrapper-multiselect", slot: "trigger" }, index.h("slot", { name: "before-input" }), index.h("div", { class: "input-container-multiselect" }, index.h("slot", { name: "leading-input" }), index.h("nv-badge", { slot: "leading-input", "prevent-auto-close": "true", color: "10", dismissible: this.selectedValues.length > 0, hidden: this.selectedValues.length <= 0, label: `${this.selectedValues.length} ${this.badgeLabel}`, "aria-label": `Clear all ${this.selectedValues.length} ${this.badgeLabel} items`, onCloseClicked: this.handleBadgeCloseOptions }), this.isFilterable || this.disabled || this.readonly ? (index.h("input", { id: this.inputId, ref: e => (this.inputElement = e), autocomplete: this.autocomplete, placeholder: this.placeholder, name: this.name, value: this.value, required: this.required, disabled: this.disabled, readOnly: this.readonly, onInput: this.handleInputOptions, onFocus: this.handleInputFocusOptions, onBlur: this.handleInputBlurOptions, onKeyDown: this.handleKeyDownOptions })) : (index.h("p", { id: this.inputId, class: "non-filterable-text", onClick: this.handleInputContainerClickOptions, tabIndex: 0, onKeyDown: this.handleKeyDownOptions, onFocus: this.handleInputFocusOptions, role: "combobox", "aria-expanded": this.open }, index.h("span", null, this.value || this.placeholder))), index.h("nv-iconbutton", { class: "toggle-dropdown-icon", name: this.open ? 'chevron-top' : 'chevron-down', size: "md", emphasis: "lower", "aria-label": this.open ? 'Hide dropdown' : 'Show dropdown', "aria-pressed": this.open.toString(), onClick: this.togglePopoverOptions })), index.h("slot", { name: "after-input" })), index.h("div", { slot: "content", role: "listbox", "aria-multiselectable": "true", style: { 'max-height': this.maxHeight, 'overflow-y': 'auto' } }, index.h("ul", { role: "presentation" }, this.parsedOptions.map(option => option.isDivider ? (index.h("hr", { class: "dropdown-divider" })) : (index.h("nv-fielddropdownitemcheck", { label: option.label, description: option.description, value: option.value, checked: this.selectedValues.includes(option.value), disabled: option.disabled })))))), this.renderDescriptions()));
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Renders the component in slots mode
|
|
1001
|
+
* @returns {any} The JSX for slots mode
|
|
1002
|
+
*/
|
|
1003
|
+
renderSlotsMode() {
|
|
1004
|
+
return (index.h(index.Host, null, (this.label || this.el.querySelector('[slot="label"]')) && (index.h("label", { htmlFor: this.inputId }, index.h("slot", { name: "label" }, this.label))), index.h("nv-popover", { ref: el => (this.popoverElement = el), triggerMode: "controlled", placement: "bottom-start", open: this.open }, index.h("div", { class: "input-wrapper-multiselect", slot: "trigger" }, index.h("slot", { name: "before-input" }), index.h("div", { class: "input-container-multiselect", onClick: this.handleInputContainerClickSlots }, index.h("slot", { name: "leading-input" }, index.h("p", null, "No leading input")), index.h("nv-badge", { slot: "leading-input", "prevent-auto-close": "true", color: "10", dismissible: this.selectedValues.length > 0, hidden: this.selectedValues.length <= 0, label: `${this.selectedValues.length} ${this.badgeLabel}`, "aria-label": `Clear all ${this.selectedValues.length} ${this.badgeLabel} items`, onCloseClicked: this.handleBadgeCloseSlots }), this.isFilterable || this.disabled || this.readonly ? (index.h("input", { id: this.inputId, ref: e => (this.inputElement = e), autocomplete: this.autocomplete, placeholder: this.placeholder, name: this.name, value: this.value, required: this.required, disabled: this.disabled, readOnly: this.readonly, onInput: this.handleInputSlots, onFocus: this.handleInputFocusSlots, onBlur: this.handleInputBlurSlots, onKeyDown: this.handleKeyDownSlots })) : (index.h("p", { id: this.inputId, class: "non-filterable-text", onClick: this.handleInputContainerClickSlots, tabIndex: 0, onKeyDown: this.handleKeyDownSlots, onFocus: this.handleInputFocusSlots, role: "combobox", "aria-expanded": this.open }, index.h("span", null, this.value || this.placeholder))), index.h("nv-iconbutton", { class: "toggle-dropdown-icon", name: this.open ? 'chevron-top' : 'chevron-down', size: "md", emphasis: "lower", "aria-label": this.open ? 'Hide dropdown' : 'Show dropdown', "aria-pressed": this.open.toString(), onClick: this.togglePopoverSlots })), index.h("slot", { name: "after-input" })), index.h("div", { slot: "content", role: "listbox", "aria-multiselectable": "true", style: { 'max-height': this.maxHeight, 'overflow-y': 'auto' } }, index.h("slot", { name: "content" }))), this.renderDescriptions()));
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Renders description and error description sections
|
|
1008
|
+
* @returns {any} The JSX for descriptions
|
|
1009
|
+
*/
|
|
1010
|
+
renderDescriptions() {
|
|
1011
|
+
return [
|
|
1012
|
+
(this.description || this.el.querySelector('[slot="description"]')) && (index.h("div", { class: "description" }, index.h("slot", { name: "description" }, this.description))),
|
|
1013
|
+
(this.errorDescription ||
|
|
1014
|
+
this.el.querySelector('[slot="error-description"]')) && (index.h("div", { hidden: !this.error, class: "error-description" }, index.h("slot", { name: "error-description" }, this.errorDescription))),
|
|
1015
|
+
];
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Main render method that decides which mode to render
|
|
1019
|
+
* @returns {any} The JSX for the component
|
|
1020
|
+
*/
|
|
1021
|
+
render() {
|
|
1022
|
+
return this.modeState === 'options'
|
|
1023
|
+
? this.renderOptionsMode()
|
|
1024
|
+
: this.renderSlotsMode();
|
|
1025
|
+
}
|
|
1026
|
+
get el() { return index.getElement(this); }
|
|
1027
|
+
static get watchers() { return {
|
|
1028
|
+
"options": ["handleOptionsChange"],
|
|
1029
|
+
"value": ["watchValueHandler"],
|
|
1030
|
+
"filterable": ["watchFilterableHandler"],
|
|
1031
|
+
"mode": ["handleModeChange"]
|
|
1032
|
+
}; }
|
|
1033
|
+
};
|
|
1034
|
+
NvFieldmultiselect.style = NvFieldmultiselectStyle0;
|
|
1035
|
+
|
|
1036
|
+
exports.nv_fieldmultiselect = NvFieldmultiselect;
|
|
1037
|
+
|
|
1038
|
+
//# sourceMappingURL=nv-fieldmultiselect.cjs.entry.js.map
|