selective-ui 1.3.1 → 1.4.1
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/selective-ui.css +0 -6
- package/dist/selective-ui.css.map +1 -1
- package/dist/selective-ui.esm.js +271 -559
- 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 +273 -561
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +12 -12
- package/src/ts/adapter/mixed-adapter.ts +147 -68
- package/src/ts/components/accessorybox.ts +14 -11
- package/src/ts/components/directive.ts +1 -1
- package/src/ts/components/option-handle.ts +12 -9
- package/src/ts/components/placeholder.ts +5 -5
- package/src/ts/components/popup/empty-state.ts +5 -5
- package/src/ts/components/popup/loading-state.ts +5 -5
- package/src/ts/components/popup/popup.ts +138 -76
- package/src/ts/components/searchbox.ts +17 -13
- package/src/ts/components/selectbox.ts +258 -83
- package/src/ts/core/base/adapter.ts +39 -14
- package/src/ts/core/base/fenwick.ts +3 -2
- package/src/ts/core/base/lifecycle.ts +14 -4
- package/src/ts/core/base/model.ts +17 -15
- package/src/ts/core/base/recyclerview.ts +7 -5
- package/src/ts/core/base/view.ts +10 -5
- package/src/ts/core/base/virtual-recyclerview.ts +89 -37
- package/src/ts/core/model-manager.ts +48 -21
- package/src/ts/core/search-controller.ts +174 -56
- package/src/ts/global.ts +5 -8
- package/src/ts/index.ts +2 -2
- package/src/ts/models/group-model.ts +33 -8
- package/src/ts/models/option-model.ts +60 -19
- package/src/ts/services/dataset-observer.ts +6 -3
- package/src/ts/services/ea-observer.ts +1 -1
- package/src/ts/services/effector.ts +22 -12
- package/src/ts/services/refresher.ts +7 -3
- package/src/ts/services/resize-observer.ts +24 -11
- package/src/ts/services/select-observer.ts +2 -2
- package/src/ts/types/components/popup.type.ts +18 -1
- package/src/ts/types/components/searchbox.type.ts +43 -30
- package/src/ts/types/components/state.box.type.ts +1 -1
- package/src/ts/types/core/base/adapter.type.ts +13 -5
- package/src/ts/types/core/base/lifecycle.type.ts +1 -2
- package/src/ts/types/core/base/model.type.ts +3 -3
- package/src/ts/types/core/base/recyclerview.type.ts +7 -5
- package/src/ts/types/core/base/view.type.ts +6 -6
- package/src/ts/types/core/base/virtual-recyclerview.type.ts +45 -46
- package/src/ts/types/core/search-controller.type.ts +18 -2
- package/src/ts/types/css.d.ts +1 -0
- package/src/ts/types/plugins/plugin.type.ts +2 -2
- package/src/ts/types/services/effector.type.ts +25 -25
- package/src/ts/types/services/resize-observer.type.ts +23 -12
- package/src/ts/types/utils/callback-scheduler.type.ts +2 -2
- package/src/ts/types/utils/ievents.type.ts +1 -1
- package/src/ts/types/utils/istorage.type.ts +62 -60
- package/src/ts/types/utils/libs.type.ts +19 -17
- package/src/ts/types/utils/selective.type.ts +6 -3
- package/src/ts/types/views/view.group.type.ts +9 -5
- package/src/ts/types/views/view.option.type.ts +39 -17
- package/src/ts/utils/callback-scheduler.ts +12 -7
- package/src/ts/utils/ievents.ts +12 -5
- package/src/ts/utils/istorage.ts +5 -3
- package/src/ts/utils/libs.ts +122 -43
- package/src/ts/utils/selective.ts +15 -8
- package/src/ts/views/group-view.ts +11 -9
- package/src/ts/views/option-view.ts +37 -18
package/dist/selective-ui.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Selective UI v1.
|
|
1
|
+
/*! Selective UI v1.4.1 | 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) :
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
*
|
|
154
154
|
* @public
|
|
155
155
|
* @param {TimerKey} key - Group identifier for callbacks.
|
|
156
|
-
* @param {(payload
|
|
156
|
+
* @param {(payload?: any[]) => void} callback - Function to execute after debounce timeout.
|
|
157
157
|
* @param {TimerOptions} [options={}] - Scheduling options (`debounce`, `once`).
|
|
158
158
|
* @returns {void}
|
|
159
159
|
*/
|
|
@@ -241,7 +241,8 @@
|
|
|
241
241
|
await resp;
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
|
-
catch {
|
|
244
|
+
catch {
|
|
245
|
+
}
|
|
245
246
|
finally {
|
|
246
247
|
if (entry.once) {
|
|
247
248
|
executes[i] = undefined;
|
|
@@ -374,9 +375,11 @@
|
|
|
374
375
|
* @param {boolean} systemNodeCreate - If true, do not clone; use original node.
|
|
375
376
|
* @returns {HTMLElement} - The processed element.
|
|
376
377
|
*/
|
|
377
|
-
static nodeCloner(node = document.documentElement, _nodeOption
|
|
378
|
+
static nodeCloner(node = document.documentElement, _nodeOption, systemNodeCreate = false) {
|
|
378
379
|
const nodeOption = { ...(_nodeOption ?? {}) };
|
|
379
|
-
const element_creation = systemNodeCreate
|
|
380
|
+
const element_creation = systemNodeCreate
|
|
381
|
+
? node
|
|
382
|
+
: node.cloneNode(true);
|
|
380
383
|
const classList = nodeOption.classList;
|
|
381
384
|
if (typeof classList === "string") {
|
|
382
385
|
element_creation.classList.add(classList);
|
|
@@ -448,11 +451,13 @@
|
|
|
448
451
|
* @param {TTags|Object} [recursiveTemp={}] - Accumulator for tag references.
|
|
449
452
|
* @returns {TTags} - Tag map or the final mount result.
|
|
450
453
|
*/
|
|
451
|
-
static mountNode(rawObj, parentE
|
|
454
|
+
static mountNode(rawObj, parentE, isPrepend = false, isRecusive = false, recursiveTemp = {}) {
|
|
452
455
|
let view = null;
|
|
453
456
|
for (const key in rawObj) {
|
|
454
457
|
const singleObj = rawObj[key];
|
|
455
|
-
const tag = singleObj?.tag?.tagName
|
|
458
|
+
const tag = singleObj?.tag?.tagName
|
|
459
|
+
? singleObj.tag
|
|
460
|
+
: this.nodeCreator(singleObj.tag);
|
|
456
461
|
recursiveTemp[key] = tag;
|
|
457
462
|
if (singleObj?.child)
|
|
458
463
|
this.mountNode(singleObj.child, tag, false, false, recursiveTemp);
|
|
@@ -669,7 +674,8 @@
|
|
|
669
674
|
n.removeAttribute(name);
|
|
670
675
|
return;
|
|
671
676
|
}
|
|
672
|
-
if (/^(href|src|xlink:href)$/i.test(name) &&
|
|
677
|
+
if (/^(href|src|xlink:href)$/i.test(name) &&
|
|
678
|
+
/^javascript:/i.test(value)) {
|
|
673
679
|
n.removeAttribute(name);
|
|
674
680
|
}
|
|
675
681
|
}
|
|
@@ -699,7 +705,10 @@
|
|
|
699
705
|
static string2normalize(str) {
|
|
700
706
|
if (str == null)
|
|
701
707
|
return "";
|
|
702
|
-
const s = String(str)
|
|
708
|
+
const s = String(str)
|
|
709
|
+
.toLowerCase()
|
|
710
|
+
.normalize("NFD")
|
|
711
|
+
.replace(/[\u0300-\u036f]/g, "");
|
|
703
712
|
return s.replace(/đ/g, "d").replace(/Đ/g, "d");
|
|
704
713
|
}
|
|
705
714
|
/**
|
|
@@ -741,7 +750,8 @@
|
|
|
741
750
|
*/
|
|
742
751
|
static IsIOS() {
|
|
743
752
|
const ua = navigator.userAgent;
|
|
744
|
-
return /iP(hone|ad|od)/.test(ua) ||
|
|
753
|
+
return (/iP(hone|ad|od)/.test(ua) ||
|
|
754
|
+
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1));
|
|
745
755
|
}
|
|
746
756
|
/**
|
|
747
757
|
* Converts an arbitrary CSS size value into pixel units by measuring a temporary element.
|
|
@@ -763,14 +773,16 @@
|
|
|
763
773
|
if (v.endsWith("rem"))
|
|
764
774
|
return fs * parseFloat(v) + "px";
|
|
765
775
|
// fallback: DOM measure
|
|
766
|
-
const el = this.nodeCreator({
|
|
776
|
+
const el = this.nodeCreator({
|
|
777
|
+
node: "div",
|
|
778
|
+
style: { height: v, opacity: "0" },
|
|
779
|
+
});
|
|
767
780
|
document.body.appendChild(el);
|
|
768
781
|
const px = el.offsetHeight + "px";
|
|
769
782
|
el.remove();
|
|
770
783
|
return px;
|
|
771
784
|
}
|
|
772
785
|
}
|
|
773
|
-
Libs._iStorage = null;
|
|
774
786
|
/**
|
|
775
787
|
* Schedules and batches function executions keyed by name, with debounced timers.
|
|
776
788
|
* Provides setExecute(), clearExecute(), and run() to manage deferred callbacks.
|
|
@@ -1297,25 +1309,6 @@
|
|
|
1297
1309
|
*/
|
|
1298
1310
|
constructor(options) {
|
|
1299
1311
|
super();
|
|
1300
|
-
/**
|
|
1301
|
-
* Root DOM element for the placeholder.
|
|
1302
|
-
*
|
|
1303
|
-
* Created during {@link initialize}. Removed from the DOM during {@link destroy}.
|
|
1304
|
-
* `null` before initialization and after destruction.
|
|
1305
|
-
*/
|
|
1306
|
-
this.node = null;
|
|
1307
|
-
/**
|
|
1308
|
-
* Configuration snapshot used to render and optionally persist placeholder content.
|
|
1309
|
-
*
|
|
1310
|
-
* Key fields used by this component:
|
|
1311
|
-
* - `placeholder`: initial/current placeholder text/markup
|
|
1312
|
-
* - `allowHtml`: controls whether HTML is rendered or stripped
|
|
1313
|
-
*
|
|
1314
|
-
* Cleared during {@link destroy}.
|
|
1315
|
-
*
|
|
1316
|
-
* @internal
|
|
1317
|
-
*/
|
|
1318
|
-
this.options = null;
|
|
1319
1312
|
if (options)
|
|
1320
1313
|
this.initialize(options);
|
|
1321
1314
|
}
|
|
@@ -1540,34 +1533,8 @@
|
|
|
1540
1533
|
*
|
|
1541
1534
|
* @param options - Feature flags and labels for the two actions.
|
|
1542
1535
|
*/
|
|
1543
|
-
constructor(options
|
|
1536
|
+
constructor(options) {
|
|
1544
1537
|
super();
|
|
1545
|
-
/**
|
|
1546
|
-
* Result returned by {@link Libs.mountNode}.
|
|
1547
|
-
*
|
|
1548
|
-
* Stores the mounted view structure so the component can keep a stable reference
|
|
1549
|
-
* to its created DOM nodes. `null` before {@link initialize}.
|
|
1550
|
-
*
|
|
1551
|
-
* @internal
|
|
1552
|
-
*/
|
|
1553
|
-
this.nodeMounted = null;
|
|
1554
|
-
/**
|
|
1555
|
-
* Root element of this control.
|
|
1556
|
-
*
|
|
1557
|
-
* Created during {@link initialize}. This node is used by {@link show}/{@link hide}
|
|
1558
|
-
* and removed during {@link destroy}.
|
|
1559
|
-
*/
|
|
1560
|
-
this.node = null;
|
|
1561
|
-
/**
|
|
1562
|
-
* Configuration snapshot used for:
|
|
1563
|
-
* - labels (`textSelectAll`, `textDeselectAll`)
|
|
1564
|
-
* - feature flags (`multiple`, `selectall`)
|
|
1565
|
-
*
|
|
1566
|
-
* Treated as read-only after initialization; cleared on {@link destroy}.
|
|
1567
|
-
*
|
|
1568
|
-
* @internal
|
|
1569
|
-
*/
|
|
1570
|
-
this.options = null;
|
|
1571
1538
|
/**
|
|
1572
1539
|
* Callback list invoked when the "Select all" control is activated.
|
|
1573
1540
|
*
|
|
@@ -1655,7 +1622,8 @@
|
|
|
1655
1622
|
available() {
|
|
1656
1623
|
if (!this.options)
|
|
1657
1624
|
return false;
|
|
1658
|
-
return Libs.string2Boolean(this.options.multiple) &&
|
|
1625
|
+
return (Libs.string2Boolean(this.options.multiple) &&
|
|
1626
|
+
Libs.string2Boolean(this.options.selectall));
|
|
1659
1627
|
}
|
|
1660
1628
|
/**
|
|
1661
1629
|
* Re-evaluates visibility and advances the lifecycle update step.
|
|
@@ -1712,7 +1680,7 @@
|
|
|
1712
1680
|
*
|
|
1713
1681
|
* @param action - Callback invoked on activation; ignored when not a function.
|
|
1714
1682
|
*/
|
|
1715
|
-
onSelectAll(action
|
|
1683
|
+
onSelectAll(action) {
|
|
1716
1684
|
if (typeof action === "function") {
|
|
1717
1685
|
this.actionOnSelectAll.push(action);
|
|
1718
1686
|
}
|
|
@@ -1728,7 +1696,7 @@
|
|
|
1728
1696
|
*
|
|
1729
1697
|
* @param action - Callback invoked on activation; ignored when not a function.
|
|
1730
1698
|
*/
|
|
1731
|
-
onDeSelectAll(action
|
|
1699
|
+
onDeSelectAll(action) {
|
|
1732
1700
|
if (typeof action === "function") {
|
|
1733
1701
|
this.actionOnDeSelectAll.push(action);
|
|
1734
1702
|
}
|
|
@@ -1796,26 +1764,10 @@
|
|
|
1796
1764
|
* If `options` are provided, initialization runs immediately (creates {@link node} and
|
|
1797
1765
|
* transitions to `INITIALIZED`).
|
|
1798
1766
|
*
|
|
1799
|
-
* @param {SelectiveOptions
|
|
1767
|
+
* @param {SelectiveOptions} [options=null] - Configuration containing empty state messages.
|
|
1800
1768
|
*/
|
|
1801
|
-
constructor(options
|
|
1769
|
+
constructor(options) {
|
|
1802
1770
|
super();
|
|
1803
|
-
/**
|
|
1804
|
-
* Root DOM element for the empty state UI.
|
|
1805
|
-
*
|
|
1806
|
-
* - Created during {@link initialize}.
|
|
1807
|
-
* - Intended to be appended by the parent container (component does not auto-attach).
|
|
1808
|
-
* - Removed from DOM during {@link destroy}.
|
|
1809
|
-
*/
|
|
1810
|
-
this.node = null;
|
|
1811
|
-
/**
|
|
1812
|
-
* Configuration source for empty state messages.
|
|
1813
|
-
*
|
|
1814
|
-
* Expected to provide at least:
|
|
1815
|
-
* - `textNoData` (for `"nodata"`)
|
|
1816
|
-
* - `textNotFound` (for `"notfound"`)
|
|
1817
|
-
*/
|
|
1818
|
-
this.options = null;
|
|
1819
1771
|
if (options)
|
|
1820
1772
|
this.initialize(options);
|
|
1821
1773
|
}
|
|
@@ -1941,25 +1893,10 @@
|
|
|
1941
1893
|
* If `options` are provided, initialization runs immediately (creates {@link node} and
|
|
1942
1894
|
* transitions to `INITIALIZED`).
|
|
1943
1895
|
*
|
|
1944
|
-
* @param {SelectiveOptions
|
|
1896
|
+
* @param {SelectiveOptions} [options=null] - Configuration containing the loading message text.
|
|
1945
1897
|
*/
|
|
1946
|
-
constructor(options
|
|
1898
|
+
constructor(options) {
|
|
1947
1899
|
super();
|
|
1948
|
-
/**
|
|
1949
|
-
* Root DOM element for the loading state UI.
|
|
1950
|
-
*
|
|
1951
|
-
* - Created during {@link initialize}.
|
|
1952
|
-
* - Intended to be appended by the parent container (component does not auto-attach).
|
|
1953
|
-
* - Removed from DOM during {@link destroy}.
|
|
1954
|
-
*/
|
|
1955
|
-
this.node = null;
|
|
1956
|
-
/**
|
|
1957
|
-
* Configuration source for loading message text.
|
|
1958
|
-
*
|
|
1959
|
-
* Expected to provide:
|
|
1960
|
-
* - `textLoading` (displayed while loading is active)
|
|
1961
|
-
*/
|
|
1962
|
-
this.options = null;
|
|
1963
1900
|
if (options)
|
|
1964
1901
|
this.initialize(options);
|
|
1965
1902
|
}
|
|
@@ -2117,27 +2054,6 @@
|
|
|
2117
2054
|
* It does **not** indicate that observers are currently attached (see {@link connect}).
|
|
2118
2055
|
*/
|
|
2119
2056
|
this.isInit = false;
|
|
2120
|
-
/**
|
|
2121
|
-
* The currently bound DOM element being observed.
|
|
2122
|
-
*
|
|
2123
|
-
* @remarks
|
|
2124
|
-
* Set by {@link connect} and cleared by {@link disconnect}.
|
|
2125
|
-
*/
|
|
2126
|
-
this.element = null;
|
|
2127
|
-
/**
|
|
2128
|
-
* Underlying `ResizeObserver` instance.
|
|
2129
|
-
*
|
|
2130
|
-
* @remarks
|
|
2131
|
-
* Allocated on {@link connect}. Disconnected and nulled on {@link disconnect}.
|
|
2132
|
-
*/
|
|
2133
|
-
this.resizeObserver = null;
|
|
2134
|
-
/**
|
|
2135
|
-
* Underlying `MutationObserver` instance watching `style` and `class` attribute changes.
|
|
2136
|
-
*
|
|
2137
|
-
* @remarks
|
|
2138
|
-
* Allocated on {@link connect}. Disconnected and nulled on {@link disconnect}.
|
|
2139
|
-
*/
|
|
2140
|
-
this.mutationObserver = null;
|
|
2141
2057
|
this.isInit = true;
|
|
2142
2058
|
this.boundUpdateChanged = this.updateChanged.bind(this);
|
|
2143
2059
|
}
|
|
@@ -2183,7 +2099,8 @@
|
|
|
2183
2099
|
return;
|
|
2184
2100
|
}
|
|
2185
2101
|
const rect = el.getBoundingClientRect();
|
|
2186
|
-
const style = typeof window !== "undefined" &&
|
|
2102
|
+
const style = typeof window !== "undefined" &&
|
|
2103
|
+
typeof window.getComputedStyle === "function"
|
|
2187
2104
|
? window.getComputedStyle(el)
|
|
2188
2105
|
: null;
|
|
2189
2106
|
const metrics = {
|
|
@@ -2315,36 +2232,10 @@
|
|
|
2315
2232
|
* @param options - Configuration options (panel sizing, flags, texts, etc.).
|
|
2316
2233
|
* @param modelManager - Model manager that supplies the adapter and recycler view.
|
|
2317
2234
|
*/
|
|
2318
|
-
constructor(select
|
|
2235
|
+
constructor(select, options, modelManager) {
|
|
2319
2236
|
super();
|
|
2320
|
-
/** Active configuration for the popup behavior and text labels */
|
|
2321
|
-
this.options = null;
|
|
2322
2237
|
/** Indicates whether the popup DOM has been attached to the document body at least once */
|
|
2323
2238
|
this.isCreated = false;
|
|
2324
|
-
/** Mixed adapter handling items/models and visibility stats */
|
|
2325
|
-
this.optionAdapter = null;
|
|
2326
|
-
/** Root popup container (the floating panel) */
|
|
2327
|
-
this.node = null;
|
|
2328
|
-
/** Effector service used to measure/animate the popup */
|
|
2329
|
-
this.effSvc = null;
|
|
2330
|
-
/** Resize observer to react to parent panel size changes */
|
|
2331
|
-
this.resizeObser = null;
|
|
2332
|
-
/** Binder map for parent elements (anchors to compute placement from) */
|
|
2333
|
-
this.parent = null;
|
|
2334
|
-
/** Header control exposing "Select All / Deselect All" actions */
|
|
2335
|
-
this.optionHandle = null;
|
|
2336
|
-
/** "Empty / Not found" feedback component */
|
|
2337
|
-
this.emptyState = null;
|
|
2338
|
-
/** Loading indicator component */
|
|
2339
|
-
this.loadingState = null;
|
|
2340
|
-
/** Virtualized recycler view for performant lists */
|
|
2341
|
-
this.recyclerView = null;
|
|
2342
|
-
/** Container that holds the list of options */
|
|
2343
|
-
this.optionsContainer = null;
|
|
2344
|
-
/** Scroll handler used by infinite scroll */
|
|
2345
|
-
this.scrollListener = null;
|
|
2346
|
-
/** Handle to defer hiding the loading indicator */
|
|
2347
|
-
this.hideLoadHandle = null;
|
|
2348
2239
|
/** Default virtual scroll configuration (tuned for typical option heights) */
|
|
2349
2240
|
this.virtualScrollConfig = {
|
|
2350
2241
|
/** Estimated item height in pixels (improves initial layout calculation) */
|
|
@@ -2352,7 +2243,7 @@
|
|
|
2352
2243
|
/** Number of extra items to render above/below the viewport */
|
|
2353
2244
|
overscan: 8,
|
|
2354
2245
|
/** Whether the list contains items with dynamic (non-uniform) heights */
|
|
2355
|
-
dynamicHeights: true
|
|
2246
|
+
dynamicHeights: true,
|
|
2356
2247
|
};
|
|
2357
2248
|
this.modelManager = modelManager;
|
|
2358
2249
|
if (select && options) {
|
|
@@ -2399,7 +2290,8 @@
|
|
|
2399
2290
|
},
|
|
2400
2291
|
}, null);
|
|
2401
2292
|
this.node = nodeMounted.view;
|
|
2402
|
-
this.optionsContainer = nodeMounted.tags
|
|
2293
|
+
this.optionsContainer = nodeMounted.tags
|
|
2294
|
+
.OptionsContainer;
|
|
2403
2295
|
this.parent = Libs.getBinderMap(select);
|
|
2404
2296
|
this.options = options;
|
|
2405
2297
|
this.init();
|
|
@@ -2408,7 +2300,7 @@
|
|
|
2408
2300
|
scrollEl: this.node,
|
|
2409
2301
|
estimateItemHeight: this.virtualScrollConfig.estimateItemHeight,
|
|
2410
2302
|
overscan: this.virtualScrollConfig.overscan,
|
|
2411
|
-
dynamicHeights: this.virtualScrollConfig.dynamicHeights
|
|
2303
|
+
dynamicHeights: this.virtualScrollConfig.dynamicHeights,
|
|
2412
2304
|
}
|
|
2413
2305
|
: {};
|
|
2414
2306
|
// Load ModelManager resources into the list container
|
|
@@ -2435,7 +2327,11 @@
|
|
|
2435
2327
|
* - Triggers a resize to accommodate layout changes
|
|
2436
2328
|
*/
|
|
2437
2329
|
async showLoading() {
|
|
2438
|
-
if (!this.options ||
|
|
2330
|
+
if (!this.options ||
|
|
2331
|
+
!this.loadingState ||
|
|
2332
|
+
!this.optionHandle ||
|
|
2333
|
+
!this.optionAdapter ||
|
|
2334
|
+
!this.modelManager)
|
|
2439
2335
|
return;
|
|
2440
2336
|
if (this.hideLoadHandle)
|
|
2441
2337
|
clearTimeout(this.hideLoadHandle);
|
|
@@ -2453,7 +2349,10 @@
|
|
|
2453
2349
|
* Debounce: Uses `animationtime` as a short delay before hiding the loading indicator.
|
|
2454
2350
|
*/
|
|
2455
2351
|
async hideLoading() {
|
|
2456
|
-
if (!this.options ||
|
|
2352
|
+
if (!this.options ||
|
|
2353
|
+
!this.loadingState ||
|
|
2354
|
+
!this.optionAdapter ||
|
|
2355
|
+
!this.modelManager)
|
|
2457
2356
|
return;
|
|
2458
2357
|
if (this.hideLoadHandle)
|
|
2459
2358
|
clearTimeout(this.hideLoadHandle);
|
|
@@ -2494,7 +2393,10 @@
|
|
|
2494
2393
|
* @param stats - Optionally provide precomputed visibility stats.
|
|
2495
2394
|
*/
|
|
2496
2395
|
updateEmptyState(stats) {
|
|
2497
|
-
if (!this.optionAdapter ||
|
|
2396
|
+
if (!this.optionAdapter ||
|
|
2397
|
+
!this.emptyState ||
|
|
2398
|
+
!this.optionHandle ||
|
|
2399
|
+
!this.optionsContainer)
|
|
2498
2400
|
return;
|
|
2499
2401
|
const s = stats ?? this.optionAdapter.getVisibilityStats();
|
|
2500
2402
|
if (s.isEmpty) {
|
|
@@ -2578,8 +2480,12 @@
|
|
|
2578
2480
|
* @param callback - Optional callback invoked when the opening animation completes.
|
|
2579
2481
|
* @param isShowEmptyState - If true, applies the empty/not-found state before animation.
|
|
2580
2482
|
*/
|
|
2581
|
-
open(callback
|
|
2582
|
-
if (!this.node ||
|
|
2483
|
+
open(callback, isShowEmptyState) {
|
|
2484
|
+
if (!this.node ||
|
|
2485
|
+
!this.options ||
|
|
2486
|
+
!this.optionHandle ||
|
|
2487
|
+
!this.parent ||
|
|
2488
|
+
!this.effSvc)
|
|
2583
2489
|
return;
|
|
2584
2490
|
// Ensure one-time initialization
|
|
2585
2491
|
this.load();
|
|
@@ -2628,8 +2534,11 @@
|
|
|
2628
2534
|
*
|
|
2629
2535
|
* @param callback - Optional callback invoked when the closing animation completes.
|
|
2630
2536
|
*/
|
|
2631
|
-
close(callback
|
|
2632
|
-
if (!this.isCreated ||
|
|
2537
|
+
close(callback) {
|
|
2538
|
+
if (!this.isCreated ||
|
|
2539
|
+
!this.options ||
|
|
2540
|
+
!this.resizeObser ||
|
|
2541
|
+
!this.effSvc)
|
|
2633
2542
|
return;
|
|
2634
2543
|
const rv = this.recyclerView;
|
|
2635
2544
|
rv?.suspend?.();
|
|
@@ -2788,7 +2697,9 @@
|
|
|
2788
2697
|
let maxHeight = configMaxHeight;
|
|
2789
2698
|
let realHeight = Math.min(contentHeight, maxHeight);
|
|
2790
2699
|
const heightOri = spaceBelow - safeMargin;
|
|
2791
|
-
if (realHeight >= configMinHeight
|
|
2700
|
+
if (realHeight >= configMinHeight
|
|
2701
|
+
? heightOri >= configMinHeight
|
|
2702
|
+
: heightOri >= realHeight) {
|
|
2792
2703
|
position = "bottom";
|
|
2793
2704
|
maxHeight = Math.min(spaceBelow - safeMargin, configMaxHeight);
|
|
2794
2705
|
}
|
|
@@ -2888,78 +2799,8 @@
|
|
|
2888
2799
|
*
|
|
2889
2800
|
* @param options - Configuration such as placeholder, accessibility IDs, and flags.
|
|
2890
2801
|
*/
|
|
2891
|
-
constructor(options
|
|
2802
|
+
constructor(options) {
|
|
2892
2803
|
super();
|
|
2893
|
-
/**
|
|
2894
|
-
* The mount result returned by {@link Libs.mountNode}.
|
|
2895
|
-
*
|
|
2896
|
-
* Provides typed access to created DOM tags (e.g., `SearchInput`) and the root view.
|
|
2897
|
-
* `null` before initialization and after destruction.
|
|
2898
|
-
*
|
|
2899
|
-
* @internal
|
|
2900
|
-
*/
|
|
2901
|
-
this.nodeMounted = null;
|
|
2902
|
-
/**
|
|
2903
|
-
* Root container node of this component.
|
|
2904
|
-
*
|
|
2905
|
-
* Created during {@link initialize} and removed during {@link destroy}.
|
|
2906
|
-
* Visibility is controlled by adding/removing the `hide` class.
|
|
2907
|
-
*/
|
|
2908
|
-
this.node = null;
|
|
2909
|
-
/**
|
|
2910
|
-
* The `<input type="search">` element used to capture user queries.
|
|
2911
|
-
*
|
|
2912
|
-
* Cached for imperative operations (focus, placeholder updates, ARIA updates).
|
|
2913
|
-
* `null` before initialization and after destruction.
|
|
2914
|
-
*
|
|
2915
|
-
* @internal
|
|
2916
|
-
*/
|
|
2917
|
-
this.SearchInput = null;
|
|
2918
|
-
/**
|
|
2919
|
-
* External "search changed" hook.
|
|
2920
|
-
*
|
|
2921
|
-
* Invoked when the user edits text (via the `input` event) and the edit is not
|
|
2922
|
-
* part of a handled control-key sequence (e.g., ArrowUp/Down/Tab/Enter/Escape).
|
|
2923
|
-
*
|
|
2924
|
-
* Ownership:
|
|
2925
|
-
* - Implementations typically filter adapter/model state and refresh the list.
|
|
2926
|
-
*/
|
|
2927
|
-
this.onSearch = null;
|
|
2928
|
-
/**
|
|
2929
|
-
* Options snapshot used for behavior toggles and attributes.
|
|
2930
|
-
*
|
|
2931
|
-
* Key fields typically consumed here:
|
|
2932
|
-
* - `placeholder`: initial placeholder string
|
|
2933
|
-
* - `searchable`: toggles readOnly + focus behavior on {@link show}
|
|
2934
|
-
* - `SEID_LIST`: used as `aria-controls` value to bind to listbox container
|
|
2935
|
-
*
|
|
2936
|
-
* Cleared during {@link destroy}.
|
|
2937
|
-
*
|
|
2938
|
-
* @internal
|
|
2939
|
-
*/
|
|
2940
|
-
this.options = null;
|
|
2941
|
-
/**
|
|
2942
|
-
* External navigation hook for list traversal.
|
|
2943
|
-
*
|
|
2944
|
-
* Called with:
|
|
2945
|
-
* - `+1` for forward (ArrowDown / Tab)
|
|
2946
|
-
* - `-1` for backward (ArrowUp)
|
|
2947
|
-
*
|
|
2948
|
-
* Typical consumers update highlight/active option in Adapter/RecyclerView.
|
|
2949
|
-
*/
|
|
2950
|
-
this.onNavigate = null;
|
|
2951
|
-
/**
|
|
2952
|
-
* External "commit" hook (Enter key).
|
|
2953
|
-
*
|
|
2954
|
-
* Typical consumers confirm selection of the highlighted option or submit the current state.
|
|
2955
|
-
*/
|
|
2956
|
-
this.onEnter = null;
|
|
2957
|
-
/**
|
|
2958
|
-
* External "cancel" hook (Escape key).
|
|
2959
|
-
*
|
|
2960
|
-
* Typical consumers close the popup, clear highlight, or reset interaction mode.
|
|
2961
|
-
*/
|
|
2962
|
-
this.onEsc = null;
|
|
2963
2804
|
this.options = options;
|
|
2964
2805
|
if (options)
|
|
2965
2806
|
this.initialize(options);
|
|
@@ -3225,17 +3066,7 @@
|
|
|
3225
3066
|
*
|
|
3226
3067
|
* @param query - CSS selector or element to control. When `null`, instance starts unbound.
|
|
3227
3068
|
*/
|
|
3228
|
-
constructor(query
|
|
3229
|
-
/**
|
|
3230
|
-
* Timeout used to finalize expand/collapse/swipe animations.
|
|
3231
|
-
* Cleared by {@link cancel}.
|
|
3232
|
-
*/
|
|
3233
|
-
this.timeOut = null;
|
|
3234
|
-
/**
|
|
3235
|
-
* Timeout used to clear transitions after resize in non-animated scenarios.
|
|
3236
|
-
* Cleared by {@link cancel}.
|
|
3237
|
-
*/
|
|
3238
|
-
this.resizeTimeout = null;
|
|
3069
|
+
constructor(query) {
|
|
3239
3070
|
/**
|
|
3240
3071
|
* Internal animation flag set while a timed animation is in-flight.
|
|
3241
3072
|
*
|
|
@@ -3419,7 +3250,9 @@
|
|
|
3419
3250
|
const { duration = 200, onComplete } = config;
|
|
3420
3251
|
const currentHeight = this.element.offsetHeight;
|
|
3421
3252
|
const currentTop = this.element.offsetTop;
|
|
3422
|
-
const position = this.element.classList.contains("position-top")
|
|
3253
|
+
const position = this.element.classList.contains("position-top")
|
|
3254
|
+
? "top"
|
|
3255
|
+
: "bottom";
|
|
3423
3256
|
const isScrollable = this.element.scrollHeight - this.element.offsetHeight > 0;
|
|
3424
3257
|
const finalTop = position === "top" ? currentTop + currentHeight : currentTop;
|
|
3425
3258
|
requestAnimationFrame(() => {
|
|
@@ -3557,7 +3390,9 @@
|
|
|
3557
3390
|
return this;
|
|
3558
3391
|
this.cancel();
|
|
3559
3392
|
const { duration = 200, width, left, top, maxHeight, realHeight, position = "bottom", animate = true, onComplete, } = config;
|
|
3560
|
-
const currentPosition = this.element.classList.contains("position-top")
|
|
3393
|
+
const currentPosition = this.element.classList.contains("position-top")
|
|
3394
|
+
? "top"
|
|
3395
|
+
: "bottom";
|
|
3561
3396
|
const isPositionChanged = currentPosition !== position;
|
|
3562
3397
|
const isScrollable = this.element.scrollHeight > maxHeight;
|
|
3563
3398
|
this.element.classList.toggle("position-top", position === "top");
|
|
@@ -3673,25 +3508,11 @@
|
|
|
3673
3508
|
* - Calls {@link Lifecycle.init} immediately (`NEW → INITIALIZED`).
|
|
3674
3509
|
*
|
|
3675
3510
|
* @param {TOptions} options - Configuration options for the model.
|
|
3676
|
-
* @param {TTarget
|
|
3677
|
-
* @param {TView
|
|
3511
|
+
* @param {TTarget} [targetElement=null] - Optional DOM element to bind.
|
|
3512
|
+
* @param {TView} [view=null] - Optional view responsible for rendering this model.
|
|
3678
3513
|
*/
|
|
3679
|
-
constructor(options, targetElement
|
|
3514
|
+
constructor(options, targetElement, view) {
|
|
3680
3515
|
super();
|
|
3681
|
-
/**
|
|
3682
|
-
* The currently bound target DOM element.
|
|
3683
|
-
*
|
|
3684
|
-
* This element typically represents the source-of-truth node in the host DOM (e.g., a native `<option>`).
|
|
3685
|
-
* May be replaced via {@link updateTarget} during reconciliation.
|
|
3686
|
-
*/
|
|
3687
|
-
this.targetElement = null;
|
|
3688
|
-
/**
|
|
3689
|
-
* View instance responsible for rendering this model.
|
|
3690
|
-
*
|
|
3691
|
-
* Ownership: this model will destroy the view on {@link destroy}.
|
|
3692
|
-
* The view may be attached/assigned by external orchestrators (Adapter/RecyclerView) after construction.
|
|
3693
|
-
*/
|
|
3694
|
-
this.view = null;
|
|
3695
3516
|
/**
|
|
3696
3517
|
* Position index used by list infrastructure for ordering/tracking.
|
|
3697
3518
|
* Semantics are library-specific (e.g., top-level index or adapter position).
|
|
@@ -3723,7 +3544,7 @@
|
|
|
3723
3544
|
* - Assigns {@link targetElement}.
|
|
3724
3545
|
* - Calls {@link Lifecycle.update} (guarded by lifecycle state).
|
|
3725
3546
|
*
|
|
3726
|
-
* @param {TTarget
|
|
3547
|
+
* @param {TTarget} targetElement - The new DOM element to associate with this model.
|
|
3727
3548
|
* @returns {void}
|
|
3728
3549
|
*/
|
|
3729
3550
|
updateTarget(targetElement) {
|
|
@@ -3923,7 +3744,7 @@
|
|
|
3923
3744
|
if (this.is(LifecycleState.DESTROYED)) {
|
|
3924
3745
|
return;
|
|
3925
3746
|
}
|
|
3926
|
-
this.items.forEach(item => {
|
|
3747
|
+
this.items.forEach((item) => {
|
|
3927
3748
|
item.destroy();
|
|
3928
3749
|
});
|
|
3929
3750
|
this.items = [];
|
|
@@ -4047,10 +3868,10 @@
|
|
|
4047
3868
|
* Creates an option model.
|
|
4048
3869
|
*
|
|
4049
3870
|
* @param {SelectiveOptions} options - Shared configuration for models/views.
|
|
4050
|
-
* @param {HTMLOptionElement
|
|
4051
|
-
* @param {OptionView
|
|
3871
|
+
* @param {HTMLOptionElement} [targetElement=null] - Backing `<option>` element.
|
|
3872
|
+
* @param {OptionView} [view=null] - Optional view used to render this model.
|
|
4052
3873
|
*/
|
|
4053
|
-
constructor(options, targetElement
|
|
3874
|
+
constructor(options, targetElement, view) {
|
|
4054
3875
|
super(options, targetElement, view);
|
|
4055
3876
|
/**
|
|
4056
3877
|
* External selection subscribers (emitted by the {@link selected} setter).
|
|
@@ -4074,11 +3895,6 @@
|
|
|
4074
3895
|
this._visible = true;
|
|
4075
3896
|
/** Highlight flag used for keyboard navigation / hover. */
|
|
4076
3897
|
this._highlighted = false;
|
|
4077
|
-
/**
|
|
4078
|
-
* Parent group model (if this option belongs to a group).
|
|
4079
|
-
* Assigned by grouping logic (e.g., GroupModel/MixedAdapter).
|
|
4080
|
-
*/
|
|
4081
|
-
this.group = null;
|
|
4082
3898
|
}
|
|
4083
3899
|
/**
|
|
4084
3900
|
* Initializes the model and precomputes the search key.
|
|
@@ -4233,7 +4049,9 @@
|
|
|
4233
4049
|
* @returns {string}
|
|
4234
4050
|
*/
|
|
4235
4051
|
get textContent() {
|
|
4236
|
-
return this.options.allowHtml
|
|
4052
|
+
return this.options.allowHtml
|
|
4053
|
+
? Libs.stripHtml(this.text).trim()
|
|
4054
|
+
: this.text.trim();
|
|
4237
4055
|
}
|
|
4238
4056
|
/**
|
|
4239
4057
|
* Dataset object of the backing `<option>` element.
|
|
@@ -4414,8 +4232,6 @@
|
|
|
4414
4232
|
constructor(options) {
|
|
4415
4233
|
super();
|
|
4416
4234
|
this.privModelList = [];
|
|
4417
|
-
this.privAdapterHandle = null;
|
|
4418
|
-
this.privRecyclerViewHandle = null;
|
|
4419
4235
|
this.options = null;
|
|
4420
4236
|
this.oldPosition = 0;
|
|
4421
4237
|
this.options = options;
|
|
@@ -4467,7 +4283,9 @@
|
|
|
4467
4283
|
else if (data.tagName === "OPTION") {
|
|
4468
4284
|
const optionModel = new OptionModel(this.options, data);
|
|
4469
4285
|
const parentGroup = data["__parentGroup"];
|
|
4470
|
-
if (parentGroup &&
|
|
4286
|
+
if (parentGroup &&
|
|
4287
|
+
currentGroup &&
|
|
4288
|
+
parentGroup === currentGroup.targetElement) {
|
|
4471
4289
|
currentGroup.addItem(optionModel);
|
|
4472
4290
|
optionModel.group = currentGroup;
|
|
4473
4291
|
}
|
|
@@ -4892,44 +4710,10 @@
|
|
|
4892
4710
|
/**
|
|
4893
4711
|
* Creates an AccessoryBox and optionally initializes it with configuration.
|
|
4894
4712
|
*
|
|
4895
|
-
* @param {SelectiveOptions
|
|
4713
|
+
* @param {SelectiveOptions} [options=null] - Configuration controlling placement/visibility and texts.
|
|
4896
4714
|
*/
|
|
4897
|
-
constructor(options
|
|
4715
|
+
constructor(options) {
|
|
4898
4716
|
super();
|
|
4899
|
-
/**
|
|
4900
|
-
* Mounted structure returned by the node mounting helper.
|
|
4901
|
-
* Contains the root element (`view`) and any tag handles (if present).
|
|
4902
|
-
*/
|
|
4903
|
-
this.nodeMounted = null;
|
|
4904
|
-
/**
|
|
4905
|
-
* Root DOM element of the accessory box (hidden by default).
|
|
4906
|
-
* Created during {@link init} and removed during {@link destroy}.
|
|
4907
|
-
*/
|
|
4908
|
-
this.node = null;
|
|
4909
|
-
/**
|
|
4910
|
-
* Component configuration (texts, behavior, placement).
|
|
4911
|
-
* This component reads:
|
|
4912
|
-
* - `accessoryStyle` ("top" or default bottom)
|
|
4913
|
-
* - `accessoryVisible` (enable/disable)
|
|
4914
|
-
* - `multiple` (multi-select mode)
|
|
4915
|
-
* - `textAccessoryDeselect` (a11y label prefix)
|
|
4916
|
-
*/
|
|
4917
|
-
this.options = null;
|
|
4918
|
-
/**
|
|
4919
|
-
* The Select UI mask element used as the positioning reference.
|
|
4920
|
-
* Provided by {@link setRoot}.
|
|
4921
|
-
*/
|
|
4922
|
-
this.selectUIMask = null;
|
|
4923
|
-
/**
|
|
4924
|
-
* Parent container that hosts both the Select UI mask and the accessory box.
|
|
4925
|
-
* Computed from `selectUIMask.parentElement`.
|
|
4926
|
-
*/
|
|
4927
|
-
this.parentMask = null;
|
|
4928
|
-
/**
|
|
4929
|
-
* ModelManager used to run selection pipelines and coordinate state updates.
|
|
4930
|
-
* This component does not own selection state; it delegates to the model layer.
|
|
4931
|
-
*/
|
|
4932
|
-
this.modelManager = null;
|
|
4933
4717
|
/**
|
|
4934
4718
|
* Current selected option models rendered as chips.
|
|
4935
4719
|
* This is a cached snapshot used for show/hide decisions and re-rendering.
|
|
@@ -5237,15 +5021,6 @@
|
|
|
5237
5021
|
*/
|
|
5238
5022
|
constructor(selectElement, modelManager, selectBox) {
|
|
5239
5023
|
super();
|
|
5240
|
-
/**
|
|
5241
|
-
* AJAX configuration; when `null`, {@link search} falls back to local filtering.
|
|
5242
|
-
* @see {@link setAjax}
|
|
5243
|
-
*/
|
|
5244
|
-
this.ajaxConfig = null;
|
|
5245
|
-
/** Abort handle used to cancel an in-flight AJAX request when a newer request starts. */
|
|
5246
|
-
this.abortController = null;
|
|
5247
|
-
/** Optional popup handle used for showing/hiding loading UI during remote operations. */
|
|
5248
|
-
this.popup = null;
|
|
5249
5024
|
/**
|
|
5250
5025
|
* SelectBox handle used by custom data builder functions that require Selective context.
|
|
5251
5026
|
* NOTE: This is a reference; the controller does not own/destroy the SelectBox.
|
|
@@ -5309,7 +5084,11 @@
|
|
|
5309
5084
|
*/
|
|
5310
5085
|
async loadByValues(values) {
|
|
5311
5086
|
if (!this.ajaxConfig) {
|
|
5312
|
-
return {
|
|
5087
|
+
return {
|
|
5088
|
+
success: false,
|
|
5089
|
+
items: [],
|
|
5090
|
+
message: "Ajax not configured",
|
|
5091
|
+
};
|
|
5313
5092
|
}
|
|
5314
5093
|
const valuesArray = Array.isArray(values) ? values : [values];
|
|
5315
5094
|
if (valuesArray.length === 0)
|
|
@@ -5326,7 +5105,7 @@
|
|
|
5326
5105
|
load_by_values: "1",
|
|
5327
5106
|
...(typeof cfg.data === "function"
|
|
5328
5107
|
? cfg.data.bind(this.selectBox.Selective.find(this.selectBox.container.targetElement))("", 0)
|
|
5329
|
-
: cfg.data ?? {}),
|
|
5108
|
+
: (cfg.data ?? {})),
|
|
5330
5109
|
};
|
|
5331
5110
|
}
|
|
5332
5111
|
let response;
|
|
@@ -5336,7 +5115,9 @@
|
|
|
5336
5115
|
response = await fetch(cfg.url, {
|
|
5337
5116
|
method: "POST",
|
|
5338
5117
|
body: formData,
|
|
5339
|
-
headers: {
|
|
5118
|
+
headers: {
|
|
5119
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
5120
|
+
},
|
|
5340
5121
|
});
|
|
5341
5122
|
}
|
|
5342
5123
|
else {
|
|
@@ -5373,7 +5154,7 @@
|
|
|
5373
5154
|
* Configures AJAX settings used for remote searching and pagination.
|
|
5374
5155
|
* Setting `null` disables AJAX mode and causes {@link search} to use local filtering.
|
|
5375
5156
|
*
|
|
5376
|
-
* @param {AjaxConfig
|
|
5157
|
+
* @param {AjaxConfig} config - AJAX configuration (endpoint, method, data builders, keepSelected, ...).
|
|
5377
5158
|
* @returns {void}
|
|
5378
5159
|
*/
|
|
5379
5160
|
setAjax(config) {
|
|
@@ -5563,7 +5344,12 @@
|
|
|
5563
5344
|
payload.selectedValue = selectedValues;
|
|
5564
5345
|
}
|
|
5565
5346
|
else {
|
|
5566
|
-
payload = {
|
|
5347
|
+
payload = {
|
|
5348
|
+
search: keyword,
|
|
5349
|
+
page,
|
|
5350
|
+
selectedValue: selectedValues,
|
|
5351
|
+
...(cfg.data ?? {}),
|
|
5352
|
+
};
|
|
5567
5353
|
}
|
|
5568
5354
|
try {
|
|
5569
5355
|
let response;
|
|
@@ -5573,13 +5359,17 @@
|
|
|
5573
5359
|
response = await fetch(cfg.url, {
|
|
5574
5360
|
method: "POST",
|
|
5575
5361
|
body: formData,
|
|
5576
|
-
headers: {
|
|
5362
|
+
headers: {
|
|
5363
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
5364
|
+
},
|
|
5577
5365
|
signal: this.abortController.signal,
|
|
5578
5366
|
});
|
|
5579
5367
|
}
|
|
5580
5368
|
else {
|
|
5581
5369
|
const params = new URLSearchParams(payload).toString();
|
|
5582
|
-
response = await fetch(`${cfg.url}?${params}`, {
|
|
5370
|
+
response = await fetch(`${cfg.url}?${params}`, {
|
|
5371
|
+
signal: this.abortController.signal,
|
|
5372
|
+
});
|
|
5583
5373
|
}
|
|
5584
5374
|
const data = await response.json();
|
|
5585
5375
|
const result = this.parseResponse(data);
|
|
@@ -5653,7 +5443,7 @@
|
|
|
5653
5443
|
hasPagination = true;
|
|
5654
5444
|
page = parseInt(data.page ?? 0, 10);
|
|
5655
5445
|
totalPages = parseInt(data.totalPages ?? data.total_page ?? 1, 10);
|
|
5656
|
-
hasMore = data.hasMore ??
|
|
5446
|
+
hasMore = data.hasMore ?? page < totalPages - 1;
|
|
5657
5447
|
}
|
|
5658
5448
|
}
|
|
5659
5449
|
else if (Array.isArray(data)) {
|
|
@@ -5664,23 +5454,39 @@
|
|
|
5664
5454
|
if (data.pagination) {
|
|
5665
5455
|
hasPagination = true;
|
|
5666
5456
|
page = parseInt(data.pagination.page ?? 0, 10);
|
|
5667
|
-
totalPages = parseInt(data.pagination.totalPages ??
|
|
5668
|
-
|
|
5457
|
+
totalPages = parseInt(data.pagination.totalPages ??
|
|
5458
|
+
data.pagination.total_page ??
|
|
5459
|
+
1, 10);
|
|
5460
|
+
hasMore = data.pagination.hasMore ?? page < totalPages - 1;
|
|
5669
5461
|
}
|
|
5670
5462
|
}
|
|
5671
5463
|
const normalized = items.map((item) => {
|
|
5672
|
-
if (item instanceof HTMLOptionElement ||
|
|
5464
|
+
if (item instanceof HTMLOptionElement ||
|
|
5465
|
+
item instanceof HTMLOptGroupElement)
|
|
5673
5466
|
return item;
|
|
5674
|
-
if (item.type === "optgroup" ||
|
|
5467
|
+
if (item.type === "optgroup" ||
|
|
5468
|
+
item.isGroup ||
|
|
5469
|
+
item.group ||
|
|
5470
|
+
item.label) {
|
|
5675
5471
|
const label = item.label ?? item.name ?? item.title ?? "";
|
|
5676
5472
|
const dataObj = item.data ?? {};
|
|
5677
5473
|
const opts = (item.options ?? item.items ?? []).map((opt) => ({
|
|
5678
5474
|
value: opt.value ?? opt.id ?? opt.key ?? "",
|
|
5679
|
-
text: opt.text ??
|
|
5475
|
+
text: opt.text ??
|
|
5476
|
+
opt.label ??
|
|
5477
|
+
opt.name ??
|
|
5478
|
+
opt.title ??
|
|
5479
|
+
"",
|
|
5680
5480
|
selected: opt.selected ?? false,
|
|
5681
|
-
data: opt.data ??
|
|
5481
|
+
data: opt.data ??
|
|
5482
|
+
(opt.imgsrc ? { imgsrc: opt.imgsrc } : {}),
|
|
5682
5483
|
}));
|
|
5683
|
-
return {
|
|
5484
|
+
return {
|
|
5485
|
+
type: "optgroup",
|
|
5486
|
+
label,
|
|
5487
|
+
data: dataObj,
|
|
5488
|
+
options: opts,
|
|
5489
|
+
};
|
|
5684
5490
|
}
|
|
5685
5491
|
const dataObj = item.data ?? {};
|
|
5686
5492
|
if (item?.imgsrc)
|
|
@@ -5722,9 +5528,12 @@
|
|
|
5722
5528
|
select.innerHTML = "";
|
|
5723
5529
|
items.forEach((item) => {
|
|
5724
5530
|
// Skip empty item (defensive guard)
|
|
5725
|
-
if ((item["type"] === "option" || !item["type"]) &&
|
|
5531
|
+
if ((item["type"] === "option" || !item["type"]) &&
|
|
5532
|
+
item["value"] === "" &&
|
|
5533
|
+
item["text"] === "")
|
|
5726
5534
|
return;
|
|
5727
|
-
if (item instanceof HTMLOptionElement ||
|
|
5535
|
+
if (item instanceof HTMLOptionElement ||
|
|
5536
|
+
item instanceof HTMLOptGroupElement) {
|
|
5728
5537
|
select.appendChild(item);
|
|
5729
5538
|
return;
|
|
5730
5539
|
}
|
|
@@ -5746,7 +5555,8 @@
|
|
|
5746
5555
|
option.dataset[key] = String(opt.data[key]);
|
|
5747
5556
|
});
|
|
5748
5557
|
}
|
|
5749
|
-
if (opt.selected ||
|
|
5558
|
+
if (opt.selected ||
|
|
5559
|
+
(keepSelected && oldSelected.includes(option.value))) {
|
|
5750
5560
|
option.selected = true;
|
|
5751
5561
|
}
|
|
5752
5562
|
optgroup.appendChild(option);
|
|
@@ -5763,7 +5573,8 @@
|
|
|
5763
5573
|
option.dataset[key] = String(item.data[key]);
|
|
5764
5574
|
});
|
|
5765
5575
|
}
|
|
5766
|
-
if (item.selected ||
|
|
5576
|
+
if (item.selected ||
|
|
5577
|
+
(keepSelected && oldSelected.includes(option.value))) {
|
|
5767
5578
|
option.selected = true;
|
|
5768
5579
|
}
|
|
5769
5580
|
select.appendChild(option);
|
|
@@ -5839,16 +5650,6 @@
|
|
|
5839
5650
|
* @param {HTMLSelectElement} select - The `<select>` element to observe for mutations.
|
|
5840
5651
|
*/
|
|
5841
5652
|
constructor(select) {
|
|
5842
|
-
/**
|
|
5843
|
-
* Debounce timer handle for batching rapid mutations.
|
|
5844
|
-
*
|
|
5845
|
-
* - Cleared and reset on each mutation event.
|
|
5846
|
-
* - Invokes {@link handleChange} after {@link _DEBOUNCE_DELAY} milliseconds of inactivity.
|
|
5847
|
-
* - Nulled during {@link disconnect}.
|
|
5848
|
-
*
|
|
5849
|
-
* @private
|
|
5850
|
-
*/
|
|
5851
|
-
this.debounceTimer = null;
|
|
5852
5653
|
/**
|
|
5853
5654
|
* Debounce delay in milliseconds.
|
|
5854
5655
|
*
|
|
@@ -5989,16 +5790,12 @@
|
|
|
5989
5790
|
* @param element - The element whose `data-*` attributes will be observed.
|
|
5990
5791
|
*/
|
|
5991
5792
|
constructor(element) {
|
|
5992
|
-
/**
|
|
5993
|
-
* Debounce timer handle for coalescing rapid attribute mutations.
|
|
5994
|
-
* Cleared/replaced whenever a new relevant mutation arrives within the debounce window.
|
|
5995
|
-
*/
|
|
5996
|
-
this.debounceTimer = null;
|
|
5997
5793
|
this.element = element;
|
|
5998
5794
|
this.observer = new MutationObserver((mutations) => {
|
|
5999
5795
|
let datasetChanged = false;
|
|
6000
5796
|
for (const mutation of mutations) {
|
|
6001
|
-
if (mutation.type === "attributes" &&
|
|
5797
|
+
if (mutation.type === "attributes" &&
|
|
5798
|
+
mutation.attributeName?.startsWith("data-")) {
|
|
6002
5799
|
datasetChanged = true;
|
|
6003
5800
|
break;
|
|
6004
5801
|
}
|
|
@@ -6204,7 +6001,9 @@
|
|
|
6204
6001
|
* @see {@link changeProp}
|
|
6205
6002
|
*/
|
|
6206
6003
|
onPropChanged(propName, callback) {
|
|
6207
|
-
Libs.callbackScheduler.on(`${propName}_${this.adapterKey}`, callback, {
|
|
6004
|
+
Libs.callbackScheduler.on(`${propName}_${this.adapterKey}`, callback, {
|
|
6005
|
+
debounce: 0,
|
|
6006
|
+
});
|
|
6208
6007
|
}
|
|
6209
6008
|
/**
|
|
6210
6009
|
* Triggers the **post-change** pipeline for a given property.
|
|
@@ -6239,7 +6038,7 @@
|
|
|
6239
6038
|
*
|
|
6240
6039
|
* @param {HTMLElement} parent - Container element that will host the viewer.
|
|
6241
6040
|
* @param {TItem} item - The model for which the viewer is created.
|
|
6242
|
-
* @returns {TViewer
|
|
6041
|
+
* @returns {TViewer} The created viewer instance; `null` by default.
|
|
6243
6042
|
*/
|
|
6244
6043
|
viewHolder(parent, item) {
|
|
6245
6044
|
return null;
|
|
@@ -6346,7 +6145,7 @@
|
|
|
6346
6145
|
return;
|
|
6347
6146
|
}
|
|
6348
6147
|
this.recyclerView = null;
|
|
6349
|
-
this.items.forEach(item => {
|
|
6148
|
+
this.items.forEach((item) => {
|
|
6350
6149
|
item?.destroy?.();
|
|
6351
6150
|
});
|
|
6352
6151
|
this.items = [];
|
|
@@ -6401,20 +6200,6 @@
|
|
|
6401
6200
|
*/
|
|
6402
6201
|
constructor(parent) {
|
|
6403
6202
|
super();
|
|
6404
|
-
/**
|
|
6405
|
-
* Host container element into which this view's root element is rendered/attached.
|
|
6406
|
-
*
|
|
6407
|
-
* This reference is captured at construction time and cleared on {@link destroy}.
|
|
6408
|
-
*/
|
|
6409
|
-
this.parent = null;
|
|
6410
|
-
/**
|
|
6411
|
-
* Mounted view result containing:
|
|
6412
|
-
* - `view`: the root element of this view
|
|
6413
|
-
* - `tags`: a strongly-typed map of child elements for fast access
|
|
6414
|
-
*
|
|
6415
|
-
* This is expected to be assigned by subclasses (or a mount helper) before {@link getView} is called.
|
|
6416
|
-
*/
|
|
6417
|
-
this.view = null;
|
|
6418
6203
|
this.parent = parent;
|
|
6419
6204
|
this.init();
|
|
6420
6205
|
}
|
|
@@ -6515,29 +6300,6 @@
|
|
|
6515
6300
|
*/
|
|
6516
6301
|
constructor(parent, options) {
|
|
6517
6302
|
super(parent);
|
|
6518
|
-
/**
|
|
6519
|
-
* Strongly-typed reference to the mounted group view structure.
|
|
6520
|
-
*
|
|
6521
|
-
* Structure:
|
|
6522
|
-
* - **view**: Root container element.
|
|
6523
|
-
* - **tags**: Named references to header and items container.
|
|
6524
|
-
*
|
|
6525
|
-
* Lifecycle:
|
|
6526
|
-
* - `null` until {@link mount} completes.
|
|
6527
|
-
* - Cleared during {@link destroy}.
|
|
6528
|
-
*
|
|
6529
|
-
* @public
|
|
6530
|
-
*/
|
|
6531
|
-
this.view = null;
|
|
6532
|
-
/**
|
|
6533
|
-
* Parsed configuration (bound from the `<select>` element via binder map).
|
|
6534
|
-
*
|
|
6535
|
-
* Provides feature flags (multiple/disabled/readonly/visible/virtualScroll/ajax/autoclose…),
|
|
6536
|
-
* a11y ids (e.g. `SEID_LIST`, `SEID_HOLDER`) and user callbacks under `options.on`.
|
|
6537
|
-
*
|
|
6538
|
-
* @internal
|
|
6539
|
-
*/
|
|
6540
|
-
this.options = null;
|
|
6541
6303
|
this.options = options;
|
|
6542
6304
|
}
|
|
6543
6305
|
/**
|
|
@@ -6631,10 +6393,10 @@
|
|
|
6631
6393
|
* - Safe to call multiple times with same value (idempotent).
|
|
6632
6394
|
*
|
|
6633
6395
|
* @public
|
|
6634
|
-
* @param {string
|
|
6396
|
+
* @param {string} [label=null] - New label to display; `null` preserves current label.
|
|
6635
6397
|
* @returns {void}
|
|
6636
6398
|
*/
|
|
6637
|
-
updateLabel(label
|
|
6399
|
+
updateLabel(label) {
|
|
6638
6400
|
if (!this.view)
|
|
6639
6401
|
return;
|
|
6640
6402
|
const headerEl = this.view.tags.GroupHeader;
|
|
@@ -6681,7 +6443,7 @@
|
|
|
6681
6443
|
if (!this.view)
|
|
6682
6444
|
return;
|
|
6683
6445
|
const items = this.view.tags.GroupItems;
|
|
6684
|
-
const visibleItems = Array.from(items.children).filter(child => !child.classList.contains("hide"));
|
|
6446
|
+
const visibleItems = Array.from(items.children).filter((child) => !child.classList.contains("hide"));
|
|
6685
6447
|
this.view.view.classList.toggle("hide", visibleItems.length === 0);
|
|
6686
6448
|
}
|
|
6687
6449
|
/**
|
|
@@ -6795,57 +6557,6 @@
|
|
|
6795
6557
|
*/
|
|
6796
6558
|
constructor(parent, options) {
|
|
6797
6559
|
super(parent);
|
|
6798
|
-
/**
|
|
6799
|
-
* Strongly-typed reference to the mounted option view structure.
|
|
6800
|
-
*
|
|
6801
|
-
* Structure:
|
|
6802
|
-
* - **view**: Root container element.
|
|
6803
|
-
* - **tags**: Named references to input, image (conditional), label, label content.
|
|
6804
|
-
*
|
|
6805
|
-
* Lifecycle:
|
|
6806
|
-
* - `null` until {@link mount} completes.
|
|
6807
|
-
* - Cleared during {@link destroy}.
|
|
6808
|
-
*
|
|
6809
|
-
* @public
|
|
6810
|
-
*/
|
|
6811
|
-
this.view = null;
|
|
6812
|
-
/**
|
|
6813
|
-
* Parsed configuration (bound from the `<select>` element via binder map).
|
|
6814
|
-
*
|
|
6815
|
-
* Provides feature flags (multiple/disabled/readonly/visible/virtualScroll/ajax/autoclose…),
|
|
6816
|
-
* a11y ids (e.g. `SEID_LIST`, `SEID_HOLDER`) and user callbacks under `options.on`.
|
|
6817
|
-
*
|
|
6818
|
-
* @internal
|
|
6819
|
-
*/
|
|
6820
|
-
this.options = null;
|
|
6821
|
-
/**
|
|
6822
|
-
* Internal configuration object (Proxy target).
|
|
6823
|
-
*
|
|
6824
|
-
* Lifecycle:
|
|
6825
|
-
* - Initialized during {@link initialize} with default values.
|
|
6826
|
-
* - Mutated via {@link configProxy} Proxy trap.
|
|
6827
|
-
*
|
|
6828
|
-
* Notes:
|
|
6829
|
-
* - **Should not be mutated directly**; use {@link configProxy} or typed setters.
|
|
6830
|
-
* - Contains default values for layout, image, and alignment.
|
|
6831
|
-
*
|
|
6832
|
-
* @private
|
|
6833
|
-
*/
|
|
6834
|
-
this.config = null;
|
|
6835
|
-
/**
|
|
6836
|
-
* Reactive Proxy wrapper around {@link config}.
|
|
6837
|
-
*
|
|
6838
|
-
* Behavior:
|
|
6839
|
-
* - Intercepts property assignments via `set` trap.
|
|
6840
|
-
* - Triggers {@link applyPartialChange} for diffed values when {@link isRendered} is `true`.
|
|
6841
|
-
* - Prevents redundant DOM updates when value hasn't changed.
|
|
6842
|
-
*
|
|
6843
|
-
* Usage:
|
|
6844
|
-
* - Accessed via {@link optionConfig} getter or typed setters ({@link isMultiple}, {@link hasImage}).
|
|
6845
|
-
*
|
|
6846
|
-
* @private
|
|
6847
|
-
*/
|
|
6848
|
-
this.configProxy = null;
|
|
6849
6560
|
/**
|
|
6850
6561
|
* Flag indicating whether the initial render has completed.
|
|
6851
6562
|
*
|
|
@@ -7010,24 +6721,30 @@
|
|
|
7010
6721
|
* - Each changed property triggers {@link applyPartialChange} individually.
|
|
7011
6722
|
*
|
|
7012
6723
|
* @public
|
|
7013
|
-
* @param {OptionConfigPatch
|
|
6724
|
+
* @param {OptionConfigPatch} config - Partial configuration patch; `null` is no-op.
|
|
7014
6725
|
* @returns {void}
|
|
7015
6726
|
*/
|
|
7016
6727
|
set optionConfig(config) {
|
|
7017
6728
|
if (!config || !this.configProxy || !this.config)
|
|
7018
6729
|
return;
|
|
7019
6730
|
const changes = {};
|
|
7020
|
-
if (config.imageWidth !== undefined &&
|
|
6731
|
+
if (config.imageWidth !== undefined &&
|
|
6732
|
+
config.imageWidth !== this.config.imageWidth)
|
|
7021
6733
|
changes.imageWidth = config.imageWidth;
|
|
7022
|
-
if (config.imageHeight !== undefined &&
|
|
6734
|
+
if (config.imageHeight !== undefined &&
|
|
6735
|
+
config.imageHeight !== this.config.imageHeight)
|
|
7023
6736
|
changes.imageHeight = config.imageHeight;
|
|
7024
|
-
if (config.imageBorderRadius !== undefined &&
|
|
6737
|
+
if (config.imageBorderRadius !== undefined &&
|
|
6738
|
+
config.imageBorderRadius !== this.config.imageBorderRadius)
|
|
7025
6739
|
changes.imageBorderRadius = config.imageBorderRadius;
|
|
7026
|
-
if (config.imagePosition !== undefined &&
|
|
6740
|
+
if (config.imagePosition !== undefined &&
|
|
6741
|
+
config.imagePosition !== this.config.imagePosition)
|
|
7027
6742
|
changes.imagePosition = config.imagePosition;
|
|
7028
|
-
if (config.labelValign !== undefined &&
|
|
6743
|
+
if (config.labelValign !== undefined &&
|
|
6744
|
+
config.labelValign !== this.config.labelValign)
|
|
7029
6745
|
changes.labelValign = config.labelValign;
|
|
7030
|
-
if (config.labelHalign !== undefined &&
|
|
6746
|
+
if (config.labelHalign !== undefined &&
|
|
6747
|
+
config.labelHalign !== this.config.labelHalign)
|
|
7031
6748
|
changes.labelHalign = config.labelHalign;
|
|
7032
6749
|
if (Object.keys(changes).length > 0) {
|
|
7033
6750
|
Object.assign(this.configProxy, changes);
|
|
@@ -7201,9 +6918,11 @@
|
|
|
7201
6918
|
case "imageBorderRadius": {
|
|
7202
6919
|
const img = v.tags?.OptionImage;
|
|
7203
6920
|
if (img) {
|
|
7204
|
-
const styleProp = prop === "imageWidth"
|
|
7205
|
-
|
|
7206
|
-
|
|
6921
|
+
const styleProp = prop === "imageWidth"
|
|
6922
|
+
? "width"
|
|
6923
|
+
: prop === "imageHeight"
|
|
6924
|
+
? "height"
|
|
6925
|
+
: "borderRadius";
|
|
7207
6926
|
img.style[styleProp] = String(newValue);
|
|
7208
6927
|
}
|
|
7209
6928
|
break;
|
|
@@ -7324,15 +7043,6 @@
|
|
|
7324
7043
|
super(items);
|
|
7325
7044
|
/** Whether the adapter operates in multi-selection mode. */
|
|
7326
7045
|
this.isMultiple = false;
|
|
7327
|
-
/**
|
|
7328
|
-
* Parsed configuration (bound from the `<select>` element via binder map).
|
|
7329
|
-
*
|
|
7330
|
-
* Provides feature flags (multiple/disabled/readonly/visible/virtualScroll/ajax/autoclose…),
|
|
7331
|
-
* a11y ids (e.g. `SEID_LIST`, `SEID_HOLDER`) and user callbacks under `options.on`.
|
|
7332
|
-
*
|
|
7333
|
-
* @internal
|
|
7334
|
-
*/
|
|
7335
|
-
this.options = null;
|
|
7336
7046
|
/**
|
|
7337
7047
|
* Subscribers for aggregated visibility statistics.
|
|
7338
7048
|
* Fired via a debounced scheduler to avoid repeated recomputation during batch updates.
|
|
@@ -7343,11 +7053,6 @@
|
|
|
7343
7053
|
* `-1` indicates "no highlight".
|
|
7344
7054
|
*/
|
|
7345
7055
|
this.currentHighlightIndex = -1;
|
|
7346
|
-
/**
|
|
7347
|
-
* Cached pointer to the selected option in single-select mode.
|
|
7348
|
-
* Used to efficiently clear previous selection when selecting a new option.
|
|
7349
|
-
*/
|
|
7350
|
-
this.selectedItemSingle = null;
|
|
7351
7056
|
/** Top-level group models (if any). */
|
|
7352
7057
|
this.groups = [];
|
|
7353
7058
|
/**
|
|
@@ -7436,7 +7141,7 @@
|
|
|
7436
7141
|
* - Performs one-time listener binding guarded by `item.isInit`.
|
|
7437
7142
|
*
|
|
7438
7143
|
* @param {MixedItem} item - {@link GroupModel} or {@link OptionModel}.
|
|
7439
|
-
* @param {GroupView | OptionView
|
|
7144
|
+
* @param {GroupView | OptionView} viewer - The view instance that will render the model.
|
|
7440
7145
|
* @param {number} position - Position in the top-level mixed list.
|
|
7441
7146
|
* @returns {void}
|
|
7442
7147
|
* @override
|
|
@@ -7663,7 +7368,7 @@
|
|
|
7663
7368
|
return;
|
|
7664
7369
|
}
|
|
7665
7370
|
Libs.callbackScheduler.clear(`sche_vis_${this.adapterKey}`);
|
|
7666
|
-
this.groups.forEach(group => {
|
|
7371
|
+
this.groups.forEach((group) => {
|
|
7667
7372
|
group.destroy();
|
|
7668
7373
|
});
|
|
7669
7374
|
this.visibilityChangedCallbacks = [];
|
|
@@ -7786,7 +7491,8 @@
|
|
|
7786
7491
|
* @returns {void}
|
|
7787
7492
|
*/
|
|
7788
7493
|
selectHighlighted() {
|
|
7789
|
-
if (this.currentHighlightIndex > -1 &&
|
|
7494
|
+
if (this.currentHighlightIndex > -1 &&
|
|
7495
|
+
this.flatOptions[this.currentHighlightIndex]) {
|
|
7790
7496
|
const item = this.flatOptions[this.currentHighlightIndex];
|
|
7791
7497
|
if (item.visible) {
|
|
7792
7498
|
const viewEl = item.view?.getView?.();
|
|
@@ -7823,7 +7529,8 @@
|
|
|
7823
7529
|
else {
|
|
7824
7530
|
index = 0;
|
|
7825
7531
|
}
|
|
7826
|
-
if (this.currentHighlightIndex > -1 &&
|
|
7532
|
+
if (this.currentHighlightIndex > -1 &&
|
|
7533
|
+
this.flatOptions[this.currentHighlightIndex]) {
|
|
7827
7534
|
this.flatOptions[this.currentHighlightIndex].highlighted = false;
|
|
7828
7535
|
}
|
|
7829
7536
|
for (let i = index; i < this.flatOptions.length; i++) {
|
|
@@ -7835,11 +7542,13 @@
|
|
|
7835
7542
|
if (isScrollToView) {
|
|
7836
7543
|
const el = item.view?.getView?.();
|
|
7837
7544
|
if (el) {
|
|
7838
|
-
el.scrollIntoView({ block:
|
|
7545
|
+
el.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
7839
7546
|
}
|
|
7840
7547
|
else {
|
|
7841
7548
|
// If virtualized, ensure the item is rendered before trying to scroll.
|
|
7842
|
-
this.recyclerView?.ensureRendered?.(i, {
|
|
7549
|
+
this.recyclerView?.ensureRendered?.(i, {
|
|
7550
|
+
scrollIntoView: true,
|
|
7551
|
+
});
|
|
7843
7552
|
}
|
|
7844
7553
|
}
|
|
7845
7554
|
this.onHighlightChange(i, item.view?.getView?.()?.id);
|
|
@@ -8068,9 +7777,9 @@
|
|
|
8068
7777
|
*
|
|
8069
7778
|
* Note: The virtualization scaffold is built when an adapter is set via {@link setAdapter}.
|
|
8070
7779
|
*
|
|
8071
|
-
* @param {HTMLDivElement
|
|
7780
|
+
* @param {HTMLDivElement} [viewElement=null] - Optional root container for the recycler view.
|
|
8072
7781
|
*/
|
|
8073
|
-
constructor(viewElement
|
|
7782
|
+
constructor(viewElement) {
|
|
8074
7783
|
super(viewElement);
|
|
8075
7784
|
/**
|
|
8076
7785
|
* Virtualization settings (materialized to `Required<VirtualOptions>`).
|
|
@@ -8106,9 +7815,6 @@
|
|
|
8106
7815
|
this.start = 0;
|
|
8107
7816
|
/** Current window end (inclusive). -1 means not initialized. */
|
|
8108
7817
|
this.end = -1;
|
|
8109
|
-
/** Pending animation frame ids for window and measurement. */
|
|
8110
|
-
this.rafId = null;
|
|
8111
|
-
this.measureRaf = null;
|
|
8112
7818
|
/** Re-entrancy/suspension flags used to prevent feedback loops. */
|
|
8113
7819
|
this.updating = false;
|
|
8114
7820
|
this.suppressResize = false;
|
|
@@ -8163,20 +7869,29 @@
|
|
|
8163
7869
|
return;
|
|
8164
7870
|
this.viewElement.replaceChildren();
|
|
8165
7871
|
const nodeMounted = Libs.mountNode({
|
|
8166
|
-
PadTop: {
|
|
8167
|
-
|
|
8168
|
-
|
|
7872
|
+
PadTop: {
|
|
7873
|
+
tag: { node: "div", classList: "seui-virtual-pad-top" },
|
|
7874
|
+
},
|
|
7875
|
+
ItemsHost: {
|
|
7876
|
+
tag: { node: "div", classList: "seui-virtual-items" },
|
|
7877
|
+
},
|
|
7878
|
+
PadBottom: {
|
|
7879
|
+
tag: { node: "div", classList: "seui-virtual-pad-bottom" },
|
|
7880
|
+
},
|
|
8169
7881
|
}, this.viewElement);
|
|
8170
7882
|
this.PadTop = nodeMounted.PadTop;
|
|
8171
7883
|
this.ItemsHost = nodeMounted.ItemsHost;
|
|
8172
7884
|
this.PadBottom = nodeMounted.PadBottom;
|
|
8173
|
-
this.scrollEl =
|
|
8174
|
-
|
|
8175
|
-
|
|
7885
|
+
this.scrollEl =
|
|
7886
|
+
this.opts.scrollEl ??
|
|
7887
|
+
this.viewElement.closest(".seui-popup") ??
|
|
7888
|
+
this.viewElement.parentElement;
|
|
8176
7889
|
if (!this.scrollEl)
|
|
8177
7890
|
throw new Error("VirtualRecyclerView: scrollEl not found");
|
|
8178
7891
|
this.boundOnScroll = this.onScroll.bind(this);
|
|
8179
|
-
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7892
|
+
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7893
|
+
passive: true,
|
|
7894
|
+
});
|
|
8180
7895
|
this.refresh(false);
|
|
8181
7896
|
this.attachResizeObserverOnce();
|
|
8182
7897
|
adapter?.onVisibilityChanged?.(() => this.refreshItem());
|
|
@@ -8215,7 +7930,9 @@
|
|
|
8215
7930
|
resume() {
|
|
8216
7931
|
this.suspended = false;
|
|
8217
7932
|
if (this.scrollEl && this.boundOnScroll) {
|
|
8218
|
-
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7933
|
+
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7934
|
+
passive: true,
|
|
7935
|
+
});
|
|
8219
7936
|
}
|
|
8220
7937
|
if (this.resumeResizeAfter) {
|
|
8221
7938
|
this.attachResizeObserverOnce();
|
|
@@ -8313,7 +8030,7 @@
|
|
|
8313
8030
|
this.scrollEl.removeEventListener("scroll", this.boundOnScroll);
|
|
8314
8031
|
}
|
|
8315
8032
|
this.resizeObs?.disconnect();
|
|
8316
|
-
this.created.forEach(el => el.remove());
|
|
8033
|
+
this.created.forEach((el) => el.remove());
|
|
8317
8034
|
this.created.clear();
|
|
8318
8035
|
}
|
|
8319
8036
|
/**
|
|
@@ -8390,7 +8107,7 @@
|
|
|
8390
8107
|
* @returns {void}
|
|
8391
8108
|
*/
|
|
8392
8109
|
resetState() {
|
|
8393
|
-
this.created.forEach(el => el.remove());
|
|
8110
|
+
this.created.forEach((el) => el.remove());
|
|
8394
8111
|
this.created.clear();
|
|
8395
8112
|
this.heightCache = [];
|
|
8396
8113
|
this.fenwick.reset(0);
|
|
@@ -8664,8 +8381,12 @@
|
|
|
8664
8381
|
el.setAttribute(VirtualRecyclerView.ATTR_INDEX, String(index));
|
|
8665
8382
|
const prev = el.previousElementSibling;
|
|
8666
8383
|
const next = el.nextElementSibling;
|
|
8667
|
-
const needsReorder = (prev &&
|
|
8668
|
-
|
|
8384
|
+
const needsReorder = (prev &&
|
|
8385
|
+
Number(prev.getAttribute(VirtualRecyclerView.ATTR_INDEX)) >
|
|
8386
|
+
index) ||
|
|
8387
|
+
(next &&
|
|
8388
|
+
Number(next.getAttribute(VirtualRecyclerView.ATTR_INDEX)) <
|
|
8389
|
+
index);
|
|
8669
8390
|
if (needsReorder) {
|
|
8670
8391
|
el.remove();
|
|
8671
8392
|
this.insertIntoHostByIndex(index, el);
|
|
@@ -8687,7 +8408,10 @@
|
|
|
8687
8408
|
if (this.resizeObs)
|
|
8688
8409
|
return;
|
|
8689
8410
|
this.resizeObs = new ResizeObserver(() => {
|
|
8690
|
-
if (this.suppressResize ||
|
|
8411
|
+
if (this.suppressResize ||
|
|
8412
|
+
this.suspended ||
|
|
8413
|
+
!this.adapter ||
|
|
8414
|
+
this.measureRaf != null)
|
|
8691
8415
|
return;
|
|
8692
8416
|
this.measureRaf = requestAnimationFrame(() => {
|
|
8693
8417
|
this.measureRaf = null;
|
|
@@ -9016,31 +8740,6 @@
|
|
|
9016
8740
|
* @internal
|
|
9017
8741
|
*/
|
|
9018
8742
|
this.oldValue = null;
|
|
9019
|
-
/**
|
|
9020
|
-
* Root wrapper DOM node for the enhanced UI.
|
|
9021
|
-
*
|
|
9022
|
-
* Created during {@link init} via {@link Libs.mountNode}, inserted into the DOM during {@link mount},
|
|
9023
|
-
* and removed during {@link destroy}.
|
|
9024
|
-
*/
|
|
9025
|
-
this.node = null;
|
|
9026
|
-
/**
|
|
9027
|
-
* Parsed configuration (bound from the `<select>` element via binder map).
|
|
9028
|
-
*
|
|
9029
|
-
* Provides feature flags (multiple/disabled/readonly/visible/virtualScroll/ajax/autoclose…),
|
|
9030
|
-
* a11y ids (e.g. `SEID_LIST`, `SEID_HOLDER`) and user callbacks under `options.on`.
|
|
9031
|
-
*
|
|
9032
|
-
* @internal
|
|
9033
|
-
*/
|
|
9034
|
-
this.options = null;
|
|
9035
|
-
/**
|
|
9036
|
-
* Manager that owns model resources and bridges the Adapter ↔ RecyclerView pipeline.
|
|
9037
|
-
*
|
|
9038
|
-
* The configured adapter is {@link MixedAdapter}. The recyclerview implementation is chosen
|
|
9039
|
-
* based on `options.virtualScroll` (standard {@link RecyclerView} vs {@link VirtualRecyclerView}).
|
|
9040
|
-
*
|
|
9041
|
-
* @internal
|
|
9042
|
-
*/
|
|
9043
|
-
this.optionModelManager = null;
|
|
9044
8743
|
/**
|
|
9045
8744
|
* Whether the popup/list UI is currently open.
|
|
9046
8745
|
*
|
|
@@ -9072,20 +8771,10 @@
|
|
|
9072
8771
|
* @internal
|
|
9073
8772
|
*/
|
|
9074
8773
|
this.hasDeInitialized = false;
|
|
9075
|
-
/**
|
|
9076
|
-
* Selective context (global helper / registry).
|
|
9077
|
-
*
|
|
9078
|
-
* Used to locate the instance wrapper via `Selective.find(...)` and to close other open instances.
|
|
9079
|
-
*/
|
|
9080
|
-
this.Selective = null;
|
|
9081
8774
|
/**
|
|
9082
8775
|
* Registered plugins for this SelectBox instance.
|
|
9083
8776
|
*/
|
|
9084
8777
|
this.plugins = [];
|
|
9085
|
-
/**
|
|
9086
|
-
* Cached plugin context for this SelectBox instance.
|
|
9087
|
-
*/
|
|
9088
|
-
this.pluginContext = null;
|
|
9089
8778
|
if (select && Selective)
|
|
9090
8779
|
this.initialize(select, Selective);
|
|
9091
8780
|
}
|
|
@@ -9210,7 +8899,9 @@
|
|
|
9210
8899
|
classList: "seui-view",
|
|
9211
8900
|
tabIndex: 0,
|
|
9212
8901
|
onkeydown: (e) => {
|
|
9213
|
-
if (e.key === "Enter" ||
|
|
8902
|
+
if (e.key === "Enter" ||
|
|
8903
|
+
e.key === " " ||
|
|
8904
|
+
e.key === "ArrowDown") {
|
|
9214
8905
|
e.preventDefault();
|
|
9215
8906
|
this.getAction()?.open();
|
|
9216
8907
|
}
|
|
@@ -9602,7 +9293,11 @@
|
|
|
9602
9293
|
get value() {
|
|
9603
9294
|
const item_list = this.valueArray;
|
|
9604
9295
|
const valLength = item_list.length;
|
|
9605
|
-
return valLength > 1
|
|
9296
|
+
return valLength > 1
|
|
9297
|
+
? item_list
|
|
9298
|
+
: valLength === 0
|
|
9299
|
+
? ""
|
|
9300
|
+
: item_list[0];
|
|
9606
9301
|
},
|
|
9607
9302
|
get valueArray() {
|
|
9608
9303
|
const item_list = [];
|
|
@@ -9637,7 +9332,11 @@
|
|
|
9637
9332
|
item_list.push(m.text);
|
|
9638
9333
|
});
|
|
9639
9334
|
const valLength = item_list.length;
|
|
9640
|
-
return valLength > 1
|
|
9335
|
+
return valLength > 1
|
|
9336
|
+
? item_list
|
|
9337
|
+
: valLength === 0
|
|
9338
|
+
? ""
|
|
9339
|
+
: item_list[0];
|
|
9641
9340
|
},
|
|
9642
9341
|
get isOpen() {
|
|
9643
9342
|
return superThis.isOpen;
|
|
@@ -9662,7 +9361,8 @@
|
|
|
9662
9361
|
},
|
|
9663
9362
|
selectAll(_evtToken, trigger = true) {
|
|
9664
9363
|
if (bindedOptions.multiple && bindedOptions.maxSelected > 0) {
|
|
9665
|
-
if (superThis.getModelOption().length >
|
|
9364
|
+
if (superThis.getModelOption().length >
|
|
9365
|
+
bindedOptions.maxSelected)
|
|
9666
9366
|
return;
|
|
9667
9367
|
}
|
|
9668
9368
|
if (this.disabled || this.readonly || !bindedOptions.multiple)
|
|
@@ -9692,12 +9392,30 @@
|
|
|
9692
9392
|
});
|
|
9693
9393
|
this.change(false, trigger);
|
|
9694
9394
|
},
|
|
9695
|
-
|
|
9395
|
+
deSelectByDataset(_evtToken, dataset, trigger = true) {
|
|
9396
|
+
if (dataset) {
|
|
9397
|
+
superThis.getModelOption().forEach((optionModel) => {
|
|
9398
|
+
if (optionModel.dataset) {
|
|
9399
|
+
for (let searchKey in dataset) {
|
|
9400
|
+
let value = dataset[searchKey];
|
|
9401
|
+
!Array.isArray(value) && (value = [value]);
|
|
9402
|
+
if (value.includes(optionModel.dataset[searchKey])) {
|
|
9403
|
+
optionModel.selectedNonTrigger = false;
|
|
9404
|
+
}
|
|
9405
|
+
}
|
|
9406
|
+
}
|
|
9407
|
+
});
|
|
9408
|
+
this.change(false, trigger);
|
|
9409
|
+
}
|
|
9410
|
+
},
|
|
9411
|
+
setValue(_evtToken, value, trigger = true, force = false) {
|
|
9696
9412
|
if (!Array.isArray(value))
|
|
9697
9413
|
value = [value];
|
|
9698
9414
|
value = value.filter((v) => v !== "" && v != null);
|
|
9699
9415
|
if (value.length === 0) {
|
|
9700
|
-
superThis
|
|
9416
|
+
superThis
|
|
9417
|
+
.getModelOption()
|
|
9418
|
+
.forEach((m) => (m.selectedNonTrigger = false));
|
|
9701
9419
|
this.change(false, trigger);
|
|
9702
9420
|
return;
|
|
9703
9421
|
}
|
|
@@ -9721,17 +9439,15 @@
|
|
|
9721
9439
|
const result = await container.searchController.loadByValues(missing);
|
|
9722
9440
|
if (result.success && result.items.length > 0) {
|
|
9723
9441
|
result.items.forEach((it) => {
|
|
9724
|
-
if (missing.includes(it.value)
|
|
9442
|
+
if (missing.includes(it.value) ||
|
|
9443
|
+
missing.includes(it.text))
|
|
9725
9444
|
it.selected = true;
|
|
9726
9445
|
});
|
|
9727
|
-
|
|
9728
|
-
container.searchController.applyAjaxResult?.(result.items, true, true);
|
|
9446
|
+
container.searchController.applyAjaxResult?.(result.items, false, false);
|
|
9729
9447
|
setTimeout(() => {
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
this.change(false, false);
|
|
9734
|
-
}, 100);
|
|
9448
|
+
container.searchController.resetPagination();
|
|
9449
|
+
this.change(false, trigger);
|
|
9450
|
+
}, 200);
|
|
9735
9451
|
}
|
|
9736
9452
|
else if (missing.length > 0) {
|
|
9737
9453
|
console.warn(`Could not load ${missing.length} values:`, missing);
|
|
@@ -9763,7 +9479,8 @@
|
|
|
9763
9479
|
this.change(false, trigger);
|
|
9764
9480
|
},
|
|
9765
9481
|
load() {
|
|
9766
|
-
if ((!superThis.hasLoadedOnce || superThis.isBeforeSearch) &&
|
|
9482
|
+
if ((!superThis.hasLoadedOnce || superThis.isBeforeSearch) &&
|
|
9483
|
+
bindedOptions?.ajax) {
|
|
9767
9484
|
container.searchController.resetPagination();
|
|
9768
9485
|
container.popup.showLoading();
|
|
9769
9486
|
superThis.hasLoadedOnce = true;
|
|
@@ -9850,9 +9567,10 @@
|
|
|
9850
9567
|
else
|
|
9851
9568
|
this.open();
|
|
9852
9569
|
},
|
|
9853
|
-
change(_evtToken
|
|
9570
|
+
change(_evtToken, canTrigger = true) {
|
|
9854
9571
|
if (canTrigger) {
|
|
9855
|
-
if (bindedOptions.multiple &&
|
|
9572
|
+
if (bindedOptions.multiple &&
|
|
9573
|
+
bindedOptions.maxSelected > 0) {
|
|
9856
9574
|
if (this.valueArray.length > bindedOptions.maxSelected) {
|
|
9857
9575
|
this.setValue(null, this.oldValue, false, true);
|
|
9858
9576
|
}
|
|
@@ -9887,7 +9605,8 @@
|
|
|
9887
9605
|
},
|
|
9888
9606
|
refreshMask() {
|
|
9889
9607
|
let mask = bindedOptions.placeholder;
|
|
9890
|
-
if (!bindedOptions.multiple &&
|
|
9608
|
+
if (!bindedOptions.multiple &&
|
|
9609
|
+
superThis.getModelOption().length > 0) {
|
|
9891
9610
|
mask = this.mask[0];
|
|
9892
9611
|
}
|
|
9893
9612
|
mask ?? (mask = bindedOptions.placeholder);
|
|
@@ -9969,7 +9688,8 @@
|
|
|
9969
9688
|
set(value) {
|
|
9970
9689
|
superThis[privateProp] = !!value;
|
|
9971
9690
|
if (superThis.container?.targetElement?.dataset) {
|
|
9972
|
-
superThis.container.targetElement.dataset[prop] =
|
|
9691
|
+
superThis.container.targetElement.dataset[prop] =
|
|
9692
|
+
String(!!value);
|
|
9973
9693
|
}
|
|
9974
9694
|
},
|
|
9975
9695
|
enumerable: true,
|
|
@@ -9997,7 +9717,7 @@
|
|
|
9997
9717
|
* @returns A flat array of option models (possibly filtered).
|
|
9998
9718
|
* @internal
|
|
9999
9719
|
*/
|
|
10000
|
-
getModelOption(isSelected
|
|
9720
|
+
getModelOption(isSelected) {
|
|
10001
9721
|
if (!this.optionModelManager)
|
|
10002
9722
|
return [];
|
|
10003
9723
|
const { modelList } = this.optionModelManager.getResources();
|
|
@@ -10077,14 +9797,6 @@
|
|
|
10077
9797
|
* @internal
|
|
10078
9798
|
*/
|
|
10079
9799
|
this.isActive = false;
|
|
10080
|
-
/**
|
|
10081
|
-
* Underlying DOM {@link MutationObserver} instance.
|
|
10082
|
-
*
|
|
10083
|
-
* `null` when disconnected.
|
|
10084
|
-
*
|
|
10085
|
-
* @internal
|
|
10086
|
-
*/
|
|
10087
|
-
this.observer = null;
|
|
10088
9800
|
/**
|
|
10089
9801
|
* Registered detection callbacks.
|
|
10090
9802
|
*
|
|
@@ -10678,7 +10390,7 @@
|
|
|
10678
10390
|
if (wasObserving)
|
|
10679
10391
|
this.EAObserver?.disconnect();
|
|
10680
10392
|
bindMap.self?.deInit?.();
|
|
10681
|
-
const wrapper =
|
|
10393
|
+
const wrapper = bindMap.container?.element ?? selectElement.parentElement;
|
|
10682
10394
|
selectElement.style.display = "";
|
|
10683
10395
|
selectElement.style.visibility = "";
|
|
10684
10396
|
selectElement.disabled = false;
|
|
@@ -10895,7 +10607,7 @@
|
|
|
10895
10607
|
if (typeof globalThis.GLOBAL_SEUI == "undefined") {
|
|
10896
10608
|
const SECLASS = new Selective();
|
|
10897
10609
|
globalThis.GLOBAL_SEUI = {
|
|
10898
|
-
version: "1.
|
|
10610
|
+
version: "1.4.1",
|
|
10899
10611
|
name: "SelectiveUI",
|
|
10900
10612
|
bind: SECLASS.bind.bind(SECLASS),
|
|
10901
10613
|
find: SECLASS.find.bind(SECLASS),
|
|
@@ -10903,7 +10615,7 @@
|
|
|
10903
10615
|
effector: Effector.bind(Effector),
|
|
10904
10616
|
rebind: SECLASS.rebind.bind(SECLASS),
|
|
10905
10617
|
registerPlugin: SECLASS.registerPlugin.bind(SECLASS),
|
|
10906
|
-
unregisterPlugin: SECLASS.unregisterPlugin.bind(SECLASS)
|
|
10618
|
+
unregisterPlugin: SECLASS.unregisterPlugin.bind(SECLASS),
|
|
10907
10619
|
};
|
|
10908
10620
|
let domInitialized = false;
|
|
10909
10621
|
function init() {
|
|
@@ -10928,7 +10640,7 @@
|
|
|
10928
10640
|
init();
|
|
10929
10641
|
}
|
|
10930
10642
|
}
|
|
10931
|
-
console.log(`[${"SelectiveUI"}] v${"1.
|
|
10643
|
+
console.log(`[${"SelectiveUI"}] v${"1.4.1"} loaded successfully`);
|
|
10932
10644
|
}
|
|
10933
10645
|
else {
|
|
10934
10646
|
console.warn(`[${globalThis.GLOBAL_SEUI.name}] Already loaded (v${globalThis.GLOBAL_SEUI.version}). ` +
|
|
@@ -10985,7 +10697,7 @@
|
|
|
10985
10697
|
* // Destroy all instances
|
|
10986
10698
|
* destroy();
|
|
10987
10699
|
*/
|
|
10988
|
-
function destroy(query
|
|
10700
|
+
function destroy(query) {
|
|
10989
10701
|
globalThis.GLOBAL_SEUI.destroy(query);
|
|
10990
10702
|
}
|
|
10991
10703
|
/**
|