selective-ui 1.2.6 → 1.3.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/README.md CHANGED
@@ -5,12 +5,5 @@ Advanced Custom Select Component - Pure JavaScript, Zero Dependencies
5
5
 
6
6
  Full docs with examples: http://selective-ui.maisoft.io.vn/
7
7
 
8
- ## Usage
9
-
10
- ```ts
11
- registerPlugin(MyPlugin);
12
- bind(".my-select");
13
- ```
14
-
15
8
  # License
16
9
  This is commercial software. See [LICENSE](https://github.com/maihcx/selective-ui/blob/main/LICENSE) for more info.
@@ -1,4 +1,4 @@
1
- /*! Selective UI v1.2.6 | MIT License */
1
+ /*! Selective UI v1.3.0 | MIT License */
2
2
  /**
3
3
  * @class
4
4
  */
@@ -27,6 +27,7 @@ class iStorage {
27
27
  autofocus: true,
28
28
  searchable: true,
29
29
  loadingfield: true,
30
+ preload: false,
30
31
  visible: true,
31
32
  skipError: false,
32
33
  customDelimiter: ",",
@@ -36,8 +37,8 @@ class iStorage {
36
37
  textSelectAll: "Select all",
37
38
  textDeselectAll: "Deselect all",
38
39
  textAccessoryDeselect: "Deselect: ",
39
- animationtime: 200, // millisecond
40
- delaysearchtime: 200, // millisecond
40
+ animationtime: 200, // milliseconds
41
+ delaysearchtime: 200, // milliseconds
41
42
  allowHtml: false,
42
43
  maxSelected: 0,
43
44
  labelHalign: "left",
@@ -655,8 +656,7 @@ class Libs {
655
656
  tmp.innerHTML = s;
656
657
  tmp.querySelectorAll("script, style, iframe, object, embed, link").forEach((n) => n.remove());
657
658
  tmp.querySelectorAll("*").forEach((n) => {
658
- for (const k in n.attributes) {
659
- const a = n.attributes[k];
659
+ for (const a of Array.from(n.attributes)) {
660
660
  const name = a.name ?? "";
661
661
  const value = a.value ?? "";
662
662
  if (/^on/i.test(name)) {
@@ -697,26 +697,34 @@ class Libs {
697
697
  return s.replace(/đ/g, "d").replace(/Đ/g, "d");
698
698
  }
699
699
  /**
700
- * Parse select element to array (including optgroups)
701
- * @param {HTMLSelectElement} selectElement
702
- * @returns {Array<HTMLOptGroupElement|HTMLOptionElement>}
700
+ * Flattens a `<select>` element into an ordered array that includes optgroups
701
+ * and their child options.
702
+ *
703
+ * Notes:
704
+ * - Keeps original DOM order.
705
+ * - Adds a non-standard `__parentGroup` pointer on options inside optgroups.
706
+ *
707
+ * @param {HTMLSelectElement} selectElement - The source select element.
708
+ * @returns {Array<HTMLOptGroupElement | HTMLOptionElement>} Flattened node list.
703
709
  */
704
710
  static parseSelectToArray(selectElement) {
705
711
  const result = [];
706
- const children = Array.from(selectElement.children);
707
- children.forEach((child) => {
712
+ const children = selectElement.children;
713
+ for (let childIndex = 0; childIndex < children.length; childIndex++) {
714
+ const child = children[childIndex];
708
715
  if (child.tagName === "OPTGROUP") {
709
716
  const group = child;
710
717
  result.push(group);
711
- Array.from(group.children).forEach((option) => {
718
+ for (let optionIndex = 0; optionIndex < group.children.length; optionIndex++) {
719
+ const option = group.children[optionIndex];
712
720
  option["__parentGroup"] = group;
713
721
  result.push(option);
714
- });
722
+ }
715
723
  }
716
724
  else if (child.tagName === "OPTION") {
717
725
  result.push(child);
718
726
  }
719
- });
727
+ }
720
728
  return result;
721
729
  }
722
730
  /**
@@ -2398,7 +2406,7 @@ class Popup extends Lifecycle {
2398
2406
  }
2399
2407
  : {};
2400
2408
  // Load ModelManager resources into the list container
2401
- this.modelManager.load(this.optionsContainer, { isMultiple: options.multiple }, recyclerViewOpt);
2409
+ this.modelManager.load(this.optionsContainer, { isMultiple: options.multiple, options: options }, recyclerViewOpt);
2402
2410
  const MMResources = this.modelManager.getResources();
2403
2411
  this.optionAdapter = MMResources.adapter;
2404
2412
  this.recyclerView = MMResources.recyclerView;
@@ -2525,36 +2533,60 @@ class Popup extends Lifecycle {
2525
2533
  setupEffector(effectorSvc) {
2526
2534
  this.effSvc = effectorSvc;
2527
2535
  }
2536
+ /**
2537
+ * Loads and initializes the popup (one-time setup):
2538
+ * - Appends the popup node to `document.body`
2539
+ * - Initializes the resize observer service
2540
+ * - Binds the effect service to the popup element
2541
+ * - Blocks mousedown events inside the popup to prevent auto-close
2542
+ *
2543
+ * Safely no-ops when the popup has already been created
2544
+ * or required dependencies are missing.
2545
+ */
2546
+ load() {
2547
+ if (!this.node || !this.parent || !this.effSvc)
2548
+ return;
2549
+ if (this.isCreated)
2550
+ return;
2551
+ document.body.appendChild(this.node);
2552
+ this.isCreated = true;
2553
+ this.resizeObser = new ResizeObserverService();
2554
+ this.effSvc.setElement(this.node);
2555
+ this.node.addEventListener("mousedown", (e) => {
2556
+ e.stopPropagation();
2557
+ e.preventDefault();
2558
+ });
2559
+ }
2528
2560
  /**
2529
2561
  * Opens (expands) the popup:
2530
- * - On first open: appends to `document.body`, sets up resize observer, and blocks outside mousedown
2531
- * - Synchronizes the OptionHandle visibility and (optionally) the empty state
2532
- * - Computes placement from the parent anchor and runs the expand animation
2533
- * - Resumes recycler view after the animation completes
2562
+ * - Ensures the popup is loaded and initialized
2563
+ * - Synchronizes option handle visibility
2564
+ * - Optionally evaluates and applies the empty/not-found state
2565
+ * - Computes placement relative to the parent anchor
2566
+ * - Runs the expand animation
2567
+ * - Connects the resize observer after animation completes
2568
+ * - Resumes the recycler view
2569
+ *
2570
+ * Safely no-ops when required dependencies are missing.
2534
2571
  *
2535
2572
  * @param callback - Optional callback invoked when the opening animation completes.
2536
- * @param isShowEmptyState - If true, evaluates and applies empty/not-found state before animation.
2573
+ * @param isShowEmptyState - If true, applies the empty/not-found state before animation.
2537
2574
  */
2538
2575
  open(callback = null, isShowEmptyState) {
2539
2576
  if (!this.node || !this.options || !this.optionHandle || !this.parent || !this.effSvc)
2540
2577
  return;
2541
- if (!this.isCreated) {
2542
- document.body.appendChild(this.node);
2543
- this.isCreated = true;
2544
- this.resizeObser = new ResizeObserverService();
2545
- this.effSvc.setElement(this.node);
2546
- // Prevent the popup from closing when clicking inside
2547
- this.node.addEventListener("mousedown", (e) => {
2548
- e.stopPropagation();
2549
- e.preventDefault();
2550
- });
2551
- }
2578
+ // Ensure one-time initialization
2579
+ this.load();
2580
+ // Sync option visibility state
2552
2581
  this.optionHandle.update();
2582
+ // Apply empty state if requested
2553
2583
  if (isShowEmptyState) {
2554
2584
  this.updateEmptyState();
2555
2585
  }
2586
+ // Compute placement based on parent anchor
2556
2587
  const location = this.getParentLocation();
2557
2588
  const { position, top, maxHeight, realHeight } = this.calculatePosition(location);
2589
+ // Run expand animation
2558
2590
  this.effSvc.expand({
2559
2591
  duration: this.options.animationtime,
2560
2592
  display: "flex",
@@ -2567,13 +2599,14 @@ class Popup extends Lifecycle {
2567
2599
  onComplete: () => {
2568
2600
  if (!this.resizeObser || !this.parent)
2569
2601
  return;
2602
+ // Recompute position on parent resize to keep behavior consistent
2570
2603
  this.resizeObser.onChanged = (_metrics) => {
2571
- // Recompute from parent each time to keep behavior identical.
2572
2604
  const loc = this.getParentLocation();
2573
2605
  this.handleResize(loc);
2574
2606
  };
2575
2607
  this.resizeObser.connect(this.parent.container.tags.ViewPanel);
2576
2608
  callback?.();
2609
+ // Resume recycler view rendering after animation
2577
2610
  const rv = this.recyclerView;
2578
2611
  rv?.resume?.();
2579
2612
  },
@@ -3782,6 +3815,7 @@ class GroupModel extends Model {
3782
3815
  */
3783
3816
  this.privOnCollapsedChanged = [];
3784
3817
  this.label = this.targetElement.label;
3818
+ this.collapsed = Libs.string2Boolean(this.targetElement.dataset?.collapsed);
3785
3819
  }
3786
3820
  /**
3787
3821
  * Initializes group state from the backing `<optgroup>` (if present) and mounts the model.
@@ -3798,7 +3832,6 @@ class GroupModel extends Model {
3798
3832
  * @override
3799
3833
  */
3800
3834
  init() {
3801
- this.collapsed = Libs.string2Boolean(this.targetElement.dataset?.collapsed);
3802
3835
  super.init();
3803
3836
  this.mount();
3804
3837
  }
@@ -5518,7 +5551,8 @@ class SearchController extends Lifecycle {
5518
5551
  .join(",");
5519
5552
  let payload;
5520
5553
  if (typeof cfg.data === "function") {
5521
- payload = cfg.data.bind(this.selectBox.Selective.find(this.selectBox.container.targetElement))(keyword, page);
5554
+ const selectiveInstance = this.selectBox?.Selective?.find(this.selectBox?.container?.targetElement);
5555
+ payload = cfg.data.call(selectiveInstance, keyword, page);
5522
5556
  if (payload && typeof payload.selectedValue === "undefined")
5523
5557
  payload.selectedValue = selectedValues;
5524
5558
  }
@@ -6463,8 +6497,18 @@ class View extends Lifecycle {
6463
6497
  * @see {@link View}
6464
6498
  */
6465
6499
  class GroupView extends View {
6466
- constructor() {
6467
- super(...arguments);
6500
+ /**
6501
+ * Creates a new GroupView bound to the given parent element.
6502
+ *
6503
+ * Initialization flow:
6504
+ * 1. Calls `super(parent)` (View base constructor).
6505
+ *
6506
+ * @public
6507
+ * @param {HTMLElement} parent - Container element that will host this group view.
6508
+ * @param {SelectiveOptions} options - Optional configuration for this group view.
6509
+ */
6510
+ constructor(parent, options) {
6511
+ super(parent);
6468
6512
  /**
6469
6513
  * Strongly-typed reference to the mounted group view structure.
6470
6514
  *
@@ -6479,6 +6523,16 @@ class GroupView extends View {
6479
6523
  * @public
6480
6524
  */
6481
6525
  this.view = null;
6526
+ /**
6527
+ * Parsed configuration (bound from the `<select>` element via binder map).
6528
+ *
6529
+ * Provides feature flags (multiple/disabled/readonly/visible/virtualScroll/ajax/autoclose…),
6530
+ * a11y ids (e.g. `SEID_LIST`, `SEID_HOLDER`) and user callbacks under `options.on`.
6531
+ *
6532
+ * @internal
6533
+ */
6534
+ this.options = null;
6535
+ this.options = options;
6482
6536
  }
6483
6537
  /**
6484
6538
  * Mounts the group view into the DOM.
@@ -6486,8 +6540,8 @@ class GroupView extends View {
6486
6540
  * Creation flow:
6487
6541
  * 1. Generates unique group ID (7-character random string).
6488
6542
  * 2. Creates DOM structure via {@link Libs.mountNode}:
6489
- * - Root: `<div role="group" aria-labelledby="seui-{id}-header">`
6490
- * - Header: `<div role="presentation" id="seui-{id}-header">`
6543
+ * - Root: `<div role="group" aria-labelledby="seui-{this.options?.SEID || default}-{id}-header">`
6544
+ * - Header: `<div role="presentation" id="seui-{this.options?.SEID || default}-{id}-header">`
6491
6545
  * - Items: `<div role="group">` (nested group for child items)
6492
6546
  * 3. Appends root to {@link parent} container.
6493
6547
  * 4. Transitions `INITIALIZED → MOUNTED` via `super.mount()`.
@@ -6513,8 +6567,8 @@ class GroupView extends View {
6513
6567
  node: "div",
6514
6568
  classList: ["seui-group"],
6515
6569
  role: "group",
6516
- ariaLabelledby: `seui-${group_id}-header`,
6517
- id: `seui-${group_id}-group`,
6570
+ ariaLabelledby: `seui-${this.options?.SEID || "default"}-${group_id}-header`,
6571
+ id: `seui-${this.options?.SEID || "default"}-${group_id}-group`,
6518
6572
  },
6519
6573
  child: {
6520
6574
  GroupHeader: {
@@ -6522,7 +6576,7 @@ class GroupView extends View {
6522
6576
  node: "div",
6523
6577
  classList: ["seui-group-header"],
6524
6578
  role: "presentation",
6525
- id: `seui-${group_id}-header`,
6579
+ id: `seui-${this.options?.SEID || "default"}-${group_id}-header`,
6526
6580
  },
6527
6581
  },
6528
6582
  GroupItems: {
@@ -6731,8 +6785,9 @@ class OptionView extends View {
6731
6785
  *
6732
6786
  * @public
6733
6787
  * @param {HTMLElement} parent - Container element that will host this option view.
6788
+ * @param {SelectiveOptions} options - Optional configuration for this option view.
6734
6789
  */
6735
- constructor(parent) {
6790
+ constructor(parent, options) {
6736
6791
  super(parent);
6737
6792
  /**
6738
6793
  * Strongly-typed reference to the mounted option view structure.
@@ -6748,6 +6803,15 @@ class OptionView extends View {
6748
6803
  * @public
6749
6804
  */
6750
6805
  this.view = null;
6806
+ /**
6807
+ * Parsed configuration (bound from the `<select>` element via binder map).
6808
+ *
6809
+ * Provides feature flags (multiple/disabled/readonly/visible/virtualScroll/ajax/autoclose…),
6810
+ * a11y ids (e.g. `SEID_LIST`, `SEID_HOLDER`) and user callbacks under `options.on`.
6811
+ *
6812
+ * @internal
6813
+ */
6814
+ this.options = null;
6751
6815
  /**
6752
6816
  * Internal configuration object (Proxy target).
6753
6817
  *
@@ -6789,6 +6853,7 @@ class OptionView extends View {
6789
6853
  * @private
6790
6854
  */
6791
6855
  this.isRendered = false;
6856
+ this.options = options;
6792
6857
  this.initialize();
6793
6858
  }
6794
6859
  /**
@@ -6993,7 +7058,7 @@ class OptionView extends View {
6993
7058
  mount() {
6994
7059
  const viewClass = ["seui-option-view"];
6995
7060
  const opt_id = Libs.randomString(7);
6996
- const inputID = `option_${opt_id}`;
7061
+ const inputID = `option_${this.options?.SEID ?? "default"}_${opt_id}`;
6997
7062
  if (this.config.isMultiple)
6998
7063
  viewClass.push("multiple");
6999
7064
  if (this.config.hasImage) {
@@ -7039,7 +7104,7 @@ class OptionView extends View {
7039
7104
  OptionView: {
7040
7105
  tag: {
7041
7106
  node: "div",
7042
- id: `seui-${opt_id}-option`,
7107
+ id: `seui-${this.options?.SEID ?? "default"}-${opt_id}-option`,
7043
7108
  classList: viewClass,
7044
7109
  role: "option",
7045
7110
  ariaSelected: "false",
@@ -7253,6 +7318,15 @@ class MixedAdapter extends Adapter {
7253
7318
  super(items);
7254
7319
  /** Whether the adapter operates in multi-selection mode. */
7255
7320
  this.isMultiple = false;
7321
+ /**
7322
+ * Parsed configuration (bound from the `<select>` element via binder map).
7323
+ *
7324
+ * Provides feature flags (multiple/disabled/readonly/visible/virtualScroll/ajax/autoclose…),
7325
+ * a11y ids (e.g. `SEID_LIST`, `SEID_HOLDER`) and user callbacks under `options.on`.
7326
+ *
7327
+ * @internal
7328
+ */
7329
+ this.options = null;
7256
7330
  /**
7257
7331
  * Subscribers for aggregated visibility statistics.
7258
7332
  * Fired via a debounced scheduler to avoid repeated recomputation during batch updates.
@@ -7345,8 +7419,8 @@ class MixedAdapter extends Adapter {
7345
7419
  */
7346
7420
  viewHolder(parent, item) {
7347
7421
  if (item instanceof GroupModel)
7348
- return new GroupView(parent);
7349
- return new OptionView(parent);
7422
+ return new GroupView(parent, this.options);
7423
+ return new OptionView(parent, this.options);
7350
7424
  }
7351
7425
  /**
7352
7426
  * Binds a model (group or option) to its view and delegates to specialized handlers.
@@ -7414,7 +7488,7 @@ class MixedAdapter extends Adapter {
7414
7488
  groupModel.items.forEach((optionModel, idx) => {
7415
7489
  let optionViewer = optionModel.view;
7416
7490
  if (!optionModel.isInit || !optionViewer) {
7417
- optionViewer = new OptionView(itemsContainer);
7491
+ optionViewer = new OptionView(itemsContainer, this.options);
7418
7492
  }
7419
7493
  this.handleOptionView(optionModel, optionViewer, idx);
7420
7494
  optionModel.isInit = true;
@@ -9171,16 +9245,17 @@ class SelectBox extends Lifecycle {
9171
9245
  });
9172
9246
  this.optionModelManager = optionModelManager;
9173
9247
  // Popup
9174
- container.popup = new Popup(select, options, optionModelManager);
9175
- container.popup.setupEffector(effector);
9176
- container.popup.setupInfiniteScroll(searchController, options);
9177
- container.popup.onAdapterPropChanged("selected", () => {
9248
+ const popup = new Popup(select, options, optionModelManager);
9249
+ container.popup = popup;
9250
+ popup.setupEffector(effector);
9251
+ popup.setupInfiniteScroll(searchController, options);
9252
+ popup.onAdapterPropChanged("selected", () => {
9178
9253
  this.getAction()?.change(null, true);
9179
9254
  });
9180
- container.popup.onAdapterPropChanged("selected_internal", () => {
9255
+ popup.onAdapterPropChanged("selected_internal", () => {
9181
9256
  this.getAction()?.change(null, false);
9182
9257
  });
9183
- container.popup.onAdapterPropChanging("select", () => {
9258
+ popup.onAdapterPropChanging("select", () => {
9184
9259
  this.oldValue = this.getAction()?.value ?? "";
9185
9260
  });
9186
9261
  accessoryBox.setRoot(container.tags.ViewPanel);
@@ -9237,7 +9312,11 @@ class SelectBox extends Lifecycle {
9237
9312
  Refresher.resizeBox(select, container.tags.ViewPanel);
9238
9313
  select.classList.add("init");
9239
9314
  // initial mask
9240
- this.getAction()?.change(null, false);
9315
+ const action = this.getAction();
9316
+ action?.change?.(null, false);
9317
+ if (this.options.preload) {
9318
+ action?.load?.();
9319
+ }
9241
9320
  // Call parent lifecycle mount
9242
9321
  super.mount();
9243
9322
  }
@@ -9677,6 +9756,26 @@ class SelectBox extends Lifecycle {
9677
9756
  }
9678
9757
  this.change(false, trigger);
9679
9758
  },
9759
+ load() {
9760
+ if ((!superThis.hasLoadedOnce || superThis.isBeforeSearch) && bindedOptions?.ajax) {
9761
+ container.searchController.resetPagination();
9762
+ container.popup.showLoading();
9763
+ superThis.hasLoadedOnce = true;
9764
+ superThis.isBeforeSearch = false;
9765
+ setTimeout(() => {
9766
+ if (!container.popup || !container.searchController)
9767
+ return;
9768
+ container.searchController
9769
+ .search("")
9770
+ .then(() => container.popup?.triggerResize?.())
9771
+ .catch((err) => console.error("Initial ajax load error:", err));
9772
+ }, bindedOptions.animationtime);
9773
+ container.popup.load();
9774
+ }
9775
+ else {
9776
+ container.popup.load();
9777
+ }
9778
+ },
9680
9779
  open() {
9681
9780
  if (superThis.isOpen)
9682
9781
  return;
@@ -9686,45 +9785,34 @@ class SelectBox extends Lifecycle {
9686
9785
  if (closeToken.isCancel)
9687
9786
  return;
9688
9787
  }
9689
- if (this.disabled)
9788
+ if (this.disabled) {
9690
9789
  return;
9790
+ }
9691
9791
  const beforeShowToken = iEvents.callEvent([getInstance()], ...bindedOptions.on.beforeShow);
9692
- if (beforeShowToken.isCancel)
9792
+ if (beforeShowToken.isCancel) {
9693
9793
  return;
9794
+ }
9694
9795
  superThis.isOpen = true;
9695
9796
  container.directive.setDropdown(true);
9696
9797
  const adapter = container.popup.optionAdapter;
9697
9798
  const selectedOption = adapter.getSelectedItem();
9698
- if (selectedOption)
9799
+ if (selectedOption) {
9699
9800
  adapter.setHighlight(selectedOption, false);
9700
- else
9701
- adapter.resetHighlight();
9702
- if ((!superThis.hasLoadedOnce || superThis.isBeforeSearch) && bindedOptions?.ajax) {
9703
- container.searchController.resetPagination();
9704
- container.popup.showLoading();
9705
- superThis.hasLoadedOnce = true;
9706
- superThis.isBeforeSearch = false;
9707
- setTimeout(() => {
9708
- if (!container.popup || !container.searchController)
9709
- return;
9710
- container.searchController
9711
- .search("")
9712
- .then(() => container.popup?.triggerResize?.())
9713
- .catch((err) => console.error("Initial ajax load error:", err));
9714
- }, bindedOptions.animationtime);
9715
- container.popup.open(null, false);
9716
9801
  }
9717
9802
  else {
9718
- container.popup.open(null, true);
9803
+ adapter.resetHighlight();
9719
9804
  }
9805
+ this.load();
9806
+ container.popup.open(null, !container.popup.loadingState.isVisible);
9720
9807
  container.searchbox.show();
9721
9808
  const ViewPanel = container.tags.ViewPanel;
9722
9809
  ViewPanel.setAttribute("aria-expanded", "true");
9723
9810
  ViewPanel.setAttribute("aria-controls", bindedOptions.SEID_LIST);
9724
9811
  ViewPanel.setAttribute("aria-haspopup", "listbox");
9725
9812
  ViewPanel.setAttribute("aria-labelledby", bindedOptions.SEID_HOLDER);
9726
- if (bindedOptions.multiple)
9813
+ if (bindedOptions.multiple) {
9727
9814
  ViewPanel.setAttribute("aria-multiselectable", "true");
9815
+ }
9728
9816
  iEvents.callEvent([getInstance()], ...bindedOptions.on.show);
9729
9817
  if (superThis.pluginContext) {
9730
9818
  superThis.runPluginHook("onOpen", (plugin) => plugin.onOpen?.(superThis.pluginContext));
@@ -9998,7 +10086,7 @@ class ElementAdditionObserver {
9998
10086
  *
9999
10087
  * @internal
10000
10088
  */
10001
- this.actions = [];
10089
+ this.actions = new Set();
10002
10090
  }
10003
10091
  /**
10004
10092
  * Registers a callback invoked whenever a matching element is detected as added to the DOM.
@@ -10010,7 +10098,7 @@ class ElementAdditionObserver {
10010
10098
  * @param action - Function executed with the newly detected element.
10011
10099
  */
10012
10100
  onDetect(action) {
10013
- this.actions.push(action);
10101
+ this.actions.add(action);
10014
10102
  }
10015
10103
  /**
10016
10104
  * Clears all registered detection callbacks.
@@ -10019,7 +10107,7 @@ class ElementAdditionObserver {
10019
10107
  * to scan mutations but will not invoke any listeners until new callbacks are registered.
10020
10108
  */
10021
10109
  clearDetect() {
10022
- this.actions = [];
10110
+ this.actions.clear();
10023
10111
  }
10024
10112
  /**
10025
10113
  * Starts observing the document for additions of elements matching the given tag name.
@@ -10093,7 +10181,9 @@ class ElementAdditionObserver {
10093
10181
  * @internal
10094
10182
  */
10095
10183
  handle(element) {
10096
- this.actions.forEach((action) => action(element));
10184
+ for (const action of this.actions) {
10185
+ action(element);
10186
+ }
10097
10187
  }
10098
10188
  }
10099
10189
 
@@ -10802,7 +10892,7 @@ const SECLASS = new Selective();
10802
10892
  *
10803
10893
  * Declared as `const` literal type to enable strict typing and easy tree-shaking.
10804
10894
  */
10805
- const version = "1.2.6";
10895
+ const version = "1.3.0";
10806
10896
  /**
10807
10897
  * Library name identifier.
10808
10898
  *