@aquera/nile-elements 1.7.2 → 1.7.3
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 +4 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +387 -265
- package/dist/nile-breadcrumb-item/nile-breadcrumb-item.cjs.js +1 -1
- package/dist/nile-breadcrumb-item/nile-breadcrumb-item.cjs.js.map +1 -1
- package/dist/nile-breadcrumb-item/nile-breadcrumb-item.esm.js +8 -6
- package/dist/nile-combobox/group-utils.cjs.js +2 -0
- package/dist/nile-combobox/group-utils.cjs.js.map +1 -0
- package/dist/nile-combobox/group-utils.esm.js +1 -0
- package/dist/nile-combobox/index.cjs.js +1 -1
- package/dist/nile-combobox/index.esm.js +1 -1
- package/dist/nile-combobox/nile-combobox.cjs.js +1 -1
- package/dist/nile-combobox/nile-combobox.cjs.js.map +1 -1
- package/dist/nile-combobox/nile-combobox.css.cjs.js +1 -1
- package/dist/nile-combobox/nile-combobox.css.cjs.js.map +1 -1
- package/dist/nile-combobox/nile-combobox.css.esm.js +77 -4
- package/dist/nile-combobox/nile-combobox.esm.js +13 -8
- package/dist/nile-combobox/renderer.cjs.js +1 -1
- package/dist/nile-combobox/renderer.cjs.js.map +1 -1
- package/dist/nile-combobox/renderer.esm.js +84 -42
- package/dist/src/nile-breadcrumb-item/nile-breadcrumb-item.js +4 -2
- package/dist/src/nile-breadcrumb-item/nile-breadcrumb-item.js.map +1 -1
- package/dist/src/nile-combobox/group-utils.d.ts +26 -0
- package/dist/src/nile-combobox/group-utils.js +140 -0
- package/dist/src/nile-combobox/group-utils.js.map +1 -0
- package/dist/src/nile-combobox/nile-combobox.css.js +77 -4
- package/dist/src/nile-combobox/nile-combobox.css.js.map +1 -1
- package/dist/src/nile-combobox/nile-combobox.d.ts +33 -0
- package/dist/src/nile-combobox/nile-combobox.js +171 -34
- package/dist/src/nile-combobox/nile-combobox.js.map +1 -1
- package/dist/src/nile-combobox/renderer.d.ts +4 -0
- package/dist/src/nile-combobox/renderer.js +71 -2
- package/dist/src/nile-combobox/renderer.js.map +1 -1
- package/dist/src/nile-combobox/types.d.ts +30 -0
- package/dist/src/nile-combobox/types.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/nile-breadcrumb-item/nile-breadcrumb-item.ts +4 -2
- package/src/nile-combobox/group-utils.ts +157 -0
- package/src/nile-combobox/nile-combobox.css.ts +77 -4
- package/src/nile-combobox/nile-combobox.ts +223 -70
- package/src/nile-combobox/renderer.ts +119 -2
- package/src/nile-combobox/types.ts +36 -0
- package/vscode-html-custom-data.json +6 -1
|
@@ -139,9 +139,10 @@ export const styles = css `
|
|
|
139
139
|
/* Size: medium */
|
|
140
140
|
.combobox--medium .combobox__trigger {
|
|
141
141
|
border-radius: var(--nile-radius-sm, var(--ng-radius-md));
|
|
142
|
-
font-size: var(--nile-type-scale-3, var(--ng-font-size-text-
|
|
143
|
-
padding: var(--nile-spacing-
|
|
142
|
+
font-size: var(--nile-type-scale-3, var(--ng-font-size-text-md));
|
|
143
|
+
padding: var(--nile-spacing-lg, var(--ng-spacing-md)) var(--nile-spacing-lg, var(--ng-spacing-lg));
|
|
144
144
|
min-height: var(--nile-height-40px, var(--ng-height-40px));
|
|
145
|
+
box-sizing: border-box;
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
/* Size: large */
|
|
@@ -254,10 +255,10 @@ export const styles = css `
|
|
|
254
255
|
margin: 0;
|
|
255
256
|
-webkit-appearance: none;
|
|
256
257
|
font-family: var(--nile-font-family-serif, var(--ng-font-family-body));
|
|
257
|
-
font-size:
|
|
258
|
+
font-size: inherit;
|
|
258
259
|
font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-regular));
|
|
259
260
|
text-overflow: ellipsis;
|
|
260
|
-
line-height: var(--nile-
|
|
261
|
+
line-height: var(--nile-line-height-xsmall, var(--ng-line-height-text-sm));
|
|
261
262
|
}
|
|
262
263
|
|
|
263
264
|
.combobox__input::placeholder {
|
|
@@ -634,6 +635,78 @@ export const styles = css `
|
|
|
634
635
|
border-radius: 0;
|
|
635
636
|
}
|
|
636
637
|
|
|
638
|
+
/* ── Group headers (data-driven grouping) ── */
|
|
639
|
+
|
|
640
|
+
/* Plain (non-virtualized) listbox: sticky relative to the scroll container.
|
|
641
|
+
* Activated only when the host has [sticky-group-header]. */
|
|
642
|
+
:host([sticky-group-header]) .combobox__options-plain .combobox__group-header {
|
|
643
|
+
position: sticky;
|
|
644
|
+
top: calc(var(--group-depth, 0) * 28px);
|
|
645
|
+
z-index: 1;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/* Virtualized listbox: each row is absolutely positioned by the virtualizer.
|
|
649
|
+
* Sticky doesn't pin against the scroll container because the row wrapper's
|
|
650
|
+
* transform creates a containing block. Instead we render a separate
|
|
651
|
+
* overlay inside the listbox that mirrors the currently-active group
|
|
652
|
+
* header (computed on scroll). */
|
|
653
|
+
.combobox__group-header-slot {
|
|
654
|
+
pointer-events: none;
|
|
655
|
+
}
|
|
656
|
+
.combobox__group-header-slot .combobox__group-header {
|
|
657
|
+
height: 100%;
|
|
658
|
+
box-sizing: border-box;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.combobox__group-sticky-overlay {
|
|
662
|
+
position: sticky;
|
|
663
|
+
top: 0;
|
|
664
|
+
z-index: 2;
|
|
665
|
+
pointer-events: none;
|
|
666
|
+
}
|
|
667
|
+
.combobox__group-sticky-overlay .combobox__group-header {
|
|
668
|
+
box-shadow: 0 1px 0 0 var(--nile-colors-neutral-300, var(--ng-colors-border-secondary));
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
.combobox__group-header {
|
|
672
|
+
display: flex;
|
|
673
|
+
align-items: center;
|
|
674
|
+
gap: 6px;
|
|
675
|
+
box-sizing: border-box;
|
|
676
|
+
width: 100%;
|
|
677
|
+
height: 100%;
|
|
678
|
+
/* Match the listbox's natural inline padding so the header sits like a
|
|
679
|
+
* section divider, edge-to-edge with options. Nested groups indent by
|
|
680
|
+
* depth. Override with --combobox-group-header-indent if needed. */
|
|
681
|
+
padding-block: 8px;
|
|
682
|
+
padding-inline-end: var(--nile-spacing-lg, var(--ng-spacing-lg));
|
|
683
|
+
padding-inline-start: calc(var(--combobox-group-header-indent, var(--nile-spacing-lg, var(--ng-spacing-lg))) + var(--group-depth, 0) * 16px);
|
|
684
|
+
font-family: var(--nile-font-family-sans-serif, var(--ng-font-family-body));
|
|
685
|
+
font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));
|
|
686
|
+
font-weight: var(--nile-font-weight-semibold, var(--ng-font-weight-semibold));
|
|
687
|
+
line-height: 1;
|
|
688
|
+
text-transform: uppercase;
|
|
689
|
+
letter-spacing: 0.06em;
|
|
690
|
+
color: var(--nile-colors-dark-500, var(--ng-colors-text-tertiary-600));
|
|
691
|
+
background: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));
|
|
692
|
+
border-bottom: 1px solid var(--nile-colors-neutral-300, var(--ng-colors-border-secondary));
|
|
693
|
+
pointer-events: none;
|
|
694
|
+
user-select: none;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
.combobox__group-label {
|
|
698
|
+
flex: 1;
|
|
699
|
+
min-width: 0;
|
|
700
|
+
overflow: hidden;
|
|
701
|
+
text-overflow: ellipsis;
|
|
702
|
+
white-space: nowrap;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
.combobox__group-prefix {
|
|
706
|
+
flex-shrink: 0;
|
|
707
|
+
color: inherit;
|
|
708
|
+
}
|
|
709
|
+
|
|
637
710
|
/* ── Help / Error ── */
|
|
638
711
|
|
|
639
712
|
.form-control__help-text {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nile-combobox.css.js","sourceRoot":"","sources":["../../../src/nile-combobox/nile-combobox.css.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAioBxB,CAAC;AAEF,eAAe,CAAC,MAAM,CAAC,CAAC","sourcesContent":["/**\n * Copyright Aquera Inc 2025\n *\n * This source code is licensed under the BSD-3-Clause license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { css } from 'lit';\n\nexport const styles = css`\n :host {\n display: block;\n box-sizing: border-box;\n -webkit-font-smoothing: var(--nile-webkit-font-smoothing, var(--ng-webkit-font-smoothing));\n -moz-osx-font-smoothing: var(--nile-moz-osx-font-smoothing, var(--ng-moz-osx-font-smoothing));\n text-rendering: var(--nile-text-rendering, var(--ng-text-rendering));\n }\n\n :host *,\n :host *::before,\n :host *::after {\n box-sizing: inherit;\n }\n\n [hidden] {\n display: none;\n }\n\n /* ── Form control ── */\n\n .form-control .form-control__label {\n display: none;\n }\n\n .form-control--has-label .form-control__label {\n display: block;\n margin-bottom: var(--nile-spacing-sm, var(--ng-spacing-sm));\n color: var(--nile-colors-dark-900, var(--ng-colors-text-secondary-700));\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-md));\n font-style: normal;\n font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-medium));\n line-height: var(--nile-spacing-2xl, var(--ng-line-height-text-sm));\n letter-spacing: 0.2px;\n }\n\n .form-control--has-label.form-control--medium .form-control__label {\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n }\n\n :host([required]) .form-control--has-label .form-control__label::after {\n content: '*';\n margin-inline-start: -2px;\n color: var(--nile-colors-red-700, var(--ng-colors-text-error-primary-600));\n }\n\n :host([disabled]) .form-control--has-label .form-control__label {\n user-select: none;\n -webkit-user-select: none;\n }\n\n /* ── Popup ── */\n\n .combobox-popup {\n flex: 1 1 auto;\n display: inline-flex;\n width: 100%;\n position: relative;\n vertical-align: middle;\n }\n\n .combobox-popup::part(popup) {\n z-index: 9999;\n }\n\n .combobox-popup[data-current-placement^='top']::part(popup) {\n transform-origin: bottom;\n }\n\n .combobox-popup[data-current-placement^='bottom']::part(popup) {\n transform-origin: top;\n }\n\n /* ── Trigger: outer row that never scrolls ── */\n\n .combobox__trigger {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n position: relative;\n font-family: var(--nile-font-family-sans-serif, var(--ng-font-family-body));\n font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-medium));\n letter-spacing: normal;\n cursor: text;\n transition: 150ms color, 150ms border, 150ms box-shadow, 150ms background-color;\n background-color: var(--nile-colors-white-base, var(--ng-colors-bg-primary));\n border: solid 1px var(--nile-colors-neutral-500, var(--ng-colors-border-primary));\n outline: none;\n }\n\n .combobox__trigger:hover {\n border-color: var(--nile-colors-dark-900, var(--ng-colors-border-neutral));\n }\n\n .combobox--open .combobox__trigger,\n .combobox--focused .combobox__trigger {\n border-color: var(--ng-colors-border-brand);\n box-shadow: 0 0 0 1px var(--ng-colors-border-brand) inset;\n background-color: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));\n }\n\n .combobox--disabled .combobox__trigger {\n background-color: var(--nile-colors-dark-200, var(--ng-colors-bg-disabled-subtle));\n border-color: var(--nile-colors-neutral-500, var(--ng-colors-border-primary));\n color: var(--nile-colors-dark-500, var(--ng-colors-text-disabled));\n cursor: not-allowed;\n user-select: none;\n }\n\n .combobox--warning .combobox__trigger {\n border-color: var(--nile-colors-yellow-500, var(--ng-colors-border-warning));\n }\n\n .combobox--error .combobox__trigger {\n border-color: var(--nile-colors-red-500, var(--ng-colors-border-error));\n }\n\n .combobox--success .combobox__trigger {\n border-color: var(--nile-colors-green-500, var(--ng-componentcolors-utility-success-500));\n }\n\n /* Size: small */\n .combobox--small .combobox__trigger {\n border-radius: var(--nile-radius-sm, var(--ng-radius-md));\n font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));\n padding: var(--nile-spacing-md, var(--ng-spacing-sm)) var(--nile-spacing-md, var(--ng-spacing-md));\n min-height: var(--nile-height-32px, var(--ng-height-32px));\n }\n\n /* Size: medium */\n .combobox--medium .combobox__trigger {\n border-radius: var(--nile-radius-sm, var(--ng-radius-md));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n padding: var(--nile-spacing-5px, var(--ng-spacing-md)) var(--nile-spacing-10px, var(--ng-spacing-lg));\n min-height: var(--nile-height-40px, var(--ng-height-40px));\n }\n\n /* Size: large */\n .combobox--large .combobox__trigger {\n border-radius: var(--nile-radius-sm, var(--ng-radius-md));\n font-size: var(--nile-type-scale-4, var(--ng-font-size-text-md));\n padding: var(--nile-spacing-xl, var(--ng-spacing-lg)) var(--nile-spacing-xl, var(--ng-spacing-xl));\n min-height: var(--nile-height-48px, var(--ng-height-48px));\n }\n\n .combobox--pill .combobox__trigger {\n border-radius: var(--nile-radius-3xl, var(--ng-radius-3xl));\n }\n\n .combobox--filled .combobox__trigger {\n border: none;\n background-color: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));\n }\n\n .combobox--filled.combobox--open .combobox__trigger,\n .combobox--filled.combobox--focused .combobox__trigger {\n outline: 3px solid rgba(0, 89, 255, 0.4);\n }\n\n /* ── Inner area: holds tags + input ── */\n\n .combobox__scroll-area {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n overflow: hidden;\n }\n\n /* Single-line: horizontal scroll for overflowing tags */\n .combobox__scroll-area--single-line {\n flex-wrap: nowrap;\n overflow-x: auto;\n overflow-y: hidden;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: thin;\n scrollbar-color: var(--nile-colors-neutral-500, var(--ng-colors-border-primary)) transparent;\n }\n\n .combobox__scroll-area--single-line::-webkit-scrollbar {\n height: 3px;\n }\n\n .combobox__scroll-area--single-line::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .combobox__scroll-area--single-line::-webkit-scrollbar-thumb {\n background: var(--nile-colors-neutral-500, var(--ng-colors-bg-quaternary));\n border-radius: 3px;\n }\n\n .combobox__scroll-area--single-line::-webkit-scrollbar-thumb:hover {\n background: var(--nile-colors-dark-500, var(--ng-colors-fg-quaternary-400));\n }\n\n /* Wrap: tags flow to multiple lines, trigger grows in height */\n .combobox__scroll-area--wrap {\n flex-wrap: wrap;\n overflow: visible;\n }\n\n /* ── Tags inside the scroll area ── */\n\n .combobox__scroll-area nile-tag {\n cursor: pointer;\n flex-shrink: 0;\n }\n\n .combobox--disabled .combobox__scroll-area nile-tag {\n cursor: not-allowed;\n }\n\n .combobox__tags-count {\n color: var(--nile-colors-primary-600, var(--ng-colors-text-brand-secondary-700));\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-medium));\n line-height: var(--nile-line-height-xsmall, var(--ng-line-height-text-sm));\n letter-spacing: 0.2px;\n white-space: nowrap;\n flex-shrink: 0;\n }\n\n /* ── Input wrapper inside scroll area ── */\n\n .combobox__input-wrapper {\n flex: 1;\n min-width: 60px;\n display: flex;\n align-items: center;\n }\n\n .combobox__input {\n width: 100%;\n font: inherit;\n border: none;\n background: none;\n color: var(--nile-colors-dark-900, var(--ng-colors-text-primary-900));\n cursor: inherit;\n overflow: hidden;\n padding: 0;\n margin: 0;\n -webkit-appearance: none;\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-md));\n font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-regular));\n text-overflow: ellipsis;\n line-height: var(--nile-spacing-2xl, var(--ng-line-height-text-sm));\n }\n\n .combobox__input::placeholder {\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n color: var(--nile-colors-text-placeholder, var(--ng-colors-text-placeholder));\n }\n\n .combobox__input:focus {\n outline: none;\n }\n\n .combobox--disabled .combobox__input {\n color: var(--nile-colors-dark-500, var(--ng-colors-text-disabled));\n pointer-events: none;\n }\n\n /* ── Hidden form value input ── */\n\n .combobox__value-input {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 0;\n margin: 0;\n opacity: 0;\n z-index: -1;\n }\n\n /* ── Action icons (clear, expand) ── */\n\n .combobox__actions {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n gap: var(--nile-spacing-sm, var(--ng-spacing-sm));\n margin-inline-start: auto;\n }\n\n .combobox__clear {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: none;\n padding: 0;\n cursor: pointer;\n color: var(--nile-colors-dark-500, var(--ng-colors-fg-quaternary-400));\n transition: 150ms color;\n }\n\n .combobox__clear:hover {\n color: var(--nile-colors-dark-900, var(--ng-colors-text-primary-900));\n }\n\n .combobox__clear:focus {\n outline: none;\n }\n\n .combobox__expand-icon {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n transition: 250ms rotate ease;\n rotate: 0;\n }\n\n .combobox--open .combobox__expand-icon {\n rotate: -180deg;\n }\n\n /* ── Prefix / suffix ── */\n\n .combobox__prefix,\n .combobox__suffix {\n flex: 0;\n display: inline-flex;\n align-items: center;\n color: var(--nile-colors-dark-500, var(--ng-colors-text-tertiary-600));\n }\n\n .combobox__prefix {\n margin-inline-end: var(--nile-spacing-md, var(--ng-spacing-md));\n }\n\n /* ── Listbox (dropdown) ── */\n\n .combobox__listbox {\n display: block;\n position: relative;\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-md));\n font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-regular));\n background: var(--nile-colors-white-base, var(--ng-colors-bg-primary));\n border: solid 1px var(--nile-colors-neutral-500, var(--ng-colors-border-secondary-alt));\n border-radius: var(--nile-radius-sm, var(--ng-radius-md));\n padding-block: var(--nile-spacing-none, var(--ng-spacing-xs));\n padding-inline: var(--nile-spacing-none, var(--ng-spacing-xs));\n overscroll-behavior: none;\n overflow-y: auto;\n max-width: var(--auto-size-available-width);\n max-height: var(--auto-size-available-height);\n box-shadow: 0px 2px 4px 0px rgba(119, 125, 130, 0.15);\n }\n\n /* ── Options ── */\n\n .combobox__options {\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n color: rgb(133, 129, 129);\n width: 100%;\n }\n\n .combobox__options.loading {\n opacity: 0.5;\n pointer-events: none;\n }\n\n .combobox__no-results {\n padding: var(--nile-spacing-lg, var(--ng-spacing-lg));\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n color: var(--nile-colors-dark-500, var(--ng-colors-text-tertiary-600));\n text-align: center;\n }\n\n .combobox__no-results-title {\n font-weight: var(--nile-font-weight-medium, var(--ng-font-weight-medium));\n color: var(--nile-colors-dark-700, var(--ng-colors-text-secondary-700));\n }\n\n .combobox__no-results-subtitle {\n margin-top: var(--nile-spacing-xs, var(--ng-spacing-xs));\n font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));\n color: var(--nile-colors-dark-500, var(--ng-colors-text-tertiary-600));\n }\n\n /* ── Options (both virtual and plain) ── */\n\n .combobox__options nile-option {\n width: 100%;\n display: block;\n }\n\n .combobox__options nile-option[selected] {\n background-color: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));\n }\n\n .combobox__options nile-option[selected]::part(base) {\n color: var(--nile-colors-primary-600, var(--ng-colors-text-brand-secondary-700));\n }\n\n .combobox__options-plain {\n display: flex;\n flex-direction: column;\n }\n\n .combobox__options-plain nile-option {\n flex-shrink: 0;\n }\n\n /* ── Top actions (Select All + Selected/All filter toggle) ── */\n\n .combobox__top-actions {\n position: sticky;\n top: 0;\n z-index: 2;\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: var(--nile-spacing-lg, var(--ng-spacing-lg));\n background: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));\n border-bottom: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));\n padding: var(--nile-spacing-lg, var(--ng-spacing-lg));\n user-select: none;\n }\n\n .combobox__top-actions--disabled {\n opacity: 0.6;\n pointer-events: none;\n }\n\n .combobox__select-all {\n display: flex;\n align-items: center;\n gap: var(--nile-spacing-md, var(--ng-spacing-md));\n cursor: pointer;\n flex: 1 1 auto;\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n line-height: 14px;\n letter-spacing: 0.2px;\n color: var(--nile-colors-dark-900, var(--ng-colors-text-primary));\n }\n\n .combobox__show-toggle {\n display: inline-flex;\n align-items: center;\n gap: var(--nile-spacing-md, var(--ng-spacing-md));\n border: none;\n background: none;\n padding: 0;\n border-radius: var(--nile-radius-sm, var(--ng-radius-sm));\n cursor: pointer;\n color: var(--nile-colors-primary-600, var(--ng-colors-text-brand-secondary-700));\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n line-height: 14px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n }\n\n .combobox__show-toggle[disabled] {\n color: var(--nile-colors-primary-500, var(--ng-colors-fg-quaternary-400));\n cursor: not-allowed;\n }\n\n /* ── Footer ── */\n\n .combobox__footer {\n position: sticky;\n bottom: 0;\n background: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));\n border-top: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n padding: var(--nile-spacing-md, var(--ng-spacing-md)) var(--nile-spacing-lg, var(--ng-spacing-lg)) var(--nile-spacing-xl, var(--ng-spacing-xl));\n gap: var(--nile-spacing-lg, var(--ng-spacing-lg));\n justify-content: space-between;\n }\n\n .combobox__footer.loading {\n opacity: 0.5;\n pointer-events: none;\n }\n\n .combobox__footer-clear {\n display: inline-flex;\n align-items: center;\n color: var(--nile-colors-primary-600, var(--ng-colors-text-brand-secondary-700));\n border: none;\n background: none;\n padding: 0;\n cursor: pointer;\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n letter-spacing: 0.2px;\n }\n\n .combobox__footer-clear:hover {\n text-decoration: underline;\n }\n\n /* ── Loader ── */\n\n .combobox__loader {\n width: 100%;\n text-align: center;\n display: block;\n }\n\n .combobox__loader--icon {\n margin-top: var(--nile-spacing-xl, var(--ng-spacing-xl));\n animation: combobox-spin 0.6s linear infinite;\n }\n\n .combobox__loader--center {\n position: absolute;\n display: flex;\n justify-content: center;\n align-items: center;\n width: 100%;\n height: 75%;\n }\n\n @keyframes combobox-spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .combobox__add-option::part(base) {\n color: var(--nile-colors-primary-600, var(--ng-colors-text-brand-secondary-700));\n }\n\n /* ── Grid layout ── */\n\n :host([grid-columns]) .combobox__listbox {\n overflow-y: auto;\n }\n\n .combobox__grid-row {\n width: 100%;\n }\n\n .combobox__grid-row nile-option {\n min-width: 0;\n overflow: hidden;\n }\n\n .combobox__grid-row nile-option::part(base) {\n border-radius: var(--nile-radius-xs, var(--ng-radius-sm));\n }\n\n /* ── Bidirectional grid layout (vertical + horizontal scroll) ── */\n\n .combobox__listbox.combobox__listbox--bidirectional {\n overflow: auto;\n max-width: none;\n scrollbar-width: thin;\n scrollbar-color: var(--nile-colors-neutral-500, var(--ng-colors-bg-quaternary)) transparent;\n }\n\n .combobox__listbox--bidirectional .combobox__options {\n width: max-content;\n min-width: 100%;\n }\n\n .combobox__listbox--bidirectional .combobox__grid-row {\n width: max-content;\n min-width: 100%;\n }\n\n /* ── Horizontal grid layout ── */\n\n .combobox__listbox.combobox__listbox--horizontal {\n overflow-x: auto;\n overflow-y: hidden;\n max-height: none;\n max-width: none;\n width: 100%;\n scrollbar-width: thin;\n scrollbar-color: var(--nile-colors-neutral-500, var(--ng-colors-bg-quaternary)) transparent;\n }\n\n .combobox__listbox--horizontal::-webkit-scrollbar {\n height: 6px;\n }\n\n .combobox__listbox--horizontal::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .combobox__listbox--horizontal::-webkit-scrollbar-thumb {\n background: var(--nile-colors-neutral-500, var(--ng-colors-bg-quaternary));\n border-radius: 3px;\n }\n\n .combobox__listbox--horizontal::-webkit-scrollbar-thumb:hover {\n background: var(--nile-colors-dark-500, var(--ng-colors-fg-quaternary-400));\n }\n\n .combobox__options--horizontal {\n display: block;\n width: max-content;\n min-width: 100%;\n }\n\n .combobox__grid-col {\n border-right: 1px solid var(--nile-colors-neutral-200, var(--ng-colors-border-secondary));\n }\n\n .combobox__grid-col:last-child {\n border-right: none;\n }\n\n .combobox__grid-col nile-option {\n width: 100%;\n display: block;\n }\n\n .combobox__grid-col nile-option::part(base) {\n border-radius: 0;\n }\n\n /* ── Help / Error ── */\n\n .form-control__help-text {\n display: none;\n }\n\n .form-control--has-help-text .form-control__help-text {\n display: block;\n color: var(--nile-colors-dark-500, var(--ng-colors-text-tertiary-600));\n margin-top: var(--nile-spacing-lg, var(--ng-spacing-lg));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n }\n`;\n\nexport default [styles];\n"]}
|
|
1
|
+
{"version":3,"file":"nile-combobox.css.js","sourceRoot":"","sources":["../../../src/nile-combobox/nile-combobox.css.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,MAAM,CAAC,MAAM,MAAM,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0sBxB,CAAC;AAEF,eAAe,CAAC,MAAM,CAAC,CAAC","sourcesContent":["/**\n * Copyright Aquera Inc 2025\n *\n * This source code is licensed under the BSD-3-Clause license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { css } from 'lit';\n\nexport const styles = css`\n :host {\n display: block;\n box-sizing: border-box;\n -webkit-font-smoothing: var(--nile-webkit-font-smoothing, var(--ng-webkit-font-smoothing));\n -moz-osx-font-smoothing: var(--nile-moz-osx-font-smoothing, var(--ng-moz-osx-font-smoothing));\n text-rendering: var(--nile-text-rendering, var(--ng-text-rendering));\n }\n\n :host *,\n :host *::before,\n :host *::after {\n box-sizing: inherit;\n }\n\n [hidden] {\n display: none;\n }\n\n /* ── Form control ── */\n\n .form-control .form-control__label {\n display: none;\n }\n\n .form-control--has-label .form-control__label {\n display: block;\n margin-bottom: var(--nile-spacing-sm, var(--ng-spacing-sm));\n color: var(--nile-colors-dark-900, var(--ng-colors-text-secondary-700));\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-md));\n font-style: normal;\n font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-medium));\n line-height: var(--nile-spacing-2xl, var(--ng-line-height-text-sm));\n letter-spacing: 0.2px;\n }\n\n .form-control--has-label.form-control--medium .form-control__label {\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n }\n\n :host([required]) .form-control--has-label .form-control__label::after {\n content: '*';\n margin-inline-start: -2px;\n color: var(--nile-colors-red-700, var(--ng-colors-text-error-primary-600));\n }\n\n :host([disabled]) .form-control--has-label .form-control__label {\n user-select: none;\n -webkit-user-select: none;\n }\n\n /* ── Popup ── */\n\n .combobox-popup {\n flex: 1 1 auto;\n display: inline-flex;\n width: 100%;\n position: relative;\n vertical-align: middle;\n }\n\n .combobox-popup::part(popup) {\n z-index: 9999;\n }\n\n .combobox-popup[data-current-placement^='top']::part(popup) {\n transform-origin: bottom;\n }\n\n .combobox-popup[data-current-placement^='bottom']::part(popup) {\n transform-origin: top;\n }\n\n /* ── Trigger: outer row that never scrolls ── */\n\n .combobox__trigger {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n position: relative;\n font-family: var(--nile-font-family-sans-serif, var(--ng-font-family-body));\n font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-medium));\n letter-spacing: normal;\n cursor: text;\n transition: 150ms color, 150ms border, 150ms box-shadow, 150ms background-color;\n background-color: var(--nile-colors-white-base, var(--ng-colors-bg-primary));\n border: solid 1px var(--nile-colors-neutral-500, var(--ng-colors-border-primary));\n outline: none;\n }\n\n .combobox__trigger:hover {\n border-color: var(--nile-colors-dark-900, var(--ng-colors-border-neutral));\n }\n\n .combobox--open .combobox__trigger,\n .combobox--focused .combobox__trigger {\n border-color: var(--ng-colors-border-brand);\n box-shadow: 0 0 0 1px var(--ng-colors-border-brand) inset;\n background-color: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));\n }\n\n .combobox--disabled .combobox__trigger {\n background-color: var(--nile-colors-dark-200, var(--ng-colors-bg-disabled-subtle));\n border-color: var(--nile-colors-neutral-500, var(--ng-colors-border-primary));\n color: var(--nile-colors-dark-500, var(--ng-colors-text-disabled));\n cursor: not-allowed;\n user-select: none;\n }\n\n .combobox--warning .combobox__trigger {\n border-color: var(--nile-colors-yellow-500, var(--ng-colors-border-warning));\n }\n\n .combobox--error .combobox__trigger {\n border-color: var(--nile-colors-red-500, var(--ng-colors-border-error));\n }\n\n .combobox--success .combobox__trigger {\n border-color: var(--nile-colors-green-500, var(--ng-componentcolors-utility-success-500));\n }\n\n /* Size: small */\n .combobox--small .combobox__trigger {\n border-radius: var(--nile-radius-sm, var(--ng-radius-md));\n font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));\n padding: var(--nile-spacing-md, var(--ng-spacing-sm)) var(--nile-spacing-md, var(--ng-spacing-md));\n min-height: var(--nile-height-32px, var(--ng-height-32px));\n }\n\n /* Size: medium */\n .combobox--medium .combobox__trigger {\n border-radius: var(--nile-radius-sm, var(--ng-radius-md));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-md));\n padding: var(--nile-spacing-lg, var(--ng-spacing-md)) var(--nile-spacing-lg, var(--ng-spacing-lg));\n min-height: var(--nile-height-40px, var(--ng-height-40px));\n box-sizing: border-box;\n }\n\n /* Size: large */\n .combobox--large .combobox__trigger {\n border-radius: var(--nile-radius-sm, var(--ng-radius-md));\n font-size: var(--nile-type-scale-4, var(--ng-font-size-text-md));\n padding: var(--nile-spacing-xl, var(--ng-spacing-lg)) var(--nile-spacing-xl, var(--ng-spacing-xl));\n min-height: var(--nile-height-48px, var(--ng-height-48px));\n }\n\n .combobox--pill .combobox__trigger {\n border-radius: var(--nile-radius-3xl, var(--ng-radius-3xl));\n }\n\n .combobox--filled .combobox__trigger {\n border: none;\n background-color: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));\n }\n\n .combobox--filled.combobox--open .combobox__trigger,\n .combobox--filled.combobox--focused .combobox__trigger {\n outline: 3px solid rgba(0, 89, 255, 0.4);\n }\n\n /* ── Inner area: holds tags + input ── */\n\n .combobox__scroll-area {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n overflow: hidden;\n }\n\n /* Single-line: horizontal scroll for overflowing tags */\n .combobox__scroll-area--single-line {\n flex-wrap: nowrap;\n overflow-x: auto;\n overflow-y: hidden;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: thin;\n scrollbar-color: var(--nile-colors-neutral-500, var(--ng-colors-border-primary)) transparent;\n }\n\n .combobox__scroll-area--single-line::-webkit-scrollbar {\n height: 3px;\n }\n\n .combobox__scroll-area--single-line::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .combobox__scroll-area--single-line::-webkit-scrollbar-thumb {\n background: var(--nile-colors-neutral-500, var(--ng-colors-bg-quaternary));\n border-radius: 3px;\n }\n\n .combobox__scroll-area--single-line::-webkit-scrollbar-thumb:hover {\n background: var(--nile-colors-dark-500, var(--ng-colors-fg-quaternary-400));\n }\n\n /* Wrap: tags flow to multiple lines, trigger grows in height */\n .combobox__scroll-area--wrap {\n flex-wrap: wrap;\n overflow: visible;\n }\n\n /* ── Tags inside the scroll area ── */\n\n .combobox__scroll-area nile-tag {\n cursor: pointer;\n flex-shrink: 0;\n }\n\n .combobox--disabled .combobox__scroll-area nile-tag {\n cursor: not-allowed;\n }\n\n .combobox__tags-count {\n color: var(--nile-colors-primary-600, var(--ng-colors-text-brand-secondary-700));\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-medium));\n line-height: var(--nile-line-height-xsmall, var(--ng-line-height-text-sm));\n letter-spacing: 0.2px;\n white-space: nowrap;\n flex-shrink: 0;\n }\n\n /* ── Input wrapper inside scroll area ── */\n\n .combobox__input-wrapper {\n flex: 1;\n min-width: 60px;\n display: flex;\n align-items: center;\n }\n\n .combobox__input {\n width: 100%;\n font: inherit;\n border: none;\n background: none;\n color: var(--nile-colors-dark-900, var(--ng-colors-text-primary-900));\n cursor: inherit;\n overflow: hidden;\n padding: 0;\n margin: 0;\n -webkit-appearance: none;\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: inherit;\n font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-regular));\n text-overflow: ellipsis;\n line-height: var(--nile-line-height-xsmall, var(--ng-line-height-text-sm));\n }\n\n .combobox__input::placeholder {\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n color: var(--nile-colors-text-placeholder, var(--ng-colors-text-placeholder));\n }\n\n .combobox__input:focus {\n outline: none;\n }\n\n .combobox--disabled .combobox__input {\n color: var(--nile-colors-dark-500, var(--ng-colors-text-disabled));\n pointer-events: none;\n }\n\n /* ── Hidden form value input ── */\n\n .combobox__value-input {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n padding: 0;\n margin: 0;\n opacity: 0;\n z-index: -1;\n }\n\n /* ── Action icons (clear, expand) ── */\n\n .combobox__actions {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n gap: var(--nile-spacing-sm, var(--ng-spacing-sm));\n margin-inline-start: auto;\n }\n\n .combobox__clear {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: none;\n background: none;\n padding: 0;\n cursor: pointer;\n color: var(--nile-colors-dark-500, var(--ng-colors-fg-quaternary-400));\n transition: 150ms color;\n }\n\n .combobox__clear:hover {\n color: var(--nile-colors-dark-900, var(--ng-colors-text-primary-900));\n }\n\n .combobox__clear:focus {\n outline: none;\n }\n\n .combobox__expand-icon {\n display: flex;\n align-items: center;\n flex-shrink: 0;\n transition: 250ms rotate ease;\n rotate: 0;\n }\n\n .combobox--open .combobox__expand-icon {\n rotate: -180deg;\n }\n\n /* ── Prefix / suffix ── */\n\n .combobox__prefix,\n .combobox__suffix {\n flex: 0;\n display: inline-flex;\n align-items: center;\n color: var(--nile-colors-dark-500, var(--ng-colors-text-tertiary-600));\n }\n\n .combobox__prefix {\n margin-inline-end: var(--nile-spacing-md, var(--ng-spacing-md));\n }\n\n /* ── Listbox (dropdown) ── */\n\n .combobox__listbox {\n display: block;\n position: relative;\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-md));\n font-weight: var(--nile-font-weight-regular, var(--ng-font-weight-regular));\n background: var(--nile-colors-white-base, var(--ng-colors-bg-primary));\n border: solid 1px var(--nile-colors-neutral-500, var(--ng-colors-border-secondary-alt));\n border-radius: var(--nile-radius-sm, var(--ng-radius-md));\n padding-block: var(--nile-spacing-none, var(--ng-spacing-xs));\n padding-inline: var(--nile-spacing-none, var(--ng-spacing-xs));\n overscroll-behavior: none;\n overflow-y: auto;\n max-width: var(--auto-size-available-width);\n max-height: var(--auto-size-available-height);\n box-shadow: 0px 2px 4px 0px rgba(119, 125, 130, 0.15);\n }\n\n /* ── Options ── */\n\n .combobox__options {\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n color: rgb(133, 129, 129);\n width: 100%;\n }\n\n .combobox__options.loading {\n opacity: 0.5;\n pointer-events: none;\n }\n\n .combobox__no-results {\n padding: var(--nile-spacing-lg, var(--ng-spacing-lg));\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n color: var(--nile-colors-dark-500, var(--ng-colors-text-tertiary-600));\n text-align: center;\n }\n\n .combobox__no-results-title {\n font-weight: var(--nile-font-weight-medium, var(--ng-font-weight-medium));\n color: var(--nile-colors-dark-700, var(--ng-colors-text-secondary-700));\n }\n\n .combobox__no-results-subtitle {\n margin-top: var(--nile-spacing-xs, var(--ng-spacing-xs));\n font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));\n color: var(--nile-colors-dark-500, var(--ng-colors-text-tertiary-600));\n }\n\n /* ── Options (both virtual and plain) ── */\n\n .combobox__options nile-option {\n width: 100%;\n display: block;\n }\n\n .combobox__options nile-option[selected] {\n background-color: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));\n }\n\n .combobox__options nile-option[selected]::part(base) {\n color: var(--nile-colors-primary-600, var(--ng-colors-text-brand-secondary-700));\n }\n\n .combobox__options-plain {\n display: flex;\n flex-direction: column;\n }\n\n .combobox__options-plain nile-option {\n flex-shrink: 0;\n }\n\n /* ── Top actions (Select All + Selected/All filter toggle) ── */\n\n .combobox__top-actions {\n position: sticky;\n top: 0;\n z-index: 2;\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: var(--nile-spacing-lg, var(--ng-spacing-lg));\n background: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));\n border-bottom: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));\n padding: var(--nile-spacing-lg, var(--ng-spacing-lg));\n user-select: none;\n }\n\n .combobox__top-actions--disabled {\n opacity: 0.6;\n pointer-events: none;\n }\n\n .combobox__select-all {\n display: flex;\n align-items: center;\n gap: var(--nile-spacing-md, var(--ng-spacing-md));\n cursor: pointer;\n flex: 1 1 auto;\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n line-height: 14px;\n letter-spacing: 0.2px;\n color: var(--nile-colors-dark-900, var(--ng-colors-text-primary));\n }\n\n .combobox__show-toggle {\n display: inline-flex;\n align-items: center;\n gap: var(--nile-spacing-md, var(--ng-spacing-md));\n border: none;\n background: none;\n padding: 0;\n border-radius: var(--nile-radius-sm, var(--ng-radius-sm));\n cursor: pointer;\n color: var(--nile-colors-primary-600, var(--ng-colors-text-brand-secondary-700));\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n line-height: 14px;\n letter-spacing: 0.2px;\n white-space: nowrap;\n }\n\n .combobox__show-toggle[disabled] {\n color: var(--nile-colors-primary-500, var(--ng-colors-fg-quaternary-400));\n cursor: not-allowed;\n }\n\n /* ── Footer ── */\n\n .combobox__footer {\n position: sticky;\n bottom: 0;\n background: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));\n border-top: 1px solid var(--nile-colors-neutral-400, var(--ng-colors-border-secondary));\n display: flex;\n flex-direction: row;\n align-items: flex-start;\n padding: var(--nile-spacing-md, var(--ng-spacing-md)) var(--nile-spacing-lg, var(--ng-spacing-lg)) var(--nile-spacing-xl, var(--ng-spacing-xl));\n gap: var(--nile-spacing-lg, var(--ng-spacing-lg));\n justify-content: space-between;\n }\n\n .combobox__footer.loading {\n opacity: 0.5;\n pointer-events: none;\n }\n\n .combobox__footer-clear {\n display: inline-flex;\n align-items: center;\n color: var(--nile-colors-primary-600, var(--ng-colors-text-brand-secondary-700));\n border: none;\n background: none;\n padding: 0;\n cursor: pointer;\n font-family: var(--nile-font-family-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n letter-spacing: 0.2px;\n }\n\n .combobox__footer-clear:hover {\n text-decoration: underline;\n }\n\n /* ── Loader ── */\n\n .combobox__loader {\n width: 100%;\n text-align: center;\n display: block;\n }\n\n .combobox__loader--icon {\n margin-top: var(--nile-spacing-xl, var(--ng-spacing-xl));\n animation: combobox-spin 0.6s linear infinite;\n }\n\n .combobox__loader--center {\n position: absolute;\n display: flex;\n justify-content: center;\n align-items: center;\n width: 100%;\n height: 75%;\n }\n\n @keyframes combobox-spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .combobox__add-option::part(base) {\n color: var(--nile-colors-primary-600, var(--ng-colors-text-brand-secondary-700));\n }\n\n /* ── Grid layout ── */\n\n :host([grid-columns]) .combobox__listbox {\n overflow-y: auto;\n }\n\n .combobox__grid-row {\n width: 100%;\n }\n\n .combobox__grid-row nile-option {\n min-width: 0;\n overflow: hidden;\n }\n\n .combobox__grid-row nile-option::part(base) {\n border-radius: var(--nile-radius-xs, var(--ng-radius-sm));\n }\n\n /* ── Bidirectional grid layout (vertical + horizontal scroll) ── */\n\n .combobox__listbox.combobox__listbox--bidirectional {\n overflow: auto;\n max-width: none;\n scrollbar-width: thin;\n scrollbar-color: var(--nile-colors-neutral-500, var(--ng-colors-bg-quaternary)) transparent;\n }\n\n .combobox__listbox--bidirectional .combobox__options {\n width: max-content;\n min-width: 100%;\n }\n\n .combobox__listbox--bidirectional .combobox__grid-row {\n width: max-content;\n min-width: 100%;\n }\n\n /* ── Horizontal grid layout ── */\n\n .combobox__listbox.combobox__listbox--horizontal {\n overflow-x: auto;\n overflow-y: hidden;\n max-height: none;\n max-width: none;\n width: 100%;\n scrollbar-width: thin;\n scrollbar-color: var(--nile-colors-neutral-500, var(--ng-colors-bg-quaternary)) transparent;\n }\n\n .combobox__listbox--horizontal::-webkit-scrollbar {\n height: 6px;\n }\n\n .combobox__listbox--horizontal::-webkit-scrollbar-track {\n background: transparent;\n }\n\n .combobox__listbox--horizontal::-webkit-scrollbar-thumb {\n background: var(--nile-colors-neutral-500, var(--ng-colors-bg-quaternary));\n border-radius: 3px;\n }\n\n .combobox__listbox--horizontal::-webkit-scrollbar-thumb:hover {\n background: var(--nile-colors-dark-500, var(--ng-colors-fg-quaternary-400));\n }\n\n .combobox__options--horizontal {\n display: block;\n width: max-content;\n min-width: 100%;\n }\n\n .combobox__grid-col {\n border-right: 1px solid var(--nile-colors-neutral-200, var(--ng-colors-border-secondary));\n }\n\n .combobox__grid-col:last-child {\n border-right: none;\n }\n\n .combobox__grid-col nile-option {\n width: 100%;\n display: block;\n }\n\n .combobox__grid-col nile-option::part(base) {\n border-radius: 0;\n }\n\n /* ── Group headers (data-driven grouping) ── */\n\n /* Plain (non-virtualized) listbox: sticky relative to the scroll container.\n * Activated only when the host has [sticky-group-header]. */\n :host([sticky-group-header]) .combobox__options-plain .combobox__group-header {\n position: sticky;\n top: calc(var(--group-depth, 0) * 28px);\n z-index: 1;\n }\n\n /* Virtualized listbox: each row is absolutely positioned by the virtualizer.\n * Sticky doesn't pin against the scroll container because the row wrapper's\n * transform creates a containing block. Instead we render a separate\n * overlay inside the listbox that mirrors the currently-active group\n * header (computed on scroll). */\n .combobox__group-header-slot {\n pointer-events: none;\n }\n .combobox__group-header-slot .combobox__group-header {\n height: 100%;\n box-sizing: border-box;\n }\n\n .combobox__group-sticky-overlay {\n position: sticky;\n top: 0;\n z-index: 2;\n pointer-events: none;\n }\n .combobox__group-sticky-overlay .combobox__group-header {\n box-shadow: 0 1px 0 0 var(--nile-colors-neutral-300, var(--ng-colors-border-secondary));\n }\n\n .combobox__group-header {\n display: flex;\n align-items: center;\n gap: 6px;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n /* Match the listbox's natural inline padding so the header sits like a\n * section divider, edge-to-edge with options. Nested groups indent by\n * depth. Override with --combobox-group-header-indent if needed. */\n padding-block: 8px;\n padding-inline-end: var(--nile-spacing-lg, var(--ng-spacing-lg));\n padding-inline-start: calc(var(--combobox-group-header-indent, var(--nile-spacing-lg, var(--ng-spacing-lg))) + var(--group-depth, 0) * 16px);\n font-family: var(--nile-font-family-sans-serif, var(--ng-font-family-body));\n font-size: var(--nile-type-scale-2, var(--ng-font-size-text-xs));\n font-weight: var(--nile-font-weight-semibold, var(--ng-font-weight-semibold));\n line-height: 1;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: var(--nile-colors-dark-500, var(--ng-colors-text-tertiary-600));\n background: var(--nile-colors-neutral-100, var(--ng-colors-bg-secondary));\n border-bottom: 1px solid var(--nile-colors-neutral-300, var(--ng-colors-border-secondary));\n pointer-events: none;\n user-select: none;\n }\n\n .combobox__group-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .combobox__group-prefix {\n flex-shrink: 0;\n color: inherit;\n }\n\n /* ── Help / Error ── */\n\n .form-control__help-text {\n display: none;\n }\n\n .form-control--has-help-text .form-control__help-text {\n display: block;\n color: var(--nile-colors-dark-500, var(--ng-colors-text-tertiary-600));\n margin-top: var(--nile-spacing-lg, var(--ng-spacing-lg));\n font-size: var(--nile-type-scale-3, var(--ng-font-size-text-sm));\n }\n`;\n\nexport default [styles];\n"]}
|
|
@@ -96,6 +96,14 @@ export declare class NileCombobox extends NileElement implements NileFormControl
|
|
|
96
96
|
selectedOptions: ComboboxOption[];
|
|
97
97
|
/** The items displayed after filtering. Renderer reads from this. */
|
|
98
98
|
filteredData: any[];
|
|
99
|
+
/**
|
|
100
|
+
* Mixed (header + option) row list, only populated when `data` contains
|
|
101
|
+
* group entries (`type: 'group'`). When non-empty, the listbox renders from
|
|
102
|
+
* this instead of `filteredData`. `filteredData` stays in sync as the
|
|
103
|
+
* option-only projection so existing select-all / strict-match / etc. logic
|
|
104
|
+
* keeps working unchanged.
|
|
105
|
+
*/
|
|
106
|
+
private filteredRows;
|
|
99
107
|
/** The complete unfiltered dataset (preserved for re-filtering). */
|
|
100
108
|
private originalData;
|
|
101
109
|
showNoResults: boolean;
|
|
@@ -104,6 +112,12 @@ export declare class NileCombobox extends NileElement implements NileFormControl
|
|
|
104
112
|
private showSelectedOnly;
|
|
105
113
|
private selectAllChecked;
|
|
106
114
|
private selectAllIndeterminate;
|
|
115
|
+
/**
|
|
116
|
+
* Index into `filteredRows` of the group header that should be pinned at
|
|
117
|
+
* the top of the (virtualized) listbox right now. -1 means none.
|
|
118
|
+
* Recomputed on scroll.
|
|
119
|
+
*/
|
|
120
|
+
private stickyHeaderIndex;
|
|
107
121
|
name: string;
|
|
108
122
|
data: any[];
|
|
109
123
|
value: string | string[];
|
|
@@ -144,6 +158,13 @@ export declare class NileCombobox extends NileElement implements NileFormControl
|
|
|
144
158
|
* non-disabled options (respects the active search filter).
|
|
145
159
|
*/
|
|
146
160
|
selectAllEnabled: boolean;
|
|
161
|
+
/**
|
|
162
|
+
* When true (default), data-driven group headers stick to the top of the
|
|
163
|
+
* listbox while scrolling through that group's options (Atlassian-style).
|
|
164
|
+
* Works in both plain and virtualized rendering modes. Set to false for
|
|
165
|
+
* inline-only headers that scroll away with their options.
|
|
166
|
+
*/
|
|
167
|
+
stickyGroupHeader: boolean;
|
|
147
168
|
portal: boolean;
|
|
148
169
|
hoist: boolean;
|
|
149
170
|
placement: ComboboxPlacement;
|
|
@@ -184,12 +205,23 @@ export declare class NileCombobox extends NileElement implements NileFormControl
|
|
|
184
205
|
protected firstUpdated(_changed: PropertyValues): void;
|
|
185
206
|
protected updated(changedProperties: PropertyValues): void;
|
|
186
207
|
private get isHorizontalGrid();
|
|
208
|
+
/** True when the source data contains at least one group entry. */
|
|
209
|
+
private get hasGroupedData();
|
|
210
|
+
/**
|
|
211
|
+
* Walk filteredRows and find the index of the deepest group header whose
|
|
212
|
+
* virtual position is at or above `scrollTop`. That's the header that
|
|
213
|
+
* should be pinned at the top of the listbox right now.
|
|
214
|
+
*/
|
|
215
|
+
private updateStickyHeader;
|
|
216
|
+
/** Recursively keep only options whose value is in `selectedSet`; drop empty groups. */
|
|
217
|
+
private pruneTreeBySelection;
|
|
187
218
|
private get hasActiveFilter();
|
|
188
219
|
private renderEmptyState;
|
|
189
220
|
private get isBidirectionalGrid();
|
|
190
221
|
private get virtualRowCount();
|
|
191
222
|
private get virtualColumnCount();
|
|
192
223
|
private updateVirtualizerCount;
|
|
224
|
+
private lastVirtualizerGrouped;
|
|
193
225
|
private getDisplayText;
|
|
194
226
|
private getItemValue;
|
|
195
227
|
private getSearchText;
|
|
@@ -273,6 +305,7 @@ export declare class NileCombobox extends NileElement implements NileFormControl
|
|
|
273
305
|
private renderTrigger;
|
|
274
306
|
private renderInlineTags;
|
|
275
307
|
private renderClearButton;
|
|
308
|
+
private renderStickyHeaderOverlay;
|
|
276
309
|
private renderListbox;
|
|
277
310
|
private renderHorizontalListbox;
|
|
278
311
|
private renderLoader;
|
|
@@ -30,6 +30,7 @@ import { ComboboxSelectionManager } from './selection-manager.js';
|
|
|
30
30
|
import { ComboboxSearchManager } from './search-manager.js';
|
|
31
31
|
import { ComboboxRenderer } from './renderer.js';
|
|
32
32
|
import { ComboboxPortalManager } from './portal-manager.js';
|
|
33
|
+
import { hasGroups, flattenRows, filterRows, getOptionRows } from './group-utils.js';
|
|
33
34
|
import { VisibilityManager } from '../utilities/visibility-manager.js';
|
|
34
35
|
/**
|
|
35
36
|
* @summary A data-driven combobox with virtualized options, inline search, multi-select tags,
|
|
@@ -120,6 +121,14 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
120
121
|
this.selectedOptions = [];
|
|
121
122
|
/** The items displayed after filtering. Renderer reads from this. */
|
|
122
123
|
this.filteredData = [];
|
|
124
|
+
/**
|
|
125
|
+
* Mixed (header + option) row list, only populated when `data` contains
|
|
126
|
+
* group entries (`type: 'group'`). When non-empty, the listbox renders from
|
|
127
|
+
* this instead of `filteredData`. `filteredData` stays in sync as the
|
|
128
|
+
* option-only projection so existing select-all / strict-match / etc. logic
|
|
129
|
+
* keeps working unchanged.
|
|
130
|
+
*/
|
|
131
|
+
this.filteredRows = [];
|
|
123
132
|
/** The complete unfiltered dataset (preserved for re-filtering). */
|
|
124
133
|
this.originalData = [];
|
|
125
134
|
this.showNoResults = false;
|
|
@@ -128,6 +137,12 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
128
137
|
this.showSelectedOnly = false;
|
|
129
138
|
this.selectAllChecked = false;
|
|
130
139
|
this.selectAllIndeterminate = false;
|
|
140
|
+
/**
|
|
141
|
+
* Index into `filteredRows` of the group header that should be pinned at
|
|
142
|
+
* the top of the (virtualized) listbox right now. -1 means none.
|
|
143
|
+
* Recomputed on scroll.
|
|
144
|
+
*/
|
|
145
|
+
this.stickyHeaderIndex = -1;
|
|
131
146
|
// ── Public properties ──
|
|
132
147
|
this.name = '';
|
|
133
148
|
this.data = [];
|
|
@@ -169,6 +184,13 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
169
184
|
* non-disabled options (respects the active search filter).
|
|
170
185
|
*/
|
|
171
186
|
this.selectAllEnabled = false;
|
|
187
|
+
/**
|
|
188
|
+
* When true (default), data-driven group headers stick to the top of the
|
|
189
|
+
* listbox while scrolling through that group's options (Atlassian-style).
|
|
190
|
+
* Works in both plain and virtualized rendering modes. Set to false for
|
|
191
|
+
* inline-only headers that scroll away with their options.
|
|
192
|
+
*/
|
|
193
|
+
this.stickyGroupHeader = true;
|
|
172
194
|
this.portal = false;
|
|
173
195
|
this.hoist = false;
|
|
174
196
|
this.placement = 'bottom';
|
|
@@ -201,6 +223,7 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
201
223
|
this.gridRows = 0;
|
|
202
224
|
/** Width of each column in horizontal grid mode (px). */
|
|
203
225
|
this.gridColumnWidth = 160;
|
|
226
|
+
this.lastVirtualizerGrouped = false;
|
|
204
227
|
}
|
|
205
228
|
// ── Accessors ──
|
|
206
229
|
get validity() {
|
|
@@ -261,6 +284,51 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
261
284
|
get isHorizontalGrid() {
|
|
262
285
|
return this.gridRows > 0 && this.gridColumns <= 1;
|
|
263
286
|
}
|
|
287
|
+
/** True when the source data contains at least one group entry. */
|
|
288
|
+
get hasGroupedData() {
|
|
289
|
+
const base = this.originalData.length > 0 ? this.originalData : this.data;
|
|
290
|
+
return hasGroups(base);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Walk filteredRows and find the index of the deepest group header whose
|
|
294
|
+
* virtual position is at or above `scrollTop`. That's the header that
|
|
295
|
+
* should be pinned at the top of the listbox right now.
|
|
296
|
+
*/
|
|
297
|
+
updateStickyHeader(scrollTop) {
|
|
298
|
+
if (!this.stickyGroupHeader || !this.hasGroupedData) {
|
|
299
|
+
if (this.stickyHeaderIndex !== -1)
|
|
300
|
+
this.stickyHeaderIndex = -1;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
let offset = 0;
|
|
304
|
+
let stuck = -1;
|
|
305
|
+
for (let i = 0; i < this.filteredRows.length; i++) {
|
|
306
|
+
const row = this.filteredRows[i];
|
|
307
|
+
const size = row.kind === 'header' ? 32 : 38;
|
|
308
|
+
if (offset > scrollTop)
|
|
309
|
+
break;
|
|
310
|
+
if (row.kind === 'header')
|
|
311
|
+
stuck = i;
|
|
312
|
+
offset += size;
|
|
313
|
+
}
|
|
314
|
+
if (stuck !== this.stickyHeaderIndex)
|
|
315
|
+
this.stickyHeaderIndex = stuck;
|
|
316
|
+
}
|
|
317
|
+
/** Recursively keep only options whose value is in `selectedSet`; drop empty groups. */
|
|
318
|
+
pruneTreeBySelection(items, selectedSet) {
|
|
319
|
+
const out = [];
|
|
320
|
+
for (const item of items) {
|
|
321
|
+
if (item && typeof item === 'object' && item.type === 'group' && Array.isArray(item.options)) {
|
|
322
|
+
const kept = this.pruneTreeBySelection(item.options, selectedSet);
|
|
323
|
+
if (kept.length > 0)
|
|
324
|
+
out.push({ ...item, options: kept });
|
|
325
|
+
}
|
|
326
|
+
else if (selectedSet.has(String(this.getItemValue(item)))) {
|
|
327
|
+
out.push(item);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return out;
|
|
331
|
+
}
|
|
264
332
|
get hasActiveFilter() {
|
|
265
333
|
return !!this.searchValue || this.showSelectedOnly;
|
|
266
334
|
}
|
|
@@ -277,6 +345,9 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
277
345
|
if (this.gridColumns > 1) {
|
|
278
346
|
return Math.ceil(this.filteredData.length / this.gridColumns);
|
|
279
347
|
}
|
|
348
|
+
if (this.hasGroupedData) {
|
|
349
|
+
return this.filteredRows.length;
|
|
350
|
+
}
|
|
280
351
|
return this.filteredData.length;
|
|
281
352
|
}
|
|
282
353
|
get virtualColumnCount() {
|
|
@@ -298,12 +369,20 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
298
369
|
else {
|
|
299
370
|
const virtualizer = this.virtualizerCtrl.getVirtualizer();
|
|
300
371
|
const count = this.virtualRowCount;
|
|
301
|
-
|
|
372
|
+
const grouped = this.hasGroupedData;
|
|
373
|
+
const countChanged = virtualizer.options.count !== count;
|
|
374
|
+
const modeChanged = this.lastVirtualizerGrouped !== grouped;
|
|
375
|
+
if (countChanged || modeChanged) {
|
|
376
|
+
const estimateSize = grouped
|
|
377
|
+
? (index) => (this.filteredRows[index]?.kind === 'header' ? 32 : 38)
|
|
378
|
+
: () => 38;
|
|
302
379
|
virtualizer.setOptions({
|
|
303
380
|
...virtualizer.options,
|
|
304
381
|
count,
|
|
382
|
+
estimateSize,
|
|
305
383
|
});
|
|
306
384
|
virtualizer.measure();
|
|
385
|
+
this.lastVirtualizerGrouped = grouped;
|
|
307
386
|
}
|
|
308
387
|
}
|
|
309
388
|
}
|
|
@@ -334,7 +413,10 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
334
413
|
}
|
|
335
414
|
// ── Selection ──
|
|
336
415
|
syncSelection() {
|
|
337
|
-
const
|
|
416
|
+
const baseData = this.originalData.length > 0 ? this.originalData : this.data;
|
|
417
|
+
const items = this.hasGroupedData
|
|
418
|
+
? getOptionRows(flattenRows(baseData)).map(r => r.item)
|
|
419
|
+
: baseData;
|
|
338
420
|
this.selectedOptions = ComboboxSelectionManager.createOptionsFromValues(this.value, items, this.getDisplayText.bind(this), this.renderItemConfig?.getValue);
|
|
339
421
|
if (!this.multiple) {
|
|
340
422
|
const label = this.selectedOptions[0]?.getTextLabel() ?? '';
|
|
@@ -649,15 +731,34 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
649
731
|
}
|
|
650
732
|
filterOptions(search, preserveScroll = false) {
|
|
651
733
|
const baseData = this.originalData.length > 0 ? this.originalData : this.data;
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
734
|
+
if (this.hasGroupedData) {
|
|
735
|
+
// Grouped path: filter the tree, derive options-only projection.
|
|
736
|
+
let tree = baseData;
|
|
737
|
+
if (this.showSelectedOnly) {
|
|
738
|
+
const selectedValues = Array.isArray(this.value) ? this.value : [this.value];
|
|
739
|
+
const selectedSet = new Set(selectedValues.map(v => String(v)));
|
|
740
|
+
tree = this.pruneTreeBySelection(baseData, selectedSet);
|
|
741
|
+
}
|
|
742
|
+
const { rows } = filterRows(tree, search, this.getSearchText.bind(this));
|
|
743
|
+
this.filteredRows = rows;
|
|
744
|
+
this.filteredData = getOptionRows(rows).map(r => r.item);
|
|
745
|
+
this.showNoResults = this.filteredData.length === 0;
|
|
746
|
+
// Recompute sticky header at current scroll (defaults to top on filter).
|
|
747
|
+
const st = this.scrollElementRef.value?.scrollTop ?? 0;
|
|
748
|
+
this.updateStickyHeader(st);
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
let source = baseData;
|
|
752
|
+
if (this.showSelectedOnly) {
|
|
753
|
+
const selectedValues = Array.isArray(this.value) ? this.value : [this.value];
|
|
754
|
+
const selectedSet = new Set(selectedValues.map(v => String(v)));
|
|
755
|
+
source = baseData.filter((item) => selectedSet.has(String(this.getItemValue(item))));
|
|
756
|
+
}
|
|
757
|
+
const { filteredItems, showNoResults } = this.searchManager.filter(search, source, this.getSearchText.bind(this));
|
|
758
|
+
this.filteredData = filteredItems;
|
|
759
|
+
this.filteredRows = [];
|
|
760
|
+
this.showNoResults = showNoResults;
|
|
657
761
|
}
|
|
658
|
-
const { filteredItems, showNoResults } = this.searchManager.filter(search, source, this.getSearchText.bind(this));
|
|
659
|
-
this.filteredData = filteredItems;
|
|
660
|
-
this.showNoResults = showNoResults;
|
|
661
762
|
this.portalManager.resetMeasuredHeight();
|
|
662
763
|
if (!preserveScroll) {
|
|
663
764
|
this.resetScrollPosition();
|
|
@@ -684,7 +785,10 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
684
785
|
return;
|
|
685
786
|
if (!this.multiple) {
|
|
686
787
|
if (this.strict) {
|
|
687
|
-
const
|
|
788
|
+
const baseData = this.originalData.length > 0 ? this.originalData : this.data;
|
|
789
|
+
const allItems = this.hasGroupedData
|
|
790
|
+
? getOptionRows(flattenRows(baseData)).map(r => r.item)
|
|
791
|
+
: baseData;
|
|
688
792
|
const match = allItems.find((item) => this.getDisplayText(item).toLowerCase() === this.searchValue.toLowerCase());
|
|
689
793
|
if (match) {
|
|
690
794
|
const val = this.getItemValue(match);
|
|
@@ -812,26 +916,14 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
812
916
|
if (this.selectedOptions.length === 0)
|
|
813
917
|
return;
|
|
814
918
|
this.showSelectedOnly = !this.showSelectedOnly;
|
|
815
|
-
if (this.showSelectedOnly)
|
|
919
|
+
if (this.showSelectedOnly)
|
|
816
920
|
this.searchValue = '';
|
|
817
|
-
|
|
818
|
-
this.filteredData = this.originalData.filter((item) => {
|
|
819
|
-
const iv = this.getItemValue(item);
|
|
820
|
-
return selectedValues.some(val => String(val) === String(iv));
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
else {
|
|
824
|
-
this.filteredData = [...this.originalData];
|
|
825
|
-
}
|
|
826
|
-
this.portalManager.resetMeasuredHeight();
|
|
827
|
-
this.resetScrollPosition();
|
|
828
|
-
this.updateSelectAllState();
|
|
829
|
-
this.requestUpdate();
|
|
921
|
+
this.filterOptions(this.searchValue);
|
|
830
922
|
}
|
|
831
923
|
clearAll() {
|
|
832
924
|
this.showSelectedOnly = false;
|
|
833
925
|
this.value = this.multiple ? [] : '';
|
|
834
|
-
this.
|
|
926
|
+
this.filterOptions('');
|
|
835
927
|
this.syncSelection();
|
|
836
928
|
this.emit('nile-change', { value: this.value, name: this.name });
|
|
837
929
|
this.emit('nile-clear', { value: this.value, name: this.name });
|
|
@@ -839,9 +931,10 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
839
931
|
}
|
|
840
932
|
// ── Scroll ──
|
|
841
933
|
onScroll(e) {
|
|
934
|
+
const target = e.target;
|
|
935
|
+
this.updateStickyHeader(target.scrollTop);
|
|
842
936
|
if (this.showSelectedOnly)
|
|
843
937
|
return;
|
|
844
|
-
const target = e.target;
|
|
845
938
|
this.emit('nile-scroll', { scrollTop: target.scrollTop, scrollLeft: target.scrollLeft, name: this.name });
|
|
846
939
|
if (!this.scrolling) {
|
|
847
940
|
this.scrolling = true;
|
|
@@ -893,8 +986,14 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
893
986
|
if (this.originalData.length === 0 && this.data.length > 0) {
|
|
894
987
|
this.originalData = [...this.data];
|
|
895
988
|
}
|
|
896
|
-
|
|
897
|
-
|
|
989
|
+
if (this.hasGroupedData) {
|
|
990
|
+
this.filterOptions(this.searchValue);
|
|
991
|
+
}
|
|
992
|
+
else {
|
|
993
|
+
this.filteredData = [...(this.originalData.length > 0 ? this.originalData : this.data)];
|
|
994
|
+
this.filteredRows = [];
|
|
995
|
+
this.showNoResults = this.filteredData.length === 0;
|
|
996
|
+
}
|
|
898
997
|
await stopAnimations(this);
|
|
899
998
|
if (this.popup?.popup) {
|
|
900
999
|
this.popup.popup.style.visibility = 'hidden';
|
|
@@ -944,7 +1043,13 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
944
1043
|
if (this.data.length > 0 && !this.showSelectedOnly) {
|
|
945
1044
|
this.originalData = [...this.data];
|
|
946
1045
|
}
|
|
947
|
-
this.
|
|
1046
|
+
if (this.hasGroupedData) {
|
|
1047
|
+
this.filterOptions(this.searchValue);
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
this.filteredData = [...this.data];
|
|
1051
|
+
this.filteredRows = [];
|
|
1052
|
+
}
|
|
948
1053
|
this.syncSelection();
|
|
949
1054
|
this.updateSelectAllState();
|
|
950
1055
|
if (!this.optionsLoading && !this.loading && this.data.length === 0) {
|
|
@@ -1189,6 +1294,21 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
1189
1294
|
</button>
|
|
1190
1295
|
`;
|
|
1191
1296
|
}
|
|
1297
|
+
renderStickyHeaderOverlay(grouped, useVirtual) {
|
|
1298
|
+
if (!this.stickyGroupHeader || !grouped || !useVirtual)
|
|
1299
|
+
return html ``;
|
|
1300
|
+
const idx = this.stickyHeaderIndex;
|
|
1301
|
+
if (idx < 0 || idx >= this.filteredRows.length)
|
|
1302
|
+
return html ``;
|
|
1303
|
+
const row = this.filteredRows[idx];
|
|
1304
|
+
if (row.kind !== 'header')
|
|
1305
|
+
return html ``;
|
|
1306
|
+
return html `
|
|
1307
|
+
<div class="combobox__group-sticky-overlay">
|
|
1308
|
+
${ComboboxRenderer.renderGroupHeader(row)}
|
|
1309
|
+
</div>
|
|
1310
|
+
`;
|
|
1311
|
+
}
|
|
1192
1312
|
renderListbox() {
|
|
1193
1313
|
const showAddOption = this.allowCustomValue
|
|
1194
1314
|
&& this.searchValue.trim()
|
|
@@ -1204,7 +1324,10 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
1204
1324
|
return this.renderHorizontalListbox(!!showAddOption);
|
|
1205
1325
|
}
|
|
1206
1326
|
const isGrid = this.gridColumns > 1 || this.isBidirectionalGrid;
|
|
1207
|
-
const
|
|
1327
|
+
const grouped = this.hasGroupedData && !isGrid;
|
|
1328
|
+
const useVirtual = grouped
|
|
1329
|
+
? this.filteredRows.length >= 5
|
|
1330
|
+
: ComboboxRenderer.shouldUseVirtualizer(this.filteredData, this.gridColumns);
|
|
1208
1331
|
const virtualizer = this.virtualizerCtrl.getVirtualizer();
|
|
1209
1332
|
const virtualItems = (useVirtual || isGrid) ? virtualizer.getVirtualItems() : [];
|
|
1210
1333
|
const totalSize = (useVirtual || isGrid) ? virtualizer.getTotalSize() : 0;
|
|
@@ -1225,13 +1348,18 @@ let NileCombobox = class NileCombobox extends NileElement {
|
|
|
1225
1348
|
>
|
|
1226
1349
|
${this.renderLoader()}
|
|
1227
1350
|
${this.renderSelectAll()}
|
|
1351
|
+
${this.renderStickyHeaderOverlay(grouped, useVirtual)}
|
|
1228
1352
|
${this.showNoResults && !this.optionsLoading && !this.loading
|
|
1229
1353
|
? this.renderEmptyState()
|
|
1230
1354
|
: isGrid
|
|
1231
1355
|
? ComboboxRenderer.renderVirtualizedGrid(virtualItems, totalSize, this.filteredData, this.value, this.multiple, this.gridColumns, this.getDisplayText.bind(this), this.getItemValue.bind(this), this.optionsLoading || this.loading, this.allowHtmlLabel, this.renderItemConfig?.getDescription ? this.getItemDescription.bind(this) : undefined, this.renderItemConfig?.getPrefix ? this.getItemPrefix.bind(this) : undefined, this.renderItemConfig?.getSuffix ? this.getItemSuffix.bind(this) : undefined, this.isBidirectionalGrid ? this.gridColumnWidth : undefined)
|
|
1232
|
-
:
|
|
1233
|
-
?
|
|
1234
|
-
|
|
1356
|
+
: grouped
|
|
1357
|
+
? (useVirtual
|
|
1358
|
+
? ComboboxRenderer.renderRowsVirtualized(virtualItems, totalSize, this.filteredRows, this.value, this.multiple, this.getDisplayText.bind(this), this.getItemValue.bind(this), this.optionsLoading || this.loading, this.allowHtmlLabel, this.renderItemConfig?.getDescription ? this.getItemDescription.bind(this) : undefined, this.renderItemConfig?.getPrefix ? this.getItemPrefix.bind(this) : undefined, this.renderItemConfig?.getSuffix ? this.getItemSuffix.bind(this) : undefined)
|
|
1359
|
+
: ComboboxRenderer.renderRowsPlain(this.filteredRows, this.value, this.multiple, this.getDisplayText.bind(this), this.getItemValue.bind(this), this.showNoResults, this.noResultsMessage, this.optionsLoading || this.loading, this.onScroll.bind(this), this.allowHtmlLabel, this.renderItemConfig?.getDescription ? this.getItemDescription.bind(this) : undefined, this.renderItemConfig?.getPrefix ? this.getItemPrefix.bind(this) : undefined, this.renderItemConfig?.getSuffix ? this.getItemSuffix.bind(this) : undefined, undefined, this.hasActiveFilter ? this.noResultsSubtitle : undefined))
|
|
1360
|
+
: useVirtual
|
|
1361
|
+
? ComboboxRenderer.renderVirtualizedOptions(virtualItems, totalSize, this.filteredData, this.value, this.multiple, this.getDisplayText.bind(this), this.getItemValue.bind(this), this.optionsLoading || this.loading, this.allowHtmlLabel, virtualizer.measureElement, this.renderItemConfig?.getDescription ? this.getItemDescription.bind(this) : undefined, this.renderItemConfig?.getPrefix ? this.getItemPrefix.bind(this) : undefined, this.renderItemConfig?.getSuffix ? this.getItemSuffix.bind(this) : undefined)
|
|
1362
|
+
: ComboboxRenderer.renderPlainOptions(this.filteredData, this.value, this.multiple, this.getDisplayText.bind(this), this.getItemValue.bind(this), this.showNoResults, this.noResultsMessage, this.optionsLoading || this.loading, this.onScroll.bind(this), this.allowHtmlLabel, this.renderItemConfig?.getDescription ? this.getItemDescription.bind(this) : undefined, this.renderItemConfig?.getPrefix ? this.getItemPrefix.bind(this) : undefined, this.renderItemConfig?.getSuffix ? this.getItemSuffix.bind(this) : undefined, undefined, this.hasActiveFilter ? this.noResultsSubtitle : undefined)}
|
|
1235
1363
|
${showAddOption ? html `
|
|
1236
1364
|
<div @mouseup=${(e) => { e.stopPropagation(); this.addCustomValue(this.searchValue.trim()); }}>
|
|
1237
1365
|
${ComboboxRenderer.renderAddCustomOption(this.searchValue.trim(), this.multiple)}
|
|
@@ -1375,6 +1503,9 @@ __decorate([
|
|
|
1375
1503
|
__decorate([
|
|
1376
1504
|
state()
|
|
1377
1505
|
], NileCombobox.prototype, "filteredData", void 0);
|
|
1506
|
+
__decorate([
|
|
1507
|
+
state()
|
|
1508
|
+
], NileCombobox.prototype, "filteredRows", void 0);
|
|
1378
1509
|
__decorate([
|
|
1379
1510
|
state()
|
|
1380
1511
|
], NileCombobox.prototype, "originalData", void 0);
|
|
@@ -1396,6 +1527,9 @@ __decorate([
|
|
|
1396
1527
|
__decorate([
|
|
1397
1528
|
state()
|
|
1398
1529
|
], NileCombobox.prototype, "selectAllIndeterminate", void 0);
|
|
1530
|
+
__decorate([
|
|
1531
|
+
state()
|
|
1532
|
+
], NileCombobox.prototype, "stickyHeaderIndex", void 0);
|
|
1399
1533
|
__decorate([
|
|
1400
1534
|
property()
|
|
1401
1535
|
], NileCombobox.prototype, "name", void 0);
|
|
@@ -1470,6 +1604,9 @@ __decorate([
|
|
|
1470
1604
|
__decorate([
|
|
1471
1605
|
property({ type: Boolean, reflect: true, attribute: 'select-all-enabled' })
|
|
1472
1606
|
], NileCombobox.prototype, "selectAllEnabled", void 0);
|
|
1607
|
+
__decorate([
|
|
1608
|
+
property({ type: Boolean, reflect: true, attribute: 'sticky-group-header' })
|
|
1609
|
+
], NileCombobox.prototype, "stickyGroupHeader", void 0);
|
|
1473
1610
|
__decorate([
|
|
1474
1611
|
property({ type: Boolean, reflect: true })
|
|
1475
1612
|
], NileCombobox.prototype, "portal", void 0);
|