@aquera/nile-elements 0.1.75-beta-1.4 → 0.1.75-beta-1.5
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/demo/index.html +0 -25
- package/dist/index.js +357 -331
- package/dist/nile-button/nile-button.cjs.js +1 -1
- package/dist/nile-button/nile-button.cjs.js.map +1 -1
- package/dist/nile-button/nile-button.esm.js +1 -1
- package/dist/nile-calendar/nile-calendar.test.cjs.js +1 -1
- package/dist/nile-calendar/nile-calendar.test.cjs.js.map +1 -1
- package/dist/nile-calendar/nile-calendar.test.esm.js +1 -1
- package/dist/nile-select/index.cjs.js +1 -1
- package/dist/nile-select/index.esm.js +1 -1
- package/dist/nile-select/nile-select.cjs.js +1 -5
- package/dist/nile-select/nile-select.cjs.js.map +1 -1
- package/dist/nile-select/nile-select.css.cjs.js +1 -1
- package/dist/nile-select/nile-select.css.cjs.js.map +1 -1
- package/dist/nile-select/nile-select.css.esm.js +1 -1
- package/dist/nile-select/nile-select.esm.js +2 -7
- package/dist/nile-select/nile-select.test.cjs.js +1 -1
- package/dist/nile-select/nile-select.test.cjs.js.map +1 -1
- package/dist/nile-select/nile-select.test.esm.js +1 -1
- package/dist/nile-select/virtual-scroll-helper.cjs.js +1 -1
- package/dist/nile-select/virtual-scroll-helper.cjs.js.map +1 -1
- package/dist/nile-select/virtual-scroll-helper.esm.js +2 -0
- package/dist/nile-virtual-select/index.cjs.js +1 -1
- package/dist/nile-virtual-select/index.esm.js +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.cjs.js +5 -1
- package/dist/nile-virtual-select/nile-virtual-select.cjs.js.map +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.css.cjs.js +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.css.cjs.js.map +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.css.esm.js +20 -1
- package/dist/nile-virtual-select/nile-virtual-select.esm.js +21 -12
- package/dist/nile-virtual-select/renderer.cjs.js +1 -1
- package/dist/nile-virtual-select/renderer.cjs.js.map +1 -1
- package/dist/nile-virtual-select/renderer.esm.js +11 -10
- package/dist/nile-virtual-select/selection-manager.cjs.js +1 -1
- package/dist/nile-virtual-select/selection-manager.cjs.js.map +1 -1
- package/dist/nile-virtual-select/selection-manager.esm.js +1 -1
- package/dist/src/nile-button/nile-button.d.ts +1 -1
- package/dist/src/nile-button/nile-button.js +1 -1
- package/dist/src/nile-button/nile-button.js.map +1 -1
- package/dist/src/nile-select/nile-select.css.js +1 -1
- package/dist/src/nile-select/nile-select.css.js.map +1 -1
- package/dist/src/nile-select/nile-select.d.ts +5 -1
- package/dist/src/nile-select/nile-select.js +31 -15
- package/dist/src/nile-select/nile-select.js.map +1 -1
- package/dist/src/nile-select/virtual-scroll-helper.js +2 -0
- package/dist/src/nile-select/virtual-scroll-helper.js.map +1 -1
- package/dist/src/nile-virtual-select/nile-virtual-select.css.js +20 -1
- package/dist/src/nile-virtual-select/nile-virtual-select.css.js.map +1 -1
- package/dist/src/nile-virtual-select/nile-virtual-select.d.ts +22 -1
- package/dist/src/nile-virtual-select/nile-virtual-select.js +148 -22
- package/dist/src/nile-virtual-select/nile-virtual-select.js.map +1 -1
- package/dist/src/nile-virtual-select/renderer.d.ts +2 -2
- package/dist/src/nile-virtual-select/renderer.js +9 -8
- package/dist/src/nile-virtual-select/renderer.js.map +1 -1
- package/dist/src/nile-virtual-select/selection-manager.js +1 -1
- package/dist/src/nile-virtual-select/selection-manager.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +15 -15
- package/src/nile-button/nile-button.ts +1 -1
- package/src/nile-select/nile-select.css.ts +1 -1
- package/src/nile-select/nile-select.ts +31 -17
- package/src/nile-select/virtual-scroll-helper.ts +2 -0
- package/src/nile-virtual-select/nile-virtual-select.css.ts +20 -1
- package/src/nile-virtual-select/nile-virtual-select.ts +158 -26
- package/src/nile-virtual-select/renderer.ts +10 -8
- package/src/nile-virtual-select/selection-manager.ts +1 -1
- package/vscode-html-custom-data.json +61 -34
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Webcomponent nile-elements following open-wc recommendations",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "nile-elements",
|
|
6
|
-
"version": "0.1.75-beta-1.
|
|
6
|
+
"version": "0.1.75-beta-1.5",
|
|
7
7
|
"main": "dist/src/index.js",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"module": "dist/src/index.js",
|
|
@@ -108,23 +108,23 @@
|
|
|
108
108
|
},
|
|
109
109
|
"dependencies": {
|
|
110
110
|
"@aquera/nile": "latest",
|
|
111
|
-
"@codemirror/lang-html": "6.4.9",
|
|
112
|
-
"@codemirror/lang-javascript": "6.2.1",
|
|
113
|
-
"@codemirror/lang-json": "^6.0.1",
|
|
114
|
-
"@codemirror/lang-sql": "6.7.0",
|
|
115
|
-
"@floating-ui/dom": "^1.2.1",
|
|
116
|
-
"@lit-labs/observers": "^2.0.6",
|
|
117
|
-
"@lit-labs/virtualizer": "^2.0.15",
|
|
118
111
|
"@open-wc/form-control": "^0.5.0",
|
|
119
112
|
"@open-wc/form-helpers": "^0.2.1",
|
|
120
|
-
"@rollup/plugin-commonjs": "^25.0.3",
|
|
121
113
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
|
122
|
-
"
|
|
123
|
-
"codemirror": "6.0.1",
|
|
124
|
-
"composed-offset-position": "^0.0.4",
|
|
114
|
+
"@rollup/plugin-commonjs": "^25.0.3",
|
|
125
115
|
"element-internals-polyfill": "^1.1.20",
|
|
116
|
+
"lit": "^3.0.0",
|
|
117
|
+
"@floating-ui/dom": "^1.2.1",
|
|
118
|
+
"composed-offset-position": "^0.0.4",
|
|
119
|
+
"@codemirror/lang-javascript": "6.2.1",
|
|
120
|
+
"@codemirror/lang-sql": "6.7.0",
|
|
121
|
+
"@codemirror/lang-json": "^6.0.1",
|
|
122
|
+
"@codemirror/lang-html": "6.4.9",
|
|
123
|
+
"@lit-labs/virtualizer": "^2.0.15",
|
|
124
|
+
"codemirror": "6.0.1",
|
|
125
|
+
"chalk": "5.3.0",
|
|
126
126
|
"figlet": "1.7.0",
|
|
127
|
-
"lit": "^
|
|
127
|
+
"@lit-labs/observers": "^2.0.6"
|
|
128
128
|
},
|
|
129
129
|
"devDependencies": {
|
|
130
130
|
"@custom-elements-manifest/analyzer": "^0.4.17",
|
|
@@ -139,7 +139,6 @@
|
|
|
139
139
|
"@web/dev-server": "^0.1.34",
|
|
140
140
|
"@web/dev-server-storybook": "^0.5.4",
|
|
141
141
|
"@web/test-runner": "^0.14.0",
|
|
142
|
-
"@web/test-runner-puppeteer": "0.16.0",
|
|
143
142
|
"concurrently": "^5.3.0",
|
|
144
143
|
"eslint": "^7.32.0",
|
|
145
144
|
"eslint-config-prettier": "^8.3.0",
|
|
@@ -156,7 +155,8 @@
|
|
|
156
155
|
"rollup-plugin-terser": "^7.0.2",
|
|
157
156
|
"tslib": "^2.3.1",
|
|
158
157
|
"typescript": "^5.6.2",
|
|
159
|
-
"x": "^0.1.2"
|
|
158
|
+
"x": "^0.1.2",
|
|
159
|
+
"@web/test-runner-puppeteer": "0.16.0"
|
|
160
160
|
},
|
|
161
161
|
"customElements": "custom-elements.json",
|
|
162
162
|
"eslintConfig": {
|
|
@@ -78,7 +78,7 @@ export class NileButton extends NileElement implements NileFormControl {
|
|
|
78
78
|
| 'secondary-blue' = 'primary';
|
|
79
79
|
|
|
80
80
|
/** The button's size. */
|
|
81
|
-
@
|
|
81
|
+
@state() size = 'medium';
|
|
82
82
|
|
|
83
83
|
/** Draws the button with a caret. Used to indicate that the button triggers a dropdown menu or similar behavior. */
|
|
84
84
|
@property({ type: Boolean, reflect: true }) caret = false;
|
|
@@ -398,7 +398,7 @@ export const styles = css`
|
|
|
398
398
|
position: sticky;
|
|
399
399
|
bottom: 0px;
|
|
400
400
|
background: var(--nile-colors-neutral-100);
|
|
401
|
-
border: 1px solid var(--nile-colors-neutral-400);
|
|
401
|
+
border-top: 1px solid var(--nile-colors-neutral-400);
|
|
402
402
|
display: flex;
|
|
403
403
|
height: 15px;
|
|
404
404
|
/* Auto layout */
|
|
@@ -42,7 +42,6 @@ import NileOptionGroup from '../nile-option-group/nile-option-group';
|
|
|
42
42
|
import { GroupAttributes } from './nile-select.interface';
|
|
43
43
|
import { ResizeController } from '@lit-labs/observers/resize-controller.js';
|
|
44
44
|
|
|
45
|
-
|
|
46
45
|
type NileRemoveEvent = CustomEvent<Record<PropertyKey, never>>;
|
|
47
46
|
|
|
48
47
|
/**
|
|
@@ -122,16 +121,7 @@ export class NileSelect extends NileElement implements NileFormControl{
|
|
|
122
121
|
private scrollTimeout: number | undefined;
|
|
123
122
|
private scrolling = false;
|
|
124
123
|
private options: NileOption[] = [];
|
|
125
|
-
private resizeController
|
|
126
|
-
callback: (entries: ResizeObserverEntry[]) => {
|
|
127
|
-
for (const entry of entries) {
|
|
128
|
-
if (entry.target.classList.contains('select__tags')) {
|
|
129
|
-
this.calculateTotalWidthOfTags();
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
|
|
124
|
+
private resizeController?: ResizeController;
|
|
135
125
|
|
|
136
126
|
@query('.select') popup: NilePopup;
|
|
137
127
|
@query('.select__combobox') combobox: HTMLSlotElement;
|
|
@@ -202,6 +192,7 @@ export class NileSelect extends NileElement implements NileFormControl{
|
|
|
202
192
|
|
|
203
193
|
|
|
204
194
|
@property({ attribute: 'help-text', reflect: true }) helpText = '';
|
|
195
|
+
@property({ type: Boolean, attribute: true, reflect: true }) autoResize = false;
|
|
205
196
|
|
|
206
197
|
@property({ attribute: 'error-message', reflect: true }) errorMessage = '';
|
|
207
198
|
|
|
@@ -285,6 +276,9 @@ export class NileSelect extends NileElement implements NileFormControl{
|
|
|
285
276
|
/** To auto focus the search input when the select is opened */
|
|
286
277
|
@property({ type: Boolean, reflect: true, attribute: true }) autoFocusSearch = false;
|
|
287
278
|
|
|
279
|
+
/** loading indicator for virtual select */
|
|
280
|
+
@property({ type: Boolean, reflect: true, attribute: true }) loading = false;
|
|
281
|
+
|
|
288
282
|
/** Gets the validity state object */
|
|
289
283
|
get validity() {
|
|
290
284
|
return this.valueInput?.validity;
|
|
@@ -326,24 +320,43 @@ export class NileSelect extends NileElement implements NileFormControl{
|
|
|
326
320
|
this.emit('nile-destroy');
|
|
327
321
|
}
|
|
328
322
|
|
|
323
|
+
private setupResizeObserver() {
|
|
324
|
+
if (this.autoResize) {
|
|
325
|
+
const tagsContainer = this.shadowRoot?.querySelector('.select__tags');
|
|
326
|
+
if (tagsContainer) {
|
|
327
|
+
this.resizeController = new ResizeController(this, {
|
|
328
|
+
callback: () => this.calculateTotalWidthOfTags()
|
|
329
|
+
});
|
|
330
|
+
this.resizeController.observe(tagsContainer);
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
this.resizeController?.unobserve?.(
|
|
334
|
+
this.shadowRoot?.querySelector('.select__tags') as Element
|
|
335
|
+
);
|
|
336
|
+
this.resizeController = undefined;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
329
340
|
protected updated(_changedProperties: PropertyValues): void {
|
|
330
341
|
if(_changedProperties.has('multiple')) {
|
|
331
342
|
this.setCheckBoxInOption(this.multiple as boolean);
|
|
332
343
|
}
|
|
344
|
+
if (_changedProperties.has('autoResize')) {
|
|
345
|
+
this.setupResizeObserver();
|
|
346
|
+
}
|
|
333
347
|
}
|
|
334
348
|
|
|
335
349
|
protected firstUpdated(_changedProperties: PropertyValues): void {
|
|
336
350
|
if(this.enableGroupHeader) {
|
|
337
351
|
this.handleGroupSearchChange();
|
|
338
352
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
this.resizeController.observe(tagsContainer);
|
|
342
|
-
}
|
|
353
|
+
|
|
354
|
+
this.setupResizeObserver();
|
|
343
355
|
if(_changedProperties.has('multiple')) {
|
|
344
356
|
this.setCheckBoxInOption(this.multiple as boolean);
|
|
345
357
|
}
|
|
346
358
|
}
|
|
359
|
+
|
|
347
360
|
setCheckBoxInOption(checked: boolean): void {
|
|
348
361
|
if(!this.options.length) {
|
|
349
362
|
this.options = this.getAllOptions();
|
|
@@ -958,8 +971,9 @@ export class NileSelect extends NileElement implements NileFormControl{
|
|
|
958
971
|
const allOptions = this.getAllOptions();
|
|
959
972
|
const value = Array.isArray(this.value) ? this.value : [this.value];
|
|
960
973
|
|
|
961
|
-
|
|
962
|
-
|
|
974
|
+
if(!this.enableVirtualScroll) {
|
|
975
|
+
this.setSelectedOptions(allOptions.filter(el => value.includes(el.value)));
|
|
976
|
+
}
|
|
963
977
|
}
|
|
964
978
|
|
|
965
979
|
@watch('open', { waitUntilFirstUpdate: true })
|
|
@@ -19,6 +19,7 @@ export class VirtualScrollHelper {
|
|
|
19
19
|
<nile-virtual-select
|
|
20
20
|
.name=${component.name}
|
|
21
21
|
.value=${component.value}
|
|
22
|
+
.autoResize=${component.autoResize}
|
|
22
23
|
.placeholder=${component.placeholder}
|
|
23
24
|
.size=${component.size}
|
|
24
25
|
.searchEnabled=${component.searchEnabled}
|
|
@@ -48,6 +49,7 @@ export class VirtualScrollHelper {
|
|
|
48
49
|
.maxOptionsVisible=${component.maxOptionsVisible}
|
|
49
50
|
.data=${component.data}
|
|
50
51
|
.open=${component.open}
|
|
52
|
+
.loading=${component.loading}
|
|
51
53
|
exportparts="
|
|
52
54
|
select-options,
|
|
53
55
|
select-no-results,
|
|
@@ -32,6 +32,11 @@ export const styles = css`
|
|
|
32
32
|
flex-direction: column;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
.virtualized.no-scroll {
|
|
36
|
+
overflow-y: hidden !important;
|
|
37
|
+
max-height: none !important;
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
.virtualized:not(:has(lit-virtualizer)) nile-option {
|
|
36
41
|
flex-shrink: 0;
|
|
37
42
|
}
|
|
@@ -421,7 +426,7 @@ export const styles = css`
|
|
|
421
426
|
position: sticky;
|
|
422
427
|
bottom: 0px;
|
|
423
428
|
background: var(--nile-colors-neutral-100);
|
|
424
|
-
border: 1px solid var(--nile-colors-neutral-400);
|
|
429
|
+
border-top: 1px solid var(--nile-colors-neutral-400);
|
|
425
430
|
display: flex;
|
|
426
431
|
height: 15px;
|
|
427
432
|
/* Auto layout */
|
|
@@ -486,6 +491,20 @@ export const styles = css`
|
|
|
486
491
|
.virtualized nile-option[selected]::part(base) {
|
|
487
492
|
color: var(--nile-colors-primary-600);
|
|
488
493
|
}
|
|
494
|
+
|
|
495
|
+
.virtual-select-loader {
|
|
496
|
+
position: absolute;
|
|
497
|
+
display: flex;
|
|
498
|
+
justify-content: center;
|
|
499
|
+
align-items: center;
|
|
500
|
+
width: 100%;
|
|
501
|
+
height: 75%;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.select__footer.loading, .select__options.loading {
|
|
505
|
+
opacity: 0.5;
|
|
506
|
+
pointer-events: none;
|
|
507
|
+
}
|
|
489
508
|
`;
|
|
490
509
|
|
|
491
510
|
export default [styles];
|
|
@@ -16,6 +16,7 @@ import '../nile-icon';
|
|
|
16
16
|
import '../nile-popup/nile-popup';
|
|
17
17
|
import '../nile-tag/nile-tag';
|
|
18
18
|
import '../nile-checkbox/nile-checkbox';
|
|
19
|
+
import '../nile-loader/nile-loader';
|
|
19
20
|
import { animateTo, stopAnimations } from '../internal/animate';
|
|
20
21
|
import { classMap } from 'lit/directives/class-map.js';
|
|
21
22
|
import { query, state } from 'lit/decorators.js';
|
|
@@ -29,7 +30,7 @@ import { HasSlotController } from '../internal/slot';
|
|
|
29
30
|
import { waitForEvent } from '../internal/event';
|
|
30
31
|
import { watch } from '../internal/watch';
|
|
31
32
|
import NileElement from '../internal/nile-element';
|
|
32
|
-
import type { CSSResultGroup, TemplateResult } from 'lit';
|
|
33
|
+
import type { CSSResultGroup, PropertyValues, TemplateResult } from 'lit';
|
|
33
34
|
import type { NileFormControl } from '../internal/nile-element';
|
|
34
35
|
import type NileOption from '../nile-option/nile-option';
|
|
35
36
|
import type NilePopup from '../nile-popup/nile-popup';
|
|
@@ -39,6 +40,7 @@ import type { VirtualOption, NileRemoveEvent, RenderItemConfig } from './types.j
|
|
|
39
40
|
import { VirtualSelectSelectionManager } from './selection-manager.js';
|
|
40
41
|
import { VirtualSelectSearchManager } from './search-manager.js';
|
|
41
42
|
import { VirtualSelectRenderer } from './renderer.js';
|
|
43
|
+
import { ResizeController } from '@lit-labs/observers/resize-controller.js';
|
|
42
44
|
|
|
43
45
|
/**
|
|
44
46
|
* Nile Virtual Select component.
|
|
@@ -73,6 +75,10 @@ import { VirtualSelectRenderer } from './renderer.js';
|
|
|
73
75
|
* @event nile-hide - Emitted when the select's menu closes.
|
|
74
76
|
* @event nile-after-hide - Emitted after the select's menu closes and all animations are complete.
|
|
75
77
|
* @event nile-invalid - Emitted when the form control has been checked for validity and its constraints aren't satisfied.
|
|
78
|
+
* @event nile-search - Emitted when the user types in the search input. The event payload includes the search query for backend search functionality.
|
|
79
|
+
* @event nile-scroll - Emitted when the user scrolls within the virtualized container. The event payload includes scroll position information.
|
|
80
|
+
* @event nile-scroll-start - Emitted when the user starts scrolling within the virtualized container.
|
|
81
|
+
* @event nile-scroll-end - Emitted when the user stops scrolling and reaches the bottom of the virtualized container (debounced).
|
|
76
82
|
*
|
|
77
83
|
* @csspart form-control - The form control that wraps the label, input, and help text.
|
|
78
84
|
* @csspart form-control-label - The label's wrapper.
|
|
@@ -111,6 +117,9 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
111
117
|
@state() displayLabel = '';
|
|
112
118
|
@state() selectedOptions: VirtualOption[] = [];
|
|
113
119
|
@state() oldValue: string | string[] = '';
|
|
120
|
+
|
|
121
|
+
private scrollTimeout: number | undefined;
|
|
122
|
+
private scrolling = false;
|
|
114
123
|
|
|
115
124
|
/** The name of the select, submitted as a name/value pair with form data. */
|
|
116
125
|
@property() name = '';
|
|
@@ -141,6 +150,9 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
141
150
|
/** Placeholder text to show as a hint when the select is empty. */
|
|
142
151
|
@property() placeholder = 'Select...';
|
|
143
152
|
|
|
153
|
+
/** Enable automatic resizing of tags area */
|
|
154
|
+
@property({ type: Boolean }) autoResize = false;
|
|
155
|
+
|
|
144
156
|
/** Current search value */
|
|
145
157
|
@state() searchValue: string = '';
|
|
146
158
|
|
|
@@ -156,6 +168,9 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
156
168
|
/** Show loading state */
|
|
157
169
|
@property({ type: Boolean, reflect: true }) optionsLoading = false;
|
|
158
170
|
|
|
171
|
+
/** Show loading state using nile-loader */
|
|
172
|
+
@property({ type: Boolean, reflect: true, attribute: true }) loading = false;
|
|
173
|
+
|
|
159
174
|
/** Allows more than one option to be selected. */
|
|
160
175
|
@property({ type: Boolean, reflect: true }) multiple = false;
|
|
161
176
|
|
|
@@ -240,6 +255,7 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
240
255
|
maxOptionsVisible = 3;
|
|
241
256
|
|
|
242
257
|
@state() oldMaxOptionsVisible: number = 1;
|
|
258
|
+
@state() showListbox: boolean = false;
|
|
243
259
|
|
|
244
260
|
/** Gets the validity state object */
|
|
245
261
|
get validity() {
|
|
@@ -265,6 +281,25 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
265
281
|
|
|
266
282
|
disconnectedCallback() {
|
|
267
283
|
this.removeOpenListeners();
|
|
284
|
+
// Clear any pending scroll timeout to prevent memory leaks
|
|
285
|
+
if (this.scrollTimeout) {
|
|
286
|
+
clearTimeout(this.scrollTimeout);
|
|
287
|
+
this.scrollTimeout = undefined;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
protected updated(changedProperties: PropertyValues): void {
|
|
292
|
+
if(changedProperties.has('value')) {
|
|
293
|
+
this.selectionChanged();
|
|
294
|
+
}
|
|
295
|
+
if (changedProperties.has('autoResize')) {
|
|
296
|
+
const tagsDiv = this.shadowRoot?.querySelector('.select__tags') as HTMLElement;
|
|
297
|
+
if (this.autoResize && tagsDiv) {
|
|
298
|
+
this.resizeController.observe(tagsDiv);
|
|
299
|
+
} else if (tagsDiv) {
|
|
300
|
+
this.resizeController.unobserve(tagsDiv);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
268
303
|
}
|
|
269
304
|
|
|
270
305
|
private initializeComponent(): void {
|
|
@@ -307,18 +342,21 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
307
342
|
this.handleDocumentFocusIn = this.handleDocumentFocusIn.bind(this);
|
|
308
343
|
this.handleDocumentKeyDown = this.handleDocumentKeyDown.bind(this);
|
|
309
344
|
this.handleDocumentMouseDown = this.handleDocumentMouseDown.bind(this);
|
|
345
|
+
this.handleWindowError = this.handleWindowError.bind(this);
|
|
310
346
|
}
|
|
311
347
|
|
|
312
348
|
private addOpenListeners(): void {
|
|
313
349
|
document.addEventListener('focusin', this.handleDocumentFocusIn);
|
|
314
350
|
document.addEventListener('keydown', this.handleDocumentKeyDown);
|
|
315
351
|
document.addEventListener('mousedown', this.handleDocumentMouseDown);
|
|
352
|
+
window.addEventListener('error', this.handleWindowError);
|
|
316
353
|
}
|
|
317
354
|
|
|
318
355
|
private removeOpenListeners(): void {
|
|
319
356
|
document.removeEventListener('focusin', this.handleDocumentFocusIn);
|
|
320
357
|
document.removeEventListener('keydown', this.handleDocumentKeyDown);
|
|
321
358
|
document.removeEventListener('mousedown', this.handleDocumentMouseDown);
|
|
359
|
+
window.removeEventListener('error', this.handleWindowError);
|
|
322
360
|
}
|
|
323
361
|
|
|
324
362
|
private handleFocus(): void {
|
|
@@ -405,6 +443,19 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
405
443
|
}
|
|
406
444
|
};
|
|
407
445
|
|
|
446
|
+
/**
|
|
447
|
+
* This is a workaround for an error in the Lit Labs virtualizer.
|
|
448
|
+
* Since there are no specific guidelines available to fix the issue,
|
|
449
|
+
* we are catching only the error message related to the virtualizer.
|
|
450
|
+
*/
|
|
451
|
+
private handleWindowError = (event: ErrorEvent): void => {
|
|
452
|
+
const errorMessage = event.error?.message || event.message || '';
|
|
453
|
+
if (errorMessage.includes('Cannot read properties of null (reading \'insertBefore\')')) {
|
|
454
|
+
event.preventDefault();
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
408
459
|
private handleFooterClick(event: MouseEvent): void {
|
|
409
460
|
event.stopPropagation();
|
|
410
461
|
event.preventDefault();
|
|
@@ -420,9 +471,10 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
420
471
|
|
|
421
472
|
if (this.showSelected) {
|
|
422
473
|
const selectedValues = Array.isArray(this.value) ? this.value : [this.value];
|
|
423
|
-
this.data = this.originalOptionItems.filter((item: any) =>
|
|
424
|
-
|
|
425
|
-
|
|
474
|
+
this.data = this.originalOptionItems.filter((item: any) => {
|
|
475
|
+
const itemValue = this.getItemValue(item);
|
|
476
|
+
return selectedValues.some(val => String(val) === String(itemValue));
|
|
477
|
+
});
|
|
426
478
|
} else {
|
|
427
479
|
this.data = [...this.originalOptionItems];
|
|
428
480
|
}
|
|
@@ -456,6 +508,16 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
456
508
|
this.open = !this.open;
|
|
457
509
|
}
|
|
458
510
|
|
|
511
|
+
private resizeController = new ResizeController(this, {
|
|
512
|
+
callback: (entries: ResizeObserverEntry[]) => {
|
|
513
|
+
for (const entry of entries) {
|
|
514
|
+
if (entry.target.classList.contains('select__tags')) {
|
|
515
|
+
this.calculateTotalWidthOfTags();
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
|
|
459
521
|
private shouldIgnoreComboboxClick(event: MouseEvent): boolean {
|
|
460
522
|
const path = event.composedPath();
|
|
461
523
|
const isIconButton = path.some(
|
|
@@ -617,6 +679,7 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
617
679
|
|
|
618
680
|
this.calculateTotalWidthOfTags();
|
|
619
681
|
}
|
|
682
|
+
|
|
620
683
|
|
|
621
684
|
handleSearchFocus(): void {
|
|
622
685
|
document.removeEventListener('keydown', this.handleDocumentKeyDown);
|
|
@@ -628,9 +691,65 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
628
691
|
|
|
629
692
|
handleSearchChange(e: any): void {
|
|
630
693
|
this.searchValue = e.detail.value;
|
|
694
|
+
|
|
695
|
+
this.emit('nile-search', {
|
|
696
|
+
query: this.searchValue,
|
|
697
|
+
name: this.name
|
|
698
|
+
});
|
|
699
|
+
|
|
631
700
|
if (!this.disableLocalSearch) {
|
|
632
701
|
this.filterVirtualOptions(this.searchValue);
|
|
633
702
|
this.resetScrollPosition();
|
|
703
|
+
this.updateComplete.then(() => {
|
|
704
|
+
const virtualized = this.shadowRoot?.querySelector('.virtualized') as HTMLElement;
|
|
705
|
+
if (virtualized) {
|
|
706
|
+
if (this.data.length <= 5) {
|
|
707
|
+
virtualized.classList.add('no-scroll');
|
|
708
|
+
} else {
|
|
709
|
+
virtualized.classList.remove('no-scroll');
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
handleScroll(e: Event): void {
|
|
717
|
+
if(this.showSelected) {
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const target = e.target as HTMLElement;
|
|
722
|
+
|
|
723
|
+
this.emit('nile-scroll', {
|
|
724
|
+
scrollTop: target.scrollTop,
|
|
725
|
+
scrollLeft: target.scrollLeft,
|
|
726
|
+
name: this.name
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
if (!this.scrolling) {
|
|
730
|
+
this.scrolling = true;
|
|
731
|
+
this.emit('nile-scroll-start', {
|
|
732
|
+
scrollTop: target.scrollTop,
|
|
733
|
+
scrollLeft: target.scrollLeft,
|
|
734
|
+
name: this.name
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
clearTimeout(this.scrollTimeout);
|
|
739
|
+
this.scrollTimeout = window.setTimeout(() => {
|
|
740
|
+
if (this.scrolling) {
|
|
741
|
+
this.scrolling = false;
|
|
742
|
+
}
|
|
743
|
+
}, 300);
|
|
744
|
+
|
|
745
|
+
const isAtBottom = Math.ceil(target.scrollTop) >= Math.floor(target.scrollHeight - target.offsetHeight);
|
|
746
|
+
if (isAtBottom && !this.searchValue) {
|
|
747
|
+
this.emit('nile-scroll-end', {
|
|
748
|
+
scrollTop: target.scrollTop,
|
|
749
|
+
scrollLeft: target.scrollLeft,
|
|
750
|
+
name: this.name,
|
|
751
|
+
isAtBottom: true
|
|
752
|
+
});
|
|
634
753
|
}
|
|
635
754
|
}
|
|
636
755
|
|
|
@@ -670,18 +789,18 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
670
789
|
|
|
671
790
|
@watch('data', { waitUntilFirstUpdate: true })
|
|
672
791
|
handleDataChange(): void {
|
|
792
|
+
if (this.data.length > 0 && this.open && !this.showSelected && !this.searchValue) {
|
|
793
|
+
this.originalOptionItems = [...this.data];
|
|
794
|
+
}
|
|
795
|
+
|
|
673
796
|
this.selectionChanged();
|
|
674
797
|
// Show no results message when data is empty and not loading
|
|
675
|
-
if (!this.optionsLoading && this.data.length === 0) {
|
|
798
|
+
if (!this.optionsLoading && !this.loading && this.data.length === 0) {
|
|
676
799
|
this.showNoResults = true;
|
|
677
800
|
} else if (this.data.length > 0) {
|
|
678
801
|
this.showNoResults = false;
|
|
679
802
|
}
|
|
680
803
|
this.requestUpdate();
|
|
681
|
-
|
|
682
|
-
if (this.open) {
|
|
683
|
-
this.resetScrollPosition();
|
|
684
|
-
}
|
|
685
804
|
}
|
|
686
805
|
|
|
687
806
|
@watch('renderItemConfig', { waitUntilFirstUpdate: true })
|
|
@@ -705,8 +824,10 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
705
824
|
async handleOpenChange(): Promise<void> {
|
|
706
825
|
if (this.open && !this.disabled) {
|
|
707
826
|
await this.handleOpen();
|
|
827
|
+
this.showListbox = true;
|
|
708
828
|
} else {
|
|
709
829
|
await this.handleClose();
|
|
830
|
+
this.showListbox = false;
|
|
710
831
|
}
|
|
711
832
|
}
|
|
712
833
|
|
|
@@ -745,7 +866,7 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
745
866
|
}
|
|
746
867
|
|
|
747
868
|
private initializeOriginalItems(): void {
|
|
748
|
-
if (this.
|
|
869
|
+
if (this.data.length > 0) {
|
|
749
870
|
this.originalOptionItems = [...this.data];
|
|
750
871
|
}
|
|
751
872
|
}
|
|
@@ -893,7 +1014,7 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
893
1014
|
>
|
|
894
1015
|
${this.renderCustomSelect(hasCustomSelect)}
|
|
895
1016
|
${this.renderCombobox(hasCustomSelect, hasClearIcon)}
|
|
896
|
-
${this.renderListbox()}
|
|
1017
|
+
${this.showListbox ? this.renderListbox() : html``}
|
|
897
1018
|
</nile-popup>
|
|
898
1019
|
`;
|
|
899
1020
|
}
|
|
@@ -1125,16 +1246,26 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
1125
1246
|
}
|
|
1126
1247
|
|
|
1127
1248
|
private renderLoader(): TemplateResult {
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1249
|
+
if (this.loading) {
|
|
1250
|
+
return html`
|
|
1251
|
+
<span part="loader" class="virtual-select-loader">
|
|
1252
|
+
<nile-loader size="sm"></nile-loader>
|
|
1253
|
+
</span>
|
|
1254
|
+
`;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
if (this.optionsLoading) {
|
|
1258
|
+
return html`
|
|
1259
|
+
<span part="loader" class="select__loader">
|
|
1260
|
+
<nile-icon
|
|
1261
|
+
class="select__loader--icon"
|
|
1262
|
+
name="button-loading-blue"
|
|
1263
|
+
></nile-icon>
|
|
1264
|
+
</span>
|
|
1265
|
+
`;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
return html``;
|
|
1138
1269
|
}
|
|
1139
1270
|
|
|
1140
1271
|
private renderFooter(): TemplateResult {
|
|
@@ -1142,7 +1273,7 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
1142
1273
|
? html`
|
|
1143
1274
|
<div
|
|
1144
1275
|
part="footer"
|
|
1145
|
-
class="select__footer"
|
|
1276
|
+
class="select__footer ${this.loading ? 'loading' : ''}"
|
|
1146
1277
|
@click="${this.handleFooterClick}"
|
|
1147
1278
|
>
|
|
1148
1279
|
<span
|
|
@@ -1197,7 +1328,8 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
1197
1328
|
this.renderItemConfig?.getValue,
|
|
1198
1329
|
this.showNoResults,
|
|
1199
1330
|
this.noResultsMessage,
|
|
1200
|
-
this.optionsLoading
|
|
1331
|
+
this.optionsLoading || this.loading,
|
|
1332
|
+
this.handleScroll.bind(this)
|
|
1201
1333
|
);
|
|
1202
1334
|
}
|
|
1203
1335
|
|
|
@@ -1272,16 +1404,16 @@ export class NileVirtualSelect extends NileElement implements NileFormControl {
|
|
|
1272
1404
|
|
|
1273
1405
|
private resetScrollPosition(): void {
|
|
1274
1406
|
this.updateComplete.then(() => {
|
|
1275
|
-
if (this.virtualizedContainer) {
|
|
1407
|
+
if (this.virtualizedContainer && this.virtualizedContainer.isConnected) {
|
|
1276
1408
|
this.virtualizedContainer.scrollTop = 0;
|
|
1277
1409
|
|
|
1278
1410
|
const listbox = this.shadowRoot?.querySelector('.select__listbox') as HTMLElement;
|
|
1279
|
-
if (listbox) {
|
|
1411
|
+
if (listbox && listbox.isConnected) {
|
|
1280
1412
|
listbox.scrollTop = 0;
|
|
1281
1413
|
}
|
|
1282
1414
|
|
|
1283
1415
|
const virtualizer = this.virtualizedContainer.querySelector('lit-virtualizer') as HTMLElement;
|
|
1284
|
-
if (virtualizer) {
|
|
1416
|
+
if (virtualizer && virtualizer.isConnected) {
|
|
1285
1417
|
virtualizer.scrollTop = 0;
|
|
1286
1418
|
}
|
|
1287
1419
|
}
|
|
@@ -21,13 +21,14 @@ export class VirtualSelectRenderer {
|
|
|
21
21
|
getItemValue?: (item: any) => string,
|
|
22
22
|
showNoResults?: boolean,
|
|
23
23
|
noResultsMessage?: string,
|
|
24
|
-
|
|
24
|
+
loading?: boolean,
|
|
25
|
+
onScroll?: (e: Event) => void
|
|
25
26
|
): TemplateResult {
|
|
26
27
|
return html`
|
|
27
28
|
<div part="select-options" class="select__options ${
|
|
28
29
|
searchEnabled ? 'select__options__search-enabled' : ``
|
|
29
|
-
}">
|
|
30
|
-
${showNoResults && !
|
|
30
|
+
} ${loading ? 'loading' : ''}">
|
|
31
|
+
${showNoResults && !loading
|
|
31
32
|
? html`
|
|
32
33
|
<div part="select-no-results" class="select__no-results">
|
|
33
34
|
${noResultsMessage || 'No results found'}
|
|
@@ -37,6 +38,7 @@ export class VirtualSelectRenderer {
|
|
|
37
38
|
<div
|
|
38
39
|
class="virtualized"
|
|
39
40
|
part="virtualized"
|
|
41
|
+
@scroll=${onScroll}
|
|
40
42
|
>
|
|
41
43
|
${VirtualSelectRenderer.shouldUseVirtualizer(data)
|
|
42
44
|
? html`
|
|
@@ -70,12 +72,12 @@ export class VirtualSelectRenderer {
|
|
|
70
72
|
|
|
71
73
|
const optionValue = valueFn(item);
|
|
72
74
|
const displayText = displayTextFn(item);
|
|
73
|
-
const isDisabled = item
|
|
74
|
-
const className = item
|
|
75
|
+
const isDisabled = item?.disabled || false;
|
|
76
|
+
const className = item?.className;
|
|
75
77
|
|
|
76
78
|
let isSelected = false;
|
|
77
79
|
if (multiple) {
|
|
78
|
-
isSelected = Array.isArray(value) && value.
|
|
80
|
+
isSelected = Array.isArray(value) && value.some(v => String(v) === String(optionValue));
|
|
79
81
|
} else {
|
|
80
82
|
isSelected = (Array.isArray(value) ? value[0] : value) === optionValue;
|
|
81
83
|
}
|
|
@@ -95,9 +97,9 @@ export class VirtualSelectRenderer {
|
|
|
95
97
|
|
|
96
98
|
/**
|
|
97
99
|
* Determines whether to use virtualizer based on dataset size
|
|
98
|
-
* For small datasets (less than
|
|
100
|
+
* For small datasets (less than 5 items), use regular rendering for better sizing
|
|
99
101
|
*/
|
|
100
102
|
private static shouldUseVirtualizer(data: any[]): boolean {
|
|
101
|
-
return data.length >=
|
|
103
|
+
return data.length >= 5;
|
|
102
104
|
}
|
|
103
105
|
}
|
|
@@ -28,7 +28,7 @@ export class VirtualSelectSelectionManager {
|
|
|
28
28
|
const item = data.find((item: any) => {
|
|
29
29
|
const itemValue = valueFn(item);
|
|
30
30
|
const itemDisplayText = displayTextFn(item);
|
|
31
|
-
return itemValue === valueItem || itemDisplayText === valueItem;
|
|
31
|
+
return String(itemValue) === String(valueItem) || String(itemDisplayText) === String(valueItem);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
const displayText = item ? displayTextFn(item) : valueItem;
|