@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.
Files changed (55) hide show
  1. package/dist/cjs/nv-alert.cjs.entry.js +1 -1
  2. package/dist/cjs/nv-alert.cjs.entry.js.map +1 -1
  3. package/dist/cjs/nv-badge_2.cjs.entry.js +7 -1
  4. package/dist/cjs/nv-badge_2.cjs.entry.js.map +1 -1
  5. package/dist/cjs/nv-fieldmultiselect.cjs.entry.js +112 -72
  6. package/dist/cjs/nv-fieldmultiselect.cjs.entry.js.map +1 -1
  7. package/dist/cjs/nv-tooltip.cjs.entry.js +1 -1
  8. package/dist/cjs/nv-tooltip.cjs.entry.js.map +1 -1
  9. package/dist/collection/components/nv-alert/nv-alert.css +1 -0
  10. package/dist/collection/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.js +7 -1
  11. package/dist/collection/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.js.map +1 -1
  12. package/dist/collection/components/nv-fieldmultiselect/nv-fieldmultiselect.js +112 -72
  13. package/dist/collection/components/nv-fieldmultiselect/nv-fieldmultiselect.js.map +1 -1
  14. package/dist/collection/components/nv-tooltip/nv-tooltip.css +2 -2
  15. package/dist/components/nv-alert.js +1 -1
  16. package/dist/components/nv-alert.js.map +1 -1
  17. package/dist/components/nv-breadcrumb.js +1 -1
  18. package/dist/components/nv-fielddropdownitemcheck.js +1 -1
  19. package/dist/components/nv-fieldmultiselect.js +113 -73
  20. package/dist/components/nv-fieldmultiselect.js.map +1 -1
  21. package/dist/components/nv-tooltip.js +1 -1
  22. package/dist/components/{p-51a156ff.js → p-ec4558aa.js} +8 -2
  23. package/dist/components/p-ec4558aa.js.map +1 -0
  24. package/dist/components/{p-2ef4fb88.js → p-f47a1e1e.js} +2 -2
  25. package/dist/{native/p-49504fd6.entry.js.map → components/p-f47a1e1e.js.map} +1 -1
  26. package/dist/esm/nv-alert.entry.js +1 -1
  27. package/dist/esm/nv-alert.entry.js.map +1 -1
  28. package/dist/esm/nv-badge_2.entry.js +7 -1
  29. package/dist/esm/nv-badge_2.entry.js.map +1 -1
  30. package/dist/esm/nv-fieldmultiselect.entry.js +112 -72
  31. package/dist/esm/nv-fieldmultiselect.entry.js.map +1 -1
  32. package/dist/esm/nv-tooltip.entry.js +1 -1
  33. package/dist/esm/nv-tooltip.entry.js.map +1 -1
  34. package/dist/native/native.css +1 -1
  35. package/dist/native/native.esm.js +1 -1
  36. package/dist/native/p-019d164d.entry.js +2 -0
  37. package/dist/native/p-019d164d.entry.js.map +1 -0
  38. package/dist/native/p-4f4ed012.entry.js +2 -0
  39. package/dist/native/p-4f4ed012.entry.js.map +1 -0
  40. package/dist/native/p-8a577f91.entry.js +2 -0
  41. package/dist/native/p-8a577f91.entry.js.map +1 -0
  42. package/dist/native/{p-13032ec1.entry.js → p-9991116a.entry.js} +2 -2
  43. package/dist/native/{p-13032ec1.entry.js.map → p-9991116a.entry.js.map} +1 -1
  44. package/dist/types/components/nv-fielddropdownitemcheck/nv-fielddropdownitemcheck.d.ts +2 -0
  45. package/dist/types/components/nv-fieldmultiselect/nv-fieldmultiselect.d.ts +3 -0
  46. package/hydrate/index.js +122 -76
  47. package/hydrate/index.mjs +122 -76
  48. package/package.json +1 -1
  49. package/dist/components/p-2ef4fb88.js.map +0 -1
  50. package/dist/components/p-51a156ff.js.map +0 -1
  51. package/dist/native/p-2a3325fb.entry.js +0 -2
  52. package/dist/native/p-2a3325fb.entry.js.map +0 -1
  53. package/dist/native/p-49504fd6.entry.js +0 -2
  54. package/dist/native/p-b2442d4b.entry.js +0 -2
  55. 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
- // Get visible and enabled option values from DOM
342
- const visibleOptionValues = this.getVisibleEnabledOptionItems();
343
- console.info('[SelectAll][Options] toggleSelectAllOptions called. selectAll:', selectAll, 'visibleOptionValues:', visibleOptionValues, 'Current value:', this.value);
344
- if (selectAll) {
345
- // Select all visible options - merge with existing selections
346
- this.value = [...new Set([...this.value, ...visibleOptionValues])];
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
- else {
349
- // Deselect only the visible options, keep others that might be filtered out
350
- this.value = this.value.filter(val => !visibleOptionValues.includes(val));
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
- // Get visible and enabled items
368
- const items = this.getVisibleEnabledSlotItems();
369
- console.info('[SelectAll][Slots] toggleSelectAllSlots called. selectAll:', selectAll, 'visible slot items:', items.map(item => item.getAttribute('value') || item.getAttribute('label')), 'Current value:', this.value);
370
- if (selectAll) {
371
- // Select all visible items
372
- const allActiveValues = items
373
- .map(item => item.getAttribute('value') || item.getAttribute('label') || '')
374
- .filter(value => value !== '');
375
- this.value = [...new Set([...this.value, ...allActiveValues])];
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
- else {
378
- // Deselect only the visible items, keep others that might be filtered out
379
- const visibleValues = items
380
- .map(item => item.getAttribute('value') || item.getAttribute('label') || '')
381
- .filter(value => value !== '');
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
- // Always update the state and emit the event when an item is checked/unchecked
564
- this.value = newValue;
565
- this.valueChanged.emit(this.value);
566
- // Update the checked state of all items to ensure consistency
567
- this.syncChildComponents();
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
- document.addEventListener('click', this.handleClickOutside.bind(this));
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
- document.removeEventListener('click', this.handleClickOutside.bind(this));
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 (shouldBeChecked) {
1186
- item.setAttribute('checked', '');
1187
- item.checked = true;
1188
- }
1189
- else {
1190
- item.removeAttribute('checked');
1191
- item.checked = false;
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
  });