selective-ui 1.4.0 → 1.4.2
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 +2 -2
- package/dist/selective-ui.css.map +1 -1
- package/dist/selective-ui.esm.js +407 -573
- 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 +409 -575
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +12 -12
- package/src/css/views/option-view.css +2 -2
- package/src/ts/adapter/mixed-adapter.ts +149 -71
- 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 +260 -84
- package/src/ts/core/base/adapter.ts +61 -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 +178 -45
- 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 +88 -20
- 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 +14 -4
- 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.4.
|
|
1
|
+
/*! Selective UI v1.4.2 | 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.
|
|
@@ -966,8 +978,9 @@
|
|
|
966
978
|
*
|
|
967
979
|
* @param select - Native `<select>` element used as the sizing reference and option source.
|
|
968
980
|
* @param view - View panel element whose inline styles will be updated.
|
|
981
|
+
* @param isWidthOnly - If true, only the width will be updated; height will be left unchanged.
|
|
969
982
|
*/
|
|
970
|
-
static resizeBox(select, view) {
|
|
983
|
+
static resizeBox(select, view, isWidthOnly = false) {
|
|
971
984
|
const bindedMap = Libs.getBinderMap(select);
|
|
972
985
|
if (!bindedMap?.options)
|
|
973
986
|
return;
|
|
@@ -987,7 +1000,12 @@
|
|
|
987
1000
|
width = options.width;
|
|
988
1001
|
if (cfgHeight > 0)
|
|
989
1002
|
height = options.height;
|
|
990
|
-
|
|
1003
|
+
if (isWidthOnly) {
|
|
1004
|
+
Object.assign(view.style, { width, maxWidth: width, minWidth });
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
Object.assign(view.style, { width, height, maxWidth: width, minWidth, minHeight });
|
|
1008
|
+
}
|
|
991
1009
|
}
|
|
992
1010
|
}
|
|
993
1011
|
|
|
@@ -1297,25 +1315,6 @@
|
|
|
1297
1315
|
*/
|
|
1298
1316
|
constructor(options) {
|
|
1299
1317
|
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
1318
|
if (options)
|
|
1320
1319
|
this.initialize(options);
|
|
1321
1320
|
}
|
|
@@ -1540,34 +1539,8 @@
|
|
|
1540
1539
|
*
|
|
1541
1540
|
* @param options - Feature flags and labels for the two actions.
|
|
1542
1541
|
*/
|
|
1543
|
-
constructor(options
|
|
1542
|
+
constructor(options) {
|
|
1544
1543
|
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
1544
|
/**
|
|
1572
1545
|
* Callback list invoked when the "Select all" control is activated.
|
|
1573
1546
|
*
|
|
@@ -1655,7 +1628,8 @@
|
|
|
1655
1628
|
available() {
|
|
1656
1629
|
if (!this.options)
|
|
1657
1630
|
return false;
|
|
1658
|
-
return Libs.string2Boolean(this.options.multiple) &&
|
|
1631
|
+
return (Libs.string2Boolean(this.options.multiple) &&
|
|
1632
|
+
Libs.string2Boolean(this.options.selectall));
|
|
1659
1633
|
}
|
|
1660
1634
|
/**
|
|
1661
1635
|
* Re-evaluates visibility and advances the lifecycle update step.
|
|
@@ -1712,7 +1686,7 @@
|
|
|
1712
1686
|
*
|
|
1713
1687
|
* @param action - Callback invoked on activation; ignored when not a function.
|
|
1714
1688
|
*/
|
|
1715
|
-
onSelectAll(action
|
|
1689
|
+
onSelectAll(action) {
|
|
1716
1690
|
if (typeof action === "function") {
|
|
1717
1691
|
this.actionOnSelectAll.push(action);
|
|
1718
1692
|
}
|
|
@@ -1728,7 +1702,7 @@
|
|
|
1728
1702
|
*
|
|
1729
1703
|
* @param action - Callback invoked on activation; ignored when not a function.
|
|
1730
1704
|
*/
|
|
1731
|
-
onDeSelectAll(action
|
|
1705
|
+
onDeSelectAll(action) {
|
|
1732
1706
|
if (typeof action === "function") {
|
|
1733
1707
|
this.actionOnDeSelectAll.push(action);
|
|
1734
1708
|
}
|
|
@@ -1796,26 +1770,10 @@
|
|
|
1796
1770
|
* If `options` are provided, initialization runs immediately (creates {@link node} and
|
|
1797
1771
|
* transitions to `INITIALIZED`).
|
|
1798
1772
|
*
|
|
1799
|
-
* @param {SelectiveOptions
|
|
1773
|
+
* @param {SelectiveOptions} [options=null] - Configuration containing empty state messages.
|
|
1800
1774
|
*/
|
|
1801
|
-
constructor(options
|
|
1775
|
+
constructor(options) {
|
|
1802
1776
|
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
1777
|
if (options)
|
|
1820
1778
|
this.initialize(options);
|
|
1821
1779
|
}
|
|
@@ -1941,25 +1899,10 @@
|
|
|
1941
1899
|
* If `options` are provided, initialization runs immediately (creates {@link node} and
|
|
1942
1900
|
* transitions to `INITIALIZED`).
|
|
1943
1901
|
*
|
|
1944
|
-
* @param {SelectiveOptions
|
|
1902
|
+
* @param {SelectiveOptions} [options=null] - Configuration containing the loading message text.
|
|
1945
1903
|
*/
|
|
1946
|
-
constructor(options
|
|
1904
|
+
constructor(options) {
|
|
1947
1905
|
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
1906
|
if (options)
|
|
1964
1907
|
this.initialize(options);
|
|
1965
1908
|
}
|
|
@@ -2117,27 +2060,6 @@
|
|
|
2117
2060
|
* It does **not** indicate that observers are currently attached (see {@link connect}).
|
|
2118
2061
|
*/
|
|
2119
2062
|
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
2063
|
this.isInit = true;
|
|
2142
2064
|
this.boundUpdateChanged = this.updateChanged.bind(this);
|
|
2143
2065
|
}
|
|
@@ -2183,7 +2105,8 @@
|
|
|
2183
2105
|
return;
|
|
2184
2106
|
}
|
|
2185
2107
|
const rect = el.getBoundingClientRect();
|
|
2186
|
-
const style = typeof window !== "undefined" &&
|
|
2108
|
+
const style = typeof window !== "undefined" &&
|
|
2109
|
+
typeof window.getComputedStyle === "function"
|
|
2187
2110
|
? window.getComputedStyle(el)
|
|
2188
2111
|
: null;
|
|
2189
2112
|
const metrics = {
|
|
@@ -2315,36 +2238,10 @@
|
|
|
2315
2238
|
* @param options - Configuration options (panel sizing, flags, texts, etc.).
|
|
2316
2239
|
* @param modelManager - Model manager that supplies the adapter and recycler view.
|
|
2317
2240
|
*/
|
|
2318
|
-
constructor(select
|
|
2241
|
+
constructor(select, options, modelManager) {
|
|
2319
2242
|
super();
|
|
2320
|
-
/** Active configuration for the popup behavior and text labels */
|
|
2321
|
-
this.options = null;
|
|
2322
2243
|
/** Indicates whether the popup DOM has been attached to the document body at least once */
|
|
2323
2244
|
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
2245
|
/** Default virtual scroll configuration (tuned for typical option heights) */
|
|
2349
2246
|
this.virtualScrollConfig = {
|
|
2350
2247
|
/** Estimated item height in pixels (improves initial layout calculation) */
|
|
@@ -2352,7 +2249,7 @@
|
|
|
2352
2249
|
/** Number of extra items to render above/below the viewport */
|
|
2353
2250
|
overscan: 8,
|
|
2354
2251
|
/** Whether the list contains items with dynamic (non-uniform) heights */
|
|
2355
|
-
dynamicHeights: true
|
|
2252
|
+
dynamicHeights: true,
|
|
2356
2253
|
};
|
|
2357
2254
|
this.modelManager = modelManager;
|
|
2358
2255
|
if (select && options) {
|
|
@@ -2399,7 +2296,8 @@
|
|
|
2399
2296
|
},
|
|
2400
2297
|
}, null);
|
|
2401
2298
|
this.node = nodeMounted.view;
|
|
2402
|
-
this.optionsContainer = nodeMounted.tags
|
|
2299
|
+
this.optionsContainer = nodeMounted.tags
|
|
2300
|
+
.OptionsContainer;
|
|
2403
2301
|
this.parent = Libs.getBinderMap(select);
|
|
2404
2302
|
this.options = options;
|
|
2405
2303
|
this.init();
|
|
@@ -2408,7 +2306,7 @@
|
|
|
2408
2306
|
scrollEl: this.node,
|
|
2409
2307
|
estimateItemHeight: this.virtualScrollConfig.estimateItemHeight,
|
|
2410
2308
|
overscan: this.virtualScrollConfig.overscan,
|
|
2411
|
-
dynamicHeights: this.virtualScrollConfig.dynamicHeights
|
|
2309
|
+
dynamicHeights: this.virtualScrollConfig.dynamicHeights,
|
|
2412
2310
|
}
|
|
2413
2311
|
: {};
|
|
2414
2312
|
// Load ModelManager resources into the list container
|
|
@@ -2435,7 +2333,11 @@
|
|
|
2435
2333
|
* - Triggers a resize to accommodate layout changes
|
|
2436
2334
|
*/
|
|
2437
2335
|
async showLoading() {
|
|
2438
|
-
if (!this.options ||
|
|
2336
|
+
if (!this.options ||
|
|
2337
|
+
!this.loadingState ||
|
|
2338
|
+
!this.optionHandle ||
|
|
2339
|
+
!this.optionAdapter ||
|
|
2340
|
+
!this.modelManager)
|
|
2439
2341
|
return;
|
|
2440
2342
|
if (this.hideLoadHandle)
|
|
2441
2343
|
clearTimeout(this.hideLoadHandle);
|
|
@@ -2453,7 +2355,10 @@
|
|
|
2453
2355
|
* Debounce: Uses `animationtime` as a short delay before hiding the loading indicator.
|
|
2454
2356
|
*/
|
|
2455
2357
|
async hideLoading() {
|
|
2456
|
-
if (!this.options ||
|
|
2358
|
+
if (!this.options ||
|
|
2359
|
+
!this.loadingState ||
|
|
2360
|
+
!this.optionAdapter ||
|
|
2361
|
+
!this.modelManager)
|
|
2457
2362
|
return;
|
|
2458
2363
|
if (this.hideLoadHandle)
|
|
2459
2364
|
clearTimeout(this.hideLoadHandle);
|
|
@@ -2494,7 +2399,10 @@
|
|
|
2494
2399
|
* @param stats - Optionally provide precomputed visibility stats.
|
|
2495
2400
|
*/
|
|
2496
2401
|
updateEmptyState(stats) {
|
|
2497
|
-
if (!this.optionAdapter ||
|
|
2402
|
+
if (!this.optionAdapter ||
|
|
2403
|
+
!this.emptyState ||
|
|
2404
|
+
!this.optionHandle ||
|
|
2405
|
+
!this.optionsContainer)
|
|
2498
2406
|
return;
|
|
2499
2407
|
const s = stats ?? this.optionAdapter.getVisibilityStats();
|
|
2500
2408
|
if (s.isEmpty) {
|
|
@@ -2578,8 +2486,12 @@
|
|
|
2578
2486
|
* @param callback - Optional callback invoked when the opening animation completes.
|
|
2579
2487
|
* @param isShowEmptyState - If true, applies the empty/not-found state before animation.
|
|
2580
2488
|
*/
|
|
2581
|
-
open(callback
|
|
2582
|
-
if (!this.node ||
|
|
2489
|
+
open(callback, isShowEmptyState) {
|
|
2490
|
+
if (!this.node ||
|
|
2491
|
+
!this.options ||
|
|
2492
|
+
!this.optionHandle ||
|
|
2493
|
+
!this.parent ||
|
|
2494
|
+
!this.effSvc)
|
|
2583
2495
|
return;
|
|
2584
2496
|
// Ensure one-time initialization
|
|
2585
2497
|
this.load();
|
|
@@ -2628,8 +2540,11 @@
|
|
|
2628
2540
|
*
|
|
2629
2541
|
* @param callback - Optional callback invoked when the closing animation completes.
|
|
2630
2542
|
*/
|
|
2631
|
-
close(callback
|
|
2632
|
-
if (!this.isCreated ||
|
|
2543
|
+
close(callback) {
|
|
2544
|
+
if (!this.isCreated ||
|
|
2545
|
+
!this.options ||
|
|
2546
|
+
!this.resizeObser ||
|
|
2547
|
+
!this.effSvc)
|
|
2633
2548
|
return;
|
|
2634
2549
|
const rv = this.recyclerView;
|
|
2635
2550
|
rv?.suspend?.();
|
|
@@ -2788,7 +2703,9 @@
|
|
|
2788
2703
|
let maxHeight = configMaxHeight;
|
|
2789
2704
|
let realHeight = Math.min(contentHeight, maxHeight);
|
|
2790
2705
|
const heightOri = spaceBelow - safeMargin;
|
|
2791
|
-
if (realHeight >= configMinHeight
|
|
2706
|
+
if (realHeight >= configMinHeight
|
|
2707
|
+
? heightOri >= configMinHeight
|
|
2708
|
+
: heightOri >= realHeight) {
|
|
2792
2709
|
position = "bottom";
|
|
2793
2710
|
maxHeight = Math.min(spaceBelow - safeMargin, configMaxHeight);
|
|
2794
2711
|
}
|
|
@@ -2888,78 +2805,8 @@
|
|
|
2888
2805
|
*
|
|
2889
2806
|
* @param options - Configuration such as placeholder, accessibility IDs, and flags.
|
|
2890
2807
|
*/
|
|
2891
|
-
constructor(options
|
|
2808
|
+
constructor(options) {
|
|
2892
2809
|
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
2810
|
this.options = options;
|
|
2964
2811
|
if (options)
|
|
2965
2812
|
this.initialize(options);
|
|
@@ -3225,17 +3072,7 @@
|
|
|
3225
3072
|
*
|
|
3226
3073
|
* @param query - CSS selector or element to control. When `null`, instance starts unbound.
|
|
3227
3074
|
*/
|
|
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;
|
|
3075
|
+
constructor(query) {
|
|
3239
3076
|
/**
|
|
3240
3077
|
* Internal animation flag set while a timed animation is in-flight.
|
|
3241
3078
|
*
|
|
@@ -3419,7 +3256,9 @@
|
|
|
3419
3256
|
const { duration = 200, onComplete } = config;
|
|
3420
3257
|
const currentHeight = this.element.offsetHeight;
|
|
3421
3258
|
const currentTop = this.element.offsetTop;
|
|
3422
|
-
const position = this.element.classList.contains("position-top")
|
|
3259
|
+
const position = this.element.classList.contains("position-top")
|
|
3260
|
+
? "top"
|
|
3261
|
+
: "bottom";
|
|
3423
3262
|
const isScrollable = this.element.scrollHeight - this.element.offsetHeight > 0;
|
|
3424
3263
|
const finalTop = position === "top" ? currentTop + currentHeight : currentTop;
|
|
3425
3264
|
requestAnimationFrame(() => {
|
|
@@ -3557,7 +3396,9 @@
|
|
|
3557
3396
|
return this;
|
|
3558
3397
|
this.cancel();
|
|
3559
3398
|
const { duration = 200, width, left, top, maxHeight, realHeight, position = "bottom", animate = true, onComplete, } = config;
|
|
3560
|
-
const currentPosition = this.element.classList.contains("position-top")
|
|
3399
|
+
const currentPosition = this.element.classList.contains("position-top")
|
|
3400
|
+
? "top"
|
|
3401
|
+
: "bottom";
|
|
3561
3402
|
const isPositionChanged = currentPosition !== position;
|
|
3562
3403
|
const isScrollable = this.element.scrollHeight > maxHeight;
|
|
3563
3404
|
this.element.classList.toggle("position-top", position === "top");
|
|
@@ -3673,25 +3514,11 @@
|
|
|
3673
3514
|
* - Calls {@link Lifecycle.init} immediately (`NEW → INITIALIZED`).
|
|
3674
3515
|
*
|
|
3675
3516
|
* @param {TOptions} options - Configuration options for the model.
|
|
3676
|
-
* @param {TTarget
|
|
3677
|
-
* @param {TView
|
|
3517
|
+
* @param {TTarget} [targetElement=null] - Optional DOM element to bind.
|
|
3518
|
+
* @param {TView} [view=null] - Optional view responsible for rendering this model.
|
|
3678
3519
|
*/
|
|
3679
|
-
constructor(options, targetElement
|
|
3520
|
+
constructor(options, targetElement, view) {
|
|
3680
3521
|
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
3522
|
/**
|
|
3696
3523
|
* Position index used by list infrastructure for ordering/tracking.
|
|
3697
3524
|
* Semantics are library-specific (e.g., top-level index or adapter position).
|
|
@@ -3723,7 +3550,7 @@
|
|
|
3723
3550
|
* - Assigns {@link targetElement}.
|
|
3724
3551
|
* - Calls {@link Lifecycle.update} (guarded by lifecycle state).
|
|
3725
3552
|
*
|
|
3726
|
-
* @param {TTarget
|
|
3553
|
+
* @param {TTarget} targetElement - The new DOM element to associate with this model.
|
|
3727
3554
|
* @returns {void}
|
|
3728
3555
|
*/
|
|
3729
3556
|
updateTarget(targetElement) {
|
|
@@ -3923,7 +3750,7 @@
|
|
|
3923
3750
|
if (this.is(LifecycleState.DESTROYED)) {
|
|
3924
3751
|
return;
|
|
3925
3752
|
}
|
|
3926
|
-
this.items.forEach(item => {
|
|
3753
|
+
this.items.forEach((item) => {
|
|
3927
3754
|
item.destroy();
|
|
3928
3755
|
});
|
|
3929
3756
|
this.items = [];
|
|
@@ -4047,10 +3874,10 @@
|
|
|
4047
3874
|
* Creates an option model.
|
|
4048
3875
|
*
|
|
4049
3876
|
* @param {SelectiveOptions} options - Shared configuration for models/views.
|
|
4050
|
-
* @param {HTMLOptionElement
|
|
4051
|
-
* @param {OptionView
|
|
3877
|
+
* @param {HTMLOptionElement} [targetElement=null] - Backing `<option>` element.
|
|
3878
|
+
* @param {OptionView} [view=null] - Optional view used to render this model.
|
|
4052
3879
|
*/
|
|
4053
|
-
constructor(options, targetElement
|
|
3880
|
+
constructor(options, targetElement, view) {
|
|
4054
3881
|
super(options, targetElement, view);
|
|
4055
3882
|
/**
|
|
4056
3883
|
* External selection subscribers (emitted by the {@link selected} setter).
|
|
@@ -4074,11 +3901,6 @@
|
|
|
4074
3901
|
this._visible = true;
|
|
4075
3902
|
/** Highlight flag used for keyboard navigation / hover. */
|
|
4076
3903
|
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
3904
|
}
|
|
4083
3905
|
/**
|
|
4084
3906
|
* Initializes the model and precomputes the search key.
|
|
@@ -4207,6 +4029,32 @@
|
|
|
4207
4029
|
}
|
|
4208
4030
|
iEvents.callEvent([this, value], ...this.privOnInternalSelected);
|
|
4209
4031
|
}
|
|
4032
|
+
/**
|
|
4033
|
+
* Resolved display mask for this option.
|
|
4034
|
+
*
|
|
4035
|
+
* The mask is the primary render label used by the UI layer and supports
|
|
4036
|
+
* optional inline tag translation / rich HTML rendering.
|
|
4037
|
+
*
|
|
4038
|
+
* Source priority:
|
|
4039
|
+
* 1. `data-mask` (`dataset.mask`)
|
|
4040
|
+
* 2. Native `<option>` text content (`targetElement.text`)
|
|
4041
|
+
*
|
|
4042
|
+
* Processing pipeline:
|
|
4043
|
+
* - Raw content is first passed through {@link Libs.tagTranslate}.
|
|
4044
|
+
* - When `options.allowHtml === true`, translated HTML is preserved.
|
|
4045
|
+
* - Otherwise, all markup is stripped via {@link Libs.stripHtml}.
|
|
4046
|
+
*
|
|
4047
|
+
* Unlike {@link text}, this getter prioritizes the custom dataset mask,
|
|
4048
|
+
* making it suitable for display overrides without mutating the native
|
|
4049
|
+
* `<option>` label.
|
|
4050
|
+
*
|
|
4051
|
+
* @returns {string} Render-ready option label.
|
|
4052
|
+
*/
|
|
4053
|
+
get mask() {
|
|
4054
|
+
const raw = this.dataset?.mask ?? this.targetElement?.text ?? "";
|
|
4055
|
+
const translated = Libs.tagTranslate(raw);
|
|
4056
|
+
return this.options.allowHtml ? translated : Libs.stripHtml(translated);
|
|
4057
|
+
}
|
|
4210
4058
|
/**
|
|
4211
4059
|
* Display label for rendering (with tag translation and HTML policy).
|
|
4212
4060
|
*
|
|
@@ -4220,7 +4068,7 @@
|
|
|
4220
4068
|
* @returns {string}
|
|
4221
4069
|
*/
|
|
4222
4070
|
get text() {
|
|
4223
|
-
const raw = this.
|
|
4071
|
+
const raw = this.targetElement?.text ?? this.dataset?.mask ?? "";
|
|
4224
4072
|
const translated = Libs.tagTranslate(raw);
|
|
4225
4073
|
return this.options.allowHtml ? translated : Libs.stripHtml(translated);
|
|
4226
4074
|
}
|
|
@@ -4233,7 +4081,9 @@
|
|
|
4233
4081
|
* @returns {string}
|
|
4234
4082
|
*/
|
|
4235
4083
|
get textContent() {
|
|
4236
|
-
return this.options.allowHtml
|
|
4084
|
+
return this.options.allowHtml
|
|
4085
|
+
? Libs.stripHtml(this.text).trim()
|
|
4086
|
+
: this.text.trim();
|
|
4237
4087
|
}
|
|
4238
4088
|
/**
|
|
4239
4089
|
* Dataset object of the backing `<option>` element.
|
|
@@ -4414,8 +4264,6 @@
|
|
|
4414
4264
|
constructor(options) {
|
|
4415
4265
|
super();
|
|
4416
4266
|
this.privModelList = [];
|
|
4417
|
-
this.privAdapterHandle = null;
|
|
4418
|
-
this.privRecyclerViewHandle = null;
|
|
4419
4267
|
this.options = null;
|
|
4420
4268
|
this.oldPosition = 0;
|
|
4421
4269
|
this.options = options;
|
|
@@ -4467,7 +4315,9 @@
|
|
|
4467
4315
|
else if (data.tagName === "OPTION") {
|
|
4468
4316
|
const optionModel = new OptionModel(this.options, data);
|
|
4469
4317
|
const parentGroup = data["__parentGroup"];
|
|
4470
|
-
if (parentGroup &&
|
|
4318
|
+
if (parentGroup &&
|
|
4319
|
+
currentGroup &&
|
|
4320
|
+
parentGroup === currentGroup.targetElement) {
|
|
4471
4321
|
currentGroup.addItem(optionModel);
|
|
4472
4322
|
optionModel.group = currentGroup;
|
|
4473
4323
|
}
|
|
@@ -4892,44 +4742,10 @@
|
|
|
4892
4742
|
/**
|
|
4893
4743
|
* Creates an AccessoryBox and optionally initializes it with configuration.
|
|
4894
4744
|
*
|
|
4895
|
-
* @param {SelectiveOptions
|
|
4745
|
+
* @param {SelectiveOptions} [options=null] - Configuration controlling placement/visibility and texts.
|
|
4896
4746
|
*/
|
|
4897
|
-
constructor(options
|
|
4747
|
+
constructor(options) {
|
|
4898
4748
|
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
4749
|
/**
|
|
4934
4750
|
* Current selected option models rendered as chips.
|
|
4935
4751
|
* This is a cached snapshot used for show/hide decisions and re-rendering.
|
|
@@ -5237,15 +5053,6 @@
|
|
|
5237
5053
|
*/
|
|
5238
5054
|
constructor(selectElement, modelManager, selectBox) {
|
|
5239
5055
|
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
5056
|
/**
|
|
5250
5057
|
* SelectBox handle used by custom data builder functions that require Selective context.
|
|
5251
5058
|
* NOTE: This is a reference; the controller does not own/destroy the SelectBox.
|
|
@@ -5309,7 +5116,11 @@
|
|
|
5309
5116
|
*/
|
|
5310
5117
|
async loadByValues(values) {
|
|
5311
5118
|
if (!this.ajaxConfig) {
|
|
5312
|
-
return {
|
|
5119
|
+
return {
|
|
5120
|
+
success: false,
|
|
5121
|
+
items: [],
|
|
5122
|
+
message: "Ajax not configured",
|
|
5123
|
+
};
|
|
5313
5124
|
}
|
|
5314
5125
|
const valuesArray = Array.isArray(values) ? values : [values];
|
|
5315
5126
|
if (valuesArray.length === 0)
|
|
@@ -5326,7 +5137,7 @@
|
|
|
5326
5137
|
load_by_values: "1",
|
|
5327
5138
|
...(typeof cfg.data === "function"
|
|
5328
5139
|
? cfg.data.bind(this.selectBox.Selective.find(this.selectBox.container.targetElement))("", 0)
|
|
5329
|
-
: cfg.data ?? {}),
|
|
5140
|
+
: (cfg.data ?? {})),
|
|
5330
5141
|
};
|
|
5331
5142
|
}
|
|
5332
5143
|
let response;
|
|
@@ -5336,7 +5147,9 @@
|
|
|
5336
5147
|
response = await fetch(cfg.url, {
|
|
5337
5148
|
method: "POST",
|
|
5338
5149
|
body: formData,
|
|
5339
|
-
headers: {
|
|
5150
|
+
headers: {
|
|
5151
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
5152
|
+
},
|
|
5340
5153
|
});
|
|
5341
5154
|
}
|
|
5342
5155
|
else {
|
|
@@ -5373,7 +5186,7 @@
|
|
|
5373
5186
|
* Configures AJAX settings used for remote searching and pagination.
|
|
5374
5187
|
* Setting `null` disables AJAX mode and causes {@link search} to use local filtering.
|
|
5375
5188
|
*
|
|
5376
|
-
* @param {AjaxConfig
|
|
5189
|
+
* @param {AjaxConfig} config - AJAX configuration (endpoint, method, data builders, keepSelected, ...).
|
|
5377
5190
|
* @returns {void}
|
|
5378
5191
|
*/
|
|
5379
5192
|
setAjax(config) {
|
|
@@ -5563,7 +5376,12 @@
|
|
|
5563
5376
|
payload.selectedValue = selectedValues;
|
|
5564
5377
|
}
|
|
5565
5378
|
else {
|
|
5566
|
-
payload = {
|
|
5379
|
+
payload = {
|
|
5380
|
+
search: keyword,
|
|
5381
|
+
page,
|
|
5382
|
+
selectedValue: selectedValues,
|
|
5383
|
+
...(cfg.data ?? {}),
|
|
5384
|
+
};
|
|
5567
5385
|
}
|
|
5568
5386
|
try {
|
|
5569
5387
|
let response;
|
|
@@ -5573,13 +5391,17 @@
|
|
|
5573
5391
|
response = await fetch(cfg.url, {
|
|
5574
5392
|
method: "POST",
|
|
5575
5393
|
body: formData,
|
|
5576
|
-
headers: {
|
|
5394
|
+
headers: {
|
|
5395
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
5396
|
+
},
|
|
5577
5397
|
signal: this.abortController.signal,
|
|
5578
5398
|
});
|
|
5579
5399
|
}
|
|
5580
5400
|
else {
|
|
5581
5401
|
const params = new URLSearchParams(payload).toString();
|
|
5582
|
-
response = await fetch(`${cfg.url}?${params}`, {
|
|
5402
|
+
response = await fetch(`${cfg.url}?${params}`, {
|
|
5403
|
+
signal: this.abortController.signal,
|
|
5404
|
+
});
|
|
5583
5405
|
}
|
|
5584
5406
|
const data = await response.json();
|
|
5585
5407
|
const result = this.parseResponse(data);
|
|
@@ -5653,7 +5475,7 @@
|
|
|
5653
5475
|
hasPagination = true;
|
|
5654
5476
|
page = parseInt(data.page ?? 0, 10);
|
|
5655
5477
|
totalPages = parseInt(data.totalPages ?? data.total_page ?? 1, 10);
|
|
5656
|
-
hasMore = data.hasMore ??
|
|
5478
|
+
hasMore = data.hasMore ?? page < totalPages - 1;
|
|
5657
5479
|
}
|
|
5658
5480
|
}
|
|
5659
5481
|
else if (Array.isArray(data)) {
|
|
@@ -5664,23 +5486,39 @@
|
|
|
5664
5486
|
if (data.pagination) {
|
|
5665
5487
|
hasPagination = true;
|
|
5666
5488
|
page = parseInt(data.pagination.page ?? 0, 10);
|
|
5667
|
-
totalPages = parseInt(data.pagination.totalPages ??
|
|
5668
|
-
|
|
5489
|
+
totalPages = parseInt(data.pagination.totalPages ??
|
|
5490
|
+
data.pagination.total_page ??
|
|
5491
|
+
1, 10);
|
|
5492
|
+
hasMore = data.pagination.hasMore ?? page < totalPages - 1;
|
|
5669
5493
|
}
|
|
5670
5494
|
}
|
|
5671
5495
|
const normalized = items.map((item) => {
|
|
5672
|
-
if (item instanceof HTMLOptionElement ||
|
|
5496
|
+
if (item instanceof HTMLOptionElement ||
|
|
5497
|
+
item instanceof HTMLOptGroupElement)
|
|
5673
5498
|
return item;
|
|
5674
|
-
if (item.type === "optgroup" ||
|
|
5499
|
+
if (item.type === "optgroup" ||
|
|
5500
|
+
item.isGroup ||
|
|
5501
|
+
item.group ||
|
|
5502
|
+
item.label) {
|
|
5675
5503
|
const label = item.label ?? item.name ?? item.title ?? "";
|
|
5676
5504
|
const dataObj = item.data ?? {};
|
|
5677
5505
|
const opts = (item.options ?? item.items ?? []).map((opt) => ({
|
|
5678
5506
|
value: opt.value ?? opt.id ?? opt.key ?? "",
|
|
5679
|
-
text: opt.text ??
|
|
5507
|
+
text: opt.text ??
|
|
5508
|
+
opt.label ??
|
|
5509
|
+
opt.name ??
|
|
5510
|
+
opt.title ??
|
|
5511
|
+
"",
|
|
5680
5512
|
selected: opt.selected ?? false,
|
|
5681
|
-
data: opt.data ??
|
|
5513
|
+
data: opt.data ??
|
|
5514
|
+
(opt.imgsrc ? { imgsrc: opt.imgsrc } : {}),
|
|
5682
5515
|
}));
|
|
5683
|
-
return {
|
|
5516
|
+
return {
|
|
5517
|
+
type: "optgroup",
|
|
5518
|
+
label,
|
|
5519
|
+
data: dataObj,
|
|
5520
|
+
options: opts,
|
|
5521
|
+
};
|
|
5684
5522
|
}
|
|
5685
5523
|
const dataObj = item.data ?? {};
|
|
5686
5524
|
if (item?.imgsrc)
|
|
@@ -5722,9 +5560,12 @@
|
|
|
5722
5560
|
select.innerHTML = "";
|
|
5723
5561
|
items.forEach((item) => {
|
|
5724
5562
|
// Skip empty item (defensive guard)
|
|
5725
|
-
if ((item["type"] === "option" || !item["type"]) &&
|
|
5563
|
+
if ((item["type"] === "option" || !item["type"]) &&
|
|
5564
|
+
item["value"] === "" &&
|
|
5565
|
+
item["text"] === "")
|
|
5726
5566
|
return;
|
|
5727
|
-
if (item instanceof HTMLOptionElement ||
|
|
5567
|
+
if (item instanceof HTMLOptionElement ||
|
|
5568
|
+
item instanceof HTMLOptGroupElement) {
|
|
5728
5569
|
select.appendChild(item);
|
|
5729
5570
|
return;
|
|
5730
5571
|
}
|
|
@@ -5746,7 +5587,8 @@
|
|
|
5746
5587
|
option.dataset[key] = String(opt.data[key]);
|
|
5747
5588
|
});
|
|
5748
5589
|
}
|
|
5749
|
-
if (opt.selected ||
|
|
5590
|
+
if (opt.selected ||
|
|
5591
|
+
(keepSelected && oldSelected.includes(option.value))) {
|
|
5750
5592
|
option.selected = true;
|
|
5751
5593
|
}
|
|
5752
5594
|
optgroup.appendChild(option);
|
|
@@ -5763,7 +5605,8 @@
|
|
|
5763
5605
|
option.dataset[key] = String(item.data[key]);
|
|
5764
5606
|
});
|
|
5765
5607
|
}
|
|
5766
|
-
if (item.selected ||
|
|
5608
|
+
if (item.selected ||
|
|
5609
|
+
(keepSelected && oldSelected.includes(option.value))) {
|
|
5767
5610
|
option.selected = true;
|
|
5768
5611
|
}
|
|
5769
5612
|
select.appendChild(option);
|
|
@@ -5839,16 +5682,6 @@
|
|
|
5839
5682
|
* @param {HTMLSelectElement} select - The `<select>` element to observe for mutations.
|
|
5840
5683
|
*/
|
|
5841
5684
|
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
5685
|
/**
|
|
5853
5686
|
* Debounce delay in milliseconds.
|
|
5854
5687
|
*
|
|
@@ -5989,16 +5822,12 @@
|
|
|
5989
5822
|
* @param element - The element whose `data-*` attributes will be observed.
|
|
5990
5823
|
*/
|
|
5991
5824
|
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
5825
|
this.element = element;
|
|
5998
5826
|
this.observer = new MutationObserver((mutations) => {
|
|
5999
5827
|
let datasetChanged = false;
|
|
6000
5828
|
for (const mutation of mutations) {
|
|
6001
|
-
if (mutation.type === "attributes" &&
|
|
5829
|
+
if (mutation.type === "attributes" &&
|
|
5830
|
+
mutation.attributeName?.startsWith("data-")) {
|
|
6002
5831
|
datasetChanged = true;
|
|
6003
5832
|
break;
|
|
6004
5833
|
}
|
|
@@ -6147,6 +5976,16 @@
|
|
|
6147
5976
|
* This flag is intentionally generic and is coordinated by higher-level components.
|
|
6148
5977
|
*/
|
|
6149
5978
|
this.isSkipEvent = false;
|
|
5979
|
+
/**
|
|
5980
|
+
* Tracks all scheduler keys registered by this adapter instance via
|
|
5981
|
+
* {@link onPropChanging} and {@link onPropChanged}.
|
|
5982
|
+
*
|
|
5983
|
+
* Used during {@link destroy} to clean up all associated pipelines
|
|
5984
|
+
* from the global {@link Libs.callbackScheduler}.
|
|
5985
|
+
*
|
|
5986
|
+
* Keys are deduplicated automatically by Set semantics.
|
|
5987
|
+
*/
|
|
5988
|
+
this.callbackSchedulerList = new Set();
|
|
6150
5989
|
this.items = items;
|
|
6151
5990
|
this.init();
|
|
6152
5991
|
}
|
|
@@ -6188,7 +6027,9 @@
|
|
|
6188
6027
|
* @see {@link changingProp}
|
|
6189
6028
|
*/
|
|
6190
6029
|
onPropChanging(propName, callback) {
|
|
6191
|
-
|
|
6030
|
+
const key = `${propName}ing_${this.adapterKey}`;
|
|
6031
|
+
Libs.callbackScheduler.on(key, callback, { debounce: 0 });
|
|
6032
|
+
this.callbackSchedulerList.add(key);
|
|
6192
6033
|
}
|
|
6193
6034
|
/**
|
|
6194
6035
|
* Registers a **post-change** callback for a property pipeline.
|
|
@@ -6204,7 +6045,11 @@
|
|
|
6204
6045
|
* @see {@link changeProp}
|
|
6205
6046
|
*/
|
|
6206
6047
|
onPropChanged(propName, callback) {
|
|
6207
|
-
|
|
6048
|
+
const key = `${propName}_${this.adapterKey}`;
|
|
6049
|
+
Libs.callbackScheduler.on(key, callback, {
|
|
6050
|
+
debounce: 0,
|
|
6051
|
+
});
|
|
6052
|
+
this.callbackSchedulerList.add(key);
|
|
6208
6053
|
}
|
|
6209
6054
|
/**
|
|
6210
6055
|
* Triggers the **post-change** pipeline for a given property.
|
|
@@ -6239,7 +6084,7 @@
|
|
|
6239
6084
|
*
|
|
6240
6085
|
* @param {HTMLElement} parent - Container element that will host the viewer.
|
|
6241
6086
|
* @param {TItem} item - The model for which the viewer is created.
|
|
6242
|
-
* @returns {TViewer
|
|
6087
|
+
* @returns {TViewer} The created viewer instance; `null` by default.
|
|
6243
6088
|
*/
|
|
6244
6089
|
viewHolder(parent, item) {
|
|
6245
6090
|
return null;
|
|
@@ -6345,11 +6190,16 @@
|
|
|
6345
6190
|
if (this.is(LifecycleState.DESTROYED)) {
|
|
6346
6191
|
return;
|
|
6347
6192
|
}
|
|
6193
|
+
this.callbackSchedulerList.forEach((key) => {
|
|
6194
|
+
Libs.callbackScheduler.off(key);
|
|
6195
|
+
});
|
|
6196
|
+
this.callbackSchedulerList.clear();
|
|
6348
6197
|
this.recyclerView = null;
|
|
6349
|
-
this.items.forEach(item => {
|
|
6198
|
+
this.items.forEach((item) => {
|
|
6350
6199
|
item?.destroy?.();
|
|
6351
6200
|
});
|
|
6352
6201
|
this.items = [];
|
|
6202
|
+
super.destroy();
|
|
6353
6203
|
}
|
|
6354
6204
|
}
|
|
6355
6205
|
|
|
@@ -6401,20 +6251,6 @@
|
|
|
6401
6251
|
*/
|
|
6402
6252
|
constructor(parent) {
|
|
6403
6253
|
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
6254
|
this.parent = parent;
|
|
6419
6255
|
this.init();
|
|
6420
6256
|
}
|
|
@@ -6515,29 +6351,6 @@
|
|
|
6515
6351
|
*/
|
|
6516
6352
|
constructor(parent, options) {
|
|
6517
6353
|
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
6354
|
this.options = options;
|
|
6542
6355
|
}
|
|
6543
6356
|
/**
|
|
@@ -6631,10 +6444,10 @@
|
|
|
6631
6444
|
* - Safe to call multiple times with same value (idempotent).
|
|
6632
6445
|
*
|
|
6633
6446
|
* @public
|
|
6634
|
-
* @param {string
|
|
6447
|
+
* @param {string} [label=null] - New label to display; `null` preserves current label.
|
|
6635
6448
|
* @returns {void}
|
|
6636
6449
|
*/
|
|
6637
|
-
updateLabel(label
|
|
6450
|
+
updateLabel(label) {
|
|
6638
6451
|
if (!this.view)
|
|
6639
6452
|
return;
|
|
6640
6453
|
const headerEl = this.view.tags.GroupHeader;
|
|
@@ -6681,7 +6494,7 @@
|
|
|
6681
6494
|
if (!this.view)
|
|
6682
6495
|
return;
|
|
6683
6496
|
const items = this.view.tags.GroupItems;
|
|
6684
|
-
const visibleItems = Array.from(items.children).filter(child => !child.classList.contains("hide"));
|
|
6497
|
+
const visibleItems = Array.from(items.children).filter((child) => !child.classList.contains("hide"));
|
|
6685
6498
|
this.view.view.classList.toggle("hide", visibleItems.length === 0);
|
|
6686
6499
|
}
|
|
6687
6500
|
/**
|
|
@@ -6795,57 +6608,6 @@
|
|
|
6795
6608
|
*/
|
|
6796
6609
|
constructor(parent, options) {
|
|
6797
6610
|
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
6611
|
/**
|
|
6850
6612
|
* Flag indicating whether the initial render has completed.
|
|
6851
6613
|
*
|
|
@@ -7010,24 +6772,30 @@
|
|
|
7010
6772
|
* - Each changed property triggers {@link applyPartialChange} individually.
|
|
7011
6773
|
*
|
|
7012
6774
|
* @public
|
|
7013
|
-
* @param {OptionConfigPatch
|
|
6775
|
+
* @param {OptionConfigPatch} config - Partial configuration patch; `null` is no-op.
|
|
7014
6776
|
* @returns {void}
|
|
7015
6777
|
*/
|
|
7016
6778
|
set optionConfig(config) {
|
|
7017
6779
|
if (!config || !this.configProxy || !this.config)
|
|
7018
6780
|
return;
|
|
7019
6781
|
const changes = {};
|
|
7020
|
-
if (config.imageWidth !== undefined &&
|
|
6782
|
+
if (config.imageWidth !== undefined &&
|
|
6783
|
+
config.imageWidth !== this.config.imageWidth)
|
|
7021
6784
|
changes.imageWidth = config.imageWidth;
|
|
7022
|
-
if (config.imageHeight !== undefined &&
|
|
6785
|
+
if (config.imageHeight !== undefined &&
|
|
6786
|
+
config.imageHeight !== this.config.imageHeight)
|
|
7023
6787
|
changes.imageHeight = config.imageHeight;
|
|
7024
|
-
if (config.imageBorderRadius !== undefined &&
|
|
6788
|
+
if (config.imageBorderRadius !== undefined &&
|
|
6789
|
+
config.imageBorderRadius !== this.config.imageBorderRadius)
|
|
7025
6790
|
changes.imageBorderRadius = config.imageBorderRadius;
|
|
7026
|
-
if (config.imagePosition !== undefined &&
|
|
6791
|
+
if (config.imagePosition !== undefined &&
|
|
6792
|
+
config.imagePosition !== this.config.imagePosition)
|
|
7027
6793
|
changes.imagePosition = config.imagePosition;
|
|
7028
|
-
if (config.labelValign !== undefined &&
|
|
6794
|
+
if (config.labelValign !== undefined &&
|
|
6795
|
+
config.labelValign !== this.config.labelValign)
|
|
7029
6796
|
changes.labelValign = config.labelValign;
|
|
7030
|
-
if (config.labelHalign !== undefined &&
|
|
6797
|
+
if (config.labelHalign !== undefined &&
|
|
6798
|
+
config.labelHalign !== this.config.labelHalign)
|
|
7031
6799
|
changes.labelHalign = config.labelHalign;
|
|
7032
6800
|
if (Object.keys(changes).length > 0) {
|
|
7033
6801
|
Object.assign(this.configProxy, changes);
|
|
@@ -7201,9 +6969,11 @@
|
|
|
7201
6969
|
case "imageBorderRadius": {
|
|
7202
6970
|
const img = v.tags?.OptionImage;
|
|
7203
6971
|
if (img) {
|
|
7204
|
-
const styleProp = prop === "imageWidth"
|
|
7205
|
-
|
|
7206
|
-
|
|
6972
|
+
const styleProp = prop === "imageWidth"
|
|
6973
|
+
? "width"
|
|
6974
|
+
: prop === "imageHeight"
|
|
6975
|
+
? "height"
|
|
6976
|
+
: "borderRadius";
|
|
7207
6977
|
img.style[styleProp] = String(newValue);
|
|
7208
6978
|
}
|
|
7209
6979
|
break;
|
|
@@ -7324,15 +7094,6 @@
|
|
|
7324
7094
|
super(items);
|
|
7325
7095
|
/** Whether the adapter operates in multi-selection mode. */
|
|
7326
7096
|
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
7097
|
/**
|
|
7337
7098
|
* Subscribers for aggregated visibility statistics.
|
|
7338
7099
|
* Fired via a debounced scheduler to avoid repeated recomputation during batch updates.
|
|
@@ -7343,11 +7104,6 @@
|
|
|
7343
7104
|
* `-1` indicates "no highlight".
|
|
7344
7105
|
*/
|
|
7345
7106
|
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
7107
|
/** Top-level group models (if any). */
|
|
7352
7108
|
this.groups = [];
|
|
7353
7109
|
/**
|
|
@@ -7436,7 +7192,7 @@
|
|
|
7436
7192
|
* - Performs one-time listener binding guarded by `item.isInit`.
|
|
7437
7193
|
*
|
|
7438
7194
|
* @param {MixedItem} item - {@link GroupModel} or {@link OptionModel}.
|
|
7439
|
-
* @param {GroupView | OptionView
|
|
7195
|
+
* @param {GroupView | OptionView} viewer - The view instance that will render the model.
|
|
7440
7196
|
* @param {number} position - Position in the top-level mixed list.
|
|
7441
7197
|
* @returns {void}
|
|
7442
7198
|
* @override
|
|
@@ -7663,7 +7419,7 @@
|
|
|
7663
7419
|
return;
|
|
7664
7420
|
}
|
|
7665
7421
|
Libs.callbackScheduler.clear(`sche_vis_${this.adapterKey}`);
|
|
7666
|
-
this.groups.forEach(group => {
|
|
7422
|
+
this.groups.forEach((group) => {
|
|
7667
7423
|
group.destroy();
|
|
7668
7424
|
});
|
|
7669
7425
|
this.visibilityChangedCallbacks = [];
|
|
@@ -7743,7 +7499,7 @@
|
|
|
7743
7499
|
* @returns {void}
|
|
7744
7500
|
*/
|
|
7745
7501
|
resetHighlight() {
|
|
7746
|
-
this.setHighlight(0);
|
|
7502
|
+
this.setHighlight(0, false);
|
|
7747
7503
|
}
|
|
7748
7504
|
/**
|
|
7749
7505
|
* Moves highlight among **visible** options and optionally scrolls the new target into view.
|
|
@@ -7786,7 +7542,8 @@
|
|
|
7786
7542
|
* @returns {void}
|
|
7787
7543
|
*/
|
|
7788
7544
|
selectHighlighted() {
|
|
7789
|
-
if (this.currentHighlightIndex > -1 &&
|
|
7545
|
+
if (this.currentHighlightIndex > -1 &&
|
|
7546
|
+
this.flatOptions[this.currentHighlightIndex]) {
|
|
7790
7547
|
const item = this.flatOptions[this.currentHighlightIndex];
|
|
7791
7548
|
if (item.visible) {
|
|
7792
7549
|
const viewEl = item.view?.getView?.();
|
|
@@ -7823,7 +7580,8 @@
|
|
|
7823
7580
|
else {
|
|
7824
7581
|
index = 0;
|
|
7825
7582
|
}
|
|
7826
|
-
if (this.currentHighlightIndex > -1 &&
|
|
7583
|
+
if (this.currentHighlightIndex > -1 &&
|
|
7584
|
+
this.flatOptions[this.currentHighlightIndex]) {
|
|
7827
7585
|
this.flatOptions[this.currentHighlightIndex].highlighted = false;
|
|
7828
7586
|
}
|
|
7829
7587
|
for (let i = index; i < this.flatOptions.length; i++) {
|
|
@@ -7834,12 +7592,13 @@
|
|
|
7834
7592
|
this.currentHighlightIndex = i;
|
|
7835
7593
|
if (isScrollToView) {
|
|
7836
7594
|
const el = item.view?.getView?.();
|
|
7837
|
-
if (el) {
|
|
7838
|
-
el.scrollIntoView({ block:
|
|
7595
|
+
if (el?.isConnected) {
|
|
7596
|
+
el.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
7839
7597
|
}
|
|
7840
7598
|
else {
|
|
7841
|
-
|
|
7842
|
-
|
|
7599
|
+
this.recyclerView?.ensureRendered?.(i, {
|
|
7600
|
+
scrollIntoView: true,
|
|
7601
|
+
});
|
|
7843
7602
|
}
|
|
7844
7603
|
}
|
|
7845
7604
|
this.onHighlightChange(i, item.view?.getView?.()?.id);
|
|
@@ -8068,9 +7827,9 @@
|
|
|
8068
7827
|
*
|
|
8069
7828
|
* Note: The virtualization scaffold is built when an adapter is set via {@link setAdapter}.
|
|
8070
7829
|
*
|
|
8071
|
-
* @param {HTMLDivElement
|
|
7830
|
+
* @param {HTMLDivElement} [viewElement=null] - Optional root container for the recycler view.
|
|
8072
7831
|
*/
|
|
8073
|
-
constructor(viewElement
|
|
7832
|
+
constructor(viewElement) {
|
|
8074
7833
|
super(viewElement);
|
|
8075
7834
|
/**
|
|
8076
7835
|
* Virtualization settings (materialized to `Required<VirtualOptions>`).
|
|
@@ -8106,15 +7865,18 @@
|
|
|
8106
7865
|
this.start = 0;
|
|
8107
7866
|
/** Current window end (inclusive). -1 means not initialized. */
|
|
8108
7867
|
this.end = -1;
|
|
8109
|
-
/** Pending animation frame ids for window and measurement. */
|
|
8110
|
-
this.rafId = null;
|
|
8111
|
-
this.measureRaf = null;
|
|
8112
7868
|
/** Re-entrancy/suspension flags used to prevent feedback loops. */
|
|
8113
7869
|
this.updating = false;
|
|
8114
7870
|
this.suppressResize = false;
|
|
8115
7871
|
this.lastRenderCount = 0;
|
|
8116
7872
|
this.suspended = false;
|
|
8117
7873
|
this.resumeResizeAfter = false;
|
|
7874
|
+
/**
|
|
7875
|
+
* When set, scrollToIndex() will be called after the next measureVisibleAndUpdate()
|
|
7876
|
+
* completes and Fenwick has been updated with real heights.
|
|
7877
|
+
* Set by ensureRendered() and cleared after the corrective scroll fires.
|
|
7878
|
+
*/
|
|
7879
|
+
this.pendingScrollToIndex = null;
|
|
8118
7880
|
/** Small cache for sticky header height (≈16ms TTL) to limit layout reads. */
|
|
8119
7881
|
this.stickyCacheTick = 0;
|
|
8120
7882
|
this.stickyCacheVal = 0;
|
|
@@ -8163,20 +7925,29 @@
|
|
|
8163
7925
|
return;
|
|
8164
7926
|
this.viewElement.replaceChildren();
|
|
8165
7927
|
const nodeMounted = Libs.mountNode({
|
|
8166
|
-
PadTop: {
|
|
8167
|
-
|
|
8168
|
-
|
|
7928
|
+
PadTop: {
|
|
7929
|
+
tag: { node: "div", classList: "seui-virtual-pad-top" },
|
|
7930
|
+
},
|
|
7931
|
+
ItemsHost: {
|
|
7932
|
+
tag: { node: "div", classList: "seui-virtual-items" },
|
|
7933
|
+
},
|
|
7934
|
+
PadBottom: {
|
|
7935
|
+
tag: { node: "div", classList: "seui-virtual-pad-bottom" },
|
|
7936
|
+
},
|
|
8169
7937
|
}, this.viewElement);
|
|
8170
7938
|
this.PadTop = nodeMounted.PadTop;
|
|
8171
7939
|
this.ItemsHost = nodeMounted.ItemsHost;
|
|
8172
7940
|
this.PadBottom = nodeMounted.PadBottom;
|
|
8173
|
-
this.scrollEl =
|
|
8174
|
-
|
|
8175
|
-
|
|
7941
|
+
this.scrollEl =
|
|
7942
|
+
this.opts.scrollEl ??
|
|
7943
|
+
this.viewElement.closest(".seui-popup") ??
|
|
7944
|
+
this.viewElement.parentElement;
|
|
8176
7945
|
if (!this.scrollEl)
|
|
8177
7946
|
throw new Error("VirtualRecyclerView: scrollEl not found");
|
|
8178
7947
|
this.boundOnScroll = this.onScroll.bind(this);
|
|
8179
|
-
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7948
|
+
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7949
|
+
passive: true,
|
|
7950
|
+
});
|
|
8180
7951
|
this.refresh(false);
|
|
8181
7952
|
this.attachResizeObserverOnce();
|
|
8182
7953
|
adapter?.onVisibilityChanged?.(() => this.refreshItem());
|
|
@@ -8215,7 +7986,9 @@
|
|
|
8215
7986
|
resume() {
|
|
8216
7987
|
this.suspended = false;
|
|
8217
7988
|
if (this.scrollEl && this.boundOnScroll) {
|
|
8218
|
-
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7989
|
+
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7990
|
+
passive: true,
|
|
7991
|
+
});
|
|
8219
7992
|
}
|
|
8220
7993
|
if (this.resumeResizeAfter) {
|
|
8221
7994
|
this.attachResizeObserverOnce();
|
|
@@ -8270,9 +8043,19 @@
|
|
|
8270
8043
|
* @returns {void}
|
|
8271
8044
|
*/
|
|
8272
8045
|
ensureRendered(index, opt) {
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
this.
|
|
8046
|
+
if (!opt?.scrollIntoView) {
|
|
8047
|
+
// No scroll requested — mount only (legacy path, used by probes).
|
|
8048
|
+
this.mountRange(index, index);
|
|
8049
|
+
return;
|
|
8050
|
+
}
|
|
8051
|
+
// Pass 1: instant — brings window to vicinity, triggers measure.
|
|
8052
|
+
// Must be instant so Pass 2 smooth scroll isn't interrupted mid-animation.
|
|
8053
|
+
this.scrollToIndex(index, "instant");
|
|
8054
|
+
// Pass 2: measureVisibleAndUpdate() will consume this and fire a corrective
|
|
8055
|
+
// smooth scroll after Fenwick has been updated with real heights.
|
|
8056
|
+
// rv.resume() is guaranteed to run before this callback (popup.open onComplete
|
|
8057
|
+
// calls rv.resume() first), so the window is already rendered when we arrive here.
|
|
8058
|
+
this.pendingScrollToIndex = index;
|
|
8276
8059
|
}
|
|
8277
8060
|
/**
|
|
8278
8061
|
* Scrolls the scroll container to align the item at `index` into view.
|
|
@@ -8286,15 +8069,25 @@
|
|
|
8286
8069
|
* @param {number} index - Item index to bring into view.
|
|
8287
8070
|
* @returns {void}
|
|
8288
8071
|
*/
|
|
8289
|
-
scrollToIndex(index) {
|
|
8072
|
+
scrollToIndex(index, behavior = "smooth") {
|
|
8290
8073
|
const count = this.adapter?.itemCount?.() ?? 0;
|
|
8291
8074
|
if (count <= 0)
|
|
8292
8075
|
return;
|
|
8293
8076
|
const topInContainer = this.offsetTopOf(index);
|
|
8294
8077
|
const containerTop = this.containerTopInScroll();
|
|
8295
|
-
const
|
|
8078
|
+
const stickyH = this.stickyTopHeight();
|
|
8079
|
+
const viewportH = Math.max(0, this.scrollEl.clientHeight - stickyH);
|
|
8080
|
+
// item height from cache, or current estimate for unmeasured items
|
|
8081
|
+
const est = this.getEstimate();
|
|
8082
|
+
const itemH = this.heightCache[index] ?? est;
|
|
8083
|
+
// Align item center to viewport center (below any sticky header).
|
|
8084
|
+
// viewportH already excludes stickyH, so no further offset needed.
|
|
8085
|
+
// Equivalent to scrollIntoView({ block: "center" }).
|
|
8086
|
+
const centeredTarget = containerTop + topInContainer
|
|
8087
|
+
- (viewportH - itemH) / 3;
|
|
8296
8088
|
const maxScroll = Math.max(0, this.scrollEl.scrollHeight - this.scrollEl.clientHeight);
|
|
8297
|
-
|
|
8089
|
+
const clamped = Math.min(Math.max(0, centeredTarget), maxScroll);
|
|
8090
|
+
this.scrollEl.scrollTo({ top: clamped, behavior });
|
|
8298
8091
|
}
|
|
8299
8092
|
/**
|
|
8300
8093
|
* Disposes runtime resources without destroying the instance.
|
|
@@ -8313,7 +8106,7 @@
|
|
|
8313
8106
|
this.scrollEl.removeEventListener("scroll", this.boundOnScroll);
|
|
8314
8107
|
}
|
|
8315
8108
|
this.resizeObs?.disconnect();
|
|
8316
|
-
this.created.forEach(el => el.remove());
|
|
8109
|
+
this.created.forEach((el) => el.remove());
|
|
8317
8110
|
this.created.clear();
|
|
8318
8111
|
}
|
|
8319
8112
|
/**
|
|
@@ -8361,9 +8154,22 @@
|
|
|
8361
8154
|
if (count <= 0)
|
|
8362
8155
|
return;
|
|
8363
8156
|
this.suspend();
|
|
8364
|
-
this.
|
|
8157
|
+
this.pendingScrollToIndex = null;
|
|
8158
|
+
// When visibility changes (search filter applied or cleared), heightCache may
|
|
8159
|
+
// contain heights measured while only a subset of items was visible. Re-using
|
|
8160
|
+
// these partial measurements in rebuildFenwick() causes incorrect prefix sums
|
|
8161
|
+
// (e.g. items measured while scrolled into a filtered window have real heights,
|
|
8162
|
+
// while surrounding items still use estimates — creating an uneven Fenwick).
|
|
8163
|
+
//
|
|
8164
|
+
// Safe fix: clear heightCache entirely on visibility change. The adaptive
|
|
8165
|
+
// estimator will re-seed from probeInitialHeight() on the next render, and
|
|
8166
|
+
// items will be re-measured as they scroll into view.
|
|
8167
|
+
this.heightCache = [];
|
|
8168
|
+
this.measuredSum = 0;
|
|
8169
|
+
this.measuredCount = 0;
|
|
8170
|
+
this.firstMeasured = false;
|
|
8171
|
+
this.resetDOM();
|
|
8365
8172
|
this.cleanupInvisibleItems();
|
|
8366
|
-
this.recomputeMeasuredStats(count);
|
|
8367
8173
|
this.rebuildFenwick(count);
|
|
8368
8174
|
this.start = 0;
|
|
8369
8175
|
this.end = -1;
|
|
@@ -8381,7 +8187,30 @@
|
|
|
8381
8187
|
}
|
|
8382
8188
|
}
|
|
8383
8189
|
/**
|
|
8384
|
-
* Resets
|
|
8190
|
+
* Resets DOM nodes, Fenwick sums, padding, and estimator stats — but preserves {@link heightCache}.
|
|
8191
|
+
*
|
|
8192
|
+
* Use this inside {@link refreshItem} so that {@link recomputeMeasuredStats} can still
|
|
8193
|
+
* read previously measured heights before the Fenwick tree is rebuilt.
|
|
8194
|
+
*
|
|
8195
|
+
* DOM side effects:
|
|
8196
|
+
* - Removes all currently mounted item elements tracked in {@link created}.
|
|
8197
|
+
* - Resets pad heights to `0px`.
|
|
8198
|
+
*
|
|
8199
|
+
* @returns {void}
|
|
8200
|
+
*/
|
|
8201
|
+
resetDOM() {
|
|
8202
|
+
this.created.forEach((el) => el.remove());
|
|
8203
|
+
this.created.clear();
|
|
8204
|
+
this.fenwick.reset(0);
|
|
8205
|
+
this.PadTop.style.height = "0px";
|
|
8206
|
+
this.PadBottom.style.height = "0px";
|
|
8207
|
+
this.firstMeasured = false;
|
|
8208
|
+
}
|
|
8209
|
+
/**
|
|
8210
|
+
* Full reset: clears DOM nodes, Fenwick sums, padding, estimator stats, AND {@link heightCache}.
|
|
8211
|
+
*
|
|
8212
|
+
* Use this for complete teardown (e.g., adapter swap, destroy sequence) where all
|
|
8213
|
+
* cached measurements should be discarded.
|
|
8385
8214
|
*
|
|
8386
8215
|
* DOM side effects:
|
|
8387
8216
|
* - Removes all currently mounted item elements tracked in {@link created}.
|
|
@@ -8390,7 +8219,7 @@
|
|
|
8390
8219
|
* @returns {void}
|
|
8391
8220
|
*/
|
|
8392
8221
|
resetState() {
|
|
8393
|
-
this.created.forEach(el => el.remove());
|
|
8222
|
+
this.created.forEach((el) => el.remove());
|
|
8394
8223
|
this.created.clear();
|
|
8395
8224
|
this.heightCache = [];
|
|
8396
8225
|
this.fenwick.reset(0);
|
|
@@ -8664,8 +8493,12 @@
|
|
|
8664
8493
|
el.setAttribute(VirtualRecyclerView.ATTR_INDEX, String(index));
|
|
8665
8494
|
const prev = el.previousElementSibling;
|
|
8666
8495
|
const next = el.nextElementSibling;
|
|
8667
|
-
const needsReorder = (prev &&
|
|
8668
|
-
|
|
8496
|
+
const needsReorder = (prev &&
|
|
8497
|
+
Number(prev.getAttribute(VirtualRecyclerView.ATTR_INDEX)) >
|
|
8498
|
+
index) ||
|
|
8499
|
+
(next &&
|
|
8500
|
+
Number(next.getAttribute(VirtualRecyclerView.ATTR_INDEX)) <
|
|
8501
|
+
index);
|
|
8669
8502
|
if (needsReorder) {
|
|
8670
8503
|
el.remove();
|
|
8671
8504
|
this.insertIntoHostByIndex(index, el);
|
|
@@ -8687,7 +8520,10 @@
|
|
|
8687
8520
|
if (this.resizeObs)
|
|
8688
8521
|
return;
|
|
8689
8522
|
this.resizeObs = new ResizeObserver(() => {
|
|
8690
|
-
if (this.suppressResize ||
|
|
8523
|
+
if (this.suppressResize ||
|
|
8524
|
+
this.suspended ||
|
|
8525
|
+
!this.adapter ||
|
|
8526
|
+
this.measureRaf != null)
|
|
8691
8527
|
return;
|
|
8692
8528
|
this.measureRaf = requestAnimationFrame(() => {
|
|
8693
8529
|
this.measureRaf = null;
|
|
@@ -8728,6 +8564,15 @@
|
|
|
8728
8564
|
this.rebuildFenwick(count);
|
|
8729
8565
|
this.scheduleUpdateWindow();
|
|
8730
8566
|
}
|
|
8567
|
+
// Corrective scroll: if ensureRendered() registered a target index, fire
|
|
8568
|
+
// scrollToIndex() now that real heights are in Fenwick. Clear the target
|
|
8569
|
+
// first to prevent infinite re-triggering (scrollToIndex may cause another
|
|
8570
|
+
// measure cycle, but heights won't change so changed === false next time).
|
|
8571
|
+
if (this.pendingScrollToIndex !== null) {
|
|
8572
|
+
const target = this.pendingScrollToIndex;
|
|
8573
|
+
this.pendingScrollToIndex = null;
|
|
8574
|
+
this.scrollToIndex(target, "smooth");
|
|
8575
|
+
}
|
|
8731
8576
|
}
|
|
8732
8577
|
/**
|
|
8733
8578
|
* Scroll event handler. Schedules a window update on the next frame.
|
|
@@ -9016,31 +8861,6 @@
|
|
|
9016
8861
|
* @internal
|
|
9017
8862
|
*/
|
|
9018
8863
|
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
8864
|
/**
|
|
9045
8865
|
* Whether the popup/list UI is currently open.
|
|
9046
8866
|
*
|
|
@@ -9072,20 +8892,10 @@
|
|
|
9072
8892
|
* @internal
|
|
9073
8893
|
*/
|
|
9074
8894
|
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
8895
|
/**
|
|
9082
8896
|
* Registered plugins for this SelectBox instance.
|
|
9083
8897
|
*/
|
|
9084
8898
|
this.plugins = [];
|
|
9085
|
-
/**
|
|
9086
|
-
* Cached plugin context for this SelectBox instance.
|
|
9087
|
-
*/
|
|
9088
|
-
this.pluginContext = null;
|
|
9089
8899
|
if (select && Selective)
|
|
9090
8900
|
this.initialize(select, Selective);
|
|
9091
8901
|
}
|
|
@@ -9210,7 +9020,9 @@
|
|
|
9210
9020
|
classList: "seui-view",
|
|
9211
9021
|
tabIndex: 0,
|
|
9212
9022
|
onkeydown: (e) => {
|
|
9213
|
-
if (e.key === "Enter" ||
|
|
9023
|
+
if (e.key === "Enter" ||
|
|
9024
|
+
e.key === " " ||
|
|
9025
|
+
e.key === "ArrowDown") {
|
|
9214
9026
|
e.preventDefault();
|
|
9215
9027
|
this.getAction()?.open();
|
|
9216
9028
|
}
|
|
@@ -9316,6 +9128,7 @@
|
|
|
9316
9128
|
e.preventDefault();
|
|
9317
9129
|
});
|
|
9318
9130
|
Refresher.resizeBox(select, container.tags.ViewPanel);
|
|
9131
|
+
Refresher.resizeBox(select, this.node, true);
|
|
9319
9132
|
select.classList.add("init");
|
|
9320
9133
|
// initial mask
|
|
9321
9134
|
const action = this.getAction();
|
|
@@ -9602,7 +9415,11 @@
|
|
|
9602
9415
|
get value() {
|
|
9603
9416
|
const item_list = this.valueArray;
|
|
9604
9417
|
const valLength = item_list.length;
|
|
9605
|
-
return valLength > 1
|
|
9418
|
+
return valLength > 1
|
|
9419
|
+
? item_list
|
|
9420
|
+
: valLength === 0
|
|
9421
|
+
? ""
|
|
9422
|
+
: item_list[0];
|
|
9606
9423
|
},
|
|
9607
9424
|
get valueArray() {
|
|
9608
9425
|
const item_list = [];
|
|
@@ -9627,7 +9444,7 @@
|
|
|
9627
9444
|
get mask() {
|
|
9628
9445
|
const item_list = [];
|
|
9629
9446
|
superThis.getModelOption(true).forEach((m) => {
|
|
9630
|
-
item_list.push(m.
|
|
9447
|
+
item_list.push(m.mask);
|
|
9631
9448
|
});
|
|
9632
9449
|
return item_list;
|
|
9633
9450
|
},
|
|
@@ -9637,7 +9454,11 @@
|
|
|
9637
9454
|
item_list.push(m.text);
|
|
9638
9455
|
});
|
|
9639
9456
|
const valLength = item_list.length;
|
|
9640
|
-
return valLength > 1
|
|
9457
|
+
return valLength > 1
|
|
9458
|
+
? item_list
|
|
9459
|
+
: valLength === 0
|
|
9460
|
+
? ""
|
|
9461
|
+
: item_list[0];
|
|
9641
9462
|
},
|
|
9642
9463
|
get isOpen() {
|
|
9643
9464
|
return superThis.isOpen;
|
|
@@ -9662,7 +9483,8 @@
|
|
|
9662
9483
|
},
|
|
9663
9484
|
selectAll(_evtToken, trigger = true) {
|
|
9664
9485
|
if (bindedOptions.multiple && bindedOptions.maxSelected > 0) {
|
|
9665
|
-
if (superThis.getModelOption().length >
|
|
9486
|
+
if (superThis.getModelOption().length >
|
|
9487
|
+
bindedOptions.maxSelected)
|
|
9666
9488
|
return;
|
|
9667
9489
|
}
|
|
9668
9490
|
if (this.disabled || this.readonly || !bindedOptions.multiple)
|
|
@@ -9694,7 +9516,7 @@
|
|
|
9694
9516
|
},
|
|
9695
9517
|
deSelectByDataset(_evtToken, dataset, trigger = true) {
|
|
9696
9518
|
if (dataset) {
|
|
9697
|
-
superThis.getModelOption().forEach(optionModel => {
|
|
9519
|
+
superThis.getModelOption().forEach((optionModel) => {
|
|
9698
9520
|
if (optionModel.dataset) {
|
|
9699
9521
|
for (let searchKey in dataset) {
|
|
9700
9522
|
let value = dataset[searchKey];
|
|
@@ -9708,12 +9530,14 @@
|
|
|
9708
9530
|
this.change(false, trigger);
|
|
9709
9531
|
}
|
|
9710
9532
|
},
|
|
9711
|
-
setValue(_evtToken
|
|
9533
|
+
setValue(_evtToken, value, trigger = true, force = false) {
|
|
9712
9534
|
if (!Array.isArray(value))
|
|
9713
9535
|
value = [value];
|
|
9714
9536
|
value = value.filter((v) => v !== "" && v != null);
|
|
9715
9537
|
if (value.length === 0) {
|
|
9716
|
-
superThis
|
|
9538
|
+
superThis
|
|
9539
|
+
.getModelOption()
|
|
9540
|
+
.forEach((m) => (m.selectedNonTrigger = false));
|
|
9717
9541
|
this.change(false, trigger);
|
|
9718
9542
|
return;
|
|
9719
9543
|
}
|
|
@@ -9727,17 +9551,19 @@
|
|
|
9727
9551
|
return;
|
|
9728
9552
|
// AJAX: load missing values
|
|
9729
9553
|
if (container.searchController?.isAjax?.()) {
|
|
9554
|
+
container.searchController.resetPagination();
|
|
9555
|
+
superThis.hasLoadedOnce = false;
|
|
9730
9556
|
const { missing } = container.searchController.checkMissingValues(value);
|
|
9731
9557
|
if (missing.length > 0) {
|
|
9732
9558
|
(async () => {
|
|
9733
9559
|
if (bindedOptions.loadingfield)
|
|
9734
9560
|
container.popup?.showLoading?.();
|
|
9735
9561
|
try {
|
|
9736
|
-
container.searchController.resetPagination();
|
|
9737
9562
|
const result = await container.searchController.loadByValues(missing);
|
|
9738
9563
|
if (result.success && result.items.length > 0) {
|
|
9739
9564
|
result.items.forEach((it) => {
|
|
9740
|
-
if (missing.includes(it.value) ||
|
|
9565
|
+
if (missing.includes(it.value) ||
|
|
9566
|
+
missing.includes(it.text))
|
|
9741
9567
|
it.selected = true;
|
|
9742
9568
|
});
|
|
9743
9569
|
container.searchController.applyAjaxResult?.(result.items, false, false);
|
|
@@ -9748,6 +9574,10 @@
|
|
|
9748
9574
|
}
|
|
9749
9575
|
else if (missing.length > 0) {
|
|
9750
9576
|
console.warn(`Could not load ${missing.length} values:`, missing);
|
|
9577
|
+
setTimeout(() => {
|
|
9578
|
+
container.searchController.resetPagination();
|
|
9579
|
+
this.change(false, trigger);
|
|
9580
|
+
}, 200);
|
|
9751
9581
|
}
|
|
9752
9582
|
}
|
|
9753
9583
|
catch (error) {
|
|
@@ -9776,7 +9606,8 @@
|
|
|
9776
9606
|
this.change(false, trigger);
|
|
9777
9607
|
},
|
|
9778
9608
|
load() {
|
|
9779
|
-
if ((!superThis.hasLoadedOnce || superThis.isBeforeSearch) &&
|
|
9609
|
+
if ((!superThis.hasLoadedOnce || superThis.isBeforeSearch) &&
|
|
9610
|
+
bindedOptions?.ajax) {
|
|
9780
9611
|
container.searchController.resetPagination();
|
|
9781
9612
|
container.popup.showLoading();
|
|
9782
9613
|
superThis.hasLoadedOnce = true;
|
|
@@ -9822,7 +9653,13 @@
|
|
|
9822
9653
|
adapter.resetHighlight();
|
|
9823
9654
|
}
|
|
9824
9655
|
this.load();
|
|
9825
|
-
container.popup.open(
|
|
9656
|
+
container.popup.open(() => {
|
|
9657
|
+
setTimeout(() => {
|
|
9658
|
+
if (selectedOption) {
|
|
9659
|
+
adapter.setHighlight(selectedOption, bindedOptions.autoscroll);
|
|
9660
|
+
}
|
|
9661
|
+
}, 100);
|
|
9662
|
+
}, !container.popup.loadingState.isVisible);
|
|
9826
9663
|
container.searchbox.show();
|
|
9827
9664
|
const ViewPanel = container.tags.ViewPanel;
|
|
9828
9665
|
ViewPanel.setAttribute("aria-expanded", "true");
|
|
@@ -9863,9 +9700,10 @@
|
|
|
9863
9700
|
else
|
|
9864
9701
|
this.open();
|
|
9865
9702
|
},
|
|
9866
|
-
change(_evtToken
|
|
9703
|
+
change(_evtToken, canTrigger = true) {
|
|
9867
9704
|
if (canTrigger) {
|
|
9868
|
-
if (bindedOptions.multiple &&
|
|
9705
|
+
if (bindedOptions.multiple &&
|
|
9706
|
+
bindedOptions.maxSelected > 0) {
|
|
9869
9707
|
if (this.valueArray.length > bindedOptions.maxSelected) {
|
|
9870
9708
|
this.setValue(null, this.oldValue, false, true);
|
|
9871
9709
|
}
|
|
@@ -9900,7 +9738,8 @@
|
|
|
9900
9738
|
},
|
|
9901
9739
|
refreshMask() {
|
|
9902
9740
|
let mask = bindedOptions.placeholder;
|
|
9903
|
-
if (!bindedOptions.multiple &&
|
|
9741
|
+
if (!bindedOptions.multiple &&
|
|
9742
|
+
superThis.getModelOption().length > 0) {
|
|
9904
9743
|
mask = this.mask[0];
|
|
9905
9744
|
}
|
|
9906
9745
|
mask ?? (mask = bindedOptions.placeholder);
|
|
@@ -9932,7 +9771,9 @@
|
|
|
9932
9771
|
.search("")
|
|
9933
9772
|
.then(() => {
|
|
9934
9773
|
container.popup?.triggerResize?.();
|
|
9935
|
-
|
|
9774
|
+
setTimeout(() => {
|
|
9775
|
+
resove(getInstance());
|
|
9776
|
+
}, 60);
|
|
9936
9777
|
})
|
|
9937
9778
|
.catch((err) => {
|
|
9938
9779
|
console.error("Initial ajax load error:", err);
|
|
@@ -9982,7 +9823,8 @@
|
|
|
9982
9823
|
set(value) {
|
|
9983
9824
|
superThis[privateProp] = !!value;
|
|
9984
9825
|
if (superThis.container?.targetElement?.dataset) {
|
|
9985
|
-
superThis.container.targetElement.dataset[prop] =
|
|
9826
|
+
superThis.container.targetElement.dataset[prop] =
|
|
9827
|
+
String(!!value);
|
|
9986
9828
|
}
|
|
9987
9829
|
},
|
|
9988
9830
|
enumerable: true,
|
|
@@ -10010,7 +9852,7 @@
|
|
|
10010
9852
|
* @returns A flat array of option models (possibly filtered).
|
|
10011
9853
|
* @internal
|
|
10012
9854
|
*/
|
|
10013
|
-
getModelOption(isSelected
|
|
9855
|
+
getModelOption(isSelected) {
|
|
10014
9856
|
if (!this.optionModelManager)
|
|
10015
9857
|
return [];
|
|
10016
9858
|
const { modelList } = this.optionModelManager.getResources();
|
|
@@ -10090,14 +9932,6 @@
|
|
|
10090
9932
|
* @internal
|
|
10091
9933
|
*/
|
|
10092
9934
|
this.isActive = false;
|
|
10093
|
-
/**
|
|
10094
|
-
* Underlying DOM {@link MutationObserver} instance.
|
|
10095
|
-
*
|
|
10096
|
-
* `null` when disconnected.
|
|
10097
|
-
*
|
|
10098
|
-
* @internal
|
|
10099
|
-
*/
|
|
10100
|
-
this.observer = null;
|
|
10101
9935
|
/**
|
|
10102
9936
|
* Registered detection callbacks.
|
|
10103
9937
|
*
|
|
@@ -10691,7 +10525,7 @@
|
|
|
10691
10525
|
if (wasObserving)
|
|
10692
10526
|
this.EAObserver?.disconnect();
|
|
10693
10527
|
bindMap.self?.deInit?.();
|
|
10694
|
-
const wrapper =
|
|
10528
|
+
const wrapper = bindMap.container?.element ?? selectElement.parentElement;
|
|
10695
10529
|
selectElement.style.display = "";
|
|
10696
10530
|
selectElement.style.visibility = "";
|
|
10697
10531
|
selectElement.disabled = false;
|
|
@@ -10908,7 +10742,7 @@
|
|
|
10908
10742
|
if (typeof globalThis.GLOBAL_SEUI == "undefined") {
|
|
10909
10743
|
const SECLASS = new Selective();
|
|
10910
10744
|
globalThis.GLOBAL_SEUI = {
|
|
10911
|
-
version: "1.4.
|
|
10745
|
+
version: "1.4.2",
|
|
10912
10746
|
name: "SelectiveUI",
|
|
10913
10747
|
bind: SECLASS.bind.bind(SECLASS),
|
|
10914
10748
|
find: SECLASS.find.bind(SECLASS),
|
|
@@ -10916,7 +10750,7 @@
|
|
|
10916
10750
|
effector: Effector.bind(Effector),
|
|
10917
10751
|
rebind: SECLASS.rebind.bind(SECLASS),
|
|
10918
10752
|
registerPlugin: SECLASS.registerPlugin.bind(SECLASS),
|
|
10919
|
-
unregisterPlugin: SECLASS.unregisterPlugin.bind(SECLASS)
|
|
10753
|
+
unregisterPlugin: SECLASS.unregisterPlugin.bind(SECLASS),
|
|
10920
10754
|
};
|
|
10921
10755
|
let domInitialized = false;
|
|
10922
10756
|
function init() {
|
|
@@ -10941,7 +10775,7 @@
|
|
|
10941
10775
|
init();
|
|
10942
10776
|
}
|
|
10943
10777
|
}
|
|
10944
|
-
console.log(`[${"SelectiveUI"}] v${"1.4.
|
|
10778
|
+
console.log(`[${"SelectiveUI"}] v${"1.4.2"} loaded successfully`);
|
|
10945
10779
|
}
|
|
10946
10780
|
else {
|
|
10947
10781
|
console.warn(`[${globalThis.GLOBAL_SEUI.name}] Already loaded (v${globalThis.GLOBAL_SEUI.version}). ` +
|
|
@@ -10998,7 +10832,7 @@
|
|
|
10998
10832
|
* // Destroy all instances
|
|
10999
10833
|
* destroy();
|
|
11000
10834
|
*/
|
|
11001
|
-
function destroy(query
|
|
10835
|
+
function destroy(query) {
|
|
11002
10836
|
globalThis.GLOBAL_SEUI.destroy(query);
|
|
11003
10837
|
}
|
|
11004
10838
|
/**
|