selective-ui 1.2.5 → 1.2.6
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 +7 -0
- package/dist/selective-ui.css +64 -58
- package/dist/selective-ui.css.map +1 -1
- package/dist/selective-ui.esm.js +240 -125
- package/dist/selective-ui.esm.js.map +1 -1
- package/dist/selective-ui.esm.min.js +2 -2
- package/dist/selective-ui.esm.min.js.br +0 -0
- package/dist/selective-ui.min.css +1 -1
- package/dist/selective-ui.min.css.br +0 -0
- package/dist/selective-ui.min.js +2 -2
- package/dist/selective-ui.min.js.br +0 -0
- package/dist/selective-ui.umd.js +245 -126
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +3 -3
- package/src/css/components/accessorybox.css +1 -1
- package/src/css/components/directive.css +2 -2
- package/src/css/components/option-handle.css +4 -4
- package/src/css/components/placeholder.css +1 -1
- package/src/css/components/popup/empty-state.css +3 -3
- package/src/css/components/popup/loading-state.css +3 -3
- package/src/css/components/popup/popup.css +5 -5
- package/src/css/components/searchbox.css +2 -2
- package/src/css/components/selectbox.css +7 -7
- package/src/css/views/group-view.css +8 -8
- package/src/css/views/option-view.css +22 -22
- package/src/ts/adapter/mixed-adapter.ts +1 -1
- package/src/ts/components/accessorybox.ts +9 -9
- package/src/ts/components/directive.ts +2 -2
- package/src/ts/components/option-handle.ts +9 -9
- package/src/ts/components/placeholder.ts +5 -5
- package/src/ts/components/popup/empty-state.ts +4 -4
- package/src/ts/components/popup/loading-state.ts +4 -4
- package/src/ts/components/popup/popup.ts +19 -38
- package/src/ts/components/searchbox.ts +6 -6
- package/src/ts/components/selectbox.ts +93 -11
- package/src/ts/core/base/adapter.ts +2 -2
- package/src/ts/core/base/virtual-recyclerview.ts +6 -6
- package/src/ts/core/model-manager.ts +10 -11
- package/src/ts/core/search-controller.ts +2 -2
- package/src/ts/global.ts +26 -5
- package/src/ts/index.ts +22 -3
- package/src/ts/models/option-model.ts +13 -5
- package/src/ts/services/refresher.ts +2 -1
- package/src/ts/services/resize-observer.ts +4 -4
- package/src/ts/types/core/base/view.type.ts +3 -3
- package/src/ts/types/core/base/virtual-recyclerview.type.ts +1 -1
- package/src/ts/types/plugins/plugin.type.ts +46 -0
- package/src/ts/types/utils/istorage.type.ts +8 -4
- package/src/ts/types/utils/libs.type.ts +2 -2
- package/src/ts/types/utils/selective.type.ts +14 -1
- package/src/ts/utils/callback-scheduler.ts +4 -4
- package/src/ts/utils/libs.ts +41 -65
- package/src/ts/utils/selective.ts +85 -21
- package/src/ts/views/group-view.ts +6 -6
- package/src/ts/views/option-view.ts +11 -11
package/dist/selective-ui.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Selective UI v1.2.
|
|
1
|
+
/*! Selective UI v1.2.6 | MIT License */
|
|
2
2
|
(function (global, factory) {
|
|
3
3
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
4
4
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
@@ -213,7 +213,7 @@
|
|
|
213
213
|
* @public
|
|
214
214
|
* @param {TimerKey} key - Key whose callbacks will be scheduled.
|
|
215
215
|
* @param {...any[]} params - Parameters passed as a shared payload to all callbacks.
|
|
216
|
-
* @returns {Promise<void>
|
|
216
|
+
* @returns {Promise<void>} Promise resolving when all callbacks finish execution.
|
|
217
217
|
*/
|
|
218
218
|
run(key, ...params) {
|
|
219
219
|
const executes = this.executeStored.get(key);
|
|
@@ -332,11 +332,11 @@
|
|
|
332
332
|
return result;
|
|
333
333
|
}
|
|
334
334
|
/**
|
|
335
|
-
* Resolves a selector, NodeList, or single
|
|
335
|
+
* Resolves a selector, NodeList, or single HTMLElement into an array of elements.
|
|
336
336
|
* Returns an empty array if nothing is found.
|
|
337
337
|
*
|
|
338
|
-
* @param {string|NodeListOf<
|
|
339
|
-
* @returns {
|
|
338
|
+
* @param {string|NodeListOf<HTMLElement>|HTMLElement|HTMLElement|ArrayLike<HTMLElement>|null} queryCommon - CSS selector, NodeList, or HTMLElement.
|
|
339
|
+
* @returns {HTMLElement[]} - Array of matched elements (empty if none).
|
|
340
340
|
*/
|
|
341
341
|
static getElements(queryCommon) {
|
|
342
342
|
if (!queryCommon)
|
|
@@ -345,7 +345,7 @@
|
|
|
345
345
|
const nodeList = document.querySelectorAll(queryCommon);
|
|
346
346
|
return Array.from(nodeList);
|
|
347
347
|
}
|
|
348
|
-
if (queryCommon instanceof
|
|
348
|
+
if (queryCommon instanceof HTMLElement) {
|
|
349
349
|
return [queryCommon];
|
|
350
350
|
}
|
|
351
351
|
// NodeList or array-like
|
|
@@ -355,27 +355,27 @@
|
|
|
355
355
|
return [];
|
|
356
356
|
}
|
|
357
357
|
/**
|
|
358
|
-
* Creates a new
|
|
358
|
+
* Creates a new HTMLElement based on a NodeSpec and applies attributes, classes, styles, dataset, and events.
|
|
359
359
|
*
|
|
360
360
|
* @param {NodeSpec} data - Specification describing the element to create.
|
|
361
|
-
* @returns {
|
|
361
|
+
* @returns {HTMLElement} - The created element.
|
|
362
362
|
*/
|
|
363
363
|
static nodeCreator(data = {}) {
|
|
364
364
|
const nodeName = (data.node ?? "div");
|
|
365
365
|
return this.nodeCloner(document.createElement(nodeName), data, true);
|
|
366
366
|
}
|
|
367
367
|
/**
|
|
368
|
-
* Clones an element (or converts a Node to
|
|
368
|
+
* Clones an element (or converts a Node to HTMLElement) and applies NodeSpec options.
|
|
369
369
|
* When systemNodeCreate=true, uses the provided node as-is.
|
|
370
370
|
*
|
|
371
|
-
* @param {
|
|
371
|
+
* @param {HTMLElement} node - The element to clone or use.
|
|
372
372
|
* @param {NodeSpec|null} _nodeOption - Options (classList, style, dataset, event, other props).
|
|
373
373
|
* @param {boolean} systemNodeCreate - If true, do not clone; use original node.
|
|
374
|
-
* @returns {
|
|
374
|
+
* @returns {HTMLElement} - The processed element.
|
|
375
375
|
*/
|
|
376
376
|
static nodeCloner(node = document.documentElement, _nodeOption = null, systemNodeCreate = false) {
|
|
377
377
|
const nodeOption = { ...(_nodeOption ?? {}) };
|
|
378
|
-
const element_creation = systemNodeCreate ? node :
|
|
378
|
+
const element_creation = systemNodeCreate ? node : node.cloneNode(true);
|
|
379
379
|
const classList = nodeOption.classList;
|
|
380
380
|
if (typeof classList === "string") {
|
|
381
381
|
element_creation.classList.add(classList);
|
|
@@ -435,40 +435,17 @@
|
|
|
435
435
|
});
|
|
436
436
|
return element_creation;
|
|
437
437
|
}
|
|
438
|
-
/**
|
|
439
|
-
* Ensures the given Node is an Element; throws if not.
|
|
440
|
-
*
|
|
441
|
-
* @param {Node} node - The node to validate.
|
|
442
|
-
* @returns {Element} - The element cast.
|
|
443
|
-
* @throws {TypeError} - If node is not an Element.
|
|
444
|
-
*/
|
|
445
|
-
static nodeToElement(node) {
|
|
446
|
-
if (node instanceof Element)
|
|
447
|
-
return node;
|
|
448
|
-
throw new TypeError("Node is not an Element");
|
|
449
|
-
}
|
|
450
|
-
/**
|
|
451
|
-
* Mounts a view from a plain object specification and returns a typed result
|
|
452
|
-
* containing the root element and a tag map.
|
|
453
|
-
*
|
|
454
|
-
* @template TTags
|
|
455
|
-
* @param {object} rawObj - The specification describing elements and tags.
|
|
456
|
-
* @returns {MountViewResult<TTags>} - The mounted view and its tag references.
|
|
457
|
-
*/
|
|
458
|
-
static mountView(rawObj) {
|
|
459
|
-
return this.mountNode(rawObj);
|
|
460
|
-
}
|
|
461
438
|
/**
|
|
462
439
|
* Recursively builds DOM nodes from a specification object, appends/prepends them
|
|
463
440
|
* to an optional parent, and returns either a tag map or a full MountViewResult.
|
|
464
441
|
*
|
|
465
442
|
* @template TTags
|
|
466
443
|
* @param {Object<string, any>} rawObj - Node spec (keys -> { tag, child }).
|
|
467
|
-
* @param {
|
|
444
|
+
* @param {HTMLElement|null} [parentE=null] - Parent to attach into; if null, returns root.
|
|
468
445
|
* @param {boolean} [isPrepend=false] - If true, prepend; otherwise append.
|
|
469
446
|
* @param {boolean} [isRecusive=false] - Internal flag for recursion control.
|
|
470
447
|
* @param {TTags|Object} [recursiveTemp={}] - Accumulator for tag references.
|
|
471
|
-
* @returns {
|
|
448
|
+
* @returns {TTags} - Tag map or the final mount result.
|
|
472
449
|
*/
|
|
473
450
|
static mountNode(rawObj, parentE = null, isPrepend = false, isRecusive = false, recursiveTemp = {}) {
|
|
474
451
|
let view = null;
|
|
@@ -587,7 +564,7 @@
|
|
|
587
564
|
/**
|
|
588
565
|
* Removes a binder map entry for the given element from the global storage.
|
|
589
566
|
*
|
|
590
|
-
* @param {HTMLElement} element -
|
|
567
|
+
* @param {HTMLElement} element - HTMLElement key to remove from the binder map.
|
|
591
568
|
* @returns {boolean} - True if an entry existed and was removed.
|
|
592
569
|
*/
|
|
593
570
|
static removeBinderMap(element) {
|
|
@@ -596,8 +573,8 @@
|
|
|
596
573
|
/**
|
|
597
574
|
* Retrieves the binder map entry associated with the given element.
|
|
598
575
|
*
|
|
599
|
-
* @param {HTMLElement} item -
|
|
600
|
-
* @returns {BinderMap |
|
|
576
|
+
* @param {HTMLElement} item - HTMLElement key whose binder map is requested.
|
|
577
|
+
* @returns {BinderMap | any} - The stored binder map value or undefined if absent.
|
|
601
578
|
*/
|
|
602
579
|
static getBinderMap(item) {
|
|
603
580
|
return this.iStorage.bindedMap.get(item);
|
|
@@ -605,7 +582,7 @@
|
|
|
605
582
|
/**
|
|
606
583
|
* Sets or updates the binder map entry for a given element.
|
|
607
584
|
*
|
|
608
|
-
* @param {HTMLElement} item -
|
|
585
|
+
* @param {HTMLElement} item - HTMLElement key to associate with the binder map.
|
|
609
586
|
* @param {BinderMap} bindMap - Value to store in the binder map.
|
|
610
587
|
*/
|
|
611
588
|
static setBinderMap(item, bindMap) {
|
|
@@ -614,7 +591,7 @@
|
|
|
614
591
|
/**
|
|
615
592
|
* Removes an unbinder map entry for the given element from the global storage.
|
|
616
593
|
*
|
|
617
|
-
* @param {HTMLElement} element -
|
|
594
|
+
* @param {HTMLElement} element - HTMLElement key to remove from the unbinder map.
|
|
618
595
|
* @returns {boolean} - True if an entry existed and was removed.
|
|
619
596
|
*/
|
|
620
597
|
static removeUnbinderMap(element) {
|
|
@@ -623,7 +600,7 @@
|
|
|
623
600
|
/**
|
|
624
601
|
* Retrieves the unbinder map entry associated with the given element.
|
|
625
602
|
*
|
|
626
|
-
* @param {HTMLElement} item -
|
|
603
|
+
* @param {HTMLElement} item - HTMLElement key whose unbinder map is requested.
|
|
627
604
|
* @returns {unknown} - The stored unbinder map value or undefined if absent.
|
|
628
605
|
*/
|
|
629
606
|
static getUnbinderMap(item) {
|
|
@@ -632,7 +609,7 @@
|
|
|
632
609
|
/**
|
|
633
610
|
* Sets or updates the unbinder map entry for a given element.
|
|
634
611
|
*
|
|
635
|
-
* @param {HTMLElement} item -
|
|
612
|
+
* @param {HTMLElement} item - HTMLElement key to associate with the unbinder map.
|
|
636
613
|
* @param {BinderMap} bindMap - Value to store in the unbinder map.
|
|
637
614
|
*/
|
|
638
615
|
static setUnbinderMap(item, bindMap) {
|
|
@@ -1280,7 +1257,7 @@
|
|
|
1280
1257
|
* getter/setter APIs for the placeholder content.
|
|
1281
1258
|
*
|
|
1282
1259
|
* ### Responsibility
|
|
1283
|
-
* - Create and own the placeholder DOM element (`.
|
|
1260
|
+
* - Create and own the placeholder DOM element (`.seui-placeholder`).
|
|
1284
1261
|
* - Render placeholder content from {@link SelectiveOptions.placeholder}.
|
|
1285
1262
|
* - Support runtime updates via {@link set}, optionally persisting into options.
|
|
1286
1263
|
* - Participate in the shared {@link Lifecycle} FSM.
|
|
@@ -1338,7 +1315,7 @@
|
|
|
1338
1315
|
* Builds the placeholder DOM node and starts the lifecycle.
|
|
1339
1316
|
*
|
|
1340
1317
|
* Side effects:
|
|
1341
|
-
* - Creates a `div.
|
|
1318
|
+
* - Creates a `div.seui-placeholder` node via {@link Libs.nodeCreator}.
|
|
1342
1319
|
* - Writes initial placeholder content into `innerHTML`.
|
|
1343
1320
|
* - Transitions the lifecycle by calling `init()`.
|
|
1344
1321
|
*
|
|
@@ -1348,7 +1325,7 @@
|
|
|
1348
1325
|
initialize(options) {
|
|
1349
1326
|
this.node = Libs.nodeCreator({
|
|
1350
1327
|
node: "div",
|
|
1351
|
-
classList: "
|
|
1328
|
+
classList: "seui-placeholder",
|
|
1352
1329
|
innerHTML: options.placeholder,
|
|
1353
1330
|
});
|
|
1354
1331
|
this.options = options;
|
|
@@ -1469,7 +1446,7 @@
|
|
|
1469
1446
|
// is guaranteed to be an HTMLElement in this context.
|
|
1470
1447
|
this.node = Libs.nodeCreator({
|
|
1471
1448
|
node: "div",
|
|
1472
|
-
classList: "
|
|
1449
|
+
classList: "seui-directive",
|
|
1473
1450
|
role: "button",
|
|
1474
1451
|
ariaLabel: "Toggle dropdown",
|
|
1475
1452
|
});
|
|
@@ -1608,9 +1585,9 @@
|
|
|
1608
1585
|
* Initializes DOM and binds event handlers.
|
|
1609
1586
|
*
|
|
1610
1587
|
* DOM structure (conceptually):
|
|
1611
|
-
* - Root: `div.
|
|
1612
|
-
* - Child: `a.
|
|
1613
|
-
* - Child: `a.
|
|
1588
|
+
* - Root: `div.seui-option-handle.hide`
|
|
1589
|
+
* - Child: `a.seui-option-handle-item` ("Select all")
|
|
1590
|
+
* - Child: `a.seui-option-handle-item` ("Deselect all")
|
|
1614
1591
|
*
|
|
1615
1592
|
* Click handlers:
|
|
1616
1593
|
* - "Select all" → dispatches {@link actionOnSelectAll} via {@link iEvents.callFunctions}
|
|
@@ -1626,12 +1603,12 @@
|
|
|
1626
1603
|
initialize(options) {
|
|
1627
1604
|
this.nodeMounted = Libs.mountNode({
|
|
1628
1605
|
OptionHandle: {
|
|
1629
|
-
tag: { node: "div", classList: ["
|
|
1606
|
+
tag: { node: "div", classList: ["seui-option-handle", "hide"] },
|
|
1630
1607
|
child: {
|
|
1631
1608
|
SelectAll: {
|
|
1632
1609
|
tag: {
|
|
1633
1610
|
node: "a",
|
|
1634
|
-
classList: "
|
|
1611
|
+
classList: "seui-option-handle-item",
|
|
1635
1612
|
textContent: options.textSelectAll,
|
|
1636
1613
|
onclick: () => {
|
|
1637
1614
|
iEvents.callFunctions(this.actionOnSelectAll);
|
|
@@ -1641,7 +1618,7 @@
|
|
|
1641
1618
|
DeSelectAll: {
|
|
1642
1619
|
tag: {
|
|
1643
1620
|
node: "a",
|
|
1644
|
-
classList: "
|
|
1621
|
+
classList: "seui-option-handle-item",
|
|
1645
1622
|
textContent: options.textDeselectAll,
|
|
1646
1623
|
onclick: () => {
|
|
1647
1624
|
iEvents.callFunctions(this.actionOnDeSelectAll);
|
|
@@ -1839,7 +1816,7 @@
|
|
|
1839
1816
|
*
|
|
1840
1817
|
* Side effects:
|
|
1841
1818
|
* - Creates the root `div` node with `role="status"` and `aria-live="polite"`.
|
|
1842
|
-
* - Applies base CSS classes: `"
|
|
1819
|
+
* - Applies base CSS classes: `"seui-empty-state"` and `"hide"`.
|
|
1843
1820
|
* - Stores the options reference and calls {@link Lifecycle.init}.
|
|
1844
1821
|
*
|
|
1845
1822
|
* @param {SelectiveOptions} options - Configuration object containing empty state messages.
|
|
@@ -1849,7 +1826,7 @@
|
|
|
1849
1826
|
this.options = options;
|
|
1850
1827
|
this.node = Libs.nodeCreator({
|
|
1851
1828
|
node: "div",
|
|
1852
|
-
classList: ["
|
|
1829
|
+
classList: ["seui-empty-state", "hide"],
|
|
1853
1830
|
role: "status",
|
|
1854
1831
|
ariaLive: "polite",
|
|
1855
1832
|
});
|
|
@@ -1982,7 +1959,7 @@
|
|
|
1982
1959
|
* Initializes internal resources for this component.
|
|
1983
1960
|
*
|
|
1984
1961
|
* Side effects:
|
|
1985
|
-
* - Creates the root `div` node with base CSS classes: `"
|
|
1962
|
+
* - Creates the root `div` node with base CSS classes: `"seui-loading-state"` and `"hide"`.
|
|
1986
1963
|
* - Sets initial text to `options.textLoading`.
|
|
1987
1964
|
* - Applies `role="status"` and `aria-live="polite"`.
|
|
1988
1965
|
* - Stores the options reference and calls {@link Lifecycle.init}.
|
|
@@ -1994,7 +1971,7 @@
|
|
|
1994
1971
|
this.options = options;
|
|
1995
1972
|
this.node = Libs.nodeCreator({
|
|
1996
1973
|
node: "div",
|
|
1997
|
-
classList: ["
|
|
1974
|
+
classList: ["seui-loading-state", "hide"],
|
|
1998
1975
|
textContent: options.textLoading,
|
|
1999
1976
|
role: "status",
|
|
2000
1977
|
ariaLive: "polite",
|
|
@@ -2255,7 +2232,7 @@
|
|
|
2255
2232
|
* Not idempotent. Call {@link disconnect} before calling `connect()` again to avoid duplicates.
|
|
2256
2233
|
*/
|
|
2257
2234
|
connect(element) {
|
|
2258
|
-
if (!(element instanceof
|
|
2235
|
+
if (!(element instanceof HTMLElement)) {
|
|
2259
2236
|
throw new Error("Invalid element");
|
|
2260
2237
|
}
|
|
2261
2238
|
this.element = element;
|
|
@@ -2395,7 +2372,7 @@
|
|
|
2395
2372
|
PopupContainer: {
|
|
2396
2373
|
tag: {
|
|
2397
2374
|
node: "div",
|
|
2398
|
-
classList: "
|
|
2375
|
+
classList: "seui-popup",
|
|
2399
2376
|
style: { maxHeight: options.panelHeight },
|
|
2400
2377
|
},
|
|
2401
2378
|
child: {
|
|
@@ -2404,7 +2381,7 @@
|
|
|
2404
2381
|
tag: {
|
|
2405
2382
|
id: options.SEID_LIST,
|
|
2406
2383
|
node: "div",
|
|
2407
|
-
classList: "
|
|
2384
|
+
classList: "seui-options-container",
|
|
2408
2385
|
role: "listbox",
|
|
2409
2386
|
},
|
|
2410
2387
|
},
|
|
@@ -2688,10 +2665,8 @@
|
|
|
2688
2665
|
if (this.is(LifecycleState.DESTROYED)) {
|
|
2689
2666
|
return;
|
|
2690
2667
|
}
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
this.hideLoadHandle = null;
|
|
2694
|
-
}
|
|
2668
|
+
clearTimeout(this.hideLoadHandle);
|
|
2669
|
+
this.hideLoadHandle = null;
|
|
2695
2670
|
if (this.node && this.scrollListener) {
|
|
2696
2671
|
this.node.removeEventListener("scroll", this.scrollListener);
|
|
2697
2672
|
this.scrollListener = null;
|
|
@@ -2699,19 +2674,14 @@
|
|
|
2699
2674
|
this.emptyState.destroy();
|
|
2700
2675
|
this.loadingState.destroy();
|
|
2701
2676
|
this.optionHandle.destroy();
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
this.
|
|
2707
|
-
try {
|
|
2708
|
-
this.effSvc?.setElement?.(null);
|
|
2709
|
-
}
|
|
2710
|
-
catch (_) { }
|
|
2711
|
-
this.effSvc = null;
|
|
2677
|
+
this.resizeObser?.disconnect?.();
|
|
2678
|
+
this.effSvc?.setElement?.(null);
|
|
2679
|
+
this.modelManager?.skipEvent?.(false);
|
|
2680
|
+
this.recyclerView?.clear?.();
|
|
2681
|
+
this.node?.remove?.();
|
|
2712
2682
|
if (this.node) {
|
|
2713
2683
|
try {
|
|
2714
|
-
const clone = this.node
|
|
2684
|
+
const clone = Libs.nodeCloner(this.node);
|
|
2715
2685
|
this.node.replaceWith(clone);
|
|
2716
2686
|
clone.remove();
|
|
2717
2687
|
}
|
|
@@ -2721,15 +2691,6 @@
|
|
|
2721
2691
|
}
|
|
2722
2692
|
this.node = null;
|
|
2723
2693
|
this.optionsContainer = null;
|
|
2724
|
-
try {
|
|
2725
|
-
this.modelManager?.skipEvent?.(false);
|
|
2726
|
-
this.recyclerView?.clear?.();
|
|
2727
|
-
this.recyclerView = null;
|
|
2728
|
-
this.optionAdapter = null;
|
|
2729
|
-
// Original behavior kept intentionally.
|
|
2730
|
-
this.node.remove();
|
|
2731
|
-
}
|
|
2732
|
-
catch (_) { }
|
|
2733
2694
|
this.modelManager = null;
|
|
2734
2695
|
this.optionHandle = null;
|
|
2735
2696
|
this.emptyState = null;
|
|
@@ -2737,6 +2698,10 @@
|
|
|
2737
2698
|
this.parent = null;
|
|
2738
2699
|
this.options = null;
|
|
2739
2700
|
this.isCreated = false;
|
|
2701
|
+
this.effSvc = null;
|
|
2702
|
+
this.resizeObser = null;
|
|
2703
|
+
this.recyclerView = null;
|
|
2704
|
+
this.optionAdapter = null;
|
|
2740
2705
|
super.destroy();
|
|
2741
2706
|
}
|
|
2742
2707
|
/**
|
|
@@ -2970,8 +2935,8 @@
|
|
|
2970
2935
|
* Initializes DOM, ARIA attributes, and interaction listeners.
|
|
2971
2936
|
*
|
|
2972
2937
|
* DOM structure (conceptually):
|
|
2973
|
-
* - Root: `div.
|
|
2974
|
-
* - Child: `input[type="search"].
|
|
2938
|
+
* - Root: `div.seui-searchbox.hide`
|
|
2939
|
+
* - Child: `input[type="search"].seui-searchbox-input`
|
|
2975
2940
|
*
|
|
2976
2941
|
* Accessibility attributes set on the input:
|
|
2977
2942
|
* - `role="searchbox"`: announces search field semantics
|
|
@@ -3001,14 +2966,14 @@
|
|
|
3001
2966
|
initialize(options) {
|
|
3002
2967
|
this.nodeMounted = Libs.mountNode({
|
|
3003
2968
|
SearchBox: {
|
|
3004
|
-
tag: { node: "div", classList: ["
|
|
2969
|
+
tag: { node: "div", classList: ["seui-searchbox", "hide"] },
|
|
3005
2970
|
child: {
|
|
3006
2971
|
SearchInput: {
|
|
3007
2972
|
tag: {
|
|
3008
2973
|
id: Libs.randomString(),
|
|
3009
2974
|
node: "input",
|
|
3010
2975
|
type: "search",
|
|
3011
|
-
classList: ["
|
|
2976
|
+
classList: ["seui-searchbox-input"],
|
|
3012
2977
|
placeholder: options.placeholder,
|
|
3013
2978
|
role: "searchbox",
|
|
3014
2979
|
ariaControls: options.SEID_LIST,
|
|
@@ -4196,15 +4161,17 @@
|
|
|
4196
4161
|
set selectedNonTrigger(value) {
|
|
4197
4162
|
const input = this.view?.view?.tags?.OptionInput;
|
|
4198
4163
|
const viewEl = this.view?.getView?.();
|
|
4199
|
-
if (input)
|
|
4164
|
+
if (input) {
|
|
4200
4165
|
input.checked = value;
|
|
4166
|
+
}
|
|
4201
4167
|
if (viewEl && this.targetElement) {
|
|
4202
4168
|
viewEl.classList.toggle("checked", !!value);
|
|
4203
4169
|
viewEl.setAttribute("aria-selected", value ? "true" : "false");
|
|
4204
4170
|
this.targetElement.toggleAttribute("selected", !!value);
|
|
4205
4171
|
}
|
|
4206
|
-
if (this.targetElement)
|
|
4172
|
+
if (this.targetElement) {
|
|
4207
4173
|
this.targetElement.selected = value;
|
|
4174
|
+
}
|
|
4208
4175
|
iEvents.callEvent([this, value], ...this.privOnInternalSelected);
|
|
4209
4176
|
}
|
|
4210
4177
|
/**
|
|
@@ -4328,8 +4295,12 @@
|
|
|
4328
4295
|
}
|
|
4329
4296
|
const imageTag = this.view.view.tags.OptionImage;
|
|
4330
4297
|
if (imageTag && this.hasImage) {
|
|
4331
|
-
imageTag.src
|
|
4332
|
-
|
|
4298
|
+
if (imageTag.src != this.imageSrc) {
|
|
4299
|
+
imageTag.src = this.imageSrc;
|
|
4300
|
+
}
|
|
4301
|
+
if (imageTag.alt != this.text) {
|
|
4302
|
+
imageTag.alt = this.text;
|
|
4303
|
+
}
|
|
4333
4304
|
}
|
|
4334
4305
|
if (this.targetElement)
|
|
4335
4306
|
this.selectedNonTrigger = this.targetElement.selected;
|
|
@@ -4461,9 +4432,8 @@
|
|
|
4461
4432
|
this.privModelList.push(currentGroup);
|
|
4462
4433
|
}
|
|
4463
4434
|
else if (data.tagName === "OPTION") {
|
|
4464
|
-
const
|
|
4465
|
-
const
|
|
4466
|
-
const parentGroup = optionEl["__parentGroup"];
|
|
4435
|
+
const optionModel = new OptionModel(this.options, data);
|
|
4436
|
+
const parentGroup = data["__parentGroup"];
|
|
4467
4437
|
if (parentGroup && currentGroup && parentGroup === currentGroup.targetElement) {
|
|
4468
4438
|
currentGroup.addItem(optionModel);
|
|
4469
4439
|
optionModel.group = currentGroup;
|
|
@@ -4874,7 +4844,7 @@
|
|
|
4874
4844
|
* popup/layout logic to recompute geometry.
|
|
4875
4845
|
*
|
|
4876
4846
|
* ### DOM & a11y side effects
|
|
4877
|
-
* - Creates a root `<div>` with classes `
|
|
4847
|
+
* - Creates a root `<div>` with classes `seui-accessorybox hide`.
|
|
4878
4848
|
* - Stops `mouseup` propagation on the root to avoid "outside click" behaviors.
|
|
4879
4849
|
* - Each chip has:
|
|
4880
4850
|
* - a `<span role="button">` with `aria-label`/`title` for screen readers and tooltips,
|
|
@@ -4954,7 +4924,7 @@
|
|
|
4954
4924
|
* Guarded: runs only when state is `NEW`.
|
|
4955
4925
|
*
|
|
4956
4926
|
* Side effects:
|
|
4957
|
-
* - Creates the root node with base classes (`
|
|
4927
|
+
* - Creates the root node with base classes (`seui-accessorybox`, `hide`).
|
|
4958
4928
|
* - Stops `mouseup` propagation to avoid outside-click handlers reacting to chip interactions.
|
|
4959
4929
|
*
|
|
4960
4930
|
* @returns {void}
|
|
@@ -4967,7 +4937,7 @@
|
|
|
4967
4937
|
AccessoryBox: {
|
|
4968
4938
|
tag: {
|
|
4969
4939
|
node: "div",
|
|
4970
|
-
classList: ["
|
|
4940
|
+
classList: ["seui-accessorybox", "hide"],
|
|
4971
4941
|
onmouseup: (evt) => {
|
|
4972
4942
|
// Prevent outside listeners from reacting to chip clicks
|
|
4973
4943
|
evt.stopPropagation();
|
|
@@ -5034,7 +5004,7 @@
|
|
|
5034
5004
|
/**
|
|
5035
5005
|
* Assigns the {@link ModelManager} used to run selection pipelines and mutate selection state.
|
|
5036
5006
|
*
|
|
5037
|
-
* @param {ModelManager<MixedItem, MixedAdapter>
|
|
5007
|
+
* @param {ModelManager<MixedItem, MixedAdapter>} modelManager - Model manager controlling option state.
|
|
5038
5008
|
* @returns {void}
|
|
5039
5009
|
*/
|
|
5040
5010
|
setModelManager(modelManager) {
|
|
@@ -6521,7 +6491,7 @@
|
|
|
6521
6491
|
*
|
|
6522
6492
|
* Creation flow:
|
|
6523
6493
|
* 1. Generates unique group ID (7-character random string).
|
|
6524
|
-
* 2. Creates DOM structure via {@link Libs.
|
|
6494
|
+
* 2. Creates DOM structure via {@link Libs.mountNode}:
|
|
6525
6495
|
* - Root: `<div role="group" aria-labelledby="seui-{id}-header">`
|
|
6526
6496
|
* - Header: `<div role="presentation" id="seui-{id}-header">`
|
|
6527
6497
|
* - Items: `<div role="group">` (nested group for child items)
|
|
@@ -6543,11 +6513,11 @@
|
|
|
6543
6513
|
*/
|
|
6544
6514
|
mount() {
|
|
6545
6515
|
const group_id = Libs.randomString(7);
|
|
6546
|
-
this.view = Libs.
|
|
6516
|
+
this.view = Libs.mountNode({
|
|
6547
6517
|
GroupView: {
|
|
6548
6518
|
tag: {
|
|
6549
6519
|
node: "div",
|
|
6550
|
-
classList: ["
|
|
6520
|
+
classList: ["seui-group"],
|
|
6551
6521
|
role: "group",
|
|
6552
6522
|
ariaLabelledby: `seui-${group_id}-header`,
|
|
6553
6523
|
id: `seui-${group_id}-group`,
|
|
@@ -6556,7 +6526,7 @@
|
|
|
6556
6526
|
GroupHeader: {
|
|
6557
6527
|
tag: {
|
|
6558
6528
|
node: "div",
|
|
6559
|
-
classList: ["
|
|
6529
|
+
classList: ["seui-group-header"],
|
|
6560
6530
|
role: "presentation",
|
|
6561
6531
|
id: `seui-${group_id}-header`,
|
|
6562
6532
|
},
|
|
@@ -6564,7 +6534,7 @@
|
|
|
6564
6534
|
GroupItems: {
|
|
6565
6535
|
tag: {
|
|
6566
6536
|
node: "div",
|
|
6567
|
-
classList: ["
|
|
6537
|
+
classList: ["seui-group-items"],
|
|
6568
6538
|
role: "group",
|
|
6569
6539
|
},
|
|
6570
6540
|
},
|
|
@@ -7009,7 +6979,7 @@
|
|
|
7009
6979
|
* - **OptionImage** (conditional): `<img>` with inline styles (width/height/borderRadius).
|
|
7010
6980
|
* - **OptionLabel**: `<label htmlFor="{inputID}">` with alignment classes.
|
|
7011
6981
|
* - **LabelContent**: `<div>` (content placeholder).
|
|
7012
|
-
* 4. Creates DOM via {@link Libs.
|
|
6982
|
+
* 4. Creates DOM via {@link Libs.mountNode}.
|
|
7013
6983
|
* 5. Appends root to {@link parent}.
|
|
7014
6984
|
* 6. Sets {@link isRendered} to `true` (enables reactive updates).
|
|
7015
6985
|
* 7. Transitions `INITIALIZED → MOUNTED` via `super.mount()`.
|
|
@@ -7027,7 +6997,7 @@
|
|
|
7027
6997
|
* @override
|
|
7028
6998
|
*/
|
|
7029
6999
|
mount() {
|
|
7030
|
-
const viewClass = ["
|
|
7000
|
+
const viewClass = ["seui-option-view"];
|
|
7031
7001
|
const opt_id = Libs.randomString(7);
|
|
7032
7002
|
const inputID = `option_${opt_id}`;
|
|
7033
7003
|
if (this.config.isMultiple)
|
|
@@ -7071,7 +7041,7 @@
|
|
|
7071
7041
|
},
|
|
7072
7042
|
},
|
|
7073
7043
|
};
|
|
7074
|
-
this.view = Libs.
|
|
7044
|
+
this.view = Libs.mountNode({
|
|
7075
7045
|
OptionView: {
|
|
7076
7046
|
tag: {
|
|
7077
7047
|
node: "div",
|
|
@@ -8119,15 +8089,15 @@
|
|
|
8119
8089
|
return;
|
|
8120
8090
|
this.viewElement.replaceChildren();
|
|
8121
8091
|
const nodeMounted = Libs.mountNode({
|
|
8122
|
-
PadTop: { tag: { node: "div", classList: "
|
|
8123
|
-
ItemsHost: { tag: { node: "div", classList: "
|
|
8124
|
-
PadBottom: { tag: { node: "div", classList: "
|
|
8092
|
+
PadTop: { tag: { node: "div", classList: "seui-virtual-pad-top" } },
|
|
8093
|
+
ItemsHost: { tag: { node: "div", classList: "seui-virtual-items" } },
|
|
8094
|
+
PadBottom: { tag: { node: "div", classList: "seui-virtual-pad-bottom" } },
|
|
8125
8095
|
}, this.viewElement);
|
|
8126
8096
|
this.PadTop = nodeMounted.PadTop;
|
|
8127
8097
|
this.ItemsHost = nodeMounted.ItemsHost;
|
|
8128
8098
|
this.PadBottom = nodeMounted.PadBottom;
|
|
8129
8099
|
this.scrollEl = this.opts.scrollEl
|
|
8130
|
-
?? this.viewElement.closest(".
|
|
8100
|
+
?? this.viewElement.closest(".seui-popup")
|
|
8131
8101
|
?? this.viewElement.parentElement;
|
|
8132
8102
|
if (!this.scrollEl)
|
|
8133
8103
|
throw new Error("VirtualRecyclerView: scrollEl not found");
|
|
@@ -8456,7 +8426,7 @@
|
|
|
8456
8426
|
const now = performance.now();
|
|
8457
8427
|
if (now - this.stickyCacheTick < 16)
|
|
8458
8428
|
return this.stickyCacheVal;
|
|
8459
|
-
const sticky = this.scrollEl.querySelector(".
|
|
8429
|
+
const sticky = this.scrollEl.querySelector(".seui-option-handle:not(.hide)");
|
|
8460
8430
|
this.stickyCacheVal = sticky?.offsetHeight ?? 0;
|
|
8461
8431
|
this.stickyCacheTick = now;
|
|
8462
8432
|
return this.stickyCacheVal;
|
|
@@ -9019,12 +8989,29 @@
|
|
|
9019
8989
|
* @internal
|
|
9020
8990
|
*/
|
|
9021
8991
|
this.isBeforeSearch = false;
|
|
8992
|
+
/**
|
|
8993
|
+
* Tracks whether {@link deInit} has already run.
|
|
8994
|
+
*
|
|
8995
|
+
* This guards teardown work (including plugin lifecycle hooks) from running more than once
|
|
8996
|
+
* when {@link deInit} is called separately before {@link destroy}.
|
|
8997
|
+
*
|
|
8998
|
+
* @internal
|
|
8999
|
+
*/
|
|
9000
|
+
this.hasDeInitialized = false;
|
|
9022
9001
|
/**
|
|
9023
9002
|
* Selective context (global helper / registry).
|
|
9024
9003
|
*
|
|
9025
9004
|
* Used to locate the instance wrapper via `Selective.find(...)` and to close other open instances.
|
|
9026
9005
|
*/
|
|
9027
9006
|
this.Selective = null;
|
|
9007
|
+
/**
|
|
9008
|
+
* Registered plugins for this SelectBox instance.
|
|
9009
|
+
*/
|
|
9010
|
+
this.plugins = [];
|
|
9011
|
+
/**
|
|
9012
|
+
* Cached plugin context for this SelectBox instance.
|
|
9013
|
+
*/
|
|
9014
|
+
this.pluginContext = null;
|
|
9028
9015
|
if (select && Selective)
|
|
9029
9016
|
this.initialize(select, Selective);
|
|
9030
9017
|
}
|
|
@@ -9141,12 +9128,12 @@
|
|
|
9141
9128
|
placeholder.node.id = String(options.SEID_HOLDER ?? "");
|
|
9142
9129
|
const container = Libs.mountNode({
|
|
9143
9130
|
Container: {
|
|
9144
|
-
tag: { node: "div", classList: "
|
|
9131
|
+
tag: { node: "div", classList: "seui-MAIN" },
|
|
9145
9132
|
child: {
|
|
9146
9133
|
ViewPanel: {
|
|
9147
9134
|
tag: {
|
|
9148
9135
|
node: "div",
|
|
9149
|
-
classList: "
|
|
9136
|
+
classList: "seui-view",
|
|
9150
9137
|
tabIndex: 0,
|
|
9151
9138
|
onkeydown: (e) => {
|
|
9152
9139
|
if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
|
|
@@ -9206,6 +9193,20 @@
|
|
|
9206
9193
|
accessoryBox.setModelManager(optionModelManager);
|
|
9207
9194
|
this.setupEventHandlers(select, container, options, searchController, searchbox);
|
|
9208
9195
|
this.setupObservers(selectObserver, datasetObserver, select, optionModelManager);
|
|
9196
|
+
this.plugins = this.Selective?.getPlugins?.() ?? [];
|
|
9197
|
+
if (this.plugins.length) {
|
|
9198
|
+
const resources = optionModelManager.getResources();
|
|
9199
|
+
const pluginContext = {
|
|
9200
|
+
selectBox: this,
|
|
9201
|
+
options,
|
|
9202
|
+
adapter: resources.adapter,
|
|
9203
|
+
recycler: resources.recyclerView,
|
|
9204
|
+
viewTags: container.tags,
|
|
9205
|
+
actions: this.getAction(),
|
|
9206
|
+
};
|
|
9207
|
+
this.pluginContext = pluginContext;
|
|
9208
|
+
this.runPluginHook("init", (plugin) => plugin.init?.(pluginContext));
|
|
9209
|
+
}
|
|
9209
9210
|
// Initial states
|
|
9210
9211
|
this.isDisabled = Libs.string2Boolean(options.disabled);
|
|
9211
9212
|
this.isReadOnly = Libs.string2Boolean(options.readonly);
|
|
@@ -9403,12 +9404,21 @@
|
|
|
9403
9404
|
* preventing memory leaks and unintended background updates.
|
|
9404
9405
|
*/
|
|
9405
9406
|
deInit() {
|
|
9407
|
+
if (this.hasDeInitialized) {
|
|
9408
|
+
return;
|
|
9409
|
+
}
|
|
9406
9410
|
const c = this.container ?? {};
|
|
9407
9411
|
const { selectObserver, datasetObserver } = c;
|
|
9412
|
+
if (this.plugins.length) {
|
|
9413
|
+
this.runPluginHook("destroy", (plugin) => plugin.destroy?.());
|
|
9414
|
+
}
|
|
9415
|
+
this.plugins = [];
|
|
9416
|
+
this.pluginContext = null;
|
|
9408
9417
|
if (selectObserver?.disconnect)
|
|
9409
9418
|
selectObserver.disconnect();
|
|
9410
9419
|
if (datasetObserver?.disconnect)
|
|
9411
9420
|
datasetObserver.disconnect();
|
|
9421
|
+
this.hasDeInitialized = true;
|
|
9412
9422
|
}
|
|
9413
9423
|
/**
|
|
9414
9424
|
* Lifecycle: `destroy` (teardown stage).
|
|
@@ -9722,6 +9732,9 @@
|
|
|
9722
9732
|
if (bindedOptions.multiple)
|
|
9723
9733
|
ViewPanel.setAttribute("aria-multiselectable", "true");
|
|
9724
9734
|
iEvents.callEvent([getInstance()], ...bindedOptions.on.show);
|
|
9735
|
+
if (superThis.pluginContext) {
|
|
9736
|
+
superThis.runPluginHook("onOpen", (plugin) => plugin.onOpen?.(superThis.pluginContext));
|
|
9737
|
+
}
|
|
9725
9738
|
return;
|
|
9726
9739
|
},
|
|
9727
9740
|
close() {
|
|
@@ -9738,6 +9751,9 @@
|
|
|
9738
9751
|
container.searchbox.hide();
|
|
9739
9752
|
container.tags.ViewPanel.setAttribute("aria-expanded", "false");
|
|
9740
9753
|
iEvents.callEvent([getInstance()], ...bindedOptions.on.close);
|
|
9754
|
+
if (superThis.pluginContext) {
|
|
9755
|
+
superThis.runPluginHook("onClose", (plugin) => plugin.onClose?.(superThis.pluginContext));
|
|
9756
|
+
}
|
|
9741
9757
|
return;
|
|
9742
9758
|
},
|
|
9743
9759
|
toggle() {
|
|
@@ -9776,6 +9792,10 @@
|
|
|
9776
9792
|
if (superThis.is(LifecycleState.MOUNTED)) {
|
|
9777
9793
|
superThis.update();
|
|
9778
9794
|
}
|
|
9795
|
+
if (superThis.pluginContext && superThis.optionModelManager) {
|
|
9796
|
+
const resources = superThis.optionModelManager.getResources();
|
|
9797
|
+
superThis.runPluginHook("onChange", (plugin) => plugin.onChange?.(this.value, resources.modelList, resources.adapter, superThis.pluginContext));
|
|
9798
|
+
}
|
|
9779
9799
|
},
|
|
9780
9800
|
refreshMask() {
|
|
9781
9801
|
let mask = bindedOptions.placeholder;
|
|
@@ -9908,6 +9928,27 @@
|
|
|
9908
9928
|
}
|
|
9909
9929
|
return flatOptions;
|
|
9910
9930
|
}
|
|
9931
|
+
/**
|
|
9932
|
+
* Safely runs a hook across all registered plugins.
|
|
9933
|
+
*
|
|
9934
|
+
* Any plugin failure is isolated to prevent breaking the current flow.
|
|
9935
|
+
*
|
|
9936
|
+
* @param hook - Hook name for logging context.
|
|
9937
|
+
* @param runner - Hook invocation handler.
|
|
9938
|
+
* @internal
|
|
9939
|
+
*/
|
|
9940
|
+
runPluginHook(hook, runner) {
|
|
9941
|
+
if (!this.plugins.length)
|
|
9942
|
+
return;
|
|
9943
|
+
this.plugins.forEach((plugin) => {
|
|
9944
|
+
try {
|
|
9945
|
+
runner(plugin);
|
|
9946
|
+
}
|
|
9947
|
+
catch (error) {
|
|
9948
|
+
console.error(`Plugin "${plugin.id}" ${hook} error:`, error);
|
|
9949
|
+
}
|
|
9950
|
+
});
|
|
9951
|
+
}
|
|
9911
9952
|
}
|
|
9912
9953
|
|
|
9913
9954
|
/**
|
|
@@ -10134,6 +10175,15 @@
|
|
|
10134
10175
|
* @private
|
|
10135
10176
|
*/
|
|
10136
10177
|
this.bindedQueries = new Map();
|
|
10178
|
+
/**
|
|
10179
|
+
* Registry of Selective plugins keyed by plugin ID.
|
|
10180
|
+
*
|
|
10181
|
+
* - Managed via {@link registerPlugin}, {@link unregisterPlugin}, and {@link getPlugin}.
|
|
10182
|
+
* - Cleared during {@link destroyAll} after invoking plugin teardown hooks.
|
|
10183
|
+
*
|
|
10184
|
+
* @private
|
|
10185
|
+
*/
|
|
10186
|
+
this.plugins = new Map();
|
|
10137
10187
|
this.init();
|
|
10138
10188
|
}
|
|
10139
10189
|
/**
|
|
@@ -10142,6 +10192,7 @@
|
|
|
10142
10192
|
* Behavior:
|
|
10143
10193
|
* - No-op if not in {@link LifecycleState.NEW} (idempotent guard).
|
|
10144
10194
|
* - Initializes {@link bindedQueries} as empty `Map`.
|
|
10195
|
+
* - Initializes {@link plugins} as empty `Map`.
|
|
10145
10196
|
* - Transitions `NEW → INITIALIZED` via `super.init()`.
|
|
10146
10197
|
*
|
|
10147
10198
|
* Notes:
|
|
@@ -10157,6 +10208,7 @@
|
|
|
10157
10208
|
return;
|
|
10158
10209
|
// Initialize core properties
|
|
10159
10210
|
this.bindedQueries = new Map();
|
|
10211
|
+
this.plugins = new Map();
|
|
10160
10212
|
super.init();
|
|
10161
10213
|
}
|
|
10162
10214
|
/**
|
|
@@ -10324,6 +10376,14 @@
|
|
|
10324
10376
|
}
|
|
10325
10377
|
return response;
|
|
10326
10378
|
}
|
|
10379
|
+
/**
|
|
10380
|
+
* Returns all registered Selective plugins.
|
|
10381
|
+
*
|
|
10382
|
+
* @returns The list of plugins in registration order.
|
|
10383
|
+
*/
|
|
10384
|
+
getPlugins() {
|
|
10385
|
+
return Array.from(this.plugins.values());
|
|
10386
|
+
}
|
|
10327
10387
|
/**
|
|
10328
10388
|
* Activates auto-binding for newly added `<select>` elements.
|
|
10329
10389
|
*
|
|
@@ -10395,14 +10455,51 @@
|
|
|
10395
10455
|
this.update();
|
|
10396
10456
|
}
|
|
10397
10457
|
}
|
|
10458
|
+
/**
|
|
10459
|
+
* Registers a plugin for Selective lifecycle integration.
|
|
10460
|
+
*
|
|
10461
|
+
* @public
|
|
10462
|
+
* @param {SelectivePlugin} plugin - Plugin instance to register.
|
|
10463
|
+
* @returns {void}
|
|
10464
|
+
*/
|
|
10465
|
+
registerPlugin(plugin) {
|
|
10466
|
+
if (!plugin?.id)
|
|
10467
|
+
return;
|
|
10468
|
+
this.plugins.set(plugin.id, plugin);
|
|
10469
|
+
}
|
|
10470
|
+
/**
|
|
10471
|
+
* Unregisters a plugin by ID.
|
|
10472
|
+
*
|
|
10473
|
+
* @public
|
|
10474
|
+
* @param {string} id - Plugin ID to remove.
|
|
10475
|
+
* @returns {void}
|
|
10476
|
+
*/
|
|
10477
|
+
unregisterPlugin(id) {
|
|
10478
|
+
if (!id)
|
|
10479
|
+
return;
|
|
10480
|
+
this.plugins.delete(id);
|
|
10481
|
+
}
|
|
10482
|
+
/**
|
|
10483
|
+
* Retrieves a plugin by ID.
|
|
10484
|
+
*
|
|
10485
|
+
* @public
|
|
10486
|
+
* @param {string} id - Plugin ID to retrieve.
|
|
10487
|
+
* @returns {SelectivePlugin | undefined} Plugin instance if found.
|
|
10488
|
+
*/
|
|
10489
|
+
getPlugin(id) {
|
|
10490
|
+
if (!id)
|
|
10491
|
+
return undefined;
|
|
10492
|
+
return this.plugins.get(id);
|
|
10493
|
+
}
|
|
10398
10494
|
/**
|
|
10399
10495
|
* Destroys all bound Selective instances and releases global resources.
|
|
10400
10496
|
*
|
|
10401
10497
|
* Teardown flow:
|
|
10402
10498
|
* 1. Iterates all registered queries and calls {@link destroyByQuery}.
|
|
10403
10499
|
* 2. Clears {@link bindedQueries} and {@link Libs.getBindedCommand}.
|
|
10404
|
-
* 3.
|
|
10405
|
-
* 4.
|
|
10500
|
+
* 3. Invokes plugin teardown hooks and clears {@link plugins}.
|
|
10501
|
+
* 4. Disconnects {@link EAObserver} (stops auto-binding).
|
|
10502
|
+
* 5. Transitions to {@link LifecycleState.DESTROYED} via `super.destroy()`.
|
|
10406
10503
|
*
|
|
10407
10504
|
* Idempotency:
|
|
10408
10505
|
* - No-op if already {@link LifecycleState.DESTROYED}.
|
|
@@ -10417,7 +10514,13 @@
|
|
|
10417
10514
|
bindedCommands.forEach((query) => this.destroyByQuery(query));
|
|
10418
10515
|
this.bindedQueries.clear();
|
|
10419
10516
|
Libs.getBindedCommand().length = 0;
|
|
10517
|
+
this.plugins.forEach((plugin) => {
|
|
10518
|
+
plugin.destroy?.();
|
|
10519
|
+
plugin.onDestroy?.();
|
|
10520
|
+
});
|
|
10521
|
+
this.plugins.clear();
|
|
10420
10522
|
this.EAObserver?.disconnect();
|
|
10523
|
+
this.plugins.clear();
|
|
10421
10524
|
// Call parent lifecycle destroy
|
|
10422
10525
|
super.destroy();
|
|
10423
10526
|
}
|
|
@@ -10484,12 +10587,8 @@
|
|
|
10484
10587
|
const wasObserving = !!this.EAObserver;
|
|
10485
10588
|
if (wasObserving)
|
|
10486
10589
|
this.EAObserver?.disconnect();
|
|
10487
|
-
|
|
10488
|
-
|
|
10489
|
-
}
|
|
10490
|
-
catch (_) { }
|
|
10491
|
-
const wrapper = bindMap.container?.element ??
|
|
10492
|
-
selectElement.parentElement;
|
|
10590
|
+
bindMap.self?.deInit?.();
|
|
10591
|
+
const wrapper = (bindMap.container?.element) ?? selectElement.parentElement;
|
|
10493
10592
|
selectElement.style.display = "";
|
|
10494
10593
|
selectElement.style.visibility = "";
|
|
10495
10594
|
selectElement.disabled = false;
|
|
@@ -10706,13 +10805,15 @@
|
|
|
10706
10805
|
if (typeof globalThis.GLOBAL_SEUI == "undefined") {
|
|
10707
10806
|
const SECLASS = new Selective();
|
|
10708
10807
|
globalThis.GLOBAL_SEUI = {
|
|
10709
|
-
version: "1.2.
|
|
10808
|
+
version: "1.2.6",
|
|
10710
10809
|
name: "SelectiveUI",
|
|
10711
10810
|
bind: SECLASS.bind.bind(SECLASS),
|
|
10712
10811
|
find: SECLASS.find.bind(SECLASS),
|
|
10713
10812
|
destroy: SECLASS.destroy.bind(SECLASS),
|
|
10714
10813
|
effector: Effector.bind(Effector),
|
|
10715
|
-
rebind: SECLASS.rebind.bind(SECLASS)
|
|
10814
|
+
rebind: SECLASS.rebind.bind(SECLASS),
|
|
10815
|
+
registerPlugin: SECLASS.registerPlugin.bind(SECLASS),
|
|
10816
|
+
unregisterPlugin: SECLASS.unregisterPlugin.bind(SECLASS)
|
|
10716
10817
|
};
|
|
10717
10818
|
let domInitialized = false;
|
|
10718
10819
|
function init() {
|
|
@@ -10737,7 +10838,7 @@
|
|
|
10737
10838
|
init();
|
|
10738
10839
|
}
|
|
10739
10840
|
}
|
|
10740
|
-
console.log(`[${"SelectiveUI"}] v${"1.2.
|
|
10841
|
+
console.log(`[${"SelectiveUI"}] v${"1.2.6"} loaded successfully`);
|
|
10741
10842
|
}
|
|
10742
10843
|
else {
|
|
10743
10844
|
console.warn(`[${globalThis.GLOBAL_SEUI.name}] Already loaded (v${globalThis.GLOBAL_SEUI.version}). ` +
|
|
@@ -10827,6 +10928,22 @@
|
|
|
10827
10928
|
function effector(element) {
|
|
10828
10929
|
return globalThis.GLOBAL_SEUI.effector(element);
|
|
10829
10930
|
}
|
|
10931
|
+
/**
|
|
10932
|
+
* Register a Selective plugin implementation.
|
|
10933
|
+
*
|
|
10934
|
+
* @param plugin - Plugin instance to register.
|
|
10935
|
+
*/
|
|
10936
|
+
function registerPlugin(plugin) {
|
|
10937
|
+
globalThis.GLOBAL_SEUI.registerPlugin(plugin);
|
|
10938
|
+
}
|
|
10939
|
+
/**
|
|
10940
|
+
* Unregister a Selective plugin implementation by id.
|
|
10941
|
+
*
|
|
10942
|
+
* @param id - Plugin id to remove.
|
|
10943
|
+
*/
|
|
10944
|
+
function unregisterPlugin(id) {
|
|
10945
|
+
globalThis.GLOBAL_SEUI.unregisterPlugin(id);
|
|
10946
|
+
}
|
|
10830
10947
|
|
|
10831
10948
|
exports.bind = bind;
|
|
10832
10949
|
exports.destroy = destroy;
|
|
@@ -10834,6 +10951,8 @@
|
|
|
10834
10951
|
exports.find = find;
|
|
10835
10952
|
exports.name = name;
|
|
10836
10953
|
exports.rebind = rebind;
|
|
10954
|
+
exports.registerPlugin = registerPlugin;
|
|
10955
|
+
exports.unregisterPlugin = unregisterPlugin;
|
|
10837
10956
|
exports.version = version;
|
|
10838
10957
|
|
|
10839
10958
|
}));
|