@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
|
@@ -9,6 +9,7 @@ const NvFieldmultiselect = class {
|
|
|
9
9
|
registerInstance(this, hostRef);
|
|
10
10
|
this.valueChanged = createEvent(this, "valueChanged", 7);
|
|
11
11
|
this.filterTextChanged = createEvent(this, "filterTextChanged", 7);
|
|
12
|
+
this.isBulkOperation = false;
|
|
12
13
|
/**
|
|
13
14
|
* Sets the ID for the input element and the for attribute of the associated
|
|
14
15
|
* label. If no ID is provided, a random one will be automatically generated
|
|
@@ -116,6 +117,9 @@ const NvFieldmultiselect = class {
|
|
|
116
117
|
this.isSelectAllSectionVisible = true;
|
|
117
118
|
// Add the flag to the class
|
|
118
119
|
this.preventBlurClose = false;
|
|
120
|
+
this.handleMouseDownPreventBlur = () => {
|
|
121
|
+
this.preventBlurClose = true;
|
|
122
|
+
};
|
|
119
123
|
/**
|
|
120
124
|
* Handle badge close for options mode.
|
|
121
125
|
*/
|
|
@@ -302,6 +306,11 @@ const NvFieldmultiselect = class {
|
|
|
302
306
|
*/
|
|
303
307
|
this.handleInputBlurSlots = () => {
|
|
304
308
|
setTimeout(() => {
|
|
309
|
+
// Honor preventBlurClose to avoid closing when interacting inside the popover
|
|
310
|
+
if (this.preventBlurClose) {
|
|
311
|
+
this.preventBlurClose = false;
|
|
312
|
+
return; // Don't close the popover
|
|
313
|
+
}
|
|
305
314
|
if (!this.el.contains(document.activeElement)) {
|
|
306
315
|
// Close the popover without affecting the divider
|
|
307
316
|
this.open = false;
|
|
@@ -338,24 +347,33 @@ const NvFieldmultiselect = class {
|
|
|
338
347
|
this.toggleSelectAllOptions = (selectAll) => {
|
|
339
348
|
if (!this.options)
|
|
340
349
|
return;
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
350
|
+
this.isBulkOperation = true; // Set flag to suppress individual emissions
|
|
351
|
+
try {
|
|
352
|
+
// Get visible and enabled option values from DOM
|
|
353
|
+
const visibleOptionValues = this.getVisibleEnabledOptionItems();
|
|
354
|
+
console.info('[SelectAll][Options] toggleSelectAllOptions called. selectAll:', selectAll, 'visibleOptionValues:', visibleOptionValues, 'Current value:', this.value);
|
|
355
|
+
if (selectAll) {
|
|
356
|
+
// Select all visible options - merge with existing selections
|
|
357
|
+
this.value = [...new Set([...this.value, ...visibleOptionValues])];
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
// Deselect only the visible options, keep others that might be filtered out
|
|
361
|
+
this.value = this.value.filter(val => !visibleOptionValues.includes(val));
|
|
362
|
+
}
|
|
363
|
+
console.info('[SelectAll][Options] New value after toggle:', this.value);
|
|
364
|
+
// Emit the change event
|
|
365
|
+
this.valueChanged.emit(this.value);
|
|
366
|
+
// Synchronize child components
|
|
367
|
+
this.syncChildComponents();
|
|
368
|
+
// Reorder content to move selected items to top
|
|
369
|
+
this.reorderOptionsContent();
|
|
347
370
|
}
|
|
348
|
-
|
|
349
|
-
//
|
|
350
|
-
|
|
371
|
+
finally {
|
|
372
|
+
// Defer reset to next frame to ensure any async child emissions are ignored
|
|
373
|
+
requestAnimationFrame(() => {
|
|
374
|
+
this.isBulkOperation = false; // Reset flag
|
|
375
|
+
});
|
|
351
376
|
}
|
|
352
|
-
console.info('[SelectAll][Options] New value after toggle:', this.value);
|
|
353
|
-
// Emit the change event
|
|
354
|
-
this.valueChanged.emit(this.value);
|
|
355
|
-
// Synchronize child components
|
|
356
|
-
this.syncChildComponents();
|
|
357
|
-
// Reorder content to move selected items to top
|
|
358
|
-
this.reorderOptionsContent();
|
|
359
377
|
};
|
|
360
378
|
/**
|
|
361
379
|
* Toggles the selection state of all non-disabled slot items.
|
|
@@ -364,31 +382,40 @@ const NvFieldmultiselect = class {
|
|
|
364
382
|
this.toggleSelectAllSlots = (selectAll) => {
|
|
365
383
|
if (this.options)
|
|
366
384
|
return; // Only for slots mode
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
385
|
+
this.isBulkOperation = true; // Set flag to suppress individual emissions
|
|
386
|
+
try {
|
|
387
|
+
// Get visible and enabled items
|
|
388
|
+
const items = this.getVisibleEnabledSlotItems();
|
|
389
|
+
console.info('[SelectAll][Slots] toggleSelectAllSlots called. selectAll:', selectAll, 'visible slot items:', items.map(item => item.getAttribute('value') || item.getAttribute('label')), 'Current value:', this.value);
|
|
390
|
+
if (selectAll) {
|
|
391
|
+
// Select all visible items
|
|
392
|
+
const allActiveValues = items
|
|
393
|
+
.map(item => item.getAttribute('value') || item.getAttribute('label') || '')
|
|
394
|
+
.filter(value => value !== '');
|
|
395
|
+
this.value = [...new Set([...this.value, ...allActiveValues])];
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
// Deselect only the visible items, keep others that might be filtered out
|
|
399
|
+
const visibleValues = items
|
|
400
|
+
.map(item => item.getAttribute('value') || item.getAttribute('label') || '')
|
|
401
|
+
.filter(value => value !== '');
|
|
402
|
+
this.value = this.value.filter(val => !visibleValues.includes(val));
|
|
403
|
+
}
|
|
404
|
+
console.info('[SelectAll][Slots] New value after toggle:', this.value);
|
|
405
|
+
// Emit the change event
|
|
406
|
+
this.valueChanged.emit(this.value);
|
|
407
|
+
// Force synchronization with a small delay to ensure DOM is updated
|
|
408
|
+
requestAnimationFrame(() => {
|
|
409
|
+
this.syncChildComponents();
|
|
410
|
+
this.reorderSlotContent();
|
|
411
|
+
});
|
|
376
412
|
}
|
|
377
|
-
|
|
378
|
-
//
|
|
379
|
-
|
|
380
|
-
.
|
|
381
|
-
|
|
382
|
-
this.value = this.value.filter(val => !visibleValues.includes(val));
|
|
413
|
+
finally {
|
|
414
|
+
// Defer reset to next frame to ensure any async child emissions are ignored
|
|
415
|
+
requestAnimationFrame(() => {
|
|
416
|
+
this.isBulkOperation = false; // Reset flag
|
|
417
|
+
});
|
|
383
418
|
}
|
|
384
|
-
console.info('[SelectAll][Slots] New value after toggle:', this.value);
|
|
385
|
-
// Emit the change event
|
|
386
|
-
this.valueChanged.emit(this.value);
|
|
387
|
-
// Force synchronization with a small delay to ensure DOM is updated
|
|
388
|
-
requestAnimationFrame(() => {
|
|
389
|
-
this.syncChildComponents();
|
|
390
|
-
this.reorderSlotContent();
|
|
391
|
-
});
|
|
392
419
|
};
|
|
393
420
|
/**
|
|
394
421
|
* Handle click on the select all checkbox in options mode.
|
|
@@ -440,16 +467,10 @@ const NvFieldmultiselect = class {
|
|
|
440
467
|
width: '0',
|
|
441
468
|
height: '0',
|
|
442
469
|
pointerEvents: 'none',
|
|
443
|
-
}, 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: ()
|
|
444
|
-
this.preventBlurClose = true;
|
|
445
|
-
}, 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: () => {
|
|
446
|
-
this.preventBlurClose = true;
|
|
447
|
-
}, 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() ===
|
|
470
|
+
}, 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() ===
|
|
448
471
|
'indeterminate', label: this.getSelectAllCheckboxStateOptions() === 'unchecked'
|
|
449
472
|
? this.selectAllLabel
|
|
450
|
-
: this.deselectAllLabel, onMouseDown:
|
|
451
|
-
this.preventBlurClose = true;
|
|
452
|
-
}, 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()));
|
|
473
|
+
: 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()));
|
|
453
474
|
};
|
|
454
475
|
/**
|
|
455
476
|
* Renders the component in slots mode
|
|
@@ -462,16 +483,10 @@ const NvFieldmultiselect = class {
|
|
|
462
483
|
width: '0',
|
|
463
484
|
height: '0',
|
|
464
485
|
pointerEvents: 'none',
|
|
465
|
-
}, 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: ()
|
|
466
|
-
this.preventBlurClose = true;
|
|
467
|
-
}, 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: () => {
|
|
468
|
-
this.preventBlurClose = true;
|
|
469
|
-
}, 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() ===
|
|
486
|
+
}, 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() ===
|
|
470
487
|
'indeterminate', label: this.getSelectAllCheckboxStateSlots() === 'unchecked'
|
|
471
488
|
? this.selectAllLabel
|
|
472
|
-
: this.deselectAllLabel, onMouseDown:
|
|
473
|
-
this.preventBlurClose = true;
|
|
474
|
-
}, onClick: this.handleSelectAllCheckboxSlotsClick }))))), h("slot", { name: "content" }))), this.renderDescriptions()));
|
|
489
|
+
: this.deselectAllLabel, onMouseDown: this.handleMouseDownPreventBlur, onClick: this.handleSelectAllCheckboxSlotsClick }))))), h("slot", { name: "content" }))), this.renderDescriptions()));
|
|
475
490
|
};
|
|
476
491
|
}
|
|
477
492
|
//#endregion EVENTS
|
|
@@ -500,7 +515,12 @@ const NvFieldmultiselect = class {
|
|
|
500
515
|
this.reorderOptionsContent();
|
|
501
516
|
}
|
|
502
517
|
watchValueHandler() {
|
|
503
|
-
console.info('[Watch:value] Value changed:', this.value);
|
|
518
|
+
console.info('[Watch:value] Value changed:', this.value, 'isBulkOperation:', this.isBulkOperation);
|
|
519
|
+
// Skip processing during bulk operations
|
|
520
|
+
if (this.isBulkOperation) {
|
|
521
|
+
console.info('[Watch:value] Skipping syncChildComponents and options update due to bulk operation');
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
504
524
|
// Synchronize child components when value changes programmatically
|
|
505
525
|
if (this.el && this.el.isConnected) {
|
|
506
526
|
this.syncChildComponents();
|
|
@@ -545,7 +565,9 @@ const NvFieldmultiselect = class {
|
|
|
545
565
|
* @param {CustomEvent} event - The event object containing the selected value and its checked state.
|
|
546
566
|
*/
|
|
547
567
|
handleItemChecked(event) {
|
|
548
|
-
if (this.disabled || this.readonly) {
|
|
568
|
+
if (this.disabled || this.readonly || this.isBulkOperation) {
|
|
569
|
+
// Skip processing itemChecked events during bulk operations
|
|
570
|
+
console.info('[Event:itemChecked] Skipped due to bulk operation or disabled/readonly', event.detail);
|
|
549
571
|
return;
|
|
550
572
|
}
|
|
551
573
|
const { value, checked } = event.detail;
|
|
@@ -553,21 +575,23 @@ const NvFieldmultiselect = class {
|
|
|
553
575
|
if (value !== undefined && value !== null) {
|
|
554
576
|
const newValue = [...this.value];
|
|
555
577
|
const valueIndex = newValue.indexOf(value);
|
|
578
|
+
let hasChanged = false;
|
|
556
579
|
if (checked && valueIndex === -1) {
|
|
557
580
|
newValue.push(value);
|
|
581
|
+
hasChanged = true;
|
|
558
582
|
}
|
|
559
583
|
else if (!checked && valueIndex > -1) {
|
|
560
584
|
newValue.splice(valueIndex, 1);
|
|
585
|
+
hasChanged = true;
|
|
561
586
|
}
|
|
562
587
|
console.info('[Event:itemChecked] newValue after update:', newValue);
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
588
|
+
if (hasChanged) {
|
|
589
|
+
this.value = newValue;
|
|
590
|
+
this.valueChanged.emit(this.value);
|
|
591
|
+
this.syncChildComponents();
|
|
592
|
+
}
|
|
568
593
|
// Preserve the filter text in the input
|
|
569
594
|
if (this.filterable && this.inputElement) {
|
|
570
|
-
// Keep the current filter text in the input
|
|
571
595
|
this.inputElement.value = this.filterText;
|
|
572
596
|
}
|
|
573
597
|
}
|
|
@@ -595,7 +619,11 @@ const NvFieldmultiselect = class {
|
|
|
595
619
|
*/
|
|
596
620
|
connectedCallback() {
|
|
597
621
|
console.info('[Lifecycle] connectedCallback - value:', this.value);
|
|
598
|
-
|
|
622
|
+
// Bind once and reuse the same reference for add/remove to avoid leaks
|
|
623
|
+
if (!this._boundHandleClickOutside) {
|
|
624
|
+
this._boundHandleClickOutside = this.handleClickOutside.bind(this);
|
|
625
|
+
}
|
|
626
|
+
document.addEventListener('click', this._boundHandleClickOutside);
|
|
599
627
|
}
|
|
600
628
|
/**
|
|
601
629
|
* Set the mode state and handle options change.
|
|
@@ -653,7 +681,9 @@ const NvFieldmultiselect = class {
|
|
|
653
681
|
*/
|
|
654
682
|
disconnectedCallback() {
|
|
655
683
|
console.info('[Lifecycle] disconnectedCallback - value:', this.value);
|
|
656
|
-
|
|
684
|
+
if (this._boundHandleClickOutside) {
|
|
685
|
+
document.removeEventListener('click', this._boundHandleClickOutside);
|
|
686
|
+
}
|
|
657
687
|
}
|
|
658
688
|
//#endregion LIFECYCLE
|
|
659
689
|
/****************************************************************************/
|
|
@@ -1182,13 +1212,23 @@ const NvFieldmultiselect = class {
|
|
|
1182
1212
|
const itemValue = item.getAttribute('value') || item.getAttribute('label') || '';
|
|
1183
1213
|
const shouldBeChecked = this.value.includes(itemValue);
|
|
1184
1214
|
console.info('[syncChildComponents] itemValue:', itemValue, 'shouldBeChecked:', shouldBeChecked, 'item.checked(before):', item.checked);
|
|
1185
|
-
if
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1215
|
+
// Only update if the checked state differs to avoid triggering unnecessary events
|
|
1216
|
+
if (item.checked !== shouldBeChecked) {
|
|
1217
|
+
// Set attribute and property, but avoid triggering itemChecked during bulk
|
|
1218
|
+
if (this.isBulkOperation) {
|
|
1219
|
+
// Directly update the DOM attribute to avoid triggering the setter
|
|
1220
|
+
if (shouldBeChecked) {
|
|
1221
|
+
item.setAttribute('checked', '');
|
|
1222
|
+
}
|
|
1223
|
+
else {
|
|
1224
|
+
item.removeAttribute('checked');
|
|
1225
|
+
}
|
|
1226
|
+
// Update internal state without emitting events (assumes nv-fielddropdownitemcheck respects this)
|
|
1227
|
+
item.checked = shouldBeChecked;
|
|
1228
|
+
}
|
|
1229
|
+
else {
|
|
1230
|
+
item.checked = shouldBeChecked;
|
|
1231
|
+
}
|
|
1192
1232
|
}
|
|
1193
1233
|
console.info('[syncChildComponents] itemValue:', itemValue, 'item.checked(after):', item.checked);
|
|
1194
1234
|
});
|