@aquera/nile-elements 1.7.0 → 1.7.2
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 +6 -0
- package/demo/index.js +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1401 -424
- package/dist/nile-combobox/index.cjs.js +2 -0
- package/dist/nile-combobox/index.cjs.js.map +1 -0
- package/dist/nile-combobox/index.esm.js +1 -0
- package/dist/nile-combobox/nile-combobox.cjs.js +2 -0
- package/dist/nile-combobox/nile-combobox.cjs.js.map +1 -0
- package/dist/nile-combobox/nile-combobox.css.cjs.js +2 -0
- package/dist/nile-combobox/nile-combobox.css.cjs.js.map +1 -0
- package/dist/nile-combobox/nile-combobox.css.esm.js +642 -0
- package/dist/nile-combobox/nile-combobox.esm.js +233 -0
- package/dist/nile-combobox/portal-manager.cjs.js +2 -0
- package/dist/nile-combobox/portal-manager.cjs.js.map +1 -0
- package/dist/nile-combobox/portal-manager.esm.js +1 -0
- package/dist/nile-combobox/renderer.cjs.js +2 -0
- package/dist/nile-combobox/renderer.cjs.js.map +1 -0
- package/dist/nile-combobox/renderer.esm.js +105 -0
- package/dist/nile-combobox/search-manager.cjs.js +2 -0
- package/dist/nile-combobox/search-manager.cjs.js.map +1 -0
- package/dist/nile-combobox/search-manager.esm.js +1 -0
- package/dist/nile-combobox/selection-manager.cjs.js +2 -0
- package/dist/nile-combobox/selection-manager.cjs.js.map +1 -0
- package/dist/nile-combobox/selection-manager.esm.js +1 -0
- package/dist/nile-combobox/types.cjs.js +2 -0
- package/dist/nile-combobox/types.cjs.js.map +1 -0
- package/dist/nile-combobox/types.esm.js +1 -0
- package/dist/nile-nav-tab/nile-nav-tab.cjs.js +1 -1
- package/dist/nile-nav-tab/nile-nav-tab.cjs.js.map +1 -1
- package/dist/nile-nav-tab/nile-nav-tab.esm.js +1 -1
- package/dist/nile-popover/nile-popover.cjs.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/nile-combobox/index.d.ts +1 -0
- package/dist/src/nile-combobox/index.js +2 -0
- package/dist/src/nile-combobox/index.js.map +1 -0
- package/dist/src/nile-combobox/nile-combobox.css.d.ts +9 -0
- package/dist/src/nile-combobox/nile-combobox.css.js +651 -0
- package/dist/src/nile-combobox/nile-combobox.css.js.map +1 -0
- package/dist/src/nile-combobox/nile-combobox.d.ts +287 -0
- package/dist/src/nile-combobox/nile-combobox.js +1602 -0
- package/dist/src/nile-combobox/nile-combobox.js.map +1 -0
- package/dist/src/nile-combobox/nile-combobox.test.d.ts +1 -0
- package/dist/src/nile-combobox/nile-combobox.test.js +551 -0
- package/dist/src/nile-combobox/nile-combobox.test.js.map +1 -0
- package/dist/src/nile-combobox/portal-manager.d.ts +26 -0
- package/dist/src/nile-combobox/portal-manager.js +218 -0
- package/dist/src/nile-combobox/portal-manager.js.map +1 -0
- package/dist/src/nile-combobox/renderer.d.ts +20 -0
- package/dist/src/nile-combobox/renderer.js +210 -0
- package/dist/src/nile-combobox/renderer.js.map +1 -0
- package/dist/src/nile-combobox/search-manager.d.ts +15 -0
- package/dist/src/nile-combobox/search-manager.js +41 -0
- package/dist/src/nile-combobox/search-manager.js.map +1 -0
- package/dist/src/nile-combobox/selection-manager.d.ts +12 -0
- package/dist/src/nile-combobox/selection-manager.js +44 -0
- package/dist/src/nile-combobox/selection-manager.js.map +1 -0
- package/dist/src/nile-combobox/types.d.ts +23 -0
- package/dist/src/nile-combobox/types.js +8 -0
- package/dist/src/nile-combobox/types.js.map +1 -0
- package/dist/src/nile-nav-tab/nile-nav-tab.js +1 -1
- package/dist/src/nile-nav-tab/nile-nav-tab.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 +6 -2
- package/rollup.config.js +4 -1
- package/src/index.ts +1 -0
- package/src/nile-combobox/index.ts +1 -0
- package/src/nile-combobox/nile-combobox.css.ts +653 -0
- package/src/nile-combobox/nile-combobox.test.ts +704 -0
- package/src/nile-combobox/nile-combobox.ts +1663 -0
- package/src/nile-combobox/portal-manager.ts +263 -0
- package/src/nile-combobox/renderer.ts +349 -0
- package/src/nile-combobox/search-manager.ts +53 -0
- package/src/nile-combobox/selection-manager.ts +57 -0
- package/src/nile-combobox/types.ts +27 -0
- package/src/nile-nav-tab/nile-nav-tab.ts +1 -1
- package/vscode-html-custom-data.json +306 -4
- package/web-dev-server.config.mjs +9 -0
- package/web-test-runner.config.mjs +11 -0
|
@@ -0,0 +1,1602 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Aquera Inc 2025
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the BSD-3-Clause license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
import { __decorate } from "tslib";
|
|
8
|
+
import { html } from 'lit';
|
|
9
|
+
import { customElement, property, query, state } from 'lit/decorators.js';
|
|
10
|
+
import { classMap } from 'lit/directives/class-map.js';
|
|
11
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
12
|
+
import { createRef, ref } from 'lit/directives/ref.js';
|
|
13
|
+
import { styles } from './nile-combobox.css';
|
|
14
|
+
import '../nile-icon';
|
|
15
|
+
import '../nile-popup/nile-popup';
|
|
16
|
+
import '../nile-tag/nile-tag';
|
|
17
|
+
import '../nile-option/nile-option';
|
|
18
|
+
import '../nile-checkbox/nile-checkbox';
|
|
19
|
+
import '../nile-loader/nile-loader';
|
|
20
|
+
import { animateTo, stopAnimations } from '../internal/animate';
|
|
21
|
+
import { defaultValue } from '../internal/default-value';
|
|
22
|
+
import { FormControlController } from '../internal/form';
|
|
23
|
+
import { getAnimation, setDefaultAnimation } from '../utilities/animation-registry';
|
|
24
|
+
import { HasSlotController } from '../internal/slot';
|
|
25
|
+
import { waitForEvent } from '../internal/event';
|
|
26
|
+
import { watch } from '../internal/watch';
|
|
27
|
+
import NileElement from '../internal/nile-element';
|
|
28
|
+
import { VirtualizerController } from '@tanstack/lit-virtual';
|
|
29
|
+
import { ComboboxSelectionManager } from './selection-manager.js';
|
|
30
|
+
import { ComboboxSearchManager } from './search-manager.js';
|
|
31
|
+
import { ComboboxRenderer } from './renderer.js';
|
|
32
|
+
import { ComboboxPortalManager } from './portal-manager.js';
|
|
33
|
+
import { VisibilityManager } from '../utilities/visibility-manager.js';
|
|
34
|
+
/**
|
|
35
|
+
* @summary A data-driven combobox with virtualized options, inline search, multi-select tags,
|
|
36
|
+
* custom value creation, and full WAI-ARIA Combobox keyboard navigation.
|
|
37
|
+
*
|
|
38
|
+
* @tag nile-combobox
|
|
39
|
+
* @status stable
|
|
40
|
+
* @since 2.0
|
|
41
|
+
*
|
|
42
|
+
* @dependency nile-icon
|
|
43
|
+
* @dependency nile-popup
|
|
44
|
+
* @dependency nile-tag
|
|
45
|
+
* @dependency nile-checkbox
|
|
46
|
+
* @dependency nile-loader
|
|
47
|
+
*
|
|
48
|
+
* @slot label - The input's label.
|
|
49
|
+
* @slot prefix - Prepend a presentational icon or element before the input.
|
|
50
|
+
* @slot clear-icon - An icon to use in lieu of the default clear icon.
|
|
51
|
+
* @slot expand-icon - The icon to show when the control is expanded/collapsed.
|
|
52
|
+
* @slot help-text - Text that describes how to use the input.
|
|
53
|
+
* @slot footer - Custom footer content (overrides default footer in multi-select mode).
|
|
54
|
+
* @slot no-results - Custom no-results content.
|
|
55
|
+
*
|
|
56
|
+
* @event nile-change - Emitted when the control's value changes.
|
|
57
|
+
* @event nile-clear - Emitted when the control's value is cleared.
|
|
58
|
+
* @event nile-input - Emitted when the control receives input.
|
|
59
|
+
* @event nile-focus - Emitted when the control gains focus.
|
|
60
|
+
* @event nile-blur - Emitted when the control loses focus.
|
|
61
|
+
* @event nile-show - Emitted when the dropdown opens.
|
|
62
|
+
* @event nile-after-show - Emitted after the dropdown opens and all animations complete.
|
|
63
|
+
* @event nile-hide - Emitted when the dropdown closes.
|
|
64
|
+
* @event nile-after-hide - Emitted after the dropdown closes and all animations complete.
|
|
65
|
+
* @event nile-search - Emitted (debounced) when the user types. Useful for API-driven filtering.
|
|
66
|
+
* @event nile-tag-remove - Emitted when a tag is removed in multi-select mode.
|
|
67
|
+
* @event nile-tag-add - Emitted when a custom value is added via allowCustomValue.
|
|
68
|
+
* @event nile-scroll-end - Emitted when scrolled to the bottom (for infinite loading).
|
|
69
|
+
* @event nile-invalid - Emitted when form validation constraints aren't satisfied.
|
|
70
|
+
* @event nile-select-all - Emitted when the Select all / Deselect all control toggles. Detail: { value, name, action: 'select-all' | 'deselect-all' }.
|
|
71
|
+
*
|
|
72
|
+
* @csspart form-control - The form control wrapper.
|
|
73
|
+
* @csspart form-control-label - The label wrapper.
|
|
74
|
+
* @csspart form-control-input - The input area wrapper.
|
|
75
|
+
* @csspart combobox - The combobox trigger (input + tags + icons).
|
|
76
|
+
* @csspart input - The text input element.
|
|
77
|
+
* @csspart listbox - The dropdown listbox.
|
|
78
|
+
* @csspart tags - The tags container in multi-select mode.
|
|
79
|
+
* @csspart tag - Each individual tag.
|
|
80
|
+
* @csspart clear-button - The clear button.
|
|
81
|
+
* @csspart expand-icon - The expand/collapse icon.
|
|
82
|
+
* @csspart top-actions - The sticky row above the option list that contains the Select all checkbox and the Selected / Show all filter toggle. Rendered only when multiple && selectAllEnabled.
|
|
83
|
+
* @csspart select-all - The Select all / Deselect all checkbox wrapper inside top-actions.
|
|
84
|
+
* @csspart show-toggle - The "Selected / Show all" filter button inside top-actions.
|
|
85
|
+
* @csspart no-results - The empty-state container shown when a search/filter returns no items. Contains no-results-title and no-results-subtitle.
|
|
86
|
+
* @csspart no-results-title - The title row of the no-results empty state.
|
|
87
|
+
* @csspart no-results-subtitle - The subtitle row of the no-results empty state.
|
|
88
|
+
* @csspart no-data - The empty-state container shown when the dataset is empty (no active search/filter).
|
|
89
|
+
* @csspart footer - The footer with "Show Selected" / "Clear All".
|
|
90
|
+
* @csspart no-results - The no-results message.
|
|
91
|
+
*/
|
|
92
|
+
let NileCombobox = class NileCombobox extends NileElement {
|
|
93
|
+
constructor() {
|
|
94
|
+
super(...arguments);
|
|
95
|
+
this.formControlController = new FormControlController(this, {
|
|
96
|
+
assumeInteractionOn: ['nile-blur', 'nile-input'],
|
|
97
|
+
});
|
|
98
|
+
this.hasSlotController = new HasSlotController(this, 'help-text', 'label');
|
|
99
|
+
this.portalManager = new ComboboxPortalManager(this);
|
|
100
|
+
this.searchManager = new ComboboxSearchManager();
|
|
101
|
+
this.scrollElementRef = createRef();
|
|
102
|
+
this.virtualizerCtrl = new VirtualizerController(this, {
|
|
103
|
+
getScrollElement: () => this.scrollElementRef.value ?? null,
|
|
104
|
+
count: 0,
|
|
105
|
+
estimateSize: () => 38,
|
|
106
|
+
overscan: 5,
|
|
107
|
+
});
|
|
108
|
+
this.hScrollElementRef = createRef();
|
|
109
|
+
this.hVirtualizerCtrl = new VirtualizerController(this, {
|
|
110
|
+
getScrollElement: () => this.hScrollElementRef.value ?? null,
|
|
111
|
+
count: 0,
|
|
112
|
+
estimateSize: () => 160,
|
|
113
|
+
horizontal: true,
|
|
114
|
+
overscan: 3,
|
|
115
|
+
});
|
|
116
|
+
this.scrolling = false;
|
|
117
|
+
this.keyboardActiveIndex = -1;
|
|
118
|
+
this.hasFocus = false;
|
|
119
|
+
this.displayLabel = '';
|
|
120
|
+
this.selectedOptions = [];
|
|
121
|
+
/** The items displayed after filtering. Renderer reads from this. */
|
|
122
|
+
this.filteredData = [];
|
|
123
|
+
/** The complete unfiltered dataset (preserved for re-filtering). */
|
|
124
|
+
this.originalData = [];
|
|
125
|
+
this.showNoResults = false;
|
|
126
|
+
this.showListbox = false;
|
|
127
|
+
this.searchValue = '';
|
|
128
|
+
this.showSelectedOnly = false;
|
|
129
|
+
this.selectAllChecked = false;
|
|
130
|
+
this.selectAllIndeterminate = false;
|
|
131
|
+
// ── Public properties ──
|
|
132
|
+
this.name = '';
|
|
133
|
+
this.data = [];
|
|
134
|
+
this.value = '';
|
|
135
|
+
this.defaultValue = '';
|
|
136
|
+
this.size = 'medium';
|
|
137
|
+
this.placeholder = 'Type to search...';
|
|
138
|
+
this.multiple = false;
|
|
139
|
+
this.label = '';
|
|
140
|
+
this.required = false;
|
|
141
|
+
this.disabled = false;
|
|
142
|
+
this.open = false;
|
|
143
|
+
this.clearable = false;
|
|
144
|
+
this.loading = false;
|
|
145
|
+
this.optionsLoading = false;
|
|
146
|
+
/** When true, skip local filtering and rely solely on the `nile-search` event for API-driven results. */
|
|
147
|
+
this.disableLocalSearch = false;
|
|
148
|
+
/** When true, displays a "+ Add [value]" option for values not in the data. */
|
|
149
|
+
this.allowCustomValue = false;
|
|
150
|
+
/** When true, typing free text and pressing Enter/Tab adds it as a tag (like nile-chip's acceptUserInput). */
|
|
151
|
+
this.acceptUserInput = false;
|
|
152
|
+
/** When true, custom values added via allowCustomValue or acceptUserInput are also appended to the suggestions list. */
|
|
153
|
+
this.addToSuggestions = false;
|
|
154
|
+
/** When true, value must match an option. On blur, reverts to the last valid value if text doesn't match. */
|
|
155
|
+
this.strict = false;
|
|
156
|
+
/** Max tags visible before showing "+N more" (0 = no limit). */
|
|
157
|
+
this.maxTagsVisible = 3;
|
|
158
|
+
/** Controls how tags wrap in multi-select mode. */
|
|
159
|
+
this.tagLayout = 'single-line';
|
|
160
|
+
/**
|
|
161
|
+
* Show footer with "Show Selected" and "Clear All" in multi-select mode.
|
|
162
|
+
* Automatically suppressed when `selectAllEnabled` is true, since the top
|
|
163
|
+
* actions row already provides the same controls.
|
|
164
|
+
*/
|
|
165
|
+
this.showFooter = true;
|
|
166
|
+
/**
|
|
167
|
+
* When true (and `multiple` is true), renders a "Select all" / "Deselect all"
|
|
168
|
+
* checkbox at the top of the listbox. Operates on the currently visible,
|
|
169
|
+
* non-disabled options (respects the active search filter).
|
|
170
|
+
*/
|
|
171
|
+
this.selectAllEnabled = false;
|
|
172
|
+
this.portal = false;
|
|
173
|
+
this.hoist = false;
|
|
174
|
+
this.placement = 'bottom';
|
|
175
|
+
this.form = '';
|
|
176
|
+
this.helpText = '';
|
|
177
|
+
this.errorMessage = '';
|
|
178
|
+
this.warning = false;
|
|
179
|
+
this.error = false;
|
|
180
|
+
this.success = false;
|
|
181
|
+
this.filled = false;
|
|
182
|
+
this.pill = false;
|
|
183
|
+
this.noResultsMessage = 'No result found';
|
|
184
|
+
this.noResultsSubtitle = 'Clear search or change filter';
|
|
185
|
+
this.noDataMessage = 'No data available';
|
|
186
|
+
/**
|
|
187
|
+
* Pre-defined autocomplete suggestions. Accepts a JSON array string attribute
|
|
188
|
+
* or a JS array property (like nile-chip). When `addToSuggestions` is true,
|
|
189
|
+
* custom values added by the user are appended to this list and persisted.
|
|
190
|
+
*/
|
|
191
|
+
this.autoCompleteOptions = [];
|
|
192
|
+
/** Debounce interval (ms) for the nile-search event. */
|
|
193
|
+
this.debounceMs = 300;
|
|
194
|
+
this.allowHtmlLabel = true;
|
|
195
|
+
this.enableVisibilityEffect = false;
|
|
196
|
+
this.enableTabClose = false;
|
|
197
|
+
this.noWidthSync = false;
|
|
198
|
+
/** Number of columns in the dropdown grid (vertical scroll). When > 1, options render in a multi-column grid layout. */
|
|
199
|
+
this.gridColumns = 1;
|
|
200
|
+
/** Number of visible rows in horizontal grid mode. When > 0, enables horizontal virtual scroll with columns scrolling left/right. */
|
|
201
|
+
this.gridRows = 0;
|
|
202
|
+
/** Width of each column in horizontal grid mode (px). */
|
|
203
|
+
this.gridColumnWidth = 160;
|
|
204
|
+
}
|
|
205
|
+
// ── Accessors ──
|
|
206
|
+
get validity() {
|
|
207
|
+
return this.valueInput?.validity;
|
|
208
|
+
}
|
|
209
|
+
get validationMessage() {
|
|
210
|
+
return this.valueInput?.validationMessage ?? '';
|
|
211
|
+
}
|
|
212
|
+
// ── Lifecycle ──
|
|
213
|
+
connectedCallback() {
|
|
214
|
+
super.connectedCallback();
|
|
215
|
+
this.open = false;
|
|
216
|
+
this.setupBoundHandlers();
|
|
217
|
+
this.emit('nile-init');
|
|
218
|
+
this.updateComplete.then(() => {
|
|
219
|
+
if (this.autoCompleteOptions.length > 0 && this.data.length === 0) {
|
|
220
|
+
this.data = [...this.autoCompleteOptions];
|
|
221
|
+
}
|
|
222
|
+
if (this.value && this.data.length > 0) {
|
|
223
|
+
this.syncSelection();
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
disconnectedCallback() {
|
|
228
|
+
this.removeOpenListeners();
|
|
229
|
+
this.visibilityManager?.cleanup();
|
|
230
|
+
this.searchManager.cancelDebounce();
|
|
231
|
+
if (this.scrollTimeout) {
|
|
232
|
+
clearTimeout(this.scrollTimeout);
|
|
233
|
+
this.scrollTimeout = undefined;
|
|
234
|
+
}
|
|
235
|
+
this.portalManager.cleanupPortal();
|
|
236
|
+
}
|
|
237
|
+
firstUpdated(_changed) {
|
|
238
|
+
this.visibilityManager = new VisibilityManager({
|
|
239
|
+
host: this,
|
|
240
|
+
target: this.combobox,
|
|
241
|
+
enableVisibilityEffect: this.enableVisibilityEffect,
|
|
242
|
+
enableTabClose: this.enableTabClose,
|
|
243
|
+
isOpen: () => this.open,
|
|
244
|
+
onAnchorOutOfView: () => {
|
|
245
|
+
this.hide();
|
|
246
|
+
this.emit('nile-visibility-change', { visible: false, reason: 'anchor-out-of-view', name: this.name });
|
|
247
|
+
},
|
|
248
|
+
onDocumentHidden: () => {
|
|
249
|
+
this.hide();
|
|
250
|
+
this.emit('nile-visibility-change', { visible: false, reason: 'document-hidden', name: this.name });
|
|
251
|
+
},
|
|
252
|
+
emit: (event, detail) => this.emit(`nile-${event}`, detail),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
updated(changedProperties) {
|
|
256
|
+
if (changedProperties.has('value')) {
|
|
257
|
+
this.syncSelection();
|
|
258
|
+
}
|
|
259
|
+
this.updateVirtualizerCount();
|
|
260
|
+
}
|
|
261
|
+
get isHorizontalGrid() {
|
|
262
|
+
return this.gridRows > 0 && this.gridColumns <= 1;
|
|
263
|
+
}
|
|
264
|
+
get hasActiveFilter() {
|
|
265
|
+
return !!this.searchValue || this.showSelectedOnly;
|
|
266
|
+
}
|
|
267
|
+
renderEmptyState() {
|
|
268
|
+
if (this.hasActiveFilter) {
|
|
269
|
+
return ComboboxRenderer.renderNoResults(this.noResultsMessage, this.noResultsSubtitle);
|
|
270
|
+
}
|
|
271
|
+
return ComboboxRenderer.renderNoData(this.noDataMessage);
|
|
272
|
+
}
|
|
273
|
+
get isBidirectionalGrid() {
|
|
274
|
+
return this.gridRows > 0 && this.gridColumns > 1;
|
|
275
|
+
}
|
|
276
|
+
get virtualRowCount() {
|
|
277
|
+
if (this.gridColumns > 1) {
|
|
278
|
+
return Math.ceil(this.filteredData.length / this.gridColumns);
|
|
279
|
+
}
|
|
280
|
+
return this.filteredData.length;
|
|
281
|
+
}
|
|
282
|
+
get virtualColumnCount() {
|
|
283
|
+
return Math.ceil(this.filteredData.length / Math.max(this.gridRows, 1));
|
|
284
|
+
}
|
|
285
|
+
updateVirtualizerCount() {
|
|
286
|
+
if (this.isHorizontalGrid) {
|
|
287
|
+
const hVirtualizer = this.hVirtualizerCtrl.getVirtualizer();
|
|
288
|
+
const colCount = this.virtualColumnCount;
|
|
289
|
+
if (hVirtualizer.options.count !== colCount || hVirtualizer.options.estimateSize(0) !== this.gridColumnWidth) {
|
|
290
|
+
hVirtualizer.setOptions({
|
|
291
|
+
...hVirtualizer.options,
|
|
292
|
+
count: colCount,
|
|
293
|
+
estimateSize: () => this.gridColumnWidth,
|
|
294
|
+
});
|
|
295
|
+
hVirtualizer.measure();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
const virtualizer = this.virtualizerCtrl.getVirtualizer();
|
|
300
|
+
const count = this.virtualRowCount;
|
|
301
|
+
if (virtualizer.options.count !== count) {
|
|
302
|
+
virtualizer.setOptions({
|
|
303
|
+
...virtualizer.options,
|
|
304
|
+
count,
|
|
305
|
+
});
|
|
306
|
+
virtualizer.measure();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// ── Data helpers ──
|
|
311
|
+
getDisplayText(item) {
|
|
312
|
+
if (this.renderItemConfig?.getDisplayText)
|
|
313
|
+
return this.renderItemConfig.getDisplayText(item);
|
|
314
|
+
return item?.label || item?.name || item?.toString?.() || '';
|
|
315
|
+
}
|
|
316
|
+
getItemValue(item) {
|
|
317
|
+
if (this.renderItemConfig?.getValue)
|
|
318
|
+
return this.renderItemConfig.getValue(item);
|
|
319
|
+
return item?.value ?? item;
|
|
320
|
+
}
|
|
321
|
+
getSearchText(item) {
|
|
322
|
+
if (this.renderItemConfig?.getSearchText)
|
|
323
|
+
return this.renderItemConfig.getSearchText(item);
|
|
324
|
+
return this.getDisplayText(item);
|
|
325
|
+
}
|
|
326
|
+
getItemDescription(item) {
|
|
327
|
+
return this.renderItemConfig?.getDescription?.(item) ?? item?.description ?? '';
|
|
328
|
+
}
|
|
329
|
+
getItemPrefix(item) {
|
|
330
|
+
return this.renderItemConfig?.getPrefix?.(item) ?? item?.prefix ?? '';
|
|
331
|
+
}
|
|
332
|
+
getItemSuffix(item) {
|
|
333
|
+
return this.renderItemConfig?.getSuffix?.(item) ?? item?.suffix ?? '';
|
|
334
|
+
}
|
|
335
|
+
// ── Selection ──
|
|
336
|
+
syncSelection() {
|
|
337
|
+
const items = this.originalData.length > 0 ? this.originalData : this.data;
|
|
338
|
+
this.selectedOptions = ComboboxSelectionManager.createOptionsFromValues(this.value, items, this.getDisplayText.bind(this), this.renderItemConfig?.getValue);
|
|
339
|
+
if (!this.multiple) {
|
|
340
|
+
const label = this.selectedOptions[0]?.getTextLabel() ?? '';
|
|
341
|
+
this.displayLabel = label;
|
|
342
|
+
if (label) {
|
|
343
|
+
this.searchValue = label;
|
|
344
|
+
}
|
|
345
|
+
else if (!this.hasFocus) {
|
|
346
|
+
this.searchValue = '';
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
this.displayLabel = '';
|
|
351
|
+
}
|
|
352
|
+
this.updateValidity();
|
|
353
|
+
this.updateSelectAllState();
|
|
354
|
+
}
|
|
355
|
+
// ── Select All ──
|
|
356
|
+
/**
|
|
357
|
+
* Returns the options eligible for Select All — the currently filtered data
|
|
358
|
+
* (so the active search/filter is respected) minus any `disabled` items.
|
|
359
|
+
* Mirrors nile-select's `getSelectableOptions()` contract.
|
|
360
|
+
*/
|
|
361
|
+
getSelectableData() {
|
|
362
|
+
return this.filteredData.filter((item) => !item?.disabled);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Derives `selectAllChecked` + `selectAllIndeterminate` from the current
|
|
366
|
+
* selection vs. the selectable set. No-op when `multiple` is false.
|
|
367
|
+
*
|
|
368
|
+
* options=0 → both false (nothing to select, e.g. empty search)
|
|
369
|
+
* selected=0 → checked=false, indeterminate=false
|
|
370
|
+
* selected=options → checked=true, indeterminate=false
|
|
371
|
+
* otherwise (partial) → checked=false, indeterminate=true
|
|
372
|
+
*/
|
|
373
|
+
updateSelectAllState() {
|
|
374
|
+
if (!this.multiple) {
|
|
375
|
+
this.selectAllChecked = false;
|
|
376
|
+
this.selectAllIndeterminate = false;
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const options = this.getSelectableData();
|
|
380
|
+
if (options.length === 0) {
|
|
381
|
+
this.selectAllChecked = false;
|
|
382
|
+
this.selectAllIndeterminate = false;
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
const selectedValues = Array.isArray(this.value) ? this.value : [];
|
|
386
|
+
const selectedSet = new Set(selectedValues.map(String));
|
|
387
|
+
const selectedInView = options.reduce((n, item) => {
|
|
388
|
+
return n + (selectedSet.has(String(this.getItemValue(item))) ? 1 : 0);
|
|
389
|
+
}, 0);
|
|
390
|
+
if (selectedInView === 0) {
|
|
391
|
+
this.selectAllChecked = false;
|
|
392
|
+
this.selectAllIndeterminate = false;
|
|
393
|
+
}
|
|
394
|
+
else if (selectedInView === options.length) {
|
|
395
|
+
this.selectAllChecked = true;
|
|
396
|
+
this.selectAllIndeterminate = false;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
this.selectAllChecked = false;
|
|
400
|
+
this.selectAllIndeterminate = true;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Toggles all selectable, visible options. Indeterminate → clears selection
|
|
405
|
+
* (matches the "Deselect All" label flip), empty → selects all,
|
|
406
|
+
* fully-selected → clears.
|
|
407
|
+
*/
|
|
408
|
+
handleSelectAllToggle(event) {
|
|
409
|
+
event.stopPropagation();
|
|
410
|
+
event.preventDefault();
|
|
411
|
+
if (!this.multiple || !this.selectAllEnabled)
|
|
412
|
+
return;
|
|
413
|
+
const options = this.getSelectableData();
|
|
414
|
+
if (options.length === 0)
|
|
415
|
+
return;
|
|
416
|
+
const shouldUnselectAll = this.selectAllChecked || this.selectAllIndeterminate;
|
|
417
|
+
const current = Array.isArray(this.value) ? [...this.value] : [];
|
|
418
|
+
const inViewValues = options.map(item => String(this.getItemValue(item)));
|
|
419
|
+
const inViewSet = new Set(inViewValues);
|
|
420
|
+
let next;
|
|
421
|
+
if (shouldUnselectAll) {
|
|
422
|
+
// Deselect only the in-view options; preserve selections outside the filter.
|
|
423
|
+
next = current.filter(v => !inViewSet.has(String(v)));
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
// Select all in-view options on top of any existing (out-of-view) selection.
|
|
427
|
+
const merged = new Set(current.map(String));
|
|
428
|
+
inViewValues.forEach(v => merged.add(v));
|
|
429
|
+
next = Array.from(merged);
|
|
430
|
+
}
|
|
431
|
+
this.value = next;
|
|
432
|
+
this.searchValue = '';
|
|
433
|
+
this.syncSelection();
|
|
434
|
+
this.filterOptions('', true);
|
|
435
|
+
this.updateComplete.then(() => {
|
|
436
|
+
this.emit('nile-input', { value: this.value, name: this.name });
|
|
437
|
+
this.emit('nile-change', { value: this.value, name: this.name });
|
|
438
|
+
this.emit('nile-select-all', {
|
|
439
|
+
value: this.value,
|
|
440
|
+
name: this.name,
|
|
441
|
+
action: shouldUnselectAll ? 'deselect-all' : 'select-all',
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
setupBoundHandlers() {
|
|
446
|
+
this.handleDocumentFocusIn = (event) => {
|
|
447
|
+
if (!this.open)
|
|
448
|
+
return;
|
|
449
|
+
const path = event.composedPath();
|
|
450
|
+
const hitSelf = path.includes(this);
|
|
451
|
+
const hitPopup = this.popup && path.includes(this.popup);
|
|
452
|
+
const hitPortal = this.portal && this.portalManager.portalContainerElement && path.includes(this.portalManager.portalContainerElement);
|
|
453
|
+
if (!hitSelf && !hitPopup && !hitPortal)
|
|
454
|
+
this.hide();
|
|
455
|
+
};
|
|
456
|
+
this.handleDocumentKeyDown = (event) => {
|
|
457
|
+
if (!this.open && event.key !== 'ArrowDown' && event.key !== 'ArrowUp' && event.key !== 'Enter')
|
|
458
|
+
return;
|
|
459
|
+
this.onGlobalKeyDown(event);
|
|
460
|
+
};
|
|
461
|
+
this.handleDocumentMouseDown = (event) => {
|
|
462
|
+
if (!this.open)
|
|
463
|
+
return;
|
|
464
|
+
const path = event.composedPath();
|
|
465
|
+
const hitSelf = path.includes(this);
|
|
466
|
+
const hitPopup = this.popup && path.includes(this.popup);
|
|
467
|
+
const hitPortal = this.portal && this.portalManager.portalContainerElement && path.includes(this.portalManager.portalContainerElement);
|
|
468
|
+
if (!hitSelf && !hitPopup && !hitPortal)
|
|
469
|
+
this.hide();
|
|
470
|
+
};
|
|
471
|
+
this.handleWindowError = (event) => {
|
|
472
|
+
const msg = event.error?.message || event.message || '';
|
|
473
|
+
if (msg.includes("Cannot read properties of null (reading 'insertBefore')")) {
|
|
474
|
+
event.preventDefault();
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
this.handleWindowResize = () => this.portalManager.updatePosition();
|
|
478
|
+
this.handleWindowScroll = () => this.portalManager.updatePosition();
|
|
479
|
+
}
|
|
480
|
+
addOpenListeners() {
|
|
481
|
+
document.addEventListener('focusin', this.handleDocumentFocusIn);
|
|
482
|
+
document.addEventListener('keydown', this.handleDocumentKeyDown);
|
|
483
|
+
document.addEventListener('mousedown', this.handleDocumentMouseDown);
|
|
484
|
+
window.addEventListener('error', this.handleWindowError);
|
|
485
|
+
if (this.portal) {
|
|
486
|
+
window.addEventListener('resize', this.handleWindowResize);
|
|
487
|
+
window.addEventListener('scroll', this.handleWindowScroll, true);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
removeOpenListeners() {
|
|
491
|
+
document.removeEventListener('focusin', this.handleDocumentFocusIn);
|
|
492
|
+
document.removeEventListener('keydown', this.handleDocumentKeyDown);
|
|
493
|
+
document.removeEventListener('mousedown', this.handleDocumentMouseDown);
|
|
494
|
+
window.removeEventListener('error', this.handleWindowError);
|
|
495
|
+
window.removeEventListener('resize', this.handleWindowResize);
|
|
496
|
+
window.removeEventListener('scroll', this.handleWindowScroll, true);
|
|
497
|
+
}
|
|
498
|
+
// ── Keyboard navigation (WAI-ARIA Combobox) ──
|
|
499
|
+
onInputKeyDown(event) {
|
|
500
|
+
switch (event.key) {
|
|
501
|
+
case 'ArrowDown':
|
|
502
|
+
event.preventDefault();
|
|
503
|
+
if (!this.open) {
|
|
504
|
+
this.show();
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
this.moveActiveOption(this.gridColumns > 1 ? this.gridColumns : 1);
|
|
508
|
+
}
|
|
509
|
+
break;
|
|
510
|
+
case 'ArrowUp':
|
|
511
|
+
event.preventDefault();
|
|
512
|
+
if (!this.open) {
|
|
513
|
+
this.show();
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
this.moveActiveOption(this.gridColumns > 1 ? -this.gridColumns : -1);
|
|
517
|
+
}
|
|
518
|
+
break;
|
|
519
|
+
case 'ArrowRight':
|
|
520
|
+
if (this.gridColumns > 1 && this.open) {
|
|
521
|
+
event.preventDefault();
|
|
522
|
+
this.moveActiveOption(1);
|
|
523
|
+
}
|
|
524
|
+
break;
|
|
525
|
+
case 'ArrowLeft':
|
|
526
|
+
if (this.gridColumns > 1 && this.open) {
|
|
527
|
+
event.preventDefault();
|
|
528
|
+
this.moveActiveOption(-1);
|
|
529
|
+
}
|
|
530
|
+
break;
|
|
531
|
+
case 'Enter':
|
|
532
|
+
event.preventDefault();
|
|
533
|
+
if (this.open) {
|
|
534
|
+
this.selectActiveOption();
|
|
535
|
+
}
|
|
536
|
+
else if (this.searchValue.trim() && (this.acceptUserInput || this.allowCustomValue)) {
|
|
537
|
+
this.addCustomValue(this.searchValue.trim());
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
this.show();
|
|
541
|
+
}
|
|
542
|
+
break;
|
|
543
|
+
case 'Escape':
|
|
544
|
+
if (this.open) {
|
|
545
|
+
event.preventDefault();
|
|
546
|
+
event.stopPropagation();
|
|
547
|
+
this.hide();
|
|
548
|
+
}
|
|
549
|
+
break;
|
|
550
|
+
case 'Home':
|
|
551
|
+
if (this.open) {
|
|
552
|
+
event.preventDefault();
|
|
553
|
+
this.setActiveOption(0);
|
|
554
|
+
}
|
|
555
|
+
break;
|
|
556
|
+
case 'End':
|
|
557
|
+
if (this.open) {
|
|
558
|
+
event.preventDefault();
|
|
559
|
+
this.setActiveOption(this.filteredData.length - 1);
|
|
560
|
+
}
|
|
561
|
+
break;
|
|
562
|
+
case 'Backspace':
|
|
563
|
+
if (this.multiple && this.searchValue === '' && this.selectedOptions.length > 0) {
|
|
564
|
+
const last = this.selectedOptions[this.selectedOptions.length - 1];
|
|
565
|
+
this.removeTag(last);
|
|
566
|
+
}
|
|
567
|
+
break;
|
|
568
|
+
case 'Tab':
|
|
569
|
+
if (this.multiple && this.acceptUserInput && this.searchValue.trim()) {
|
|
570
|
+
this.addCustomValue(this.searchValue.trim());
|
|
571
|
+
}
|
|
572
|
+
if (this.open)
|
|
573
|
+
this.hide();
|
|
574
|
+
break;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
onGlobalKeyDown(event) {
|
|
578
|
+
if (event.key === 'Escape' && this.open) {
|
|
579
|
+
event.preventDefault();
|
|
580
|
+
event.stopPropagation();
|
|
581
|
+
this.hide();
|
|
582
|
+
this.inputElement?.focus({ preventScroll: true });
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
getVisibleOptions() {
|
|
586
|
+
const root = this.portal ? this.portalManager.portalContainerElement : this.shadowRoot;
|
|
587
|
+
if (!root)
|
|
588
|
+
return [];
|
|
589
|
+
return root.querySelectorAll('nile-option:not([disabled])');
|
|
590
|
+
}
|
|
591
|
+
moveActiveOption(delta) {
|
|
592
|
+
const options = this.getVisibleOptions();
|
|
593
|
+
if (!options.length)
|
|
594
|
+
return;
|
|
595
|
+
let next = this.keyboardActiveIndex + delta;
|
|
596
|
+
if (next < 0)
|
|
597
|
+
next = options.length - 1;
|
|
598
|
+
if (next >= options.length)
|
|
599
|
+
next = 0;
|
|
600
|
+
this.setActiveOption(next);
|
|
601
|
+
}
|
|
602
|
+
setActiveOption(index) {
|
|
603
|
+
const options = this.getVisibleOptions();
|
|
604
|
+
if (!options.length)
|
|
605
|
+
return;
|
|
606
|
+
options.forEach((opt, i) => {
|
|
607
|
+
const el = opt;
|
|
608
|
+
if (i === index) {
|
|
609
|
+
el.setAttribute('aria-current', 'true');
|
|
610
|
+
el.classList.add('combobox__option--active');
|
|
611
|
+
el.scrollIntoView?.({ block: 'nearest' });
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
el.removeAttribute('aria-current');
|
|
615
|
+
el.classList.remove('combobox__option--active');
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
this.keyboardActiveIndex = index;
|
|
619
|
+
}
|
|
620
|
+
selectActiveOption() {
|
|
621
|
+
const options = this.getVisibleOptions();
|
|
622
|
+
if (this.keyboardActiveIndex >= 0 && this.keyboardActiveIndex < options.length) {
|
|
623
|
+
const opt = options[this.keyboardActiveIndex];
|
|
624
|
+
if (opt && !opt.disabled) {
|
|
625
|
+
this.handleOptionSelection(opt.value);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
else if (this.searchValue.trim() && (this.allowCustomValue || this.acceptUserInput)) {
|
|
629
|
+
this.addCustomValue(this.searchValue.trim());
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
// ── Input handling ──
|
|
633
|
+
onInputChange(event) {
|
|
634
|
+
event.stopPropagation();
|
|
635
|
+
}
|
|
636
|
+
onInputHandler(event) {
|
|
637
|
+
const target = event.target;
|
|
638
|
+
this.searchValue = target.value;
|
|
639
|
+
if (!this.open)
|
|
640
|
+
this.show();
|
|
641
|
+
this.emit('nile-input', { value: this.searchValue, name: this.name });
|
|
642
|
+
if (this.disableLocalSearch) {
|
|
643
|
+
this.searchManager.debounceSearch((q) => this.emit('nile-search', { query: q, name: this.name }), this.searchValue, this.debounceMs);
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
this.filterOptions(this.searchValue);
|
|
647
|
+
}
|
|
648
|
+
this.keyboardActiveIndex = -1;
|
|
649
|
+
}
|
|
650
|
+
filterOptions(search, preserveScroll = false) {
|
|
651
|
+
const baseData = this.originalData.length > 0 ? this.originalData : this.data;
|
|
652
|
+
let source = baseData;
|
|
653
|
+
if (this.showSelectedOnly) {
|
|
654
|
+
const selectedValues = Array.isArray(this.value) ? this.value : [this.value];
|
|
655
|
+
const selectedSet = new Set(selectedValues.map(v => String(v)));
|
|
656
|
+
source = baseData.filter((item) => selectedSet.has(String(this.getItemValue(item))));
|
|
657
|
+
}
|
|
658
|
+
const { filteredItems, showNoResults } = this.searchManager.filter(search, source, this.getSearchText.bind(this));
|
|
659
|
+
this.filteredData = filteredItems;
|
|
660
|
+
this.showNoResults = showNoResults;
|
|
661
|
+
this.portalManager.resetMeasuredHeight();
|
|
662
|
+
if (!preserveScroll) {
|
|
663
|
+
this.resetScrollPosition();
|
|
664
|
+
}
|
|
665
|
+
this.updateSelectAllState();
|
|
666
|
+
this.requestUpdate();
|
|
667
|
+
}
|
|
668
|
+
// ── Focus ──
|
|
669
|
+
onFocusIn() {
|
|
670
|
+
this.hasFocus = true;
|
|
671
|
+
this.emit('nile-focus');
|
|
672
|
+
if (!this.multiple && this.displayLabel) {
|
|
673
|
+
this.searchValue = this.displayLabel;
|
|
674
|
+
this.requestUpdate();
|
|
675
|
+
this.updateComplete.then(() => {
|
|
676
|
+
this.inputElement?.select();
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
onFocusOut() {
|
|
681
|
+
this.hasFocus = false;
|
|
682
|
+
this.emit('nile-blur');
|
|
683
|
+
if (this.open)
|
|
684
|
+
return;
|
|
685
|
+
if (!this.multiple) {
|
|
686
|
+
if (this.strict) {
|
|
687
|
+
const allItems = this.originalData.length > 0 ? this.originalData : this.data;
|
|
688
|
+
const match = allItems.find((item) => this.getDisplayText(item).toLowerCase() === this.searchValue.toLowerCase());
|
|
689
|
+
if (match) {
|
|
690
|
+
const val = this.getItemValue(match);
|
|
691
|
+
if (this.value !== val) {
|
|
692
|
+
this.value = val;
|
|
693
|
+
this.syncSelection();
|
|
694
|
+
this.emit('nile-change', { value: this.value, name: this.name });
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
this.searchValue = this.displayLabel;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
this.searchValue = this.displayLabel;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
// ── Combobox trigger interaction ──
|
|
707
|
+
onTriggerMouseDown(event) {
|
|
708
|
+
if (this.disabled)
|
|
709
|
+
return;
|
|
710
|
+
const path = event.composedPath();
|
|
711
|
+
if (path.some(el => el instanceof Element && el.tagName.toLowerCase() === 'nile-icon-button'))
|
|
712
|
+
return;
|
|
713
|
+
event.preventDefault();
|
|
714
|
+
this.inputElement?.focus({ preventScroll: true });
|
|
715
|
+
if (this.open) {
|
|
716
|
+
this.hide();
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
this.show();
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
// ── Option click ──
|
|
723
|
+
onOptionClick(event) {
|
|
724
|
+
// Use composedPath to find nile-option across shadow boundaries
|
|
725
|
+
const path = event.composedPath();
|
|
726
|
+
const option = path.find((el) => el.tagName === 'NILE-OPTION');
|
|
727
|
+
if (!option || option.disabled)
|
|
728
|
+
return;
|
|
729
|
+
this.handleOptionSelection(option.value);
|
|
730
|
+
}
|
|
731
|
+
handleOptionSelection(optionValue) {
|
|
732
|
+
const oldValue = this.value;
|
|
733
|
+
if (this.multiple) {
|
|
734
|
+
const current = Array.isArray(this.value) ? this.value : [];
|
|
735
|
+
this.value = ComboboxSelectionManager.toggleMultiValue(current, optionValue);
|
|
736
|
+
this.searchValue = '';
|
|
737
|
+
this.syncSelection();
|
|
738
|
+
this.filterOptions('', true);
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
this.value = optionValue;
|
|
742
|
+
this.syncSelection();
|
|
743
|
+
this.searchValue = this.displayLabel;
|
|
744
|
+
this.hide();
|
|
745
|
+
}
|
|
746
|
+
if (JSON.stringify(this.value) !== JSON.stringify(oldValue)) {
|
|
747
|
+
this.updateComplete.then(() => {
|
|
748
|
+
this.emit('nile-input', { value: this.value, name: this.name });
|
|
749
|
+
this.emit('nile-change', { value: this.value, name: this.name });
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
this.inputElement?.focus({ preventScroll: true });
|
|
753
|
+
}
|
|
754
|
+
addCustomValue(val) {
|
|
755
|
+
if (this.addToSuggestions) {
|
|
756
|
+
const allItems = this.originalData.length > 0 ? this.originalData : this.data;
|
|
757
|
+
const alreadyExists = allItems.some((item) => String(this.getItemValue(item)) === val || this.getDisplayText(item) === val);
|
|
758
|
+
if (!alreadyExists) {
|
|
759
|
+
const newItem = this.renderItemConfig?.getValue
|
|
760
|
+
? { value: val, label: val }
|
|
761
|
+
: val;
|
|
762
|
+
this.originalData = [...allItems, newItem];
|
|
763
|
+
this.data = [...this.originalData];
|
|
764
|
+
this.autoCompleteOptions = [...this.originalData];
|
|
765
|
+
this.filteredData = [...this.originalData];
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
if (this.multiple) {
|
|
769
|
+
const current = Array.isArray(this.value) ? this.value : [];
|
|
770
|
+
if (!current.includes(val)) {
|
|
771
|
+
this.value = [...current, val];
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
this.value = val;
|
|
776
|
+
this.hide();
|
|
777
|
+
}
|
|
778
|
+
this.searchValue = this.multiple ? '' : val;
|
|
779
|
+
this.syncSelection();
|
|
780
|
+
this.filterOptions(this.searchValue);
|
|
781
|
+
this.emit('nile-tag-add', { value: val, name: this.name });
|
|
782
|
+
this.emit('nile-change', { value: this.value, name: this.name });
|
|
783
|
+
}
|
|
784
|
+
// ── Clear ──
|
|
785
|
+
onClearClick(event) {
|
|
786
|
+
event.stopPropagation();
|
|
787
|
+
this.value = this.multiple ? [] : '';
|
|
788
|
+
this.searchValue = '';
|
|
789
|
+
this.syncSelection();
|
|
790
|
+
this.filterOptions('');
|
|
791
|
+
this.emit('nile-clear', { value: this.value, name: this.name });
|
|
792
|
+
this.emit('nile-change', { value: this.value, name: this.name });
|
|
793
|
+
}
|
|
794
|
+
// ── Tags ──
|
|
795
|
+
removeTag(option) {
|
|
796
|
+
if (this.disabled)
|
|
797
|
+
return;
|
|
798
|
+
const current = Array.isArray(this.value) ? this.value : [];
|
|
799
|
+
this.value = ComboboxSelectionManager.removeValue(current, option.value);
|
|
800
|
+
this.syncSelection();
|
|
801
|
+
this.emit('nile-tag-remove', { value: this.value, name: this.name, removedtagvalue: option.value });
|
|
802
|
+
this.emit('nile-change', { value: this.value, name: this.name });
|
|
803
|
+
}
|
|
804
|
+
// ── Footer ──
|
|
805
|
+
onFooterClick(event) {
|
|
806
|
+
event.stopPropagation();
|
|
807
|
+
event.preventDefault();
|
|
808
|
+
}
|
|
809
|
+
toggleShowSelected(event) {
|
|
810
|
+
event.stopPropagation();
|
|
811
|
+
event.preventDefault();
|
|
812
|
+
if (this.selectedOptions.length === 0)
|
|
813
|
+
return;
|
|
814
|
+
this.showSelectedOnly = !this.showSelectedOnly;
|
|
815
|
+
if (this.showSelectedOnly) {
|
|
816
|
+
this.searchValue = '';
|
|
817
|
+
const selectedValues = Array.isArray(this.value) ? this.value : [this.value];
|
|
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();
|
|
830
|
+
}
|
|
831
|
+
clearAll() {
|
|
832
|
+
this.showSelectedOnly = false;
|
|
833
|
+
this.value = this.multiple ? [] : '';
|
|
834
|
+
this.filteredData = [...this.originalData];
|
|
835
|
+
this.syncSelection();
|
|
836
|
+
this.emit('nile-change', { value: this.value, name: this.name });
|
|
837
|
+
this.emit('nile-clear', { value: this.value, name: this.name });
|
|
838
|
+
this.resetScrollPosition();
|
|
839
|
+
}
|
|
840
|
+
// ── Scroll ──
|
|
841
|
+
onScroll(e) {
|
|
842
|
+
if (this.showSelectedOnly)
|
|
843
|
+
return;
|
|
844
|
+
const target = e.target;
|
|
845
|
+
this.emit('nile-scroll', { scrollTop: target.scrollTop, scrollLeft: target.scrollLeft, name: this.name });
|
|
846
|
+
if (!this.scrolling) {
|
|
847
|
+
this.scrolling = true;
|
|
848
|
+
this.emit('nile-scroll-start', { scrollTop: target.scrollTop, scrollLeft: target.scrollLeft, name: this.name });
|
|
849
|
+
}
|
|
850
|
+
clearTimeout(this.scrollTimeout);
|
|
851
|
+
this.scrollTimeout = window.setTimeout(() => {
|
|
852
|
+
this.scrolling = false;
|
|
853
|
+
}, 300);
|
|
854
|
+
const isAtBottom = Math.ceil(target.scrollTop) >= Math.floor(target.scrollHeight - target.offsetHeight);
|
|
855
|
+
if (isAtBottom) {
|
|
856
|
+
this.emit('nile-scroll-end', { scrollTop: target.scrollTop, scrollLeft: target.scrollLeft, name: this.name, isAtBottom: true });
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
// ── Show / Hide ──
|
|
860
|
+
async show() {
|
|
861
|
+
if (this.open || this.disabled)
|
|
862
|
+
return undefined;
|
|
863
|
+
this.open = true;
|
|
864
|
+
return waitForEvent(this, 'nile-after-show');
|
|
865
|
+
}
|
|
866
|
+
async hide() {
|
|
867
|
+
if (!this.open)
|
|
868
|
+
return undefined;
|
|
869
|
+
this.open = false;
|
|
870
|
+
return waitForEvent(this, 'nile-after-hide');
|
|
871
|
+
}
|
|
872
|
+
async handleOpenChange() {
|
|
873
|
+
if (this.open && !this.disabled) {
|
|
874
|
+
this.visibilityManager?.setup();
|
|
875
|
+
this.showListbox = true;
|
|
876
|
+
await this.updateComplete;
|
|
877
|
+
await this.doOpen();
|
|
878
|
+
if (this.portal)
|
|
879
|
+
this.portalManager.setupPortal();
|
|
880
|
+
}
|
|
881
|
+
else {
|
|
882
|
+
this.visibilityManager?.cleanup();
|
|
883
|
+
await this.doClose();
|
|
884
|
+
this.showListbox = false;
|
|
885
|
+
if (this.portal)
|
|
886
|
+
this.portalManager.cleanupPortal();
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
async doOpen() {
|
|
890
|
+
this.emit('nile-show', { value: this.value, name: this.name });
|
|
891
|
+
this.addOpenListeners();
|
|
892
|
+
this.keyboardActiveIndex = -1;
|
|
893
|
+
if (this.originalData.length === 0 && this.data.length > 0) {
|
|
894
|
+
this.originalData = [...this.data];
|
|
895
|
+
}
|
|
896
|
+
this.filteredData = [...(this.originalData.length > 0 ? this.originalData : this.data)];
|
|
897
|
+
this.showNoResults = this.filteredData.length === 0;
|
|
898
|
+
await stopAnimations(this);
|
|
899
|
+
if (this.popup?.popup) {
|
|
900
|
+
this.popup.popup.style.visibility = 'hidden';
|
|
901
|
+
}
|
|
902
|
+
this.popup.active = true;
|
|
903
|
+
await new Promise(r => requestAnimationFrame(r));
|
|
904
|
+
await new Promise(r => requestAnimationFrame(r));
|
|
905
|
+
if (this.popup?.popup) {
|
|
906
|
+
this.popup.popup.style.visibility = '';
|
|
907
|
+
}
|
|
908
|
+
const { keyframes, options } = getAnimation(this, 'combobox.show', { dir: 'ltr' });
|
|
909
|
+
await animateTo(this.popup.popup, keyframes, options);
|
|
910
|
+
this.resetScrollPosition();
|
|
911
|
+
this.emit('nile-after-show', { value: this.value, name: this.name });
|
|
912
|
+
}
|
|
913
|
+
async doClose() {
|
|
914
|
+
this.emit('nile-hide', { value: this.value, name: this.name });
|
|
915
|
+
this.removeOpenListeners();
|
|
916
|
+
await stopAnimations(this);
|
|
917
|
+
const { keyframes, options } = getAnimation(this, 'combobox.hide', { dir: 'ltr' });
|
|
918
|
+
await animateTo(this.popup.popup, keyframes, options);
|
|
919
|
+
this.popup.active = false;
|
|
920
|
+
if (this.popup?.popup)
|
|
921
|
+
this.popup.popup.style.visibility = '';
|
|
922
|
+
this.showSelectedOnly = false;
|
|
923
|
+
this.portalManager.resetMeasuredHeight();
|
|
924
|
+
if (!this.multiple) {
|
|
925
|
+
this.searchValue = this.displayLabel;
|
|
926
|
+
}
|
|
927
|
+
this.emit('nile-after-hide', { value: this.value, name: this.name });
|
|
928
|
+
}
|
|
929
|
+
// ── Watchers ──
|
|
930
|
+
handleDisabledChange() {
|
|
931
|
+
if (this.disabled) {
|
|
932
|
+
this.open = false;
|
|
933
|
+
this.handleOpenChange();
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
handleValueChange() {
|
|
937
|
+
this.syncSelection();
|
|
938
|
+
this.requestUpdate();
|
|
939
|
+
if (this.portal && this.portalManager.portalContainerElement) {
|
|
940
|
+
this.portalManager.updatePosition();
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
handleDataChange() {
|
|
944
|
+
if (this.data.length > 0 && !this.showSelectedOnly) {
|
|
945
|
+
this.originalData = [...this.data];
|
|
946
|
+
}
|
|
947
|
+
this.filteredData = [...this.data];
|
|
948
|
+
this.syncSelection();
|
|
949
|
+
this.updateSelectAllState();
|
|
950
|
+
if (!this.optionsLoading && !this.loading && this.data.length === 0) {
|
|
951
|
+
this.showNoResults = true;
|
|
952
|
+
}
|
|
953
|
+
else if (this.data.length > 0) {
|
|
954
|
+
this.showNoResults = false;
|
|
955
|
+
}
|
|
956
|
+
this.portalManager.resetMeasuredHeight();
|
|
957
|
+
if (this.portal && this.portalManager.portalContainerElement) {
|
|
958
|
+
this.portalManager.updatePosition();
|
|
959
|
+
}
|
|
960
|
+
this.requestUpdate();
|
|
961
|
+
}
|
|
962
|
+
handleAutoCompleteOptionsChange() {
|
|
963
|
+
if (this.autoCompleteOptions.length > 0) {
|
|
964
|
+
this.data = [...this.autoCompleteOptions];
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
handleSelectAllConfigChange() {
|
|
968
|
+
this.updateSelectAllState();
|
|
969
|
+
}
|
|
970
|
+
handlePortalChange() {
|
|
971
|
+
if (this.open) {
|
|
972
|
+
if (this.portal)
|
|
973
|
+
this.portalManager.setupPortal();
|
|
974
|
+
else
|
|
975
|
+
this.portalManager.cleanupPortal();
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
// ── Form integration ──
|
|
979
|
+
checkValidity() { return this.valueInput.checkValidity(); }
|
|
980
|
+
getForm() { return this.formControlController.getForm(); }
|
|
981
|
+
reportValidity() { return this.valueInput.reportValidity(); }
|
|
982
|
+
setCustomValidity(message) {
|
|
983
|
+
this.valueInput.setCustomValidity(message);
|
|
984
|
+
this.formControlController.updateValidity();
|
|
985
|
+
}
|
|
986
|
+
focus(options) { this.inputElement?.focus(options); }
|
|
987
|
+
blur() { this.inputElement?.blur(); }
|
|
988
|
+
handleInvalid(event) {
|
|
989
|
+
this.formControlController.setValidity(false);
|
|
990
|
+
this.formControlController.emitInvalidEvent(event);
|
|
991
|
+
}
|
|
992
|
+
updateValidity() {
|
|
993
|
+
this.updateComplete.then(() => this.formControlController.updateValidity());
|
|
994
|
+
}
|
|
995
|
+
async resetScrollPosition() {
|
|
996
|
+
await this.portalManager.resetScrollPosition();
|
|
997
|
+
}
|
|
998
|
+
// ── Render ──
|
|
999
|
+
render() {
|
|
1000
|
+
const hasLabelSlot = this.hasSlotController.test('label');
|
|
1001
|
+
const hasLabel = this.label ? true : !!hasLabelSlot;
|
|
1002
|
+
const hasValue = this.multiple
|
|
1003
|
+
? (Array.isArray(this.value) && this.value.length > 0)
|
|
1004
|
+
: (typeof this.value === 'string' && this.value !== '');
|
|
1005
|
+
const hasClearIcon = this.clearable && !this.disabled && hasValue;
|
|
1006
|
+
const hasHelpText = !!this.helpText;
|
|
1007
|
+
const hasErrorMessage = !!this.errorMessage;
|
|
1008
|
+
const isEmpty = !hasValue;
|
|
1009
|
+
return html `
|
|
1010
|
+
<div
|
|
1011
|
+
part="form-control"
|
|
1012
|
+
class=${classMap({
|
|
1013
|
+
'form-control': true,
|
|
1014
|
+
'form-control--small': this.size === 'small',
|
|
1015
|
+
'form-control--medium': this.size === 'medium',
|
|
1016
|
+
'form-control--large': this.size === 'large',
|
|
1017
|
+
'form-control--has-label': hasLabel,
|
|
1018
|
+
'form-control--has-help-text': hasHelpText,
|
|
1019
|
+
})}
|
|
1020
|
+
>
|
|
1021
|
+
<label
|
|
1022
|
+
id="label"
|
|
1023
|
+
part="form-control-label"
|
|
1024
|
+
class="form-control__label"
|
|
1025
|
+
aria-hidden=${hasLabel ? 'false' : 'true'}
|
|
1026
|
+
@click=${() => this.inputElement?.focus()}
|
|
1027
|
+
>
|
|
1028
|
+
<slot name="label">${this.label}</slot>
|
|
1029
|
+
</label>
|
|
1030
|
+
|
|
1031
|
+
<div part="form-control-input" class="form-control-input">
|
|
1032
|
+
<nile-popup
|
|
1033
|
+
class=${classMap({
|
|
1034
|
+
'combobox-popup': true,
|
|
1035
|
+
'combobox--open': this.open,
|
|
1036
|
+
'combobox--closed': !this.open,
|
|
1037
|
+
'combobox--disabled': this.disabled,
|
|
1038
|
+
'combobox--focused': this.hasFocus,
|
|
1039
|
+
'combobox--multiple': this.multiple,
|
|
1040
|
+
'combobox--empty': isEmpty,
|
|
1041
|
+
'combobox--small': this.size === 'small',
|
|
1042
|
+
'combobox--medium': this.size === 'medium',
|
|
1043
|
+
'combobox--large': this.size === 'large',
|
|
1044
|
+
'combobox--warning': this.warning,
|
|
1045
|
+
'combobox--error': this.error,
|
|
1046
|
+
'combobox--success': this.success,
|
|
1047
|
+
'combobox--filled': this.filled,
|
|
1048
|
+
'combobox--pill': this.pill,
|
|
1049
|
+
})}
|
|
1050
|
+
placement=${this.placement}
|
|
1051
|
+
strategy=${this.portal ? 'fixed' : (this.hoist ? 'fixed' : 'absolute')}
|
|
1052
|
+
distance="6"
|
|
1053
|
+
?flip=${!this.portal}
|
|
1054
|
+
?shift=${!this.portal}
|
|
1055
|
+
sync=${ifDefined(!this.noWidthSync ? 'width' : undefined)}
|
|
1056
|
+
auto-size="vertical"
|
|
1057
|
+
auto-size-padding="10"
|
|
1058
|
+
>
|
|
1059
|
+
${this.renderTrigger(hasClearIcon)}
|
|
1060
|
+
${this.showListbox ? this.renderListbox() : html ``}
|
|
1061
|
+
</nile-popup>
|
|
1062
|
+
|
|
1063
|
+
${hasHelpText ? html `<nile-form-help-text>${this.helpText}</nile-form-help-text>` : html ``}
|
|
1064
|
+
${hasErrorMessage ? html `<nile-form-error-message>${this.errorMessage}</nile-form-error-message>` : html ``}
|
|
1065
|
+
</div>
|
|
1066
|
+
</div>
|
|
1067
|
+
`;
|
|
1068
|
+
}
|
|
1069
|
+
renderTrigger(hasClearIcon) {
|
|
1070
|
+
const hasTags = this.multiple && this.selectedOptions.length > 0;
|
|
1071
|
+
const inputPlaceholder = hasTags ? '' : this.placeholder;
|
|
1072
|
+
return html `
|
|
1073
|
+
<div
|
|
1074
|
+
part="combobox"
|
|
1075
|
+
class="combobox__trigger"
|
|
1076
|
+
slot="anchor"
|
|
1077
|
+
@mousedown=${this.onTriggerMouseDown}
|
|
1078
|
+
>
|
|
1079
|
+
<slot part="prefix" name="prefix" class="combobox__prefix"></slot>
|
|
1080
|
+
|
|
1081
|
+
<div class=${classMap({
|
|
1082
|
+
'combobox__scroll-area': true,
|
|
1083
|
+
'combobox__scroll-area--single-line': this.multiple && this.tagLayout === 'single-line',
|
|
1084
|
+
'combobox__scroll-area--wrap': this.multiple && (this.tagLayout === 'wrap' || this.tagLayout === 'fixed-height'),
|
|
1085
|
+
})}>
|
|
1086
|
+
${hasTags ? this.renderInlineTags() : html ``}
|
|
1087
|
+
|
|
1088
|
+
<div class="combobox__input-wrapper">
|
|
1089
|
+
<input
|
|
1090
|
+
part="input"
|
|
1091
|
+
class="combobox__input"
|
|
1092
|
+
type="text"
|
|
1093
|
+
placeholder=${inputPlaceholder}
|
|
1094
|
+
.disabled=${this.disabled}
|
|
1095
|
+
.value=${this.searchValue}
|
|
1096
|
+
autocomplete="off"
|
|
1097
|
+
spellcheck="false"
|
|
1098
|
+
autocapitalize="off"
|
|
1099
|
+
aria-controls="listbox"
|
|
1100
|
+
aria-expanded=${this.open ? 'true' : 'false'}
|
|
1101
|
+
aria-haspopup="listbox"
|
|
1102
|
+
aria-labelledby="label"
|
|
1103
|
+
aria-disabled=${this.disabled ? 'true' : 'false'}
|
|
1104
|
+
aria-describedby="help-text"
|
|
1105
|
+
aria-activedescendant=${this.keyboardActiveIndex >= 0 ? `option-${this.keyboardActiveIndex}` : ''}
|
|
1106
|
+
role="combobox"
|
|
1107
|
+
tabindex="0"
|
|
1108
|
+
@input=${this.onInputHandler}
|
|
1109
|
+
@keydown=${this.onInputKeyDown}
|
|
1110
|
+
@focus=${this.onFocusIn}
|
|
1111
|
+
@blur=${this.onFocusOut}
|
|
1112
|
+
@change=${this.onInputChange}
|
|
1113
|
+
/>
|
|
1114
|
+
</div>
|
|
1115
|
+
</div>
|
|
1116
|
+
|
|
1117
|
+
<input
|
|
1118
|
+
class="combobox__value-input"
|
|
1119
|
+
type="text"
|
|
1120
|
+
?disabled=${this.disabled}
|
|
1121
|
+
?required=${this.required}
|
|
1122
|
+
.value=${Array.isArray(this.value) ? this.value.join(', ') : this.value}
|
|
1123
|
+
tabindex="-1"
|
|
1124
|
+
aria-hidden="true"
|
|
1125
|
+
@focus=${() => this.focus()}
|
|
1126
|
+
@invalid=${this.handleInvalid}
|
|
1127
|
+
/>
|
|
1128
|
+
|
|
1129
|
+
<div class="combobox__actions">
|
|
1130
|
+
${hasClearIcon ? this.renderClearButton() : html ``}
|
|
1131
|
+
|
|
1132
|
+
<slot name="expand-icon" part="expand-icon" class="combobox__expand-icon">
|
|
1133
|
+
<nile-icon
|
|
1134
|
+
library="system"
|
|
1135
|
+
name="var(--nile-icon-arrow-down, var(--ng-icon-chevron-down))"
|
|
1136
|
+
method="var(--nile-svg-method-fill, var(--ng-svg-method-stroke))"
|
|
1137
|
+
color="var(--nile-colors-dark-500, var(--ng-colors-fg-quaternary-400))"
|
|
1138
|
+
></nile-icon>
|
|
1139
|
+
</slot>
|
|
1140
|
+
</div>
|
|
1141
|
+
</div>
|
|
1142
|
+
`;
|
|
1143
|
+
}
|
|
1144
|
+
renderInlineTags() {
|
|
1145
|
+
const tags = [];
|
|
1146
|
+
this.selectedOptions.forEach((option, index) => {
|
|
1147
|
+
if (this.maxTagsVisible > 0 && index >= this.maxTagsVisible) {
|
|
1148
|
+
if (index === this.maxTagsVisible) {
|
|
1149
|
+
tags.push(html `<span class="combobox__tags-count">+${this.selectedOptions.length - this.maxTagsVisible} More</span>`);
|
|
1150
|
+
}
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
tags.push(html `
|
|
1154
|
+
<nile-tag
|
|
1155
|
+
part="tag"
|
|
1156
|
+
exportparts="base:tag__base, content:tag__content, remove-button:tag__remove-button"
|
|
1157
|
+
?pill=${this.pill}
|
|
1158
|
+
size=${this.size}
|
|
1159
|
+
removable
|
|
1160
|
+
?disabled=${this.disabled}
|
|
1161
|
+
@nile-remove=${(e) => { e.stopPropagation(); this.removeTag(option); }}
|
|
1162
|
+
>
|
|
1163
|
+
${option.getTextLabel()}
|
|
1164
|
+
</nile-tag>
|
|
1165
|
+
`);
|
|
1166
|
+
});
|
|
1167
|
+
return tags;
|
|
1168
|
+
}
|
|
1169
|
+
renderClearButton() {
|
|
1170
|
+
return html `
|
|
1171
|
+
<button
|
|
1172
|
+
part="clear-button"
|
|
1173
|
+
class="combobox__clear"
|
|
1174
|
+
type="button"
|
|
1175
|
+
aria-label="Clear"
|
|
1176
|
+
@mousedown=${(e) => e.stopPropagation()}
|
|
1177
|
+
@click=${this.onClearClick}
|
|
1178
|
+
tabindex="-1"
|
|
1179
|
+
>
|
|
1180
|
+
<slot name="clear-icon">
|
|
1181
|
+
<nile-icon
|
|
1182
|
+
name="var(--nile-icon-close, var(--ng-icon-x-close))"
|
|
1183
|
+
size="var(--nile-height-14px, var(--ng-height-16px))"
|
|
1184
|
+
method="var(--nile-svg-method-fill, var(--ng-svg-method-stroke))"
|
|
1185
|
+
library="system"
|
|
1186
|
+
color="var(--nile-colors-dark-500, var(--ng-colors-fg-quaternary-400))"
|
|
1187
|
+
></nile-icon>
|
|
1188
|
+
</slot>
|
|
1189
|
+
</button>
|
|
1190
|
+
`;
|
|
1191
|
+
}
|
|
1192
|
+
renderListbox() {
|
|
1193
|
+
const showAddOption = this.allowCustomValue
|
|
1194
|
+
&& this.searchValue.trim()
|
|
1195
|
+
&& !this.filteredData.some((item) => {
|
|
1196
|
+
const v = this.getItemValue(item);
|
|
1197
|
+
return String(v).toLowerCase() === this.searchValue.trim().toLowerCase();
|
|
1198
|
+
})
|
|
1199
|
+
&& !this.filteredData.some((item) => {
|
|
1200
|
+
const t = this.getDisplayText(item);
|
|
1201
|
+
return t.toLowerCase() === this.searchValue.trim().toLowerCase();
|
|
1202
|
+
});
|
|
1203
|
+
if (this.isHorizontalGrid) {
|
|
1204
|
+
return this.renderHorizontalListbox(!!showAddOption);
|
|
1205
|
+
}
|
|
1206
|
+
const isGrid = this.gridColumns > 1 || this.isBidirectionalGrid;
|
|
1207
|
+
const useVirtual = ComboboxRenderer.shouldUseVirtualizer(this.filteredData, this.gridColumns);
|
|
1208
|
+
const virtualizer = this.virtualizerCtrl.getVirtualizer();
|
|
1209
|
+
const virtualItems = (useVirtual || isGrid) ? virtualizer.getVirtualItems() : [];
|
|
1210
|
+
const totalSize = (useVirtual || isGrid) ? virtualizer.getTotalSize() : 0;
|
|
1211
|
+
return html `
|
|
1212
|
+
<div
|
|
1213
|
+
id="listbox"
|
|
1214
|
+
role="listbox"
|
|
1215
|
+
aria-expanded=${this.open ? 'true' : 'false'}
|
|
1216
|
+
aria-multiselectable=${this.multiple ? 'true' : 'false'}
|
|
1217
|
+
aria-labelledby="label"
|
|
1218
|
+
part="listbox"
|
|
1219
|
+
class="combobox__listbox ${this.isBidirectionalGrid ? 'combobox__listbox--bidirectional' : ''} ${this.portal ? 'combobox__portal-hidden' : ''}"
|
|
1220
|
+
tabindex="-1"
|
|
1221
|
+
@mouseup=${this.onOptionClick}
|
|
1222
|
+
@scroll=${this.onScroll}
|
|
1223
|
+
style="${this.portal ? 'display: none;' : ''}${this.isBidirectionalGrid ? `max-height:${this.gridRows * 38 + 16}px;` : ''}"
|
|
1224
|
+
${ref(this.scrollElementRef)}
|
|
1225
|
+
>
|
|
1226
|
+
${this.renderLoader()}
|
|
1227
|
+
${this.renderSelectAll()}
|
|
1228
|
+
${this.showNoResults && !this.optionsLoading && !this.loading
|
|
1229
|
+
? this.renderEmptyState()
|
|
1230
|
+
: isGrid
|
|
1231
|
+
? 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
|
+
: useVirtual
|
|
1233
|
+
? 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)
|
|
1234
|
+
: 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
|
+
${showAddOption ? html `
|
|
1236
|
+
<div @mouseup=${(e) => { e.stopPropagation(); this.addCustomValue(this.searchValue.trim()); }}>
|
|
1237
|
+
${ComboboxRenderer.renderAddCustomOption(this.searchValue.trim(), this.multiple)}
|
|
1238
|
+
</div>
|
|
1239
|
+
` : ''}
|
|
1240
|
+
${this.multiple && this.showFooter && !this.selectAllEnabled ? this.renderFooter() : ''}
|
|
1241
|
+
</div>
|
|
1242
|
+
`;
|
|
1243
|
+
}
|
|
1244
|
+
renderHorizontalListbox(showAddOption) {
|
|
1245
|
+
const hVirtualizer = this.hVirtualizerCtrl.getVirtualizer();
|
|
1246
|
+
const virtualItems = hVirtualizer.getVirtualItems();
|
|
1247
|
+
const totalSize = hVirtualizer.getTotalSize();
|
|
1248
|
+
return html `
|
|
1249
|
+
<div
|
|
1250
|
+
id="listbox"
|
|
1251
|
+
role="listbox"
|
|
1252
|
+
aria-expanded=${this.open ? 'true' : 'false'}
|
|
1253
|
+
aria-multiselectable=${this.multiple ? 'true' : 'false'}
|
|
1254
|
+
aria-labelledby="label"
|
|
1255
|
+
part="listbox"
|
|
1256
|
+
class="combobox__listbox combobox__listbox--horizontal ${this.portal ? 'combobox__portal-hidden' : ''}"
|
|
1257
|
+
tabindex="-1"
|
|
1258
|
+
@mouseup=${this.onOptionClick}
|
|
1259
|
+
style=${this.portal ? 'display: none;' : ''}
|
|
1260
|
+
${ref(this.hScrollElementRef)}
|
|
1261
|
+
>
|
|
1262
|
+
${this.renderLoader()}
|
|
1263
|
+
${this.renderSelectAll()}
|
|
1264
|
+
${this.showNoResults && !this.optionsLoading && !this.loading
|
|
1265
|
+
? this.renderEmptyState()
|
|
1266
|
+
: ComboboxRenderer.renderHorizontalGrid(virtualItems, totalSize, this.filteredData, this.value, this.multiple, this.gridRows, this.gridColumnWidth, 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)}
|
|
1267
|
+
${showAddOption ? html `
|
|
1268
|
+
<div @mouseup=${(e) => { e.stopPropagation(); this.addCustomValue(this.searchValue.trim()); }}>
|
|
1269
|
+
${ComboboxRenderer.renderAddCustomOption(this.searchValue.trim(), this.multiple)}
|
|
1270
|
+
</div>
|
|
1271
|
+
` : ''}
|
|
1272
|
+
${this.multiple && this.showFooter && !this.selectAllEnabled ? this.renderFooter() : ''}
|
|
1273
|
+
</div>
|
|
1274
|
+
`;
|
|
1275
|
+
}
|
|
1276
|
+
renderLoader() {
|
|
1277
|
+
if (this.loading) {
|
|
1278
|
+
return html `<span class="combobox__loader--center"><nile-loader size="sm"></nile-loader></span>`;
|
|
1279
|
+
}
|
|
1280
|
+
if (this.optionsLoading) {
|
|
1281
|
+
return html `<span class="combobox__loader"><nile-icon class="combobox__loader--icon" name="button-loading-blue"></nile-icon></span>`;
|
|
1282
|
+
}
|
|
1283
|
+
return html ``;
|
|
1284
|
+
}
|
|
1285
|
+
renderSelectAll() {
|
|
1286
|
+
if (!this.multiple || !this.selectAllEnabled)
|
|
1287
|
+
return html ``;
|
|
1288
|
+
const disabled = this.showNoResults || this.optionsLoading || this.loading;
|
|
1289
|
+
const anySelected = this.selectAllChecked || this.selectAllIndeterminate;
|
|
1290
|
+
const label = anySelected ? 'Deselect all' : 'Select all';
|
|
1291
|
+
const showToggleDisabled = this.selectedOptions.length === 0 || this.showNoResults;
|
|
1292
|
+
const iconColor = showToggleDisabled
|
|
1293
|
+
? 'var(--nile-colors-primary-500, var(--ng-colors-fg-quaternary-400))'
|
|
1294
|
+
: 'var(--nile-colors-primary-600, var(--ng-colors-fg-brand-secondary-600))';
|
|
1295
|
+
return html `
|
|
1296
|
+
<div
|
|
1297
|
+
part="top-actions"
|
|
1298
|
+
class="combobox__top-actions ${disabled ? 'combobox__top-actions--disabled' : ''}"
|
|
1299
|
+
@mousedown=${(e) => e.preventDefault()}
|
|
1300
|
+
>
|
|
1301
|
+
<div
|
|
1302
|
+
class="combobox__select-all"
|
|
1303
|
+
@click=${this.handleSelectAllToggle}
|
|
1304
|
+
>
|
|
1305
|
+
<nile-checkbox
|
|
1306
|
+
?checked=${this.selectAllChecked}
|
|
1307
|
+
?indeterminate=${this.selectAllIndeterminate}
|
|
1308
|
+
?disabled=${disabled}
|
|
1309
|
+
>
|
|
1310
|
+
${label}
|
|
1311
|
+
</nile-checkbox>
|
|
1312
|
+
</div>
|
|
1313
|
+
|
|
1314
|
+
<button
|
|
1315
|
+
part="show-toggle"
|
|
1316
|
+
class="combobox__show-toggle"
|
|
1317
|
+
type="button"
|
|
1318
|
+
?disabled=${showToggleDisabled}
|
|
1319
|
+
@click=${this.toggleShowSelected}
|
|
1320
|
+
>
|
|
1321
|
+
<nile-icon
|
|
1322
|
+
library="system"
|
|
1323
|
+
name="filter"
|
|
1324
|
+
color=${iconColor}
|
|
1325
|
+
></nile-icon>
|
|
1326
|
+
${this.showSelectedOnly ? 'Show all' : 'Selected'}
|
|
1327
|
+
</button>
|
|
1328
|
+
</div>
|
|
1329
|
+
`;
|
|
1330
|
+
}
|
|
1331
|
+
renderFooter() {
|
|
1332
|
+
return html `
|
|
1333
|
+
<div
|
|
1334
|
+
part="footer"
|
|
1335
|
+
class="combobox__footer ${this.loading ? 'loading' : ''}"
|
|
1336
|
+
@click=${this.onFooterClick}
|
|
1337
|
+
>
|
|
1338
|
+
<span @click=${this.toggleShowSelected} style="cursor: pointer;">
|
|
1339
|
+
<nile-checkbox
|
|
1340
|
+
?disabled=${this.selectedOptions.length === 0}
|
|
1341
|
+
?checked=${this.showSelectedOnly}
|
|
1342
|
+
>
|
|
1343
|
+
Show Selected
|
|
1344
|
+
</nile-checkbox>
|
|
1345
|
+
</span>
|
|
1346
|
+
${this.selectedOptions.length > 0
|
|
1347
|
+
? html `<span class="combobox__footer-clear" @click=${this.clearAll}>Clear All</span>`
|
|
1348
|
+
: ''}
|
|
1349
|
+
</div>
|
|
1350
|
+
`;
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
NileCombobox.styles = styles;
|
|
1354
|
+
__decorate([
|
|
1355
|
+
query('.combobox-popup')
|
|
1356
|
+
], NileCombobox.prototype, "popup", void 0);
|
|
1357
|
+
__decorate([
|
|
1358
|
+
query('.combobox__trigger')
|
|
1359
|
+
], NileCombobox.prototype, "combobox", void 0);
|
|
1360
|
+
__decorate([
|
|
1361
|
+
query('.combobox__input')
|
|
1362
|
+
], NileCombobox.prototype, "inputElement", void 0);
|
|
1363
|
+
__decorate([
|
|
1364
|
+
query('.combobox__value-input')
|
|
1365
|
+
], NileCombobox.prototype, "valueInput", void 0);
|
|
1366
|
+
__decorate([
|
|
1367
|
+
state()
|
|
1368
|
+
], NileCombobox.prototype, "hasFocus", void 0);
|
|
1369
|
+
__decorate([
|
|
1370
|
+
state()
|
|
1371
|
+
], NileCombobox.prototype, "displayLabel", void 0);
|
|
1372
|
+
__decorate([
|
|
1373
|
+
state()
|
|
1374
|
+
], NileCombobox.prototype, "selectedOptions", void 0);
|
|
1375
|
+
__decorate([
|
|
1376
|
+
state()
|
|
1377
|
+
], NileCombobox.prototype, "filteredData", void 0);
|
|
1378
|
+
__decorate([
|
|
1379
|
+
state()
|
|
1380
|
+
], NileCombobox.prototype, "originalData", void 0);
|
|
1381
|
+
__decorate([
|
|
1382
|
+
state()
|
|
1383
|
+
], NileCombobox.prototype, "showNoResults", void 0);
|
|
1384
|
+
__decorate([
|
|
1385
|
+
state()
|
|
1386
|
+
], NileCombobox.prototype, "showListbox", void 0);
|
|
1387
|
+
__decorate([
|
|
1388
|
+
state()
|
|
1389
|
+
], NileCombobox.prototype, "searchValue", void 0);
|
|
1390
|
+
__decorate([
|
|
1391
|
+
state()
|
|
1392
|
+
], NileCombobox.prototype, "showSelectedOnly", void 0);
|
|
1393
|
+
__decorate([
|
|
1394
|
+
state()
|
|
1395
|
+
], NileCombobox.prototype, "selectAllChecked", void 0);
|
|
1396
|
+
__decorate([
|
|
1397
|
+
state()
|
|
1398
|
+
], NileCombobox.prototype, "selectAllIndeterminate", void 0);
|
|
1399
|
+
__decorate([
|
|
1400
|
+
property()
|
|
1401
|
+
], NileCombobox.prototype, "name", void 0);
|
|
1402
|
+
__decorate([
|
|
1403
|
+
property({ type: Array })
|
|
1404
|
+
], NileCombobox.prototype, "data", void 0);
|
|
1405
|
+
__decorate([
|
|
1406
|
+
property({
|
|
1407
|
+
converter: {
|
|
1408
|
+
fromAttribute: (value) => value.split(' '),
|
|
1409
|
+
toAttribute: (value) => value.join(' '),
|
|
1410
|
+
},
|
|
1411
|
+
})
|
|
1412
|
+
], NileCombobox.prototype, "value", void 0);
|
|
1413
|
+
__decorate([
|
|
1414
|
+
defaultValue()
|
|
1415
|
+
], NileCombobox.prototype, "defaultValue", void 0);
|
|
1416
|
+
__decorate([
|
|
1417
|
+
property()
|
|
1418
|
+
], NileCombobox.prototype, "size", void 0);
|
|
1419
|
+
__decorate([
|
|
1420
|
+
property()
|
|
1421
|
+
], NileCombobox.prototype, "placeholder", void 0);
|
|
1422
|
+
__decorate([
|
|
1423
|
+
property({ type: Boolean, reflect: true })
|
|
1424
|
+
], NileCombobox.prototype, "multiple", void 0);
|
|
1425
|
+
__decorate([
|
|
1426
|
+
property()
|
|
1427
|
+
], NileCombobox.prototype, "label", void 0);
|
|
1428
|
+
__decorate([
|
|
1429
|
+
property({ type: Boolean, reflect: true })
|
|
1430
|
+
], NileCombobox.prototype, "required", void 0);
|
|
1431
|
+
__decorate([
|
|
1432
|
+
property({ type: Boolean, reflect: true })
|
|
1433
|
+
], NileCombobox.prototype, "disabled", void 0);
|
|
1434
|
+
__decorate([
|
|
1435
|
+
property({ type: Boolean, reflect: true })
|
|
1436
|
+
], NileCombobox.prototype, "open", void 0);
|
|
1437
|
+
__decorate([
|
|
1438
|
+
property({ type: Boolean, reflect: true })
|
|
1439
|
+
], NileCombobox.prototype, "clearable", void 0);
|
|
1440
|
+
__decorate([
|
|
1441
|
+
property({ type: Boolean, reflect: true })
|
|
1442
|
+
], NileCombobox.prototype, "loading", void 0);
|
|
1443
|
+
__decorate([
|
|
1444
|
+
property({ type: Boolean, reflect: true })
|
|
1445
|
+
], NileCombobox.prototype, "optionsLoading", void 0);
|
|
1446
|
+
__decorate([
|
|
1447
|
+
property({ type: Boolean, reflect: true })
|
|
1448
|
+
], NileCombobox.prototype, "disableLocalSearch", void 0);
|
|
1449
|
+
__decorate([
|
|
1450
|
+
property({ type: Boolean, reflect: true })
|
|
1451
|
+
], NileCombobox.prototype, "allowCustomValue", void 0);
|
|
1452
|
+
__decorate([
|
|
1453
|
+
property({ type: Boolean, reflect: true })
|
|
1454
|
+
], NileCombobox.prototype, "acceptUserInput", void 0);
|
|
1455
|
+
__decorate([
|
|
1456
|
+
property({ type: Boolean, reflect: true })
|
|
1457
|
+
], NileCombobox.prototype, "addToSuggestions", void 0);
|
|
1458
|
+
__decorate([
|
|
1459
|
+
property({ type: Boolean, reflect: true })
|
|
1460
|
+
], NileCombobox.prototype, "strict", void 0);
|
|
1461
|
+
__decorate([
|
|
1462
|
+
property({ attribute: 'max-tags-visible', type: Number })
|
|
1463
|
+
], NileCombobox.prototype, "maxTagsVisible", void 0);
|
|
1464
|
+
__decorate([
|
|
1465
|
+
property()
|
|
1466
|
+
], NileCombobox.prototype, "tagLayout", void 0);
|
|
1467
|
+
__decorate([
|
|
1468
|
+
property({ type: Boolean, reflect: true })
|
|
1469
|
+
], NileCombobox.prototype, "showFooter", void 0);
|
|
1470
|
+
__decorate([
|
|
1471
|
+
property({ type: Boolean, reflect: true, attribute: 'select-all-enabled' })
|
|
1472
|
+
], NileCombobox.prototype, "selectAllEnabled", void 0);
|
|
1473
|
+
__decorate([
|
|
1474
|
+
property({ type: Boolean, reflect: true })
|
|
1475
|
+
], NileCombobox.prototype, "portal", void 0);
|
|
1476
|
+
__decorate([
|
|
1477
|
+
property({ type: Boolean })
|
|
1478
|
+
], NileCombobox.prototype, "hoist", void 0);
|
|
1479
|
+
__decorate([
|
|
1480
|
+
property({ reflect: true })
|
|
1481
|
+
], NileCombobox.prototype, "placement", void 0);
|
|
1482
|
+
__decorate([
|
|
1483
|
+
property({ reflect: true })
|
|
1484
|
+
], NileCombobox.prototype, "form", void 0);
|
|
1485
|
+
__decorate([
|
|
1486
|
+
property({ attribute: 'help-text', reflect: true })
|
|
1487
|
+
], NileCombobox.prototype, "helpText", void 0);
|
|
1488
|
+
__decorate([
|
|
1489
|
+
property({ attribute: 'error-message', reflect: true })
|
|
1490
|
+
], NileCombobox.prototype, "errorMessage", void 0);
|
|
1491
|
+
__decorate([
|
|
1492
|
+
property({ type: Boolean })
|
|
1493
|
+
], NileCombobox.prototype, "warning", void 0);
|
|
1494
|
+
__decorate([
|
|
1495
|
+
property({ type: Boolean })
|
|
1496
|
+
], NileCombobox.prototype, "error", void 0);
|
|
1497
|
+
__decorate([
|
|
1498
|
+
property({ type: Boolean })
|
|
1499
|
+
], NileCombobox.prototype, "success", void 0);
|
|
1500
|
+
__decorate([
|
|
1501
|
+
property({ type: Boolean, reflect: true })
|
|
1502
|
+
], NileCombobox.prototype, "filled", void 0);
|
|
1503
|
+
__decorate([
|
|
1504
|
+
property({ type: Boolean, reflect: true })
|
|
1505
|
+
], NileCombobox.prototype, "pill", void 0);
|
|
1506
|
+
__decorate([
|
|
1507
|
+
property({ type: String })
|
|
1508
|
+
], NileCombobox.prototype, "noResultsMessage", void 0);
|
|
1509
|
+
__decorate([
|
|
1510
|
+
property({ type: String, attribute: 'no-results-subtitle' })
|
|
1511
|
+
], NileCombobox.prototype, "noResultsSubtitle", void 0);
|
|
1512
|
+
__decorate([
|
|
1513
|
+
property({ type: String, attribute: 'no-data-message' })
|
|
1514
|
+
], NileCombobox.prototype, "noDataMessage", void 0);
|
|
1515
|
+
__decorate([
|
|
1516
|
+
property({
|
|
1517
|
+
type: Array,
|
|
1518
|
+
converter: {
|
|
1519
|
+
fromAttribute: (value) => {
|
|
1520
|
+
if (!value)
|
|
1521
|
+
return [];
|
|
1522
|
+
try {
|
|
1523
|
+
return JSON.parse(value);
|
|
1524
|
+
}
|
|
1525
|
+
catch {
|
|
1526
|
+
return [];
|
|
1527
|
+
}
|
|
1528
|
+
},
|
|
1529
|
+
toAttribute: (value) => JSON.stringify(value),
|
|
1530
|
+
},
|
|
1531
|
+
})
|
|
1532
|
+
], NileCombobox.prototype, "autoCompleteOptions", void 0);
|
|
1533
|
+
__decorate([
|
|
1534
|
+
property({ type: Number })
|
|
1535
|
+
], NileCombobox.prototype, "debounceMs", void 0);
|
|
1536
|
+
__decorate([
|
|
1537
|
+
property({ attribute: false })
|
|
1538
|
+
], NileCombobox.prototype, "renderItemConfig", void 0);
|
|
1539
|
+
__decorate([
|
|
1540
|
+
property({ type: Boolean, reflect: true })
|
|
1541
|
+
], NileCombobox.prototype, "allowHtmlLabel", void 0);
|
|
1542
|
+
__decorate([
|
|
1543
|
+
property({ type: Boolean, reflect: true, attribute: true })
|
|
1544
|
+
], NileCombobox.prototype, "enableVisibilityEffect", void 0);
|
|
1545
|
+
__decorate([
|
|
1546
|
+
property({ type: Boolean, reflect: true, attribute: true })
|
|
1547
|
+
], NileCombobox.prototype, "enableTabClose", void 0);
|
|
1548
|
+
__decorate([
|
|
1549
|
+
property({ type: Boolean, reflect: true })
|
|
1550
|
+
], NileCombobox.prototype, "noWidthSync", void 0);
|
|
1551
|
+
__decorate([
|
|
1552
|
+
property({ attribute: 'grid-columns', type: Number, reflect: true })
|
|
1553
|
+
], NileCombobox.prototype, "gridColumns", void 0);
|
|
1554
|
+
__decorate([
|
|
1555
|
+
property({ attribute: 'grid-rows', type: Number, reflect: true })
|
|
1556
|
+
], NileCombobox.prototype, "gridRows", void 0);
|
|
1557
|
+
__decorate([
|
|
1558
|
+
property({ attribute: 'grid-column-width', type: Number })
|
|
1559
|
+
], NileCombobox.prototype, "gridColumnWidth", void 0);
|
|
1560
|
+
__decorate([
|
|
1561
|
+
watch('open', { waitUntilFirstUpdate: true })
|
|
1562
|
+
], NileCombobox.prototype, "handleOpenChange", null);
|
|
1563
|
+
__decorate([
|
|
1564
|
+
watch('disabled', { waitUntilFirstUpdate: true })
|
|
1565
|
+
], NileCombobox.prototype, "handleDisabledChange", null);
|
|
1566
|
+
__decorate([
|
|
1567
|
+
watch('value', { waitUntilFirstUpdate: true })
|
|
1568
|
+
], NileCombobox.prototype, "handleValueChange", null);
|
|
1569
|
+
__decorate([
|
|
1570
|
+
watch('data', { waitUntilFirstUpdate: true })
|
|
1571
|
+
], NileCombobox.prototype, "handleDataChange", null);
|
|
1572
|
+
__decorate([
|
|
1573
|
+
watch('autoCompleteOptions', { waitUntilFirstUpdate: true })
|
|
1574
|
+
], NileCombobox.prototype, "handleAutoCompleteOptionsChange", null);
|
|
1575
|
+
__decorate([
|
|
1576
|
+
watch('multiple', { waitUntilFirstUpdate: true }),
|
|
1577
|
+
watch('selectAllEnabled', { waitUntilFirstUpdate: true })
|
|
1578
|
+
], NileCombobox.prototype, "handleSelectAllConfigChange", null);
|
|
1579
|
+
__decorate([
|
|
1580
|
+
watch('portal', { waitUntilFirstUpdate: true })
|
|
1581
|
+
], NileCombobox.prototype, "handlePortalChange", null);
|
|
1582
|
+
NileCombobox = __decorate([
|
|
1583
|
+
customElement('nile-combobox')
|
|
1584
|
+
], NileCombobox);
|
|
1585
|
+
export { NileCombobox };
|
|
1586
|
+
// ── Default animations ──
|
|
1587
|
+
setDefaultAnimation('combobox.show', {
|
|
1588
|
+
keyframes: [
|
|
1589
|
+
{ opacity: 0, scale: 0.9 },
|
|
1590
|
+
{ opacity: 1, scale: 1 },
|
|
1591
|
+
],
|
|
1592
|
+
options: { duration: 100, easing: 'ease' },
|
|
1593
|
+
});
|
|
1594
|
+
setDefaultAnimation('combobox.hide', {
|
|
1595
|
+
keyframes: [
|
|
1596
|
+
{ opacity: 1, scale: 1 },
|
|
1597
|
+
{ opacity: 0, scale: 0.9 },
|
|
1598
|
+
],
|
|
1599
|
+
options: { duration: 100, easing: 'ease' },
|
|
1600
|
+
});
|
|
1601
|
+
export default NileCombobox;
|
|
1602
|
+
//# sourceMappingURL=nile-combobox.js.map
|