selective-ui 1.4.0 → 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 +252 -553
- 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 +254 -555
- 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 +242 -81
- 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.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Selective UI v1.4.
|
|
1
|
+
/*! Selective UI v1.4.1 | MIT License */
|
|
2
2
|
/**
|
|
3
3
|
* @class
|
|
4
4
|
*/
|
|
@@ -147,7 +147,7 @@ class CallbackScheduler {
|
|
|
147
147
|
*
|
|
148
148
|
* @public
|
|
149
149
|
* @param {TimerKey} key - Group identifier for callbacks.
|
|
150
|
-
* @param {(payload
|
|
150
|
+
* @param {(payload?: any[]) => void} callback - Function to execute after debounce timeout.
|
|
151
151
|
* @param {TimerOptions} [options={}] - Scheduling options (`debounce`, `once`).
|
|
152
152
|
* @returns {void}
|
|
153
153
|
*/
|
|
@@ -235,7 +235,8 @@ class CallbackScheduler {
|
|
|
235
235
|
await resp;
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
|
-
catch {
|
|
238
|
+
catch {
|
|
239
|
+
}
|
|
239
240
|
finally {
|
|
240
241
|
if (entry.once) {
|
|
241
242
|
executes[i] = undefined;
|
|
@@ -368,9 +369,11 @@ class Libs {
|
|
|
368
369
|
* @param {boolean} systemNodeCreate - If true, do not clone; use original node.
|
|
369
370
|
* @returns {HTMLElement} - The processed element.
|
|
370
371
|
*/
|
|
371
|
-
static nodeCloner(node = document.documentElement, _nodeOption
|
|
372
|
+
static nodeCloner(node = document.documentElement, _nodeOption, systemNodeCreate = false) {
|
|
372
373
|
const nodeOption = { ...(_nodeOption ?? {}) };
|
|
373
|
-
const element_creation = systemNodeCreate
|
|
374
|
+
const element_creation = systemNodeCreate
|
|
375
|
+
? node
|
|
376
|
+
: node.cloneNode(true);
|
|
374
377
|
const classList = nodeOption.classList;
|
|
375
378
|
if (typeof classList === "string") {
|
|
376
379
|
element_creation.classList.add(classList);
|
|
@@ -442,11 +445,13 @@ class Libs {
|
|
|
442
445
|
* @param {TTags|Object} [recursiveTemp={}] - Accumulator for tag references.
|
|
443
446
|
* @returns {TTags} - Tag map or the final mount result.
|
|
444
447
|
*/
|
|
445
|
-
static mountNode(rawObj, parentE
|
|
448
|
+
static mountNode(rawObj, parentE, isPrepend = false, isRecusive = false, recursiveTemp = {}) {
|
|
446
449
|
let view = null;
|
|
447
450
|
for (const key in rawObj) {
|
|
448
451
|
const singleObj = rawObj[key];
|
|
449
|
-
const tag = singleObj?.tag?.tagName
|
|
452
|
+
const tag = singleObj?.tag?.tagName
|
|
453
|
+
? singleObj.tag
|
|
454
|
+
: this.nodeCreator(singleObj.tag);
|
|
450
455
|
recursiveTemp[key] = tag;
|
|
451
456
|
if (singleObj?.child)
|
|
452
457
|
this.mountNode(singleObj.child, tag, false, false, recursiveTemp);
|
|
@@ -663,7 +668,8 @@ class Libs {
|
|
|
663
668
|
n.removeAttribute(name);
|
|
664
669
|
return;
|
|
665
670
|
}
|
|
666
|
-
if (/^(href|src|xlink:href)$/i.test(name) &&
|
|
671
|
+
if (/^(href|src|xlink:href)$/i.test(name) &&
|
|
672
|
+
/^javascript:/i.test(value)) {
|
|
667
673
|
n.removeAttribute(name);
|
|
668
674
|
}
|
|
669
675
|
}
|
|
@@ -693,7 +699,10 @@ class Libs {
|
|
|
693
699
|
static string2normalize(str) {
|
|
694
700
|
if (str == null)
|
|
695
701
|
return "";
|
|
696
|
-
const s = String(str)
|
|
702
|
+
const s = String(str)
|
|
703
|
+
.toLowerCase()
|
|
704
|
+
.normalize("NFD")
|
|
705
|
+
.replace(/[\u0300-\u036f]/g, "");
|
|
697
706
|
return s.replace(/đ/g, "d").replace(/Đ/g, "d");
|
|
698
707
|
}
|
|
699
708
|
/**
|
|
@@ -735,7 +744,8 @@ class Libs {
|
|
|
735
744
|
*/
|
|
736
745
|
static IsIOS() {
|
|
737
746
|
const ua = navigator.userAgent;
|
|
738
|
-
return /iP(hone|ad|od)/.test(ua) ||
|
|
747
|
+
return (/iP(hone|ad|od)/.test(ua) ||
|
|
748
|
+
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1));
|
|
739
749
|
}
|
|
740
750
|
/**
|
|
741
751
|
* Converts an arbitrary CSS size value into pixel units by measuring a temporary element.
|
|
@@ -757,14 +767,16 @@ class Libs {
|
|
|
757
767
|
if (v.endsWith("rem"))
|
|
758
768
|
return fs * parseFloat(v) + "px";
|
|
759
769
|
// fallback: DOM measure
|
|
760
|
-
const el = this.nodeCreator({
|
|
770
|
+
const el = this.nodeCreator({
|
|
771
|
+
node: "div",
|
|
772
|
+
style: { height: v, opacity: "0" },
|
|
773
|
+
});
|
|
761
774
|
document.body.appendChild(el);
|
|
762
775
|
const px = el.offsetHeight + "px";
|
|
763
776
|
el.remove();
|
|
764
777
|
return px;
|
|
765
778
|
}
|
|
766
779
|
}
|
|
767
|
-
Libs._iStorage = null;
|
|
768
780
|
/**
|
|
769
781
|
* Schedules and batches function executions keyed by name, with debounced timers.
|
|
770
782
|
* Provides setExecute(), clearExecute(), and run() to manage deferred callbacks.
|
|
@@ -1291,25 +1303,6 @@ class PlaceHolder extends Lifecycle {
|
|
|
1291
1303
|
*/
|
|
1292
1304
|
constructor(options) {
|
|
1293
1305
|
super();
|
|
1294
|
-
/**
|
|
1295
|
-
* Root DOM element for the placeholder.
|
|
1296
|
-
*
|
|
1297
|
-
* Created during {@link initialize}. Removed from the DOM during {@link destroy}.
|
|
1298
|
-
* `null` before initialization and after destruction.
|
|
1299
|
-
*/
|
|
1300
|
-
this.node = null;
|
|
1301
|
-
/**
|
|
1302
|
-
* Configuration snapshot used to render and optionally persist placeholder content.
|
|
1303
|
-
*
|
|
1304
|
-
* Key fields used by this component:
|
|
1305
|
-
* - `placeholder`: initial/current placeholder text/markup
|
|
1306
|
-
* - `allowHtml`: controls whether HTML is rendered or stripped
|
|
1307
|
-
*
|
|
1308
|
-
* Cleared during {@link destroy}.
|
|
1309
|
-
*
|
|
1310
|
-
* @internal
|
|
1311
|
-
*/
|
|
1312
|
-
this.options = null;
|
|
1313
1306
|
if (options)
|
|
1314
1307
|
this.initialize(options);
|
|
1315
1308
|
}
|
|
@@ -1534,34 +1527,8 @@ class OptionHandle extends Lifecycle {
|
|
|
1534
1527
|
*
|
|
1535
1528
|
* @param options - Feature flags and labels for the two actions.
|
|
1536
1529
|
*/
|
|
1537
|
-
constructor(options
|
|
1530
|
+
constructor(options) {
|
|
1538
1531
|
super();
|
|
1539
|
-
/**
|
|
1540
|
-
* Result returned by {@link Libs.mountNode}.
|
|
1541
|
-
*
|
|
1542
|
-
* Stores the mounted view structure so the component can keep a stable reference
|
|
1543
|
-
* to its created DOM nodes. `null` before {@link initialize}.
|
|
1544
|
-
*
|
|
1545
|
-
* @internal
|
|
1546
|
-
*/
|
|
1547
|
-
this.nodeMounted = null;
|
|
1548
|
-
/**
|
|
1549
|
-
* Root element of this control.
|
|
1550
|
-
*
|
|
1551
|
-
* Created during {@link initialize}. This node is used by {@link show}/{@link hide}
|
|
1552
|
-
* and removed during {@link destroy}.
|
|
1553
|
-
*/
|
|
1554
|
-
this.node = null;
|
|
1555
|
-
/**
|
|
1556
|
-
* Configuration snapshot used for:
|
|
1557
|
-
* - labels (`textSelectAll`, `textDeselectAll`)
|
|
1558
|
-
* - feature flags (`multiple`, `selectall`)
|
|
1559
|
-
*
|
|
1560
|
-
* Treated as read-only after initialization; cleared on {@link destroy}.
|
|
1561
|
-
*
|
|
1562
|
-
* @internal
|
|
1563
|
-
*/
|
|
1564
|
-
this.options = null;
|
|
1565
1532
|
/**
|
|
1566
1533
|
* Callback list invoked when the "Select all" control is activated.
|
|
1567
1534
|
*
|
|
@@ -1649,7 +1616,8 @@ class OptionHandle extends Lifecycle {
|
|
|
1649
1616
|
available() {
|
|
1650
1617
|
if (!this.options)
|
|
1651
1618
|
return false;
|
|
1652
|
-
return Libs.string2Boolean(this.options.multiple) &&
|
|
1619
|
+
return (Libs.string2Boolean(this.options.multiple) &&
|
|
1620
|
+
Libs.string2Boolean(this.options.selectall));
|
|
1653
1621
|
}
|
|
1654
1622
|
/**
|
|
1655
1623
|
* Re-evaluates visibility and advances the lifecycle update step.
|
|
@@ -1706,7 +1674,7 @@ class OptionHandle extends Lifecycle {
|
|
|
1706
1674
|
*
|
|
1707
1675
|
* @param action - Callback invoked on activation; ignored when not a function.
|
|
1708
1676
|
*/
|
|
1709
|
-
onSelectAll(action
|
|
1677
|
+
onSelectAll(action) {
|
|
1710
1678
|
if (typeof action === "function") {
|
|
1711
1679
|
this.actionOnSelectAll.push(action);
|
|
1712
1680
|
}
|
|
@@ -1722,7 +1690,7 @@ class OptionHandle extends Lifecycle {
|
|
|
1722
1690
|
*
|
|
1723
1691
|
* @param action - Callback invoked on activation; ignored when not a function.
|
|
1724
1692
|
*/
|
|
1725
|
-
onDeSelectAll(action
|
|
1693
|
+
onDeSelectAll(action) {
|
|
1726
1694
|
if (typeof action === "function") {
|
|
1727
1695
|
this.actionOnDeSelectAll.push(action);
|
|
1728
1696
|
}
|
|
@@ -1790,26 +1758,10 @@ class EmptyState extends Lifecycle {
|
|
|
1790
1758
|
* If `options` are provided, initialization runs immediately (creates {@link node} and
|
|
1791
1759
|
* transitions to `INITIALIZED`).
|
|
1792
1760
|
*
|
|
1793
|
-
* @param {SelectiveOptions
|
|
1761
|
+
* @param {SelectiveOptions} [options=null] - Configuration containing empty state messages.
|
|
1794
1762
|
*/
|
|
1795
|
-
constructor(options
|
|
1763
|
+
constructor(options) {
|
|
1796
1764
|
super();
|
|
1797
|
-
/**
|
|
1798
|
-
* Root DOM element for the empty state UI.
|
|
1799
|
-
*
|
|
1800
|
-
* - Created during {@link initialize}.
|
|
1801
|
-
* - Intended to be appended by the parent container (component does not auto-attach).
|
|
1802
|
-
* - Removed from DOM during {@link destroy}.
|
|
1803
|
-
*/
|
|
1804
|
-
this.node = null;
|
|
1805
|
-
/**
|
|
1806
|
-
* Configuration source for empty state messages.
|
|
1807
|
-
*
|
|
1808
|
-
* Expected to provide at least:
|
|
1809
|
-
* - `textNoData` (for `"nodata"`)
|
|
1810
|
-
* - `textNotFound` (for `"notfound"`)
|
|
1811
|
-
*/
|
|
1812
|
-
this.options = null;
|
|
1813
1765
|
if (options)
|
|
1814
1766
|
this.initialize(options);
|
|
1815
1767
|
}
|
|
@@ -1935,25 +1887,10 @@ class LoadingState extends Lifecycle {
|
|
|
1935
1887
|
* If `options` are provided, initialization runs immediately (creates {@link node} and
|
|
1936
1888
|
* transitions to `INITIALIZED`).
|
|
1937
1889
|
*
|
|
1938
|
-
* @param {SelectiveOptions
|
|
1890
|
+
* @param {SelectiveOptions} [options=null] - Configuration containing the loading message text.
|
|
1939
1891
|
*/
|
|
1940
|
-
constructor(options
|
|
1892
|
+
constructor(options) {
|
|
1941
1893
|
super();
|
|
1942
|
-
/**
|
|
1943
|
-
* Root DOM element for the loading state UI.
|
|
1944
|
-
*
|
|
1945
|
-
* - Created during {@link initialize}.
|
|
1946
|
-
* - Intended to be appended by the parent container (component does not auto-attach).
|
|
1947
|
-
* - Removed from DOM during {@link destroy}.
|
|
1948
|
-
*/
|
|
1949
|
-
this.node = null;
|
|
1950
|
-
/**
|
|
1951
|
-
* Configuration source for loading message text.
|
|
1952
|
-
*
|
|
1953
|
-
* Expected to provide:
|
|
1954
|
-
* - `textLoading` (displayed while loading is active)
|
|
1955
|
-
*/
|
|
1956
|
-
this.options = null;
|
|
1957
1894
|
if (options)
|
|
1958
1895
|
this.initialize(options);
|
|
1959
1896
|
}
|
|
@@ -2111,27 +2048,6 @@ class ResizeObserverService {
|
|
|
2111
2048
|
* It does **not** indicate that observers are currently attached (see {@link connect}).
|
|
2112
2049
|
*/
|
|
2113
2050
|
this.isInit = false;
|
|
2114
|
-
/**
|
|
2115
|
-
* The currently bound DOM element being observed.
|
|
2116
|
-
*
|
|
2117
|
-
* @remarks
|
|
2118
|
-
* Set by {@link connect} and cleared by {@link disconnect}.
|
|
2119
|
-
*/
|
|
2120
|
-
this.element = null;
|
|
2121
|
-
/**
|
|
2122
|
-
* Underlying `ResizeObserver` instance.
|
|
2123
|
-
*
|
|
2124
|
-
* @remarks
|
|
2125
|
-
* Allocated on {@link connect}. Disconnected and nulled on {@link disconnect}.
|
|
2126
|
-
*/
|
|
2127
|
-
this.resizeObserver = null;
|
|
2128
|
-
/**
|
|
2129
|
-
* Underlying `MutationObserver` instance watching `style` and `class` attribute changes.
|
|
2130
|
-
*
|
|
2131
|
-
* @remarks
|
|
2132
|
-
* Allocated on {@link connect}. Disconnected and nulled on {@link disconnect}.
|
|
2133
|
-
*/
|
|
2134
|
-
this.mutationObserver = null;
|
|
2135
2051
|
this.isInit = true;
|
|
2136
2052
|
this.boundUpdateChanged = this.updateChanged.bind(this);
|
|
2137
2053
|
}
|
|
@@ -2177,7 +2093,8 @@ class ResizeObserverService {
|
|
|
2177
2093
|
return;
|
|
2178
2094
|
}
|
|
2179
2095
|
const rect = el.getBoundingClientRect();
|
|
2180
|
-
const style = typeof window !== "undefined" &&
|
|
2096
|
+
const style = typeof window !== "undefined" &&
|
|
2097
|
+
typeof window.getComputedStyle === "function"
|
|
2181
2098
|
? window.getComputedStyle(el)
|
|
2182
2099
|
: null;
|
|
2183
2100
|
const metrics = {
|
|
@@ -2309,36 +2226,10 @@ class Popup extends Lifecycle {
|
|
|
2309
2226
|
* @param options - Configuration options (panel sizing, flags, texts, etc.).
|
|
2310
2227
|
* @param modelManager - Model manager that supplies the adapter and recycler view.
|
|
2311
2228
|
*/
|
|
2312
|
-
constructor(select
|
|
2229
|
+
constructor(select, options, modelManager) {
|
|
2313
2230
|
super();
|
|
2314
|
-
/** Active configuration for the popup behavior and text labels */
|
|
2315
|
-
this.options = null;
|
|
2316
2231
|
/** Indicates whether the popup DOM has been attached to the document body at least once */
|
|
2317
2232
|
this.isCreated = false;
|
|
2318
|
-
/** Mixed adapter handling items/models and visibility stats */
|
|
2319
|
-
this.optionAdapter = null;
|
|
2320
|
-
/** Root popup container (the floating panel) */
|
|
2321
|
-
this.node = null;
|
|
2322
|
-
/** Effector service used to measure/animate the popup */
|
|
2323
|
-
this.effSvc = null;
|
|
2324
|
-
/** Resize observer to react to parent panel size changes */
|
|
2325
|
-
this.resizeObser = null;
|
|
2326
|
-
/** Binder map for parent elements (anchors to compute placement from) */
|
|
2327
|
-
this.parent = null;
|
|
2328
|
-
/** Header control exposing "Select All / Deselect All" actions */
|
|
2329
|
-
this.optionHandle = null;
|
|
2330
|
-
/** "Empty / Not found" feedback component */
|
|
2331
|
-
this.emptyState = null;
|
|
2332
|
-
/** Loading indicator component */
|
|
2333
|
-
this.loadingState = null;
|
|
2334
|
-
/** Virtualized recycler view for performant lists */
|
|
2335
|
-
this.recyclerView = null;
|
|
2336
|
-
/** Container that holds the list of options */
|
|
2337
|
-
this.optionsContainer = null;
|
|
2338
|
-
/** Scroll handler used by infinite scroll */
|
|
2339
|
-
this.scrollListener = null;
|
|
2340
|
-
/** Handle to defer hiding the loading indicator */
|
|
2341
|
-
this.hideLoadHandle = null;
|
|
2342
2233
|
/** Default virtual scroll configuration (tuned for typical option heights) */
|
|
2343
2234
|
this.virtualScrollConfig = {
|
|
2344
2235
|
/** Estimated item height in pixels (improves initial layout calculation) */
|
|
@@ -2346,7 +2237,7 @@ class Popup extends Lifecycle {
|
|
|
2346
2237
|
/** Number of extra items to render above/below the viewport */
|
|
2347
2238
|
overscan: 8,
|
|
2348
2239
|
/** Whether the list contains items with dynamic (non-uniform) heights */
|
|
2349
|
-
dynamicHeights: true
|
|
2240
|
+
dynamicHeights: true,
|
|
2350
2241
|
};
|
|
2351
2242
|
this.modelManager = modelManager;
|
|
2352
2243
|
if (select && options) {
|
|
@@ -2393,7 +2284,8 @@ class Popup extends Lifecycle {
|
|
|
2393
2284
|
},
|
|
2394
2285
|
}, null);
|
|
2395
2286
|
this.node = nodeMounted.view;
|
|
2396
|
-
this.optionsContainer = nodeMounted.tags
|
|
2287
|
+
this.optionsContainer = nodeMounted.tags
|
|
2288
|
+
.OptionsContainer;
|
|
2397
2289
|
this.parent = Libs.getBinderMap(select);
|
|
2398
2290
|
this.options = options;
|
|
2399
2291
|
this.init();
|
|
@@ -2402,7 +2294,7 @@ class Popup extends Lifecycle {
|
|
|
2402
2294
|
scrollEl: this.node,
|
|
2403
2295
|
estimateItemHeight: this.virtualScrollConfig.estimateItemHeight,
|
|
2404
2296
|
overscan: this.virtualScrollConfig.overscan,
|
|
2405
|
-
dynamicHeights: this.virtualScrollConfig.dynamicHeights
|
|
2297
|
+
dynamicHeights: this.virtualScrollConfig.dynamicHeights,
|
|
2406
2298
|
}
|
|
2407
2299
|
: {};
|
|
2408
2300
|
// Load ModelManager resources into the list container
|
|
@@ -2429,7 +2321,11 @@ class Popup extends Lifecycle {
|
|
|
2429
2321
|
* - Triggers a resize to accommodate layout changes
|
|
2430
2322
|
*/
|
|
2431
2323
|
async showLoading() {
|
|
2432
|
-
if (!this.options ||
|
|
2324
|
+
if (!this.options ||
|
|
2325
|
+
!this.loadingState ||
|
|
2326
|
+
!this.optionHandle ||
|
|
2327
|
+
!this.optionAdapter ||
|
|
2328
|
+
!this.modelManager)
|
|
2433
2329
|
return;
|
|
2434
2330
|
if (this.hideLoadHandle)
|
|
2435
2331
|
clearTimeout(this.hideLoadHandle);
|
|
@@ -2447,7 +2343,10 @@ class Popup extends Lifecycle {
|
|
|
2447
2343
|
* Debounce: Uses `animationtime` as a short delay before hiding the loading indicator.
|
|
2448
2344
|
*/
|
|
2449
2345
|
async hideLoading() {
|
|
2450
|
-
if (!this.options ||
|
|
2346
|
+
if (!this.options ||
|
|
2347
|
+
!this.loadingState ||
|
|
2348
|
+
!this.optionAdapter ||
|
|
2349
|
+
!this.modelManager)
|
|
2451
2350
|
return;
|
|
2452
2351
|
if (this.hideLoadHandle)
|
|
2453
2352
|
clearTimeout(this.hideLoadHandle);
|
|
@@ -2488,7 +2387,10 @@ class Popup extends Lifecycle {
|
|
|
2488
2387
|
* @param stats - Optionally provide precomputed visibility stats.
|
|
2489
2388
|
*/
|
|
2490
2389
|
updateEmptyState(stats) {
|
|
2491
|
-
if (!this.optionAdapter ||
|
|
2390
|
+
if (!this.optionAdapter ||
|
|
2391
|
+
!this.emptyState ||
|
|
2392
|
+
!this.optionHandle ||
|
|
2393
|
+
!this.optionsContainer)
|
|
2492
2394
|
return;
|
|
2493
2395
|
const s = stats ?? this.optionAdapter.getVisibilityStats();
|
|
2494
2396
|
if (s.isEmpty) {
|
|
@@ -2572,8 +2474,12 @@ class Popup extends Lifecycle {
|
|
|
2572
2474
|
* @param callback - Optional callback invoked when the opening animation completes.
|
|
2573
2475
|
* @param isShowEmptyState - If true, applies the empty/not-found state before animation.
|
|
2574
2476
|
*/
|
|
2575
|
-
open(callback
|
|
2576
|
-
if (!this.node ||
|
|
2477
|
+
open(callback, isShowEmptyState) {
|
|
2478
|
+
if (!this.node ||
|
|
2479
|
+
!this.options ||
|
|
2480
|
+
!this.optionHandle ||
|
|
2481
|
+
!this.parent ||
|
|
2482
|
+
!this.effSvc)
|
|
2577
2483
|
return;
|
|
2578
2484
|
// Ensure one-time initialization
|
|
2579
2485
|
this.load();
|
|
@@ -2622,8 +2528,11 @@ class Popup extends Lifecycle {
|
|
|
2622
2528
|
*
|
|
2623
2529
|
* @param callback - Optional callback invoked when the closing animation completes.
|
|
2624
2530
|
*/
|
|
2625
|
-
close(callback
|
|
2626
|
-
if (!this.isCreated ||
|
|
2531
|
+
close(callback) {
|
|
2532
|
+
if (!this.isCreated ||
|
|
2533
|
+
!this.options ||
|
|
2534
|
+
!this.resizeObser ||
|
|
2535
|
+
!this.effSvc)
|
|
2627
2536
|
return;
|
|
2628
2537
|
const rv = this.recyclerView;
|
|
2629
2538
|
rv?.suspend?.();
|
|
@@ -2782,7 +2691,9 @@ class Popup extends Lifecycle {
|
|
|
2782
2691
|
let maxHeight = configMaxHeight;
|
|
2783
2692
|
let realHeight = Math.min(contentHeight, maxHeight);
|
|
2784
2693
|
const heightOri = spaceBelow - safeMargin;
|
|
2785
|
-
if (realHeight >= configMinHeight
|
|
2694
|
+
if (realHeight >= configMinHeight
|
|
2695
|
+
? heightOri >= configMinHeight
|
|
2696
|
+
: heightOri >= realHeight) {
|
|
2786
2697
|
position = "bottom";
|
|
2787
2698
|
maxHeight = Math.min(spaceBelow - safeMargin, configMaxHeight);
|
|
2788
2699
|
}
|
|
@@ -2882,78 +2793,8 @@ class SearchBox extends Lifecycle {
|
|
|
2882
2793
|
*
|
|
2883
2794
|
* @param options - Configuration such as placeholder, accessibility IDs, and flags.
|
|
2884
2795
|
*/
|
|
2885
|
-
constructor(options
|
|
2796
|
+
constructor(options) {
|
|
2886
2797
|
super();
|
|
2887
|
-
/**
|
|
2888
|
-
* The mount result returned by {@link Libs.mountNode}.
|
|
2889
|
-
*
|
|
2890
|
-
* Provides typed access to created DOM tags (e.g., `SearchInput`) and the root view.
|
|
2891
|
-
* `null` before initialization and after destruction.
|
|
2892
|
-
*
|
|
2893
|
-
* @internal
|
|
2894
|
-
*/
|
|
2895
|
-
this.nodeMounted = null;
|
|
2896
|
-
/**
|
|
2897
|
-
* Root container node of this component.
|
|
2898
|
-
*
|
|
2899
|
-
* Created during {@link initialize} and removed during {@link destroy}.
|
|
2900
|
-
* Visibility is controlled by adding/removing the `hide` class.
|
|
2901
|
-
*/
|
|
2902
|
-
this.node = null;
|
|
2903
|
-
/**
|
|
2904
|
-
* The `<input type="search">` element used to capture user queries.
|
|
2905
|
-
*
|
|
2906
|
-
* Cached for imperative operations (focus, placeholder updates, ARIA updates).
|
|
2907
|
-
* `null` before initialization and after destruction.
|
|
2908
|
-
*
|
|
2909
|
-
* @internal
|
|
2910
|
-
*/
|
|
2911
|
-
this.SearchInput = null;
|
|
2912
|
-
/**
|
|
2913
|
-
* External "search changed" hook.
|
|
2914
|
-
*
|
|
2915
|
-
* Invoked when the user edits text (via the `input` event) and the edit is not
|
|
2916
|
-
* part of a handled control-key sequence (e.g., ArrowUp/Down/Tab/Enter/Escape).
|
|
2917
|
-
*
|
|
2918
|
-
* Ownership:
|
|
2919
|
-
* - Implementations typically filter adapter/model state and refresh the list.
|
|
2920
|
-
*/
|
|
2921
|
-
this.onSearch = null;
|
|
2922
|
-
/**
|
|
2923
|
-
* Options snapshot used for behavior toggles and attributes.
|
|
2924
|
-
*
|
|
2925
|
-
* Key fields typically consumed here:
|
|
2926
|
-
* - `placeholder`: initial placeholder string
|
|
2927
|
-
* - `searchable`: toggles readOnly + focus behavior on {@link show}
|
|
2928
|
-
* - `SEID_LIST`: used as `aria-controls` value to bind to listbox container
|
|
2929
|
-
*
|
|
2930
|
-
* Cleared during {@link destroy}.
|
|
2931
|
-
*
|
|
2932
|
-
* @internal
|
|
2933
|
-
*/
|
|
2934
|
-
this.options = null;
|
|
2935
|
-
/**
|
|
2936
|
-
* External navigation hook for list traversal.
|
|
2937
|
-
*
|
|
2938
|
-
* Called with:
|
|
2939
|
-
* - `+1` for forward (ArrowDown / Tab)
|
|
2940
|
-
* - `-1` for backward (ArrowUp)
|
|
2941
|
-
*
|
|
2942
|
-
* Typical consumers update highlight/active option in Adapter/RecyclerView.
|
|
2943
|
-
*/
|
|
2944
|
-
this.onNavigate = null;
|
|
2945
|
-
/**
|
|
2946
|
-
* External "commit" hook (Enter key).
|
|
2947
|
-
*
|
|
2948
|
-
* Typical consumers confirm selection of the highlighted option or submit the current state.
|
|
2949
|
-
*/
|
|
2950
|
-
this.onEnter = null;
|
|
2951
|
-
/**
|
|
2952
|
-
* External "cancel" hook (Escape key).
|
|
2953
|
-
*
|
|
2954
|
-
* Typical consumers close the popup, clear highlight, or reset interaction mode.
|
|
2955
|
-
*/
|
|
2956
|
-
this.onEsc = null;
|
|
2957
2798
|
this.options = options;
|
|
2958
2799
|
if (options)
|
|
2959
2800
|
this.initialize(options);
|
|
@@ -3219,17 +3060,7 @@ class EffectorImpl {
|
|
|
3219
3060
|
*
|
|
3220
3061
|
* @param query - CSS selector or element to control. When `null`, instance starts unbound.
|
|
3221
3062
|
*/
|
|
3222
|
-
constructor(query
|
|
3223
|
-
/**
|
|
3224
|
-
* Timeout used to finalize expand/collapse/swipe animations.
|
|
3225
|
-
* Cleared by {@link cancel}.
|
|
3226
|
-
*/
|
|
3227
|
-
this.timeOut = null;
|
|
3228
|
-
/**
|
|
3229
|
-
* Timeout used to clear transitions after resize in non-animated scenarios.
|
|
3230
|
-
* Cleared by {@link cancel}.
|
|
3231
|
-
*/
|
|
3232
|
-
this.resizeTimeout = null;
|
|
3063
|
+
constructor(query) {
|
|
3233
3064
|
/**
|
|
3234
3065
|
* Internal animation flag set while a timed animation is in-flight.
|
|
3235
3066
|
*
|
|
@@ -3413,7 +3244,9 @@ class EffectorImpl {
|
|
|
3413
3244
|
const { duration = 200, onComplete } = config;
|
|
3414
3245
|
const currentHeight = this.element.offsetHeight;
|
|
3415
3246
|
const currentTop = this.element.offsetTop;
|
|
3416
|
-
const position = this.element.classList.contains("position-top")
|
|
3247
|
+
const position = this.element.classList.contains("position-top")
|
|
3248
|
+
? "top"
|
|
3249
|
+
: "bottom";
|
|
3417
3250
|
const isScrollable = this.element.scrollHeight - this.element.offsetHeight > 0;
|
|
3418
3251
|
const finalTop = position === "top" ? currentTop + currentHeight : currentTop;
|
|
3419
3252
|
requestAnimationFrame(() => {
|
|
@@ -3551,7 +3384,9 @@ class EffectorImpl {
|
|
|
3551
3384
|
return this;
|
|
3552
3385
|
this.cancel();
|
|
3553
3386
|
const { duration = 200, width, left, top, maxHeight, realHeight, position = "bottom", animate = true, onComplete, } = config;
|
|
3554
|
-
const currentPosition = this.element.classList.contains("position-top")
|
|
3387
|
+
const currentPosition = this.element.classList.contains("position-top")
|
|
3388
|
+
? "top"
|
|
3389
|
+
: "bottom";
|
|
3555
3390
|
const isPositionChanged = currentPosition !== position;
|
|
3556
3391
|
const isScrollable = this.element.scrollHeight > maxHeight;
|
|
3557
3392
|
this.element.classList.toggle("position-top", position === "top");
|
|
@@ -3667,25 +3502,11 @@ class Model extends Lifecycle {
|
|
|
3667
3502
|
* - Calls {@link Lifecycle.init} immediately (`NEW → INITIALIZED`).
|
|
3668
3503
|
*
|
|
3669
3504
|
* @param {TOptions} options - Configuration options for the model.
|
|
3670
|
-
* @param {TTarget
|
|
3671
|
-
* @param {TView
|
|
3505
|
+
* @param {TTarget} [targetElement=null] - Optional DOM element to bind.
|
|
3506
|
+
* @param {TView} [view=null] - Optional view responsible for rendering this model.
|
|
3672
3507
|
*/
|
|
3673
|
-
constructor(options, targetElement
|
|
3508
|
+
constructor(options, targetElement, view) {
|
|
3674
3509
|
super();
|
|
3675
|
-
/**
|
|
3676
|
-
* The currently bound target DOM element.
|
|
3677
|
-
*
|
|
3678
|
-
* This element typically represents the source-of-truth node in the host DOM (e.g., a native `<option>`).
|
|
3679
|
-
* May be replaced via {@link updateTarget} during reconciliation.
|
|
3680
|
-
*/
|
|
3681
|
-
this.targetElement = null;
|
|
3682
|
-
/**
|
|
3683
|
-
* View instance responsible for rendering this model.
|
|
3684
|
-
*
|
|
3685
|
-
* Ownership: this model will destroy the view on {@link destroy}.
|
|
3686
|
-
* The view may be attached/assigned by external orchestrators (Adapter/RecyclerView) after construction.
|
|
3687
|
-
*/
|
|
3688
|
-
this.view = null;
|
|
3689
3510
|
/**
|
|
3690
3511
|
* Position index used by list infrastructure for ordering/tracking.
|
|
3691
3512
|
* Semantics are library-specific (e.g., top-level index or adapter position).
|
|
@@ -3717,7 +3538,7 @@ class Model extends Lifecycle {
|
|
|
3717
3538
|
* - Assigns {@link targetElement}.
|
|
3718
3539
|
* - Calls {@link Lifecycle.update} (guarded by lifecycle state).
|
|
3719
3540
|
*
|
|
3720
|
-
* @param {TTarget
|
|
3541
|
+
* @param {TTarget} targetElement - The new DOM element to associate with this model.
|
|
3721
3542
|
* @returns {void}
|
|
3722
3543
|
*/
|
|
3723
3544
|
updateTarget(targetElement) {
|
|
@@ -3917,7 +3738,7 @@ class GroupModel extends Model {
|
|
|
3917
3738
|
if (this.is(LifecycleState.DESTROYED)) {
|
|
3918
3739
|
return;
|
|
3919
3740
|
}
|
|
3920
|
-
this.items.forEach(item => {
|
|
3741
|
+
this.items.forEach((item) => {
|
|
3921
3742
|
item.destroy();
|
|
3922
3743
|
});
|
|
3923
3744
|
this.items = [];
|
|
@@ -4041,10 +3862,10 @@ class OptionModel extends Model {
|
|
|
4041
3862
|
* Creates an option model.
|
|
4042
3863
|
*
|
|
4043
3864
|
* @param {SelectiveOptions} options - Shared configuration for models/views.
|
|
4044
|
-
* @param {HTMLOptionElement
|
|
4045
|
-
* @param {OptionView
|
|
3865
|
+
* @param {HTMLOptionElement} [targetElement=null] - Backing `<option>` element.
|
|
3866
|
+
* @param {OptionView} [view=null] - Optional view used to render this model.
|
|
4046
3867
|
*/
|
|
4047
|
-
constructor(options, targetElement
|
|
3868
|
+
constructor(options, targetElement, view) {
|
|
4048
3869
|
super(options, targetElement, view);
|
|
4049
3870
|
/**
|
|
4050
3871
|
* External selection subscribers (emitted by the {@link selected} setter).
|
|
@@ -4068,11 +3889,6 @@ class OptionModel extends Model {
|
|
|
4068
3889
|
this._visible = true;
|
|
4069
3890
|
/** Highlight flag used for keyboard navigation / hover. */
|
|
4070
3891
|
this._highlighted = false;
|
|
4071
|
-
/**
|
|
4072
|
-
* Parent group model (if this option belongs to a group).
|
|
4073
|
-
* Assigned by grouping logic (e.g., GroupModel/MixedAdapter).
|
|
4074
|
-
*/
|
|
4075
|
-
this.group = null;
|
|
4076
3892
|
}
|
|
4077
3893
|
/**
|
|
4078
3894
|
* Initializes the model and precomputes the search key.
|
|
@@ -4227,7 +4043,9 @@ class OptionModel extends Model {
|
|
|
4227
4043
|
* @returns {string}
|
|
4228
4044
|
*/
|
|
4229
4045
|
get textContent() {
|
|
4230
|
-
return this.options.allowHtml
|
|
4046
|
+
return this.options.allowHtml
|
|
4047
|
+
? Libs.stripHtml(this.text).trim()
|
|
4048
|
+
: this.text.trim();
|
|
4231
4049
|
}
|
|
4232
4050
|
/**
|
|
4233
4051
|
* Dataset object of the backing `<option>` element.
|
|
@@ -4408,8 +4226,6 @@ class ModelManager extends Lifecycle {
|
|
|
4408
4226
|
constructor(options) {
|
|
4409
4227
|
super();
|
|
4410
4228
|
this.privModelList = [];
|
|
4411
|
-
this.privAdapterHandle = null;
|
|
4412
|
-
this.privRecyclerViewHandle = null;
|
|
4413
4229
|
this.options = null;
|
|
4414
4230
|
this.oldPosition = 0;
|
|
4415
4231
|
this.options = options;
|
|
@@ -4461,7 +4277,9 @@ class ModelManager extends Lifecycle {
|
|
|
4461
4277
|
else if (data.tagName === "OPTION") {
|
|
4462
4278
|
const optionModel = new OptionModel(this.options, data);
|
|
4463
4279
|
const parentGroup = data["__parentGroup"];
|
|
4464
|
-
if (parentGroup &&
|
|
4280
|
+
if (parentGroup &&
|
|
4281
|
+
currentGroup &&
|
|
4282
|
+
parentGroup === currentGroup.targetElement) {
|
|
4465
4283
|
currentGroup.addItem(optionModel);
|
|
4466
4284
|
optionModel.group = currentGroup;
|
|
4467
4285
|
}
|
|
@@ -4886,44 +4704,10 @@ class AccessoryBox extends Lifecycle {
|
|
|
4886
4704
|
/**
|
|
4887
4705
|
* Creates an AccessoryBox and optionally initializes it with configuration.
|
|
4888
4706
|
*
|
|
4889
|
-
* @param {SelectiveOptions
|
|
4707
|
+
* @param {SelectiveOptions} [options=null] - Configuration controlling placement/visibility and texts.
|
|
4890
4708
|
*/
|
|
4891
|
-
constructor(options
|
|
4709
|
+
constructor(options) {
|
|
4892
4710
|
super();
|
|
4893
|
-
/**
|
|
4894
|
-
* Mounted structure returned by the node mounting helper.
|
|
4895
|
-
* Contains the root element (`view`) and any tag handles (if present).
|
|
4896
|
-
*/
|
|
4897
|
-
this.nodeMounted = null;
|
|
4898
|
-
/**
|
|
4899
|
-
* Root DOM element of the accessory box (hidden by default).
|
|
4900
|
-
* Created during {@link init} and removed during {@link destroy}.
|
|
4901
|
-
*/
|
|
4902
|
-
this.node = null;
|
|
4903
|
-
/**
|
|
4904
|
-
* Component configuration (texts, behavior, placement).
|
|
4905
|
-
* This component reads:
|
|
4906
|
-
* - `accessoryStyle` ("top" or default bottom)
|
|
4907
|
-
* - `accessoryVisible` (enable/disable)
|
|
4908
|
-
* - `multiple` (multi-select mode)
|
|
4909
|
-
* - `textAccessoryDeselect` (a11y label prefix)
|
|
4910
|
-
*/
|
|
4911
|
-
this.options = null;
|
|
4912
|
-
/**
|
|
4913
|
-
* The Select UI mask element used as the positioning reference.
|
|
4914
|
-
* Provided by {@link setRoot}.
|
|
4915
|
-
*/
|
|
4916
|
-
this.selectUIMask = null;
|
|
4917
|
-
/**
|
|
4918
|
-
* Parent container that hosts both the Select UI mask and the accessory box.
|
|
4919
|
-
* Computed from `selectUIMask.parentElement`.
|
|
4920
|
-
*/
|
|
4921
|
-
this.parentMask = null;
|
|
4922
|
-
/**
|
|
4923
|
-
* ModelManager used to run selection pipelines and coordinate state updates.
|
|
4924
|
-
* This component does not own selection state; it delegates to the model layer.
|
|
4925
|
-
*/
|
|
4926
|
-
this.modelManager = null;
|
|
4927
4711
|
/**
|
|
4928
4712
|
* Current selected option models rendered as chips.
|
|
4929
4713
|
* This is a cached snapshot used for show/hide decisions and re-rendering.
|
|
@@ -5231,15 +5015,6 @@ class SearchController extends Lifecycle {
|
|
|
5231
5015
|
*/
|
|
5232
5016
|
constructor(selectElement, modelManager, selectBox) {
|
|
5233
5017
|
super();
|
|
5234
|
-
/**
|
|
5235
|
-
* AJAX configuration; when `null`, {@link search} falls back to local filtering.
|
|
5236
|
-
* @see {@link setAjax}
|
|
5237
|
-
*/
|
|
5238
|
-
this.ajaxConfig = null;
|
|
5239
|
-
/** Abort handle used to cancel an in-flight AJAX request when a newer request starts. */
|
|
5240
|
-
this.abortController = null;
|
|
5241
|
-
/** Optional popup handle used for showing/hiding loading UI during remote operations. */
|
|
5242
|
-
this.popup = null;
|
|
5243
5018
|
/**
|
|
5244
5019
|
* SelectBox handle used by custom data builder functions that require Selective context.
|
|
5245
5020
|
* NOTE: This is a reference; the controller does not own/destroy the SelectBox.
|
|
@@ -5303,7 +5078,11 @@ class SearchController extends Lifecycle {
|
|
|
5303
5078
|
*/
|
|
5304
5079
|
async loadByValues(values) {
|
|
5305
5080
|
if (!this.ajaxConfig) {
|
|
5306
|
-
return {
|
|
5081
|
+
return {
|
|
5082
|
+
success: false,
|
|
5083
|
+
items: [],
|
|
5084
|
+
message: "Ajax not configured",
|
|
5085
|
+
};
|
|
5307
5086
|
}
|
|
5308
5087
|
const valuesArray = Array.isArray(values) ? values : [values];
|
|
5309
5088
|
if (valuesArray.length === 0)
|
|
@@ -5320,7 +5099,7 @@ class SearchController extends Lifecycle {
|
|
|
5320
5099
|
load_by_values: "1",
|
|
5321
5100
|
...(typeof cfg.data === "function"
|
|
5322
5101
|
? cfg.data.bind(this.selectBox.Selective.find(this.selectBox.container.targetElement))("", 0)
|
|
5323
|
-
: cfg.data ?? {}),
|
|
5102
|
+
: (cfg.data ?? {})),
|
|
5324
5103
|
};
|
|
5325
5104
|
}
|
|
5326
5105
|
let response;
|
|
@@ -5330,7 +5109,9 @@ class SearchController extends Lifecycle {
|
|
|
5330
5109
|
response = await fetch(cfg.url, {
|
|
5331
5110
|
method: "POST",
|
|
5332
5111
|
body: formData,
|
|
5333
|
-
headers: {
|
|
5112
|
+
headers: {
|
|
5113
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
5114
|
+
},
|
|
5334
5115
|
});
|
|
5335
5116
|
}
|
|
5336
5117
|
else {
|
|
@@ -5367,7 +5148,7 @@ class SearchController extends Lifecycle {
|
|
|
5367
5148
|
* Configures AJAX settings used for remote searching and pagination.
|
|
5368
5149
|
* Setting `null` disables AJAX mode and causes {@link search} to use local filtering.
|
|
5369
5150
|
*
|
|
5370
|
-
* @param {AjaxConfig
|
|
5151
|
+
* @param {AjaxConfig} config - AJAX configuration (endpoint, method, data builders, keepSelected, ...).
|
|
5371
5152
|
* @returns {void}
|
|
5372
5153
|
*/
|
|
5373
5154
|
setAjax(config) {
|
|
@@ -5557,7 +5338,12 @@ class SearchController extends Lifecycle {
|
|
|
5557
5338
|
payload.selectedValue = selectedValues;
|
|
5558
5339
|
}
|
|
5559
5340
|
else {
|
|
5560
|
-
payload = {
|
|
5341
|
+
payload = {
|
|
5342
|
+
search: keyword,
|
|
5343
|
+
page,
|
|
5344
|
+
selectedValue: selectedValues,
|
|
5345
|
+
...(cfg.data ?? {}),
|
|
5346
|
+
};
|
|
5561
5347
|
}
|
|
5562
5348
|
try {
|
|
5563
5349
|
let response;
|
|
@@ -5567,13 +5353,17 @@ class SearchController extends Lifecycle {
|
|
|
5567
5353
|
response = await fetch(cfg.url, {
|
|
5568
5354
|
method: "POST",
|
|
5569
5355
|
body: formData,
|
|
5570
|
-
headers: {
|
|
5356
|
+
headers: {
|
|
5357
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
5358
|
+
},
|
|
5571
5359
|
signal: this.abortController.signal,
|
|
5572
5360
|
});
|
|
5573
5361
|
}
|
|
5574
5362
|
else {
|
|
5575
5363
|
const params = new URLSearchParams(payload).toString();
|
|
5576
|
-
response = await fetch(`${cfg.url}?${params}`, {
|
|
5364
|
+
response = await fetch(`${cfg.url}?${params}`, {
|
|
5365
|
+
signal: this.abortController.signal,
|
|
5366
|
+
});
|
|
5577
5367
|
}
|
|
5578
5368
|
const data = await response.json();
|
|
5579
5369
|
const result = this.parseResponse(data);
|
|
@@ -5647,7 +5437,7 @@ class SearchController extends Lifecycle {
|
|
|
5647
5437
|
hasPagination = true;
|
|
5648
5438
|
page = parseInt(data.page ?? 0, 10);
|
|
5649
5439
|
totalPages = parseInt(data.totalPages ?? data.total_page ?? 1, 10);
|
|
5650
|
-
hasMore = data.hasMore ??
|
|
5440
|
+
hasMore = data.hasMore ?? page < totalPages - 1;
|
|
5651
5441
|
}
|
|
5652
5442
|
}
|
|
5653
5443
|
else if (Array.isArray(data)) {
|
|
@@ -5658,23 +5448,39 @@ class SearchController extends Lifecycle {
|
|
|
5658
5448
|
if (data.pagination) {
|
|
5659
5449
|
hasPagination = true;
|
|
5660
5450
|
page = parseInt(data.pagination.page ?? 0, 10);
|
|
5661
|
-
totalPages = parseInt(data.pagination.totalPages ??
|
|
5662
|
-
|
|
5451
|
+
totalPages = parseInt(data.pagination.totalPages ??
|
|
5452
|
+
data.pagination.total_page ??
|
|
5453
|
+
1, 10);
|
|
5454
|
+
hasMore = data.pagination.hasMore ?? page < totalPages - 1;
|
|
5663
5455
|
}
|
|
5664
5456
|
}
|
|
5665
5457
|
const normalized = items.map((item) => {
|
|
5666
|
-
if (item instanceof HTMLOptionElement ||
|
|
5458
|
+
if (item instanceof HTMLOptionElement ||
|
|
5459
|
+
item instanceof HTMLOptGroupElement)
|
|
5667
5460
|
return item;
|
|
5668
|
-
if (item.type === "optgroup" ||
|
|
5461
|
+
if (item.type === "optgroup" ||
|
|
5462
|
+
item.isGroup ||
|
|
5463
|
+
item.group ||
|
|
5464
|
+
item.label) {
|
|
5669
5465
|
const label = item.label ?? item.name ?? item.title ?? "";
|
|
5670
5466
|
const dataObj = item.data ?? {};
|
|
5671
5467
|
const opts = (item.options ?? item.items ?? []).map((opt) => ({
|
|
5672
5468
|
value: opt.value ?? opt.id ?? opt.key ?? "",
|
|
5673
|
-
text: opt.text ??
|
|
5469
|
+
text: opt.text ??
|
|
5470
|
+
opt.label ??
|
|
5471
|
+
opt.name ??
|
|
5472
|
+
opt.title ??
|
|
5473
|
+
"",
|
|
5674
5474
|
selected: opt.selected ?? false,
|
|
5675
|
-
data: opt.data ??
|
|
5475
|
+
data: opt.data ??
|
|
5476
|
+
(opt.imgsrc ? { imgsrc: opt.imgsrc } : {}),
|
|
5676
5477
|
}));
|
|
5677
|
-
return {
|
|
5478
|
+
return {
|
|
5479
|
+
type: "optgroup",
|
|
5480
|
+
label,
|
|
5481
|
+
data: dataObj,
|
|
5482
|
+
options: opts,
|
|
5483
|
+
};
|
|
5678
5484
|
}
|
|
5679
5485
|
const dataObj = item.data ?? {};
|
|
5680
5486
|
if (item?.imgsrc)
|
|
@@ -5716,9 +5522,12 @@ class SearchController extends Lifecycle {
|
|
|
5716
5522
|
select.innerHTML = "";
|
|
5717
5523
|
items.forEach((item) => {
|
|
5718
5524
|
// Skip empty item (defensive guard)
|
|
5719
|
-
if ((item["type"] === "option" || !item["type"]) &&
|
|
5525
|
+
if ((item["type"] === "option" || !item["type"]) &&
|
|
5526
|
+
item["value"] === "" &&
|
|
5527
|
+
item["text"] === "")
|
|
5720
5528
|
return;
|
|
5721
|
-
if (item instanceof HTMLOptionElement ||
|
|
5529
|
+
if (item instanceof HTMLOptionElement ||
|
|
5530
|
+
item instanceof HTMLOptGroupElement) {
|
|
5722
5531
|
select.appendChild(item);
|
|
5723
5532
|
return;
|
|
5724
5533
|
}
|
|
@@ -5740,7 +5549,8 @@ class SearchController extends Lifecycle {
|
|
|
5740
5549
|
option.dataset[key] = String(opt.data[key]);
|
|
5741
5550
|
});
|
|
5742
5551
|
}
|
|
5743
|
-
if (opt.selected ||
|
|
5552
|
+
if (opt.selected ||
|
|
5553
|
+
(keepSelected && oldSelected.includes(option.value))) {
|
|
5744
5554
|
option.selected = true;
|
|
5745
5555
|
}
|
|
5746
5556
|
optgroup.appendChild(option);
|
|
@@ -5757,7 +5567,8 @@ class SearchController extends Lifecycle {
|
|
|
5757
5567
|
option.dataset[key] = String(item.data[key]);
|
|
5758
5568
|
});
|
|
5759
5569
|
}
|
|
5760
|
-
if (item.selected ||
|
|
5570
|
+
if (item.selected ||
|
|
5571
|
+
(keepSelected && oldSelected.includes(option.value))) {
|
|
5761
5572
|
option.selected = true;
|
|
5762
5573
|
}
|
|
5763
5574
|
select.appendChild(option);
|
|
@@ -5833,16 +5644,6 @@ class SelectObserver {
|
|
|
5833
5644
|
* @param {HTMLSelectElement} select - The `<select>` element to observe for mutations.
|
|
5834
5645
|
*/
|
|
5835
5646
|
constructor(select) {
|
|
5836
|
-
/**
|
|
5837
|
-
* Debounce timer handle for batching rapid mutations.
|
|
5838
|
-
*
|
|
5839
|
-
* - Cleared and reset on each mutation event.
|
|
5840
|
-
* - Invokes {@link handleChange} after {@link _DEBOUNCE_DELAY} milliseconds of inactivity.
|
|
5841
|
-
* - Nulled during {@link disconnect}.
|
|
5842
|
-
*
|
|
5843
|
-
* @private
|
|
5844
|
-
*/
|
|
5845
|
-
this.debounceTimer = null;
|
|
5846
5647
|
/**
|
|
5847
5648
|
* Debounce delay in milliseconds.
|
|
5848
5649
|
*
|
|
@@ -5983,16 +5784,12 @@ class DatasetObserver {
|
|
|
5983
5784
|
* @param element - The element whose `data-*` attributes will be observed.
|
|
5984
5785
|
*/
|
|
5985
5786
|
constructor(element) {
|
|
5986
|
-
/**
|
|
5987
|
-
* Debounce timer handle for coalescing rapid attribute mutations.
|
|
5988
|
-
* Cleared/replaced whenever a new relevant mutation arrives within the debounce window.
|
|
5989
|
-
*/
|
|
5990
|
-
this.debounceTimer = null;
|
|
5991
5787
|
this.element = element;
|
|
5992
5788
|
this.observer = new MutationObserver((mutations) => {
|
|
5993
5789
|
let datasetChanged = false;
|
|
5994
5790
|
for (const mutation of mutations) {
|
|
5995
|
-
if (mutation.type === "attributes" &&
|
|
5791
|
+
if (mutation.type === "attributes" &&
|
|
5792
|
+
mutation.attributeName?.startsWith("data-")) {
|
|
5996
5793
|
datasetChanged = true;
|
|
5997
5794
|
break;
|
|
5998
5795
|
}
|
|
@@ -6198,7 +5995,9 @@ class Adapter extends Lifecycle {
|
|
|
6198
5995
|
* @see {@link changeProp}
|
|
6199
5996
|
*/
|
|
6200
5997
|
onPropChanged(propName, callback) {
|
|
6201
|
-
Libs.callbackScheduler.on(`${propName}_${this.adapterKey}`, callback, {
|
|
5998
|
+
Libs.callbackScheduler.on(`${propName}_${this.adapterKey}`, callback, {
|
|
5999
|
+
debounce: 0,
|
|
6000
|
+
});
|
|
6202
6001
|
}
|
|
6203
6002
|
/**
|
|
6204
6003
|
* Triggers the **post-change** pipeline for a given property.
|
|
@@ -6233,7 +6032,7 @@ class Adapter extends Lifecycle {
|
|
|
6233
6032
|
*
|
|
6234
6033
|
* @param {HTMLElement} parent - Container element that will host the viewer.
|
|
6235
6034
|
* @param {TItem} item - The model for which the viewer is created.
|
|
6236
|
-
* @returns {TViewer
|
|
6035
|
+
* @returns {TViewer} The created viewer instance; `null` by default.
|
|
6237
6036
|
*/
|
|
6238
6037
|
viewHolder(parent, item) {
|
|
6239
6038
|
return null;
|
|
@@ -6340,7 +6139,7 @@ class Adapter extends Lifecycle {
|
|
|
6340
6139
|
return;
|
|
6341
6140
|
}
|
|
6342
6141
|
this.recyclerView = null;
|
|
6343
|
-
this.items.forEach(item => {
|
|
6142
|
+
this.items.forEach((item) => {
|
|
6344
6143
|
item?.destroy?.();
|
|
6345
6144
|
});
|
|
6346
6145
|
this.items = [];
|
|
@@ -6395,20 +6194,6 @@ class View extends Lifecycle {
|
|
|
6395
6194
|
*/
|
|
6396
6195
|
constructor(parent) {
|
|
6397
6196
|
super();
|
|
6398
|
-
/**
|
|
6399
|
-
* Host container element into which this view's root element is rendered/attached.
|
|
6400
|
-
*
|
|
6401
|
-
* This reference is captured at construction time and cleared on {@link destroy}.
|
|
6402
|
-
*/
|
|
6403
|
-
this.parent = null;
|
|
6404
|
-
/**
|
|
6405
|
-
* Mounted view result containing:
|
|
6406
|
-
* - `view`: the root element of this view
|
|
6407
|
-
* - `tags`: a strongly-typed map of child elements for fast access
|
|
6408
|
-
*
|
|
6409
|
-
* This is expected to be assigned by subclasses (or a mount helper) before {@link getView} is called.
|
|
6410
|
-
*/
|
|
6411
|
-
this.view = null;
|
|
6412
6197
|
this.parent = parent;
|
|
6413
6198
|
this.init();
|
|
6414
6199
|
}
|
|
@@ -6509,29 +6294,6 @@ class GroupView extends View {
|
|
|
6509
6294
|
*/
|
|
6510
6295
|
constructor(parent, options) {
|
|
6511
6296
|
super(parent);
|
|
6512
|
-
/**
|
|
6513
|
-
* Strongly-typed reference to the mounted group view structure.
|
|
6514
|
-
*
|
|
6515
|
-
* Structure:
|
|
6516
|
-
* - **view**: Root container element.
|
|
6517
|
-
* - **tags**: Named references to header and items container.
|
|
6518
|
-
*
|
|
6519
|
-
* Lifecycle:
|
|
6520
|
-
* - `null` until {@link mount} completes.
|
|
6521
|
-
* - Cleared during {@link destroy}.
|
|
6522
|
-
*
|
|
6523
|
-
* @public
|
|
6524
|
-
*/
|
|
6525
|
-
this.view = null;
|
|
6526
|
-
/**
|
|
6527
|
-
* Parsed configuration (bound from the `<select>` element via binder map).
|
|
6528
|
-
*
|
|
6529
|
-
* Provides feature flags (multiple/disabled/readonly/visible/virtualScroll/ajax/autoclose…),
|
|
6530
|
-
* a11y ids (e.g. `SEID_LIST`, `SEID_HOLDER`) and user callbacks under `options.on`.
|
|
6531
|
-
*
|
|
6532
|
-
* @internal
|
|
6533
|
-
*/
|
|
6534
|
-
this.options = null;
|
|
6535
6297
|
this.options = options;
|
|
6536
6298
|
}
|
|
6537
6299
|
/**
|
|
@@ -6625,10 +6387,10 @@ class GroupView extends View {
|
|
|
6625
6387
|
* - Safe to call multiple times with same value (idempotent).
|
|
6626
6388
|
*
|
|
6627
6389
|
* @public
|
|
6628
|
-
* @param {string
|
|
6390
|
+
* @param {string} [label=null] - New label to display; `null` preserves current label.
|
|
6629
6391
|
* @returns {void}
|
|
6630
6392
|
*/
|
|
6631
|
-
updateLabel(label
|
|
6393
|
+
updateLabel(label) {
|
|
6632
6394
|
if (!this.view)
|
|
6633
6395
|
return;
|
|
6634
6396
|
const headerEl = this.view.tags.GroupHeader;
|
|
@@ -6675,7 +6437,7 @@ class GroupView extends View {
|
|
|
6675
6437
|
if (!this.view)
|
|
6676
6438
|
return;
|
|
6677
6439
|
const items = this.view.tags.GroupItems;
|
|
6678
|
-
const visibleItems = Array.from(items.children).filter(child => !child.classList.contains("hide"));
|
|
6440
|
+
const visibleItems = Array.from(items.children).filter((child) => !child.classList.contains("hide"));
|
|
6679
6441
|
this.view.view.classList.toggle("hide", visibleItems.length === 0);
|
|
6680
6442
|
}
|
|
6681
6443
|
/**
|
|
@@ -6789,57 +6551,6 @@ class OptionView extends View {
|
|
|
6789
6551
|
*/
|
|
6790
6552
|
constructor(parent, options) {
|
|
6791
6553
|
super(parent);
|
|
6792
|
-
/**
|
|
6793
|
-
* Strongly-typed reference to the mounted option view structure.
|
|
6794
|
-
*
|
|
6795
|
-
* Structure:
|
|
6796
|
-
* - **view**: Root container element.
|
|
6797
|
-
* - **tags**: Named references to input, image (conditional), label, label content.
|
|
6798
|
-
*
|
|
6799
|
-
* Lifecycle:
|
|
6800
|
-
* - `null` until {@link mount} completes.
|
|
6801
|
-
* - Cleared during {@link destroy}.
|
|
6802
|
-
*
|
|
6803
|
-
* @public
|
|
6804
|
-
*/
|
|
6805
|
-
this.view = null;
|
|
6806
|
-
/**
|
|
6807
|
-
* Parsed configuration (bound from the `<select>` element via binder map).
|
|
6808
|
-
*
|
|
6809
|
-
* Provides feature flags (multiple/disabled/readonly/visible/virtualScroll/ajax/autoclose…),
|
|
6810
|
-
* a11y ids (e.g. `SEID_LIST`, `SEID_HOLDER`) and user callbacks under `options.on`.
|
|
6811
|
-
*
|
|
6812
|
-
* @internal
|
|
6813
|
-
*/
|
|
6814
|
-
this.options = null;
|
|
6815
|
-
/**
|
|
6816
|
-
* Internal configuration object (Proxy target).
|
|
6817
|
-
*
|
|
6818
|
-
* Lifecycle:
|
|
6819
|
-
* - Initialized during {@link initialize} with default values.
|
|
6820
|
-
* - Mutated via {@link configProxy} Proxy trap.
|
|
6821
|
-
*
|
|
6822
|
-
* Notes:
|
|
6823
|
-
* - **Should not be mutated directly**; use {@link configProxy} or typed setters.
|
|
6824
|
-
* - Contains default values for layout, image, and alignment.
|
|
6825
|
-
*
|
|
6826
|
-
* @private
|
|
6827
|
-
*/
|
|
6828
|
-
this.config = null;
|
|
6829
|
-
/**
|
|
6830
|
-
* Reactive Proxy wrapper around {@link config}.
|
|
6831
|
-
*
|
|
6832
|
-
* Behavior:
|
|
6833
|
-
* - Intercepts property assignments via `set` trap.
|
|
6834
|
-
* - Triggers {@link applyPartialChange} for diffed values when {@link isRendered} is `true`.
|
|
6835
|
-
* - Prevents redundant DOM updates when value hasn't changed.
|
|
6836
|
-
*
|
|
6837
|
-
* Usage:
|
|
6838
|
-
* - Accessed via {@link optionConfig} getter or typed setters ({@link isMultiple}, {@link hasImage}).
|
|
6839
|
-
*
|
|
6840
|
-
* @private
|
|
6841
|
-
*/
|
|
6842
|
-
this.configProxy = null;
|
|
6843
6554
|
/**
|
|
6844
6555
|
* Flag indicating whether the initial render has completed.
|
|
6845
6556
|
*
|
|
@@ -7004,24 +6715,30 @@ class OptionView extends View {
|
|
|
7004
6715
|
* - Each changed property triggers {@link applyPartialChange} individually.
|
|
7005
6716
|
*
|
|
7006
6717
|
* @public
|
|
7007
|
-
* @param {OptionConfigPatch
|
|
6718
|
+
* @param {OptionConfigPatch} config - Partial configuration patch; `null` is no-op.
|
|
7008
6719
|
* @returns {void}
|
|
7009
6720
|
*/
|
|
7010
6721
|
set optionConfig(config) {
|
|
7011
6722
|
if (!config || !this.configProxy || !this.config)
|
|
7012
6723
|
return;
|
|
7013
6724
|
const changes = {};
|
|
7014
|
-
if (config.imageWidth !== undefined &&
|
|
6725
|
+
if (config.imageWidth !== undefined &&
|
|
6726
|
+
config.imageWidth !== this.config.imageWidth)
|
|
7015
6727
|
changes.imageWidth = config.imageWidth;
|
|
7016
|
-
if (config.imageHeight !== undefined &&
|
|
6728
|
+
if (config.imageHeight !== undefined &&
|
|
6729
|
+
config.imageHeight !== this.config.imageHeight)
|
|
7017
6730
|
changes.imageHeight = config.imageHeight;
|
|
7018
|
-
if (config.imageBorderRadius !== undefined &&
|
|
6731
|
+
if (config.imageBorderRadius !== undefined &&
|
|
6732
|
+
config.imageBorderRadius !== this.config.imageBorderRadius)
|
|
7019
6733
|
changes.imageBorderRadius = config.imageBorderRadius;
|
|
7020
|
-
if (config.imagePosition !== undefined &&
|
|
6734
|
+
if (config.imagePosition !== undefined &&
|
|
6735
|
+
config.imagePosition !== this.config.imagePosition)
|
|
7021
6736
|
changes.imagePosition = config.imagePosition;
|
|
7022
|
-
if (config.labelValign !== undefined &&
|
|
6737
|
+
if (config.labelValign !== undefined &&
|
|
6738
|
+
config.labelValign !== this.config.labelValign)
|
|
7023
6739
|
changes.labelValign = config.labelValign;
|
|
7024
|
-
if (config.labelHalign !== undefined &&
|
|
6740
|
+
if (config.labelHalign !== undefined &&
|
|
6741
|
+
config.labelHalign !== this.config.labelHalign)
|
|
7025
6742
|
changes.labelHalign = config.labelHalign;
|
|
7026
6743
|
if (Object.keys(changes).length > 0) {
|
|
7027
6744
|
Object.assign(this.configProxy, changes);
|
|
@@ -7195,9 +6912,11 @@ class OptionView extends View {
|
|
|
7195
6912
|
case "imageBorderRadius": {
|
|
7196
6913
|
const img = v.tags?.OptionImage;
|
|
7197
6914
|
if (img) {
|
|
7198
|
-
const styleProp = prop === "imageWidth"
|
|
7199
|
-
|
|
7200
|
-
|
|
6915
|
+
const styleProp = prop === "imageWidth"
|
|
6916
|
+
? "width"
|
|
6917
|
+
: prop === "imageHeight"
|
|
6918
|
+
? "height"
|
|
6919
|
+
: "borderRadius";
|
|
7201
6920
|
img.style[styleProp] = String(newValue);
|
|
7202
6921
|
}
|
|
7203
6922
|
break;
|
|
@@ -7318,15 +7037,6 @@ class MixedAdapter extends Adapter {
|
|
|
7318
7037
|
super(items);
|
|
7319
7038
|
/** Whether the adapter operates in multi-selection mode. */
|
|
7320
7039
|
this.isMultiple = false;
|
|
7321
|
-
/**
|
|
7322
|
-
* Parsed configuration (bound from the `<select>` element via binder map).
|
|
7323
|
-
*
|
|
7324
|
-
* Provides feature flags (multiple/disabled/readonly/visible/virtualScroll/ajax/autoclose…),
|
|
7325
|
-
* a11y ids (e.g. `SEID_LIST`, `SEID_HOLDER`) and user callbacks under `options.on`.
|
|
7326
|
-
*
|
|
7327
|
-
* @internal
|
|
7328
|
-
*/
|
|
7329
|
-
this.options = null;
|
|
7330
7040
|
/**
|
|
7331
7041
|
* Subscribers for aggregated visibility statistics.
|
|
7332
7042
|
* Fired via a debounced scheduler to avoid repeated recomputation during batch updates.
|
|
@@ -7337,11 +7047,6 @@ class MixedAdapter extends Adapter {
|
|
|
7337
7047
|
* `-1` indicates "no highlight".
|
|
7338
7048
|
*/
|
|
7339
7049
|
this.currentHighlightIndex = -1;
|
|
7340
|
-
/**
|
|
7341
|
-
* Cached pointer to the selected option in single-select mode.
|
|
7342
|
-
* Used to efficiently clear previous selection when selecting a new option.
|
|
7343
|
-
*/
|
|
7344
|
-
this.selectedItemSingle = null;
|
|
7345
7050
|
/** Top-level group models (if any). */
|
|
7346
7051
|
this.groups = [];
|
|
7347
7052
|
/**
|
|
@@ -7430,7 +7135,7 @@ class MixedAdapter extends Adapter {
|
|
|
7430
7135
|
* - Performs one-time listener binding guarded by `item.isInit`.
|
|
7431
7136
|
*
|
|
7432
7137
|
* @param {MixedItem} item - {@link GroupModel} or {@link OptionModel}.
|
|
7433
|
-
* @param {GroupView | OptionView
|
|
7138
|
+
* @param {GroupView | OptionView} viewer - The view instance that will render the model.
|
|
7434
7139
|
* @param {number} position - Position in the top-level mixed list.
|
|
7435
7140
|
* @returns {void}
|
|
7436
7141
|
* @override
|
|
@@ -7657,7 +7362,7 @@ class MixedAdapter extends Adapter {
|
|
|
7657
7362
|
return;
|
|
7658
7363
|
}
|
|
7659
7364
|
Libs.callbackScheduler.clear(`sche_vis_${this.adapterKey}`);
|
|
7660
|
-
this.groups.forEach(group => {
|
|
7365
|
+
this.groups.forEach((group) => {
|
|
7661
7366
|
group.destroy();
|
|
7662
7367
|
});
|
|
7663
7368
|
this.visibilityChangedCallbacks = [];
|
|
@@ -7780,7 +7485,8 @@ class MixedAdapter extends Adapter {
|
|
|
7780
7485
|
* @returns {void}
|
|
7781
7486
|
*/
|
|
7782
7487
|
selectHighlighted() {
|
|
7783
|
-
if (this.currentHighlightIndex > -1 &&
|
|
7488
|
+
if (this.currentHighlightIndex > -1 &&
|
|
7489
|
+
this.flatOptions[this.currentHighlightIndex]) {
|
|
7784
7490
|
const item = this.flatOptions[this.currentHighlightIndex];
|
|
7785
7491
|
if (item.visible) {
|
|
7786
7492
|
const viewEl = item.view?.getView?.();
|
|
@@ -7817,7 +7523,8 @@ class MixedAdapter extends Adapter {
|
|
|
7817
7523
|
else {
|
|
7818
7524
|
index = 0;
|
|
7819
7525
|
}
|
|
7820
|
-
if (this.currentHighlightIndex > -1 &&
|
|
7526
|
+
if (this.currentHighlightIndex > -1 &&
|
|
7527
|
+
this.flatOptions[this.currentHighlightIndex]) {
|
|
7821
7528
|
this.flatOptions[this.currentHighlightIndex].highlighted = false;
|
|
7822
7529
|
}
|
|
7823
7530
|
for (let i = index; i < this.flatOptions.length; i++) {
|
|
@@ -7829,11 +7536,13 @@ class MixedAdapter extends Adapter {
|
|
|
7829
7536
|
if (isScrollToView) {
|
|
7830
7537
|
const el = item.view?.getView?.();
|
|
7831
7538
|
if (el) {
|
|
7832
|
-
el.scrollIntoView({ block:
|
|
7539
|
+
el.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
7833
7540
|
}
|
|
7834
7541
|
else {
|
|
7835
7542
|
// If virtualized, ensure the item is rendered before trying to scroll.
|
|
7836
|
-
this.recyclerView?.ensureRendered?.(i, {
|
|
7543
|
+
this.recyclerView?.ensureRendered?.(i, {
|
|
7544
|
+
scrollIntoView: true,
|
|
7545
|
+
});
|
|
7837
7546
|
}
|
|
7838
7547
|
}
|
|
7839
7548
|
this.onHighlightChange(i, item.view?.getView?.()?.id);
|
|
@@ -8062,9 +7771,9 @@ class VirtualRecyclerView extends RecyclerView {
|
|
|
8062
7771
|
*
|
|
8063
7772
|
* Note: The virtualization scaffold is built when an adapter is set via {@link setAdapter}.
|
|
8064
7773
|
*
|
|
8065
|
-
* @param {HTMLDivElement
|
|
7774
|
+
* @param {HTMLDivElement} [viewElement=null] - Optional root container for the recycler view.
|
|
8066
7775
|
*/
|
|
8067
|
-
constructor(viewElement
|
|
7776
|
+
constructor(viewElement) {
|
|
8068
7777
|
super(viewElement);
|
|
8069
7778
|
/**
|
|
8070
7779
|
* Virtualization settings (materialized to `Required<VirtualOptions>`).
|
|
@@ -8100,9 +7809,6 @@ class VirtualRecyclerView extends RecyclerView {
|
|
|
8100
7809
|
this.start = 0;
|
|
8101
7810
|
/** Current window end (inclusive). -1 means not initialized. */
|
|
8102
7811
|
this.end = -1;
|
|
8103
|
-
/** Pending animation frame ids for window and measurement. */
|
|
8104
|
-
this.rafId = null;
|
|
8105
|
-
this.measureRaf = null;
|
|
8106
7812
|
/** Re-entrancy/suspension flags used to prevent feedback loops. */
|
|
8107
7813
|
this.updating = false;
|
|
8108
7814
|
this.suppressResize = false;
|
|
@@ -8157,20 +7863,29 @@ class VirtualRecyclerView extends RecyclerView {
|
|
|
8157
7863
|
return;
|
|
8158
7864
|
this.viewElement.replaceChildren();
|
|
8159
7865
|
const nodeMounted = Libs.mountNode({
|
|
8160
|
-
PadTop: {
|
|
8161
|
-
|
|
8162
|
-
|
|
7866
|
+
PadTop: {
|
|
7867
|
+
tag: { node: "div", classList: "seui-virtual-pad-top" },
|
|
7868
|
+
},
|
|
7869
|
+
ItemsHost: {
|
|
7870
|
+
tag: { node: "div", classList: "seui-virtual-items" },
|
|
7871
|
+
},
|
|
7872
|
+
PadBottom: {
|
|
7873
|
+
tag: { node: "div", classList: "seui-virtual-pad-bottom" },
|
|
7874
|
+
},
|
|
8163
7875
|
}, this.viewElement);
|
|
8164
7876
|
this.PadTop = nodeMounted.PadTop;
|
|
8165
7877
|
this.ItemsHost = nodeMounted.ItemsHost;
|
|
8166
7878
|
this.PadBottom = nodeMounted.PadBottom;
|
|
8167
|
-
this.scrollEl =
|
|
8168
|
-
|
|
8169
|
-
|
|
7879
|
+
this.scrollEl =
|
|
7880
|
+
this.opts.scrollEl ??
|
|
7881
|
+
this.viewElement.closest(".seui-popup") ??
|
|
7882
|
+
this.viewElement.parentElement;
|
|
8170
7883
|
if (!this.scrollEl)
|
|
8171
7884
|
throw new Error("VirtualRecyclerView: scrollEl not found");
|
|
8172
7885
|
this.boundOnScroll = this.onScroll.bind(this);
|
|
8173
|
-
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7886
|
+
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7887
|
+
passive: true,
|
|
7888
|
+
});
|
|
8174
7889
|
this.refresh(false);
|
|
8175
7890
|
this.attachResizeObserverOnce();
|
|
8176
7891
|
adapter?.onVisibilityChanged?.(() => this.refreshItem());
|
|
@@ -8209,7 +7924,9 @@ class VirtualRecyclerView extends RecyclerView {
|
|
|
8209
7924
|
resume() {
|
|
8210
7925
|
this.suspended = false;
|
|
8211
7926
|
if (this.scrollEl && this.boundOnScroll) {
|
|
8212
|
-
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7927
|
+
this.scrollEl.addEventListener("scroll", this.boundOnScroll, {
|
|
7928
|
+
passive: true,
|
|
7929
|
+
});
|
|
8213
7930
|
}
|
|
8214
7931
|
if (this.resumeResizeAfter) {
|
|
8215
7932
|
this.attachResizeObserverOnce();
|
|
@@ -8307,7 +8024,7 @@ class VirtualRecyclerView extends RecyclerView {
|
|
|
8307
8024
|
this.scrollEl.removeEventListener("scroll", this.boundOnScroll);
|
|
8308
8025
|
}
|
|
8309
8026
|
this.resizeObs?.disconnect();
|
|
8310
|
-
this.created.forEach(el => el.remove());
|
|
8027
|
+
this.created.forEach((el) => el.remove());
|
|
8311
8028
|
this.created.clear();
|
|
8312
8029
|
}
|
|
8313
8030
|
/**
|
|
@@ -8384,7 +8101,7 @@ class VirtualRecyclerView extends RecyclerView {
|
|
|
8384
8101
|
* @returns {void}
|
|
8385
8102
|
*/
|
|
8386
8103
|
resetState() {
|
|
8387
|
-
this.created.forEach(el => el.remove());
|
|
8104
|
+
this.created.forEach((el) => el.remove());
|
|
8388
8105
|
this.created.clear();
|
|
8389
8106
|
this.heightCache = [];
|
|
8390
8107
|
this.fenwick.reset(0);
|
|
@@ -8658,8 +8375,12 @@ class VirtualRecyclerView extends RecyclerView {
|
|
|
8658
8375
|
el.setAttribute(VirtualRecyclerView.ATTR_INDEX, String(index));
|
|
8659
8376
|
const prev = el.previousElementSibling;
|
|
8660
8377
|
const next = el.nextElementSibling;
|
|
8661
|
-
const needsReorder = (prev &&
|
|
8662
|
-
|
|
8378
|
+
const needsReorder = (prev &&
|
|
8379
|
+
Number(prev.getAttribute(VirtualRecyclerView.ATTR_INDEX)) >
|
|
8380
|
+
index) ||
|
|
8381
|
+
(next &&
|
|
8382
|
+
Number(next.getAttribute(VirtualRecyclerView.ATTR_INDEX)) <
|
|
8383
|
+
index);
|
|
8663
8384
|
if (needsReorder) {
|
|
8664
8385
|
el.remove();
|
|
8665
8386
|
this.insertIntoHostByIndex(index, el);
|
|
@@ -8681,7 +8402,10 @@ class VirtualRecyclerView extends RecyclerView {
|
|
|
8681
8402
|
if (this.resizeObs)
|
|
8682
8403
|
return;
|
|
8683
8404
|
this.resizeObs = new ResizeObserver(() => {
|
|
8684
|
-
if (this.suppressResize ||
|
|
8405
|
+
if (this.suppressResize ||
|
|
8406
|
+
this.suspended ||
|
|
8407
|
+
!this.adapter ||
|
|
8408
|
+
this.measureRaf != null)
|
|
8685
8409
|
return;
|
|
8686
8410
|
this.measureRaf = requestAnimationFrame(() => {
|
|
8687
8411
|
this.measureRaf = null;
|
|
@@ -9010,31 +8734,6 @@ class SelectBox extends Lifecycle {
|
|
|
9010
8734
|
* @internal
|
|
9011
8735
|
*/
|
|
9012
8736
|
this.oldValue = null;
|
|
9013
|
-
/**
|
|
9014
|
-
* Root wrapper DOM node for the enhanced UI.
|
|
9015
|
-
*
|
|
9016
|
-
* Created during {@link init} via {@link Libs.mountNode}, inserted into the DOM during {@link mount},
|
|
9017
|
-
* and removed during {@link destroy}.
|
|
9018
|
-
*/
|
|
9019
|
-
this.node = null;
|
|
9020
|
-
/**
|
|
9021
|
-
* Parsed configuration (bound from the `<select>` element via binder map).
|
|
9022
|
-
*
|
|
9023
|
-
* Provides feature flags (multiple/disabled/readonly/visible/virtualScroll/ajax/autoclose…),
|
|
9024
|
-
* a11y ids (e.g. `SEID_LIST`, `SEID_HOLDER`) and user callbacks under `options.on`.
|
|
9025
|
-
*
|
|
9026
|
-
* @internal
|
|
9027
|
-
*/
|
|
9028
|
-
this.options = null;
|
|
9029
|
-
/**
|
|
9030
|
-
* Manager that owns model resources and bridges the Adapter ↔ RecyclerView pipeline.
|
|
9031
|
-
*
|
|
9032
|
-
* The configured adapter is {@link MixedAdapter}. The recyclerview implementation is chosen
|
|
9033
|
-
* based on `options.virtualScroll` (standard {@link RecyclerView} vs {@link VirtualRecyclerView}).
|
|
9034
|
-
*
|
|
9035
|
-
* @internal
|
|
9036
|
-
*/
|
|
9037
|
-
this.optionModelManager = null;
|
|
9038
8737
|
/**
|
|
9039
8738
|
* Whether the popup/list UI is currently open.
|
|
9040
8739
|
*
|
|
@@ -9066,20 +8765,10 @@ class SelectBox extends Lifecycle {
|
|
|
9066
8765
|
* @internal
|
|
9067
8766
|
*/
|
|
9068
8767
|
this.hasDeInitialized = false;
|
|
9069
|
-
/**
|
|
9070
|
-
* Selective context (global helper / registry).
|
|
9071
|
-
*
|
|
9072
|
-
* Used to locate the instance wrapper via `Selective.find(...)` and to close other open instances.
|
|
9073
|
-
*/
|
|
9074
|
-
this.Selective = null;
|
|
9075
8768
|
/**
|
|
9076
8769
|
* Registered plugins for this SelectBox instance.
|
|
9077
8770
|
*/
|
|
9078
8771
|
this.plugins = [];
|
|
9079
|
-
/**
|
|
9080
|
-
* Cached plugin context for this SelectBox instance.
|
|
9081
|
-
*/
|
|
9082
|
-
this.pluginContext = null;
|
|
9083
8772
|
if (select && Selective)
|
|
9084
8773
|
this.initialize(select, Selective);
|
|
9085
8774
|
}
|
|
@@ -9204,7 +8893,9 @@ class SelectBox extends Lifecycle {
|
|
|
9204
8893
|
classList: "seui-view",
|
|
9205
8894
|
tabIndex: 0,
|
|
9206
8895
|
onkeydown: (e) => {
|
|
9207
|
-
if (e.key === "Enter" ||
|
|
8896
|
+
if (e.key === "Enter" ||
|
|
8897
|
+
e.key === " " ||
|
|
8898
|
+
e.key === "ArrowDown") {
|
|
9208
8899
|
e.preventDefault();
|
|
9209
8900
|
this.getAction()?.open();
|
|
9210
8901
|
}
|
|
@@ -9596,7 +9287,11 @@ class SelectBox extends Lifecycle {
|
|
|
9596
9287
|
get value() {
|
|
9597
9288
|
const item_list = this.valueArray;
|
|
9598
9289
|
const valLength = item_list.length;
|
|
9599
|
-
return valLength > 1
|
|
9290
|
+
return valLength > 1
|
|
9291
|
+
? item_list
|
|
9292
|
+
: valLength === 0
|
|
9293
|
+
? ""
|
|
9294
|
+
: item_list[0];
|
|
9600
9295
|
},
|
|
9601
9296
|
get valueArray() {
|
|
9602
9297
|
const item_list = [];
|
|
@@ -9631,7 +9326,11 @@ class SelectBox extends Lifecycle {
|
|
|
9631
9326
|
item_list.push(m.text);
|
|
9632
9327
|
});
|
|
9633
9328
|
const valLength = item_list.length;
|
|
9634
|
-
return valLength > 1
|
|
9329
|
+
return valLength > 1
|
|
9330
|
+
? item_list
|
|
9331
|
+
: valLength === 0
|
|
9332
|
+
? ""
|
|
9333
|
+
: item_list[0];
|
|
9635
9334
|
},
|
|
9636
9335
|
get isOpen() {
|
|
9637
9336
|
return superThis.isOpen;
|
|
@@ -9656,7 +9355,8 @@ class SelectBox extends Lifecycle {
|
|
|
9656
9355
|
},
|
|
9657
9356
|
selectAll(_evtToken, trigger = true) {
|
|
9658
9357
|
if (bindedOptions.multiple && bindedOptions.maxSelected > 0) {
|
|
9659
|
-
if (superThis.getModelOption().length >
|
|
9358
|
+
if (superThis.getModelOption().length >
|
|
9359
|
+
bindedOptions.maxSelected)
|
|
9660
9360
|
return;
|
|
9661
9361
|
}
|
|
9662
9362
|
if (this.disabled || this.readonly || !bindedOptions.multiple)
|
|
@@ -9688,7 +9388,7 @@ class SelectBox extends Lifecycle {
|
|
|
9688
9388
|
},
|
|
9689
9389
|
deSelectByDataset(_evtToken, dataset, trigger = true) {
|
|
9690
9390
|
if (dataset) {
|
|
9691
|
-
superThis.getModelOption().forEach(optionModel => {
|
|
9391
|
+
superThis.getModelOption().forEach((optionModel) => {
|
|
9692
9392
|
if (optionModel.dataset) {
|
|
9693
9393
|
for (let searchKey in dataset) {
|
|
9694
9394
|
let value = dataset[searchKey];
|
|
@@ -9702,12 +9402,14 @@ class SelectBox extends Lifecycle {
|
|
|
9702
9402
|
this.change(false, trigger);
|
|
9703
9403
|
}
|
|
9704
9404
|
},
|
|
9705
|
-
setValue(_evtToken
|
|
9405
|
+
setValue(_evtToken, value, trigger = true, force = false) {
|
|
9706
9406
|
if (!Array.isArray(value))
|
|
9707
9407
|
value = [value];
|
|
9708
9408
|
value = value.filter((v) => v !== "" && v != null);
|
|
9709
9409
|
if (value.length === 0) {
|
|
9710
|
-
superThis
|
|
9410
|
+
superThis
|
|
9411
|
+
.getModelOption()
|
|
9412
|
+
.forEach((m) => (m.selectedNonTrigger = false));
|
|
9711
9413
|
this.change(false, trigger);
|
|
9712
9414
|
return;
|
|
9713
9415
|
}
|
|
@@ -9731,7 +9433,8 @@ class SelectBox extends Lifecycle {
|
|
|
9731
9433
|
const result = await container.searchController.loadByValues(missing);
|
|
9732
9434
|
if (result.success && result.items.length > 0) {
|
|
9733
9435
|
result.items.forEach((it) => {
|
|
9734
|
-
if (missing.includes(it.value) ||
|
|
9436
|
+
if (missing.includes(it.value) ||
|
|
9437
|
+
missing.includes(it.text))
|
|
9735
9438
|
it.selected = true;
|
|
9736
9439
|
});
|
|
9737
9440
|
container.searchController.applyAjaxResult?.(result.items, false, false);
|
|
@@ -9770,7 +9473,8 @@ class SelectBox extends Lifecycle {
|
|
|
9770
9473
|
this.change(false, trigger);
|
|
9771
9474
|
},
|
|
9772
9475
|
load() {
|
|
9773
|
-
if ((!superThis.hasLoadedOnce || superThis.isBeforeSearch) &&
|
|
9476
|
+
if ((!superThis.hasLoadedOnce || superThis.isBeforeSearch) &&
|
|
9477
|
+
bindedOptions?.ajax) {
|
|
9774
9478
|
container.searchController.resetPagination();
|
|
9775
9479
|
container.popup.showLoading();
|
|
9776
9480
|
superThis.hasLoadedOnce = true;
|
|
@@ -9857,9 +9561,10 @@ class SelectBox extends Lifecycle {
|
|
|
9857
9561
|
else
|
|
9858
9562
|
this.open();
|
|
9859
9563
|
},
|
|
9860
|
-
change(_evtToken
|
|
9564
|
+
change(_evtToken, canTrigger = true) {
|
|
9861
9565
|
if (canTrigger) {
|
|
9862
|
-
if (bindedOptions.multiple &&
|
|
9566
|
+
if (bindedOptions.multiple &&
|
|
9567
|
+
bindedOptions.maxSelected > 0) {
|
|
9863
9568
|
if (this.valueArray.length > bindedOptions.maxSelected) {
|
|
9864
9569
|
this.setValue(null, this.oldValue, false, true);
|
|
9865
9570
|
}
|
|
@@ -9894,7 +9599,8 @@ class SelectBox extends Lifecycle {
|
|
|
9894
9599
|
},
|
|
9895
9600
|
refreshMask() {
|
|
9896
9601
|
let mask = bindedOptions.placeholder;
|
|
9897
|
-
if (!bindedOptions.multiple &&
|
|
9602
|
+
if (!bindedOptions.multiple &&
|
|
9603
|
+
superThis.getModelOption().length > 0) {
|
|
9898
9604
|
mask = this.mask[0];
|
|
9899
9605
|
}
|
|
9900
9606
|
mask ?? (mask = bindedOptions.placeholder);
|
|
@@ -9976,7 +9682,8 @@ class SelectBox extends Lifecycle {
|
|
|
9976
9682
|
set(value) {
|
|
9977
9683
|
superThis[privateProp] = !!value;
|
|
9978
9684
|
if (superThis.container?.targetElement?.dataset) {
|
|
9979
|
-
superThis.container.targetElement.dataset[prop] =
|
|
9685
|
+
superThis.container.targetElement.dataset[prop] =
|
|
9686
|
+
String(!!value);
|
|
9980
9687
|
}
|
|
9981
9688
|
},
|
|
9982
9689
|
enumerable: true,
|
|
@@ -10004,7 +9711,7 @@ class SelectBox extends Lifecycle {
|
|
|
10004
9711
|
* @returns A flat array of option models (possibly filtered).
|
|
10005
9712
|
* @internal
|
|
10006
9713
|
*/
|
|
10007
|
-
getModelOption(isSelected
|
|
9714
|
+
getModelOption(isSelected) {
|
|
10008
9715
|
if (!this.optionModelManager)
|
|
10009
9716
|
return [];
|
|
10010
9717
|
const { modelList } = this.optionModelManager.getResources();
|
|
@@ -10084,14 +9791,6 @@ class ElementAdditionObserver {
|
|
|
10084
9791
|
* @internal
|
|
10085
9792
|
*/
|
|
10086
9793
|
this.isActive = false;
|
|
10087
|
-
/**
|
|
10088
|
-
* Underlying DOM {@link MutationObserver} instance.
|
|
10089
|
-
*
|
|
10090
|
-
* `null` when disconnected.
|
|
10091
|
-
*
|
|
10092
|
-
* @internal
|
|
10093
|
-
*/
|
|
10094
|
-
this.observer = null;
|
|
10095
9794
|
/**
|
|
10096
9795
|
* Registered detection callbacks.
|
|
10097
9796
|
*
|
|
@@ -10685,7 +10384,7 @@ class Selective extends Lifecycle {
|
|
|
10685
10384
|
if (wasObserving)
|
|
10686
10385
|
this.EAObserver?.disconnect();
|
|
10687
10386
|
bindMap.self?.deInit?.();
|
|
10688
|
-
const wrapper =
|
|
10387
|
+
const wrapper = bindMap.container?.element ?? selectElement.parentElement;
|
|
10689
10388
|
selectElement.style.display = "";
|
|
10690
10389
|
selectElement.style.visibility = "";
|
|
10691
10390
|
selectElement.disabled = false;
|
|
@@ -10905,7 +10604,7 @@ const SECLASS = new Selective();
|
|
|
10905
10604
|
*
|
|
10906
10605
|
* Declared as `const` literal type to enable strict typing and easy tree-shaking.
|
|
10907
10606
|
*/
|
|
10908
|
-
const version = "1.4.
|
|
10607
|
+
const version = "1.4.1";
|
|
10909
10608
|
/**
|
|
10910
10609
|
* Library name identifier.
|
|
10911
10610
|
*
|
|
@@ -10951,7 +10650,7 @@ function find(query) {
|
|
|
10951
10650
|
* // Destroy all instances
|
|
10952
10651
|
* destroy();
|
|
10953
10652
|
*/
|
|
10954
|
-
function destroy(query
|
|
10653
|
+
function destroy(query) {
|
|
10955
10654
|
SECLASS.destroy(query);
|
|
10956
10655
|
}
|
|
10957
10656
|
/**
|