@nova-design-system/nova-webcomponents 3.10.0 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/nv-alert.cjs.entry.js +1 -1
- package/dist/cjs/nv-alert.cjs.entry.js.map +1 -1
- package/dist/cjs/nv-badge_2.cjs.entry.js +7 -1
- package/dist/cjs/nv-badge_2.cjs.entry.js.map +1 -1
- package/dist/cjs/nv-fieldmultiselect.cjs.entry.js +112 -72
- package/dist/cjs/nv-fieldmultiselect.cjs.entry.js.map +1 -1
- package/dist/cjs/nv-tooltip.cjs.entry.js +1 -1
- package/dist/cjs/nv-tooltip.cjs.entry.js.map +1 -1
- package/dist/collection/components/nv-alert/nv-alert.css +1 -0
- package/dist/collection/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.js +7 -1
- package/dist/collection/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.js.map +1 -1
- package/dist/collection/components/nv-fieldmultiselect/nv-fieldmultiselect.js +112 -72
- package/dist/collection/components/nv-fieldmultiselect/nv-fieldmultiselect.js.map +1 -1
- package/dist/collection/components/nv-tooltip/nv-tooltip.css +2 -2
- package/dist/components/nv-alert.js +1 -1
- package/dist/components/nv-alert.js.map +1 -1
- package/dist/components/nv-breadcrumb.js +1 -1
- package/dist/components/nv-fielddropdownitemcheck.js +1 -1
- package/dist/components/nv-fieldmultiselect.js +113 -73
- package/dist/components/nv-fieldmultiselect.js.map +1 -1
- package/dist/components/nv-tooltip.js +1 -1
- package/dist/components/{p-51a156ff.js → p-ec4558aa.js} +8 -2
- package/dist/components/p-ec4558aa.js.map +1 -0
- package/dist/components/{p-2ef4fb88.js → p-f47a1e1e.js} +2 -2
- package/dist/{native/p-49504fd6.entry.js.map → components/p-f47a1e1e.js.map} +1 -1
- package/dist/esm/nv-alert.entry.js +1 -1
- package/dist/esm/nv-alert.entry.js.map +1 -1
- package/dist/esm/nv-badge_2.entry.js +7 -1
- package/dist/esm/nv-badge_2.entry.js.map +1 -1
- package/dist/esm/nv-fieldmultiselect.entry.js +112 -72
- package/dist/esm/nv-fieldmultiselect.entry.js.map +1 -1
- package/dist/esm/nv-tooltip.entry.js +1 -1
- package/dist/esm/nv-tooltip.entry.js.map +1 -1
- package/dist/native/native.css +1 -1
- package/dist/native/native.esm.js +1 -1
- package/dist/native/p-019d164d.entry.js +2 -0
- package/dist/native/p-019d164d.entry.js.map +1 -0
- package/dist/native/p-4f4ed012.entry.js +2 -0
- package/dist/native/p-4f4ed012.entry.js.map +1 -0
- package/dist/native/p-8a577f91.entry.js +2 -0
- package/dist/native/p-8a577f91.entry.js.map +1 -0
- package/dist/native/{p-13032ec1.entry.js → p-9991116a.entry.js} +2 -2
- package/dist/native/{p-13032ec1.entry.js.map → p-9991116a.entry.js.map} +1 -1
- package/dist/types/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.d.ts +2 -0
- package/dist/types/components/nv-fieldmultiselect/nv-fieldmultiselect.d.ts +3 -0
- package/hydrate/index.js +122 -76
- package/hydrate/index.mjs +122 -76
- package/package.json +1 -1
- package/dist/components/p-2ef4fb88.js.map +0 -1
- package/dist/components/p-51a156ff.js.map +0 -1
- package/dist/native/p-2a3325fb.entry.js +0 -2
- package/dist/native/p-2a3325fb.entry.js.map +0 -1
- package/dist/native/p-49504fd6.entry.js +0 -2
- package/dist/native/p-b2442d4b.entry.js +0 -2
- package/dist/native/p-b2442d4b.entry.js.map +0 -1
|
@@ -93,6 +93,7 @@ nv-alert > nv-icon.icon-neutral {
|
|
|
93
93
|
nv-alert > .content {
|
|
94
94
|
display: flex;
|
|
95
95
|
padding: var(--alert-padding);
|
|
96
|
+
padding-right: calc(var(--alert-padding) + var(--icon-md) + var(--spacing-1));
|
|
96
97
|
padding-left: 0;
|
|
97
98
|
flex-direction: column;
|
|
98
99
|
gap: var(--alert-gap-y);
|
|
@@ -17,6 +17,9 @@ export class NvFielddropdownitemcheck {
|
|
|
17
17
|
* Disables the item, preventing any user interaction.
|
|
18
18
|
*/
|
|
19
19
|
this.disabled = false;
|
|
20
|
+
//#endregion EVENTS
|
|
21
|
+
/****************************************************************************/
|
|
22
|
+
//#region METHODS
|
|
20
23
|
/**
|
|
21
24
|
* when the child <nv-fieldcheckbox> change its `checked` state,
|
|
22
25
|
* update `this.checked` and emit `itemChecked`.
|
|
@@ -45,8 +48,11 @@ export class NvFielddropdownitemcheck {
|
|
|
45
48
|
}
|
|
46
49
|
};
|
|
47
50
|
}
|
|
51
|
+
//#endregion METHODS
|
|
52
|
+
/****************************************************************************/
|
|
53
|
+
//#region RENDER
|
|
48
54
|
render() {
|
|
49
|
-
return (h(Host, { key: '
|
|
55
|
+
return (h(Host, { key: 'cb922f0c0224c950ab5a87783028f2909eb1e39c', onClick: this.handleClick }, h("nv-fieldcheckbox", { key: '6f8748022131a8cd31b0f69851313f80c330172a', checked: this.checked, name: this.label || this.value, label: this.label || this.value, labelPlacement: "after", description: this.description, disabled: this.disabled, tabindex: "-1", onCheckedChanged: this.onFieldcheckboxChanged }, h("slot", { key: '20d1014accc6cfc00658703057b25037175287af' }), h("slot", { key: 'a7e61eed6bc72d3a14096cac2c195629a0ce2928', name: "main" }), h("slot", { key: '93f6bce370ac36f1ce3e0f2c1d9cdd598c14cb14', name: "label" }), h("slot", { key: '837359fc1fea49e0dea221039dc7466cdc86b757', name: "description" }))));
|
|
50
56
|
}
|
|
51
57
|
static get is() { return "nv-fielddropdownitemcheck"; }
|
|
52
58
|
static get originalStyleUrls() {
|
package/dist/collection/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nv-fielddropdownitemcheck.js","sourceRoot":"","sources":["../../../src/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,IAAI,EACJ,CAAC,EACD,IAAI,EACJ,KAAK,EAEL,OAAO,GACR,MAAM,eAAe,CAAC;AAEvB;;;;;GAKG;AAMH,MAAM,OAAO,wBAAwB;IALrC;QAQE,8EAA8E;QAC9E,oBAAoB;QAEpB;;WAEG;QAEH,YAAO,GAAY,KAAK,CAAC;QA2BzB;;WAEG;QAEM,aAAQ,GAAY,KAAK,CAAC;QA0BnC;;;;WAIG;QACK,2BAAsB,GAAG,CAAC,KAA2B,EAAE,EAAE;YAC/D,IAAI,IAAI,CAAC,QAAQ;gBAAE,OAAO;
|
|
1
|
+
{"version":3,"file":"nv-fielddropdownitemcheck.js","sourceRoot":"","sources":["../../../src/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,IAAI,EACJ,CAAC,EACD,IAAI,EACJ,KAAK,EAEL,OAAO,GACR,MAAM,eAAe,CAAC;AAEvB;;;;;GAKG;AAMH,MAAM,OAAO,wBAAwB;IALrC;QAQE,8EAA8E;QAC9E,oBAAoB;QAEpB;;WAEG;QAEH,YAAO,GAAY,KAAK,CAAC;QA2BzB;;WAEG;QAEM,aAAQ,GAAY,KAAK,CAAC;QA0BnC,mBAAmB;QACnB,8EAA8E;QAC9E,iBAAiB;QAEjB;;;;WAIG;QACK,2BAAsB,GAAG,CAAC,KAA2B,EAAE,EAAE;YAC/D,IAAI,IAAI,CAAC,QAAQ;gBAAE,OAAO;YAE1B,6CAA6C;YAC7C,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,oBAAoB;YACjD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;gBACpB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,KAAK,EAAE,IAAI,CAAC,KAAK;aAClB,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,2EAA2E;QACnE,gBAAW,GAAG,GAAG,EAAE;YACzB,IAAI,IAAI,CAAC,QAAQ;gBAAE,OAAO;YAE1B,IAAI,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC3C,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACtB,CAAC;QACH,CAAC,CAAC;KA8BH;IA5BC,oBAAoB;IACpB,8EAA8E;IAC9E,gBAAgB;IAEhB,MAAM;QACJ,OAAO,CACL,EAAC,IAAI,qDAAC,OAAO,EAAE,IAAI,CAAC,WAAW;YAC7B,yEACE,OAAO,EAAE,IAAI,CAAC,OAAO,EACrB,IAAI,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAC9B,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAC/B,cAAc,EAAC,OAAO,EACtB,WAAW,EAAE,IAAI,CAAC,WAAW,EAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ,EACvB,QAAQ,EAAC,IAAI,EACb,gBAAgB,EAAE,IAAI,CAAC,sBAAsB;gBAE7C,8DAAa;gBACb,6DAAM,IAAI,EAAC,MAAM,GAAQ;gBACzB,6DAAM,IAAI,EAAC,OAAO,GAAQ;gBAC1B,6DAAM,IAAI,EAAC,aAAa,GAAQ,CACf,CACd,CACR,CAAC;IACJ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAIF","sourcesContent":["import {\n Component,\n Host,\n h,\n Prop,\n Event,\n EventEmitter,\n Element,\n} from '@stencil/core';\n\n/**\n * @slot default - Next to the label and description.\n * @slot main - Replaces the label and description.\n * @slot label - Content to be placed as the label, will override the label prop.\n * @slot description - Content to be placed as the description, will override the description prop.\n */\n@Component({\n tag: 'nv-fielddropdownitemcheck',\n styleUrl: 'nv-fielddropdownitemcheck.scss',\n shadow: false,\n})\nexport class NvFielddropdownitemcheck {\n @Element() el: HTMLNvFielddropdownitemcheckElement;\n\n /****************************************************************************/\n //#region PROPERTIES\n\n /**\n * Indicates whether the checkbox is selected.\n */\n @Prop({ reflect: true, mutable: true })\n checked: boolean = false;\n\n /**\n * The value associated with this item.\n */\n @Prop({ reflect: true })\n readonly value?: string;\n\n /**\n * The label displayed alongside the checkbox.\n */\n @Prop({ reflect: true })\n readonly label?: string;\n\n /**\n * A description providing additional context or information about the\n * checkbox.\n */\n @Prop({ reflect: true })\n readonly description?: string;\n\n /**\n * The group this item belongs to, if applicable.\n */\n @Prop({ reflect: true })\n readonly group?: string;\n\n /**\n * Disables the item, preventing any user interaction.\n */\n @Prop({ reflect: true })\n readonly disabled: boolean = false;\n\n //#endregion PROPERTIES\n /****************************************************************************/\n //#region EVENTS\n\n /**\n * Event emitted when the checkbox is toggled.\n * It provides details about the current state of the item.\n */\n @Event()\n itemChecked: EventEmitter<{\n /**\n * The value associated with this item\n */\n value: string | undefined;\n /**\n * Whether the checkbox is currently checked\n */\n checked: boolean;\n /**\n * The group this item belongs to, if any\n */\n group?: string | undefined;\n }>;\n\n //#endregion EVENTS\n /****************************************************************************/\n //#region METHODS\n\n /**\n * when the child <nv-fieldcheckbox> change its `checked` state,\n * update `this.checked` and emit `itemChecked`.\n * @param {CustomEvent<boolean>} event - The event emitted by the <nv-fieldcheckbox> component.\n */\n private onFieldcheckboxChanged = (event: CustomEvent<boolean>) => {\n if (this.disabled) return;\n\n // NvFieldcheckbox has emitted checkedChanged\n this.checked = event.detail; // get the new state\n this.itemChecked.emit({\n value: this.value,\n checked: this.checked,\n group: this.group,\n });\n };\n\n /** Make sure the checkbox is checked when clicked anywhere in the item. */\n private handleClick = () => {\n if (this.disabled) return;\n\n if (this.el.querySelector('input').checked) {\n this.checked = false;\n } else {\n this.checked = true;\n }\n };\n\n //#endregion METHODS\n /****************************************************************************/\n //#region RENDER\n\n render() {\n return (\n <Host onClick={this.handleClick}>\n <nv-fieldcheckbox\n checked={this.checked}\n name={this.label || this.value}\n label={this.label || this.value}\n labelPlacement=\"after\"\n description={this.description}\n disabled={this.disabled}\n tabindex=\"-1\"\n onCheckedChanged={this.onFieldcheckboxChanged}\n >\n <slot></slot>\n <slot name=\"main\"></slot>\n <slot name=\"label\"></slot>\n <slot name=\"description\"></slot>\n </nv-fieldcheckbox>\n </Host>\n );\n }\n\n //#endregion RENDER\n /****************************************************************************/\n}\n"]}
|
|
@@ -11,6 +11,7 @@ import { v4 as uuidv4 } from "uuid";
|
|
|
11
11
|
*/
|
|
12
12
|
export class NvFieldmultiselect {
|
|
13
13
|
constructor() {
|
|
14
|
+
this.isBulkOperation = false;
|
|
14
15
|
/**
|
|
15
16
|
* Sets the ID for the input element and the for attribute of the associated
|
|
16
17
|
* label. If no ID is provided, a random one will be automatically generated
|
|
@@ -118,6 +119,9 @@ export class NvFieldmultiselect {
|
|
|
118
119
|
this.isSelectAllSectionVisible = true;
|
|
119
120
|
// Add the flag to the class
|
|
120
121
|
this.preventBlurClose = false;
|
|
122
|
+
this.handleMouseDownPreventBlur = () => {
|
|
123
|
+
this.preventBlurClose = true;
|
|
124
|
+
};
|
|
121
125
|
/**
|
|
122
126
|
* Handle badge close for options mode.
|
|
123
127
|
*/
|
|
@@ -304,6 +308,11 @@ export class NvFieldmultiselect {
|
|
|
304
308
|
*/
|
|
305
309
|
this.handleInputBlurSlots = () => {
|
|
306
310
|
setTimeout(() => {
|
|
311
|
+
// Honor preventBlurClose to avoid closing when interacting inside the popover
|
|
312
|
+
if (this.preventBlurClose) {
|
|
313
|
+
this.preventBlurClose = false;
|
|
314
|
+
return; // Don't close the popover
|
|
315
|
+
}
|
|
307
316
|
if (!this.el.contains(document.activeElement)) {
|
|
308
317
|
// Close the popover without affecting the divider
|
|
309
318
|
this.open = false;
|
|
@@ -340,24 +349,33 @@ export class NvFieldmultiselect {
|
|
|
340
349
|
this.toggleSelectAllOptions = (selectAll) => {
|
|
341
350
|
if (!this.options)
|
|
342
351
|
return;
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
352
|
+
this.isBulkOperation = true; // Set flag to suppress individual emissions
|
|
353
|
+
try {
|
|
354
|
+
// Get visible and enabled option values from DOM
|
|
355
|
+
const visibleOptionValues = this.getVisibleEnabledOptionItems();
|
|
356
|
+
console.info('[SelectAll][Options] toggleSelectAllOptions called. selectAll:', selectAll, 'visibleOptionValues:', visibleOptionValues, 'Current value:', this.value);
|
|
357
|
+
if (selectAll) {
|
|
358
|
+
// Select all visible options - merge with existing selections
|
|
359
|
+
this.value = [...new Set([...this.value, ...visibleOptionValues])];
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
// Deselect only the visible options, keep others that might be filtered out
|
|
363
|
+
this.value = this.value.filter(val => !visibleOptionValues.includes(val));
|
|
364
|
+
}
|
|
365
|
+
console.info('[SelectAll][Options] New value after toggle:', this.value);
|
|
366
|
+
// Emit the change event
|
|
367
|
+
this.valueChanged.emit(this.value);
|
|
368
|
+
// Synchronize child components
|
|
369
|
+
this.syncChildComponents();
|
|
370
|
+
// Reorder content to move selected items to top
|
|
371
|
+
this.reorderOptionsContent();
|
|
349
372
|
}
|
|
350
|
-
|
|
351
|
-
//
|
|
352
|
-
|
|
373
|
+
finally {
|
|
374
|
+
// Defer reset to next frame to ensure any async child emissions are ignored
|
|
375
|
+
requestAnimationFrame(() => {
|
|
376
|
+
this.isBulkOperation = false; // Reset flag
|
|
377
|
+
});
|
|
353
378
|
}
|
|
354
|
-
console.info('[SelectAll][Options] New value after toggle:', this.value);
|
|
355
|
-
// Emit the change event
|
|
356
|
-
this.valueChanged.emit(this.value);
|
|
357
|
-
// Synchronize child components
|
|
358
|
-
this.syncChildComponents();
|
|
359
|
-
// Reorder content to move selected items to top
|
|
360
|
-
this.reorderOptionsContent();
|
|
361
379
|
};
|
|
362
380
|
/**
|
|
363
381
|
* Toggles the selection state of all non-disabled slot items.
|
|
@@ -366,31 +384,40 @@ export class NvFieldmultiselect {
|
|
|
366
384
|
this.toggleSelectAllSlots = (selectAll) => {
|
|
367
385
|
if (this.options)
|
|
368
386
|
return; // Only for slots mode
|
|
369
|
-
//
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
387
|
+
this.isBulkOperation = true; // Set flag to suppress individual emissions
|
|
388
|
+
try {
|
|
389
|
+
// Get visible and enabled items
|
|
390
|
+
const items = this.getVisibleEnabledSlotItems();
|
|
391
|
+
console.info('[SelectAll][Slots] toggleSelectAllSlots called. selectAll:', selectAll, 'visible slot items:', items.map(item => item.getAttribute('value') || item.getAttribute('label')), 'Current value:', this.value);
|
|
392
|
+
if (selectAll) {
|
|
393
|
+
// Select all visible items
|
|
394
|
+
const allActiveValues = items
|
|
395
|
+
.map(item => item.getAttribute('value') || item.getAttribute('label') || '')
|
|
396
|
+
.filter(value => value !== '');
|
|
397
|
+
this.value = [...new Set([...this.value, ...allActiveValues])];
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
// Deselect only the visible items, keep others that might be filtered out
|
|
401
|
+
const visibleValues = items
|
|
402
|
+
.map(item => item.getAttribute('value') || item.getAttribute('label') || '')
|
|
403
|
+
.filter(value => value !== '');
|
|
404
|
+
this.value = this.value.filter(val => !visibleValues.includes(val));
|
|
405
|
+
}
|
|
406
|
+
console.info('[SelectAll][Slots] New value after toggle:', this.value);
|
|
407
|
+
// Emit the change event
|
|
408
|
+
this.valueChanged.emit(this.value);
|
|
409
|
+
// Force synchronization with a small delay to ensure DOM is updated
|
|
410
|
+
requestAnimationFrame(() => {
|
|
411
|
+
this.syncChildComponents();
|
|
412
|
+
this.reorderSlotContent();
|
|
413
|
+
});
|
|
378
414
|
}
|
|
379
|
-
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
.
|
|
383
|
-
|
|
384
|
-
this.value = this.value.filter(val => !visibleValues.includes(val));
|
|
415
|
+
finally {
|
|
416
|
+
// Defer reset to next frame to ensure any async child emissions are ignored
|
|
417
|
+
requestAnimationFrame(() => {
|
|
418
|
+
this.isBulkOperation = false; // Reset flag
|
|
419
|
+
});
|
|
385
420
|
}
|
|
386
|
-
console.info('[SelectAll][Slots] New value after toggle:', this.value);
|
|
387
|
-
// Emit the change event
|
|
388
|
-
this.valueChanged.emit(this.value);
|
|
389
|
-
// Force synchronization with a small delay to ensure DOM is updated
|
|
390
|
-
requestAnimationFrame(() => {
|
|
391
|
-
this.syncChildComponents();
|
|
392
|
-
this.reorderSlotContent();
|
|
393
|
-
});
|
|
394
421
|
};
|
|
395
422
|
/**
|
|
396
423
|
* Handle click on the select all checkbox in options mode.
|
|
@@ -442,16 +469,10 @@ export class NvFieldmultiselect {
|
|
|
442
469
|
width: '0',
|
|
443
470
|
height: '0',
|
|
444
471
|
pointerEvents: 'none',
|
|
445
|
-
}, tabIndex: -1, "aria-hidden": "true", autoComplete: this.autocomplete, name: this.name, onFocus: this.handleInputFocusOptions }), h("p", { id: this.inputId, class: "non-filterable-text", onClick: this.handleInputContainerClickOptions, tabIndex: 0, onKeyDown: this.handleKeyDown, onFocus: this.handleInputFocusOptions, "aria-label": this.label, "aria-controls": `${this.inputId}-listbox`, "data-scope": "focusable", role: "button" }, h("span", null, this.placeholder)))), this.error && (h("nv-icon", { name: "alert-circle", class: "validation", size: "md" })), this.filterable && this.filterText.length > 0 && (h("nv-iconbutton", { "data-scope": "clear-filter", name: "x", size: "md", emphasis: "lower", "aria-label": "Clear filter text", tabindex: "-1", title: "Clear filter text", onMouseDown: ()
|
|
446
|
-
this.preventBlurClose = true;
|
|
447
|
-
}, onClick: this.clearFilterText })), h("nv-iconbutton", { "data-scope": "toggle-dropdown", name: this.open ? 'chevron-top' : 'chevron-down', size: "md", emphasis: "lower", "aria-label": this.open ? 'Hide dropdown' : 'Show dropdown', title: this.open ? 'Hide dropdown' : 'Show dropdown', onMouseDown: () => {
|
|
448
|
-
this.preventBlurClose = true;
|
|
449
|
-
}, onClick: this.togglePopoverOptions })), h("slot", { name: "after-input" })), h("div", { id: `${this.inputId}-listbox`, slot: "content", style: this.maxHeight ? { maxHeight: this.maxHeight } : {} }, this.shouldShowToggleAllOptionsButton() && (h("div", { class: "select-all-container" }, h("div", { class: "select-all-header" }, this.isSelectAllSectionVisible && (h("nv-fieldcheckbox", { checked: this.getSelectAllCheckboxStateOptions() === 'checked', indeterminate: this.getSelectAllCheckboxStateOptions() ===
|
|
472
|
+
}, tabIndex: -1, "aria-hidden": "true", autoComplete: this.autocomplete, name: this.name, onFocus: this.handleInputFocusOptions }), h("p", { id: this.inputId, class: "non-filterable-text", onClick: this.handleInputContainerClickOptions, tabIndex: 0, onKeyDown: this.handleKeyDown, onFocus: this.handleInputFocusOptions, "aria-label": this.label, "aria-controls": `${this.inputId}-listbox`, "data-scope": "focusable", role: "button" }, h("span", null, this.placeholder)))), this.error && (h("nv-icon", { name: "alert-circle", class: "validation", size: "md" })), this.filterable && this.filterText.length > 0 && (h("nv-iconbutton", { "data-scope": "clear-filter", name: "x", size: "md", emphasis: "lower", "aria-label": "Clear filter text", tabindex: "-1", title: "Clear filter text", onMouseDown: this.handleMouseDownPreventBlur, onClick: this.clearFilterText })), h("nv-iconbutton", { "data-scope": "toggle-dropdown", name: this.open ? 'chevron-top' : 'chevron-down', size: "md", emphasis: "lower", "aria-label": this.open ? 'Hide dropdown' : 'Show dropdown', title: this.open ? 'Hide dropdown' : 'Show dropdown', onMouseDown: this.handleMouseDownPreventBlur, onClick: this.togglePopoverOptions })), h("slot", { name: "after-input" })), h("div", { id: `${this.inputId}-listbox`, slot: "content", style: this.maxHeight ? { maxHeight: this.maxHeight } : {}, onMouseDown: this.handleMouseDownPreventBlur }, this.shouldShowToggleAllOptionsButton() && (h("div", { class: "select-all-container" }, h("div", { class: "select-all-header" }, this.isSelectAllSectionVisible && (h("nv-fieldcheckbox", { checked: this.getSelectAllCheckboxStateOptions() === 'checked', indeterminate: this.getSelectAllCheckboxStateOptions() ===
|
|
450
473
|
'indeterminate', label: this.getSelectAllCheckboxStateOptions() === 'unchecked'
|
|
451
474
|
? this.selectAllLabel
|
|
452
|
-
: this.deselectAllLabel, onMouseDown:
|
|
453
|
-
this.preventBlurClose = true;
|
|
454
|
-
}, onClick: this.handleSelectAllCheckboxOptionsClick }))))), h("ul", { role: "listbox", "aria-multiselectable": "true" }, this.options.map(option => (h("nv-fielddropdownitemcheck", { role: "option", label: option.label, description: option.description, value: option.value, checked: this.value.includes(option.value), disabled: option.disabled })))))), this.renderDescriptions()));
|
|
475
|
+
: this.deselectAllLabel, onMouseDown: this.handleMouseDownPreventBlur, onClick: this.handleSelectAllCheckboxOptionsClick }))))), h("ul", { role: "listbox", "aria-multiselectable": "true" }, this.options.map(option => (h("nv-fielddropdownitemcheck", { role: "option", label: option.label, description: option.description, value: option.value, checked: this.value.includes(option.value), disabled: option.disabled })))))), this.renderDescriptions()));
|
|
455
476
|
};
|
|
456
477
|
/**
|
|
457
478
|
* Renders the component in slots mode
|
|
@@ -464,16 +485,10 @@ export class NvFieldmultiselect {
|
|
|
464
485
|
width: '0',
|
|
465
486
|
height: '0',
|
|
466
487
|
pointerEvents: 'none',
|
|
467
|
-
}, tabIndex: -1, "aria-hidden": "true", autoComplete: this.autocomplete, name: this.name, onFocus: this.handleInputFocusSlots }), h("p", { id: this.inputId, class: "non-filterable-text", "aria-label": this.label, onClick: this.handleInputContainerClickSlots, tabIndex: 0, onKeyDown: this.handleKeyDown, onFocus: this.handleInputFocusSlots, "aria-controls": `${this.inputId}-listbox`, "data-scope": "focusable", role: "button" }, h("span", null, this.placeholder)))), this.error && (h("nv-icon", { name: "alert-circle", class: "validation", size: "md" })), this.filterable && this.filterText.length > 0 && (h("nv-iconbutton", { name: "x", size: "md", emphasis: "lower", tabindex: "-1", onMouseDown: ()
|
|
468
|
-
this.preventBlurClose = true;
|
|
469
|
-
}, onClick: this.clearFilterText, "aria-label": "Clear filter text" })), h("nv-iconbutton", { "data-scope": "toggle-dropdown", name: this.open ? 'chevron-top' : 'chevron-down', size: "md", emphasis: "lower", "aria-label": this.open ? 'Hide dropdown' : 'Show dropdown', title: this.open ? 'Hide dropdown' : 'Show dropdown', onMouseDown: () => {
|
|
470
|
-
this.preventBlurClose = true;
|
|
471
|
-
}, onClick: this.togglePopoverSlots })), h("slot", { name: "after-input" })), h("div", { id: `${this.inputId}-listbox`, slot: "content", style: this.maxHeight ? { maxHeight: this.maxHeight } : {} }, this.shouldShowToggleAllSlotButton() && (h("div", { class: "select-all-container" }, h("div", { class: "select-all-header" }, this.isSelectAllSectionVisible && (h("nv-fieldcheckbox", { checked: this.getSelectAllCheckboxStateSlots() === 'checked', indeterminate: this.getSelectAllCheckboxStateSlots() ===
|
|
488
|
+
}, tabIndex: -1, "aria-hidden": "true", autoComplete: this.autocomplete, name: this.name, onFocus: this.handleInputFocusSlots }), h("p", { id: this.inputId, class: "non-filterable-text", "aria-label": this.label, onClick: this.handleInputContainerClickSlots, tabIndex: 0, onKeyDown: this.handleKeyDown, onFocus: this.handleInputFocusSlots, "aria-controls": `${this.inputId}-listbox`, "data-scope": "focusable", role: "button" }, h("span", null, this.placeholder)))), this.error && (h("nv-icon", { name: "alert-circle", class: "validation", size: "md" })), this.filterable && this.filterText.length > 0 && (h("nv-iconbutton", { name: "x", size: "md", emphasis: "lower", tabindex: "-1", onMouseDown: this.handleMouseDownPreventBlur, onClick: this.clearFilterText, "aria-label": "Clear filter text" })), h("nv-iconbutton", { "data-scope": "toggle-dropdown", name: this.open ? 'chevron-top' : 'chevron-down', size: "md", emphasis: "lower", "aria-label": this.open ? 'Hide dropdown' : 'Show dropdown', title: this.open ? 'Hide dropdown' : 'Show dropdown', onMouseDown: this.handleMouseDownPreventBlur, onClick: this.togglePopoverSlots })), h("slot", { name: "after-input" })), h("div", { id: `${this.inputId}-listbox`, slot: "content", style: this.maxHeight ? { maxHeight: this.maxHeight } : {}, onMouseDown: this.handleMouseDownPreventBlur }, this.shouldShowToggleAllSlotButton() && (h("div", { class: "select-all-container" }, h("div", { class: "select-all-header" }, this.isSelectAllSectionVisible && (h("nv-fieldcheckbox", { checked: this.getSelectAllCheckboxStateSlots() === 'checked', indeterminate: this.getSelectAllCheckboxStateSlots() ===
|
|
472
489
|
'indeterminate', label: this.getSelectAllCheckboxStateSlots() === 'unchecked'
|
|
473
490
|
? this.selectAllLabel
|
|
474
|
-
: this.deselectAllLabel, onMouseDown:
|
|
475
|
-
this.preventBlurClose = true;
|
|
476
|
-
}, onClick: this.handleSelectAllCheckboxSlotsClick }))))), h("slot", { name: "content" }))), this.renderDescriptions()));
|
|
491
|
+
: this.deselectAllLabel, onMouseDown: this.handleMouseDownPreventBlur, onClick: this.handleSelectAllCheckboxSlotsClick }))))), h("slot", { name: "content" }))), this.renderDescriptions()));
|
|
477
492
|
};
|
|
478
493
|
}
|
|
479
494
|
//#endregion EVENTS
|
|
@@ -502,7 +517,12 @@ export class NvFieldmultiselect {
|
|
|
502
517
|
this.reorderOptionsContent();
|
|
503
518
|
}
|
|
504
519
|
watchValueHandler() {
|
|
505
|
-
console.info('[Watch:value] Value changed:', this.value);
|
|
520
|
+
console.info('[Watch:value] Value changed:', this.value, 'isBulkOperation:', this.isBulkOperation);
|
|
521
|
+
// Skip processing during bulk operations
|
|
522
|
+
if (this.isBulkOperation) {
|
|
523
|
+
console.info('[Watch:value] Skipping syncChildComponents and options update due to bulk operation');
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
506
526
|
// Synchronize child components when value changes programmatically
|
|
507
527
|
if (this.el && this.el.isConnected) {
|
|
508
528
|
this.syncChildComponents();
|
|
@@ -547,7 +567,9 @@ export class NvFieldmultiselect {
|
|
|
547
567
|
* @param {CustomEvent} event - The event object containing the selected value and its checked state.
|
|
548
568
|
*/
|
|
549
569
|
handleItemChecked(event) {
|
|
550
|
-
if (this.disabled || this.readonly) {
|
|
570
|
+
if (this.disabled || this.readonly || this.isBulkOperation) {
|
|
571
|
+
// Skip processing itemChecked events during bulk operations
|
|
572
|
+
console.info('[Event:itemChecked] Skipped due to bulk operation or disabled/readonly', event.detail);
|
|
551
573
|
return;
|
|
552
574
|
}
|
|
553
575
|
const { value, checked } = event.detail;
|
|
@@ -555,21 +577,23 @@ export class NvFieldmultiselect {
|
|
|
555
577
|
if (value !== undefined && value !== null) {
|
|
556
578
|
const newValue = [...this.value];
|
|
557
579
|
const valueIndex = newValue.indexOf(value);
|
|
580
|
+
let hasChanged = false;
|
|
558
581
|
if (checked && valueIndex === -1) {
|
|
559
582
|
newValue.push(value);
|
|
583
|
+
hasChanged = true;
|
|
560
584
|
}
|
|
561
585
|
else if (!checked && valueIndex > -1) {
|
|
562
586
|
newValue.splice(valueIndex, 1);
|
|
587
|
+
hasChanged = true;
|
|
563
588
|
}
|
|
564
589
|
console.info('[Event:itemChecked] newValue after update:', newValue);
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
590
|
+
if (hasChanged) {
|
|
591
|
+
this.value = newValue;
|
|
592
|
+
this.valueChanged.emit(this.value);
|
|
593
|
+
this.syncChildComponents();
|
|
594
|
+
}
|
|
570
595
|
// Preserve the filter text in the input
|
|
571
596
|
if (this.filterable && this.inputElement) {
|
|
572
|
-
// Keep the current filter text in the input
|
|
573
597
|
this.inputElement.value = this.filterText;
|
|
574
598
|
}
|
|
575
599
|
}
|
|
@@ -597,7 +621,11 @@ export class NvFieldmultiselect {
|
|
|
597
621
|
*/
|
|
598
622
|
connectedCallback() {
|
|
599
623
|
console.info('[Lifecycle] connectedCallback - value:', this.value);
|
|
600
|
-
|
|
624
|
+
// Bind once and reuse the same reference for add/remove to avoid leaks
|
|
625
|
+
if (!this._boundHandleClickOutside) {
|
|
626
|
+
this._boundHandleClickOutside = this.handleClickOutside.bind(this);
|
|
627
|
+
}
|
|
628
|
+
document.addEventListener('click', this._boundHandleClickOutside);
|
|
601
629
|
}
|
|
602
630
|
/**
|
|
603
631
|
* Set the mode state and handle options change.
|
|
@@ -655,7 +683,9 @@ export class NvFieldmultiselect {
|
|
|
655
683
|
*/
|
|
656
684
|
disconnectedCallback() {
|
|
657
685
|
console.info('[Lifecycle] disconnectedCallback - value:', this.value);
|
|
658
|
-
|
|
686
|
+
if (this._boundHandleClickOutside) {
|
|
687
|
+
document.removeEventListener('click', this._boundHandleClickOutside);
|
|
688
|
+
}
|
|
659
689
|
}
|
|
660
690
|
//#endregion LIFECYCLE
|
|
661
691
|
/****************************************************************************/
|
|
@@ -1184,13 +1214,23 @@ export class NvFieldmultiselect {
|
|
|
1184
1214
|
const itemValue = item.getAttribute('value') || item.getAttribute('label') || '';
|
|
1185
1215
|
const shouldBeChecked = this.value.includes(itemValue);
|
|
1186
1216
|
console.info('[syncChildComponents] itemValue:', itemValue, 'shouldBeChecked:', shouldBeChecked, 'item.checked(before):', item.checked);
|
|
1187
|
-
if
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1217
|
+
// Only update if the checked state differs to avoid triggering unnecessary events
|
|
1218
|
+
if (item.checked !== shouldBeChecked) {
|
|
1219
|
+
// Set attribute and property, but avoid triggering itemChecked during bulk
|
|
1220
|
+
if (this.isBulkOperation) {
|
|
1221
|
+
// Directly update the DOM attribute to avoid triggering the setter
|
|
1222
|
+
if (shouldBeChecked) {
|
|
1223
|
+
item.setAttribute('checked', '');
|
|
1224
|
+
}
|
|
1225
|
+
else {
|
|
1226
|
+
item.removeAttribute('checked');
|
|
1227
|
+
}
|
|
1228
|
+
// Update internal state without emitting events (assumes nv-fielddropdownitemcheck respects this)
|
|
1229
|
+
item.checked = shouldBeChecked;
|
|
1230
|
+
}
|
|
1231
|
+
else {
|
|
1232
|
+
item.checked = shouldBeChecked;
|
|
1233
|
+
}
|
|
1194
1234
|
}
|
|
1195
1235
|
console.info('[syncChildComponents] itemValue:', itemValue, 'item.checked(after):', item.checked);
|
|
1196
1236
|
});
|