selective-ui 1.1.0 → 1.1.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.esm.js +225 -200
- package/dist/selective-ui.esm.js.map +1 -1
- package/dist/selective-ui.esm.min.js +2 -1
- package/dist/selective-ui.esm.min.js.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 +225 -200
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +16 -15
- package/src/ts/adapter/mixed-adapter.ts +6 -7
- package/src/ts/components/accessorybox.ts +0 -10
- package/src/ts/components/directive.ts +0 -3
- package/src/ts/components/empty-state.ts +0 -3
- package/src/ts/components/loading-state.ts +3 -1
- package/src/ts/components/option-handle.ts +0 -5
- package/src/ts/components/placeholder.ts +0 -3
- package/src/ts/components/popup.ts +70 -11
- package/src/ts/components/searchbox.ts +2 -12
- package/src/ts/components/selectbox.ts +4 -2
- package/src/ts/core/base/adapter.ts +4 -5
- package/src/ts/core/base/model.ts +0 -1
- package/src/ts/core/base/recyclerview.ts +0 -6
- package/src/ts/core/base/view.ts +0 -24
- package/src/ts/core/model-manager.ts +3 -24
- package/src/ts/index.ts +10 -9
- package/src/ts/models/group-model.ts +0 -1
- package/src/ts/models/option-model.ts +3 -3
- package/src/ts/services/ea-observer.ts +0 -2
- package/src/ts/services/effector.ts +3 -4
- package/src/ts/services/refresher.ts +1 -1
- package/src/ts/services/resize-observer.ts +0 -1
- package/src/ts/services/select-observer.ts +0 -2
- package/src/ts/types/core/base/view.type.ts +0 -18
- package/src/ts/types/services/effector.type.ts +14 -0
- package/src/ts/types/utils/callback-scheduler.type.ts +69 -0
- package/src/ts/types/utils/libs.type.ts +0 -46
- package/src/ts/utils/callback-scheduler.ts +135 -0
- package/src/ts/utils/guard.ts +4 -10
- package/src/ts/utils/libs.ts +6 -77
- package/src/ts/utils/selective.ts +7 -5
- package/src/ts/views/group-view.ts +0 -1
- package/src/ts/views/option-view.ts +2 -0
package/dist/selective-ui.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Selective UI v1.1.
|
|
1
|
+
/*! Selective UI v1.1.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) :
|
|
@@ -72,6 +72,126 @@
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
class CallbackScheduler {
|
|
76
|
+
constructor() {
|
|
77
|
+
/**
|
|
78
|
+
* Stores callbacks by key in registration order.
|
|
79
|
+
*
|
|
80
|
+
* Notes:
|
|
81
|
+
* - Entries may become `undefined` after execution when `once` is enabled.
|
|
82
|
+
* This preserves indices so timer bookkeeping remains stable.
|
|
83
|
+
*/
|
|
84
|
+
this.executeStored = new Map();
|
|
85
|
+
/**
|
|
86
|
+
* Per-key timer registry.
|
|
87
|
+
*
|
|
88
|
+
* - Outer Map: groups timers by `TimerKey`
|
|
89
|
+
* - Inner Map: maps callback index -> active timeout handle
|
|
90
|
+
*
|
|
91
|
+
* Each callback index has its own debounce timer, allowing independent scheduling.
|
|
92
|
+
*/
|
|
93
|
+
this.timerRunner = new Map();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Registers a callback under a key.
|
|
97
|
+
*
|
|
98
|
+
* @param key - Group identifier for callbacks.
|
|
99
|
+
* @param callback - Function to execute after the debounce timeout.
|
|
100
|
+
* @param options - Scheduling options.
|
|
101
|
+
* @returns The index of the registered callback within its key bucket.
|
|
102
|
+
*
|
|
103
|
+
* Behavior:
|
|
104
|
+
* - Callbacks are stored in registration order.
|
|
105
|
+
* - `options.debounce` is treated as a per-callback delay (milliseconds).
|
|
106
|
+
* - `options.once` removes the entry after its first execution (index is preserved).
|
|
107
|
+
*/
|
|
108
|
+
on(key, callback, options = {}) {
|
|
109
|
+
const timeout = options.debounce ?? 50;
|
|
110
|
+
const once = options.once ?? false;
|
|
111
|
+
if (!this.executeStored.has(key))
|
|
112
|
+
this.executeStored.set(key, []);
|
|
113
|
+
const bucket = this.executeStored.get(key);
|
|
114
|
+
bucket.push({ callback, timeout, once });
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Removes all callbacks associated with a key and clears any active timers.
|
|
118
|
+
*
|
|
119
|
+
* @param key - Key whose callbacks and timers will be removed.
|
|
120
|
+
*/
|
|
121
|
+
off(key) {
|
|
122
|
+
const runner = this.timerRunner.get(key);
|
|
123
|
+
if (runner) {
|
|
124
|
+
for (const t of runner.values())
|
|
125
|
+
clearTimeout(t);
|
|
126
|
+
runner.clear();
|
|
127
|
+
this.timerRunner.delete(key);
|
|
128
|
+
}
|
|
129
|
+
this.executeStored.delete(key);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Schedules execution for all callbacks registered under a key.
|
|
133
|
+
*
|
|
134
|
+
* @param key - Key whose callbacks will be scheduled.
|
|
135
|
+
* @param params - Parameters collected and passed as a single payload.
|
|
136
|
+
*
|
|
137
|
+
* Payload rules:
|
|
138
|
+
* - If `run(key)` is called without params, callbacks receive `null`.
|
|
139
|
+
* - If `run(key, ...params)` is called with params, callbacks receive `params` as an array.
|
|
140
|
+
*
|
|
141
|
+
* Debounce rules:
|
|
142
|
+
* - Each callback has its own timer (by index).
|
|
143
|
+
* - Calling `run()` again before the timeout clears the previous timer for that callback.
|
|
144
|
+
*
|
|
145
|
+
* Once rules:
|
|
146
|
+
* - If an entry has `once = true`, it is removed after execution by setting its slot to `undefined`.
|
|
147
|
+
* (The list is not spliced to preserve indices.)
|
|
148
|
+
*/
|
|
149
|
+
run(key, ...params) {
|
|
150
|
+
const executes = this.executeStored.get(key);
|
|
151
|
+
if (!executes)
|
|
152
|
+
return;
|
|
153
|
+
if (!this.timerRunner.has(key))
|
|
154
|
+
this.timerRunner.set(key, new Map());
|
|
155
|
+
const runner = this.timerRunner.get(key);
|
|
156
|
+
for (let i = 0; i < executes.length; i++) {
|
|
157
|
+
const entry = executes[i];
|
|
158
|
+
if (!entry)
|
|
159
|
+
continue;
|
|
160
|
+
const prev = runner.get(i);
|
|
161
|
+
if (prev)
|
|
162
|
+
clearTimeout(prev);
|
|
163
|
+
const timer = setTimeout(() => {
|
|
164
|
+
entry.callback(params.length > 0 ? params : null);
|
|
165
|
+
if (entry.once) {
|
|
166
|
+
// Preserve index stability by leaving an empty slot.
|
|
167
|
+
executes[i] = undefined;
|
|
168
|
+
// Cleanup the timer handle for this index.
|
|
169
|
+
const current = runner.get(i);
|
|
170
|
+
if (current)
|
|
171
|
+
clearTimeout(current);
|
|
172
|
+
runner.delete(i);
|
|
173
|
+
}
|
|
174
|
+
}, entry.timeout);
|
|
175
|
+
runner.set(i, timer);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Clears callbacks and timers.
|
|
180
|
+
*
|
|
181
|
+
* @param key - When provided, clears only that key; otherwise clears all keys.
|
|
182
|
+
*/
|
|
183
|
+
clear(key) {
|
|
184
|
+
if (key !== undefined) {
|
|
185
|
+
this.off(key);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
// Iterate over a snapshot of keys because `off()` mutates the maps.
|
|
189
|
+
for (const k of Array.from(this.executeStored.keys())) {
|
|
190
|
+
this.off(k);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
75
195
|
/**
|
|
76
196
|
* @class
|
|
77
197
|
*/
|
|
@@ -86,18 +206,6 @@
|
|
|
86
206
|
this._iStorage = new iStorage();
|
|
87
207
|
return this._iStorage;
|
|
88
208
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Checks whether a value is null/undefined/empty-string/"0"/0.
|
|
91
|
-
* Booleans are always considered non-empty.
|
|
92
|
-
*
|
|
93
|
-
* @param {unknown} value - The value to test.
|
|
94
|
-
* @returns {boolean} - True if considered empty; otherwise false.
|
|
95
|
-
*/
|
|
96
|
-
static isNullOrEmpty(value) {
|
|
97
|
-
if (typeof value === "boolean")
|
|
98
|
-
return false;
|
|
99
|
-
return value == null || value === "" || value === 0 || value === "0";
|
|
100
|
-
}
|
|
101
209
|
/**
|
|
102
210
|
* Deep-copies plain objects/arrays recursively. Returns primitives as-is.
|
|
103
211
|
*
|
|
@@ -295,23 +403,6 @@
|
|
|
295
403
|
}
|
|
296
404
|
return recursiveTemp;
|
|
297
405
|
}
|
|
298
|
-
/**
|
|
299
|
-
* Applies inline CSS styles to all matched elements. Accepts either a style
|
|
300
|
-
* object or a single property + value pair.
|
|
301
|
-
*
|
|
302
|
-
* @param {string|NodeListOf<HTMLElement>|HTMLElement} queryCommon - Selector or element(s).
|
|
303
|
-
* @param {Record<string, string>|string} styles - Style object or a single property name.
|
|
304
|
-
* @param {string|null} [value=null] - Value for the single property form.
|
|
305
|
-
*/
|
|
306
|
-
static setStyle(queryCommon, styles, value = null) {
|
|
307
|
-
const apply_styles = typeof styles === "string" ? { [styles]: value } : { ...styles };
|
|
308
|
-
const queryItems = this.getElements(queryCommon);
|
|
309
|
-
for (let i = 0; i < queryItems.length; i++) {
|
|
310
|
-
const item = queryItems[i];
|
|
311
|
-
if (item)
|
|
312
|
-
Object.assign(item.style, apply_styles);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
406
|
/**
|
|
316
407
|
* Builds a configuration object by copying defaults and then overriding with
|
|
317
408
|
* matching element properties or data-* attributes when present.
|
|
@@ -596,41 +687,7 @@
|
|
|
596
687
|
* Schedules and batches function executions keyed by name, with debounced timers.
|
|
597
688
|
* Provides setExecute(), clearExecute(), and run() to manage deferred callbacks.
|
|
598
689
|
*/
|
|
599
|
-
Libs.
|
|
600
|
-
executeStored: {},
|
|
601
|
-
timerRunner: {},
|
|
602
|
-
setExecute(keyExecute, execute, timeout = 50, once = false) {
|
|
603
|
-
if (!this.executeStored[keyExecute])
|
|
604
|
-
this.executeStored[keyExecute] = [];
|
|
605
|
-
this.executeStored[keyExecute].push({ execute, timeout, once });
|
|
606
|
-
},
|
|
607
|
-
clearExecute(keyExecute) {
|
|
608
|
-
delete this.executeStored[keyExecute];
|
|
609
|
-
},
|
|
610
|
-
run(keyExecute, ...params) {
|
|
611
|
-
const executes = this.executeStored[keyExecute];
|
|
612
|
-
if (!executes)
|
|
613
|
-
return;
|
|
614
|
-
if (!this.timerRunner[keyExecute])
|
|
615
|
-
this.timerRunner[keyExecute] = {};
|
|
616
|
-
for (const key in executes) {
|
|
617
|
-
const entry = executes[Number(key)];
|
|
618
|
-
if (!entry)
|
|
619
|
-
continue;
|
|
620
|
-
if (!this.timerRunner[keyExecute][key]) {
|
|
621
|
-
// placeholder, will be overwritten by setTimeout
|
|
622
|
-
this.timerRunner[keyExecute][key] = setTimeout(() => { }, 0);
|
|
623
|
-
clearTimeout(this.timerRunner[keyExecute][key]);
|
|
624
|
-
}
|
|
625
|
-
clearTimeout(this.timerRunner[keyExecute][key]);
|
|
626
|
-
this.timerRunner[keyExecute][key] = setTimeout(() => {
|
|
627
|
-
entry.execute(params.length > 0 ? params : null);
|
|
628
|
-
if (entry.once)
|
|
629
|
-
delete this.executeStored[keyExecute][Number(key)];
|
|
630
|
-
}, entry.timeout);
|
|
631
|
-
}
|
|
632
|
-
},
|
|
633
|
-
};
|
|
690
|
+
Libs.callbackScheduler = new CallbackScheduler();
|
|
634
691
|
|
|
635
692
|
/**
|
|
636
693
|
* Provides a lightweight event utility with cancel/continue control:
|
|
@@ -749,7 +806,7 @@
|
|
|
749
806
|
width = options.width;
|
|
750
807
|
if (cfgHeight > 0)
|
|
751
808
|
height = options.height;
|
|
752
|
-
|
|
809
|
+
Object.assign(view.style, { width, height, minWidth, minHeight });
|
|
753
810
|
}
|
|
754
811
|
}
|
|
755
812
|
|
|
@@ -762,9 +819,6 @@
|
|
|
762
819
|
* Supports HTML content based on configuration and provides methods to get or set the placeholder value.
|
|
763
820
|
*/
|
|
764
821
|
constructor(options) {
|
|
765
|
-
/**
|
|
766
|
-
* @type {HTMLElement | null}
|
|
767
|
-
*/
|
|
768
822
|
this.node = null;
|
|
769
823
|
this._options = null;
|
|
770
824
|
if (options)
|
|
@@ -849,14 +903,9 @@
|
|
|
849
903
|
*/
|
|
850
904
|
constructor(options = null) {
|
|
851
905
|
this.nodeMounted = null;
|
|
852
|
-
/**
|
|
853
|
-
* @type {HTMLDivElement | null}
|
|
854
|
-
*/
|
|
855
906
|
this.node = null;
|
|
856
907
|
this.options = null;
|
|
857
|
-
/** @type {Function[]} */
|
|
858
908
|
this._ActionOnSelectAll = [];
|
|
859
|
-
/** @type {Function[]} */
|
|
860
909
|
this._ActionOnDeSelectAll = [];
|
|
861
910
|
if (options)
|
|
862
911
|
this.init(options);
|
|
@@ -965,9 +1014,6 @@
|
|
|
965
1014
|
* Provides methods to show/hide the state and check its visibility.
|
|
966
1015
|
*/
|
|
967
1016
|
constructor(options = null) {
|
|
968
|
-
/**
|
|
969
|
-
* @type {HTMLDivElement | null}
|
|
970
|
-
*/
|
|
971
1017
|
this.node = null;
|
|
972
1018
|
this.options = null;
|
|
973
1019
|
if (options)
|
|
@@ -1018,13 +1064,15 @@
|
|
|
1018
1064
|
}
|
|
1019
1065
|
}
|
|
1020
1066
|
|
|
1067
|
+
/**
|
|
1068
|
+
* @class
|
|
1069
|
+
*/
|
|
1021
1070
|
class LoadingState {
|
|
1022
1071
|
/**
|
|
1023
1072
|
* Represents a loading state component that displays a loading message during data fetch or processing.
|
|
1024
1073
|
* Provides methods to show/hide the state and check its visibility.
|
|
1025
1074
|
*/
|
|
1026
1075
|
constructor(options = null) {
|
|
1027
|
-
/** @type {HTMLDivElement | null} */
|
|
1028
1076
|
this.node = null;
|
|
1029
1077
|
this.options = null;
|
|
1030
1078
|
if (options)
|
|
@@ -1086,7 +1134,6 @@
|
|
|
1086
1134
|
constructor() {
|
|
1087
1135
|
this.isInit = false;
|
|
1088
1136
|
this.element = null;
|
|
1089
|
-
/** @type {ResizeObserver|null} */
|
|
1090
1137
|
this._resizeObserver = null;
|
|
1091
1138
|
this._mutationObserver = null;
|
|
1092
1139
|
this.isInit = true;
|
|
@@ -1216,27 +1263,17 @@
|
|
|
1216
1263
|
constructor(select = null, options = null, modelManager = null) {
|
|
1217
1264
|
this.options = null;
|
|
1218
1265
|
this.isCreated = false;
|
|
1219
|
-
/** @type {MixedAdapter | null} */
|
|
1220
1266
|
this.optionAdapter = null;
|
|
1221
|
-
/** @type {HTMLDivElement | null} */
|
|
1222
1267
|
this.node = null;
|
|
1223
|
-
/** @type {EffectorInterface | null} */
|
|
1224
1268
|
this._effSvc = null;
|
|
1225
|
-
/** @type {ResizeObserverService | null} */
|
|
1226
1269
|
this._resizeObser = null;
|
|
1227
1270
|
this._parent = null;
|
|
1228
|
-
/** @type {OptionHandle | null} */
|
|
1229
1271
|
this.optionHandle = null;
|
|
1230
|
-
/** @type {EmptyState | null} */
|
|
1231
1272
|
this.emptyState = null;
|
|
1232
|
-
/** @type {LoadingState | null} */
|
|
1233
1273
|
this.loadingState = null;
|
|
1234
|
-
/** @type {RecyclerViewContract<MixedAdapter> | null} */
|
|
1235
1274
|
this.recyclerView = null;
|
|
1236
|
-
/** @type {HTMLDivElement | null} */
|
|
1237
1275
|
this._optionsContainer = null;
|
|
1238
1276
|
this._scrollListener = null;
|
|
1239
|
-
/** @type {ReturnType<typeof setTimeout> | null} */
|
|
1240
1277
|
this._hideLoadHandle = null;
|
|
1241
1278
|
this._modelManager = modelManager;
|
|
1242
1279
|
if (select && options) {
|
|
@@ -1455,6 +1492,9 @@
|
|
|
1455
1492
|
/**
|
|
1456
1493
|
* Enables infinite scroll by listening to container scroll events and loading more data
|
|
1457
1494
|
* when nearing the bottom, respecting pagination state (enabled/loading/hasMore).
|
|
1495
|
+
*
|
|
1496
|
+
* @param searchController - Provides pagination state and a method to load more items.
|
|
1497
|
+
* @param _options - Optional SelectiveOptions (reserved for future behavior tuning).
|
|
1458
1498
|
*/
|
|
1459
1499
|
setupInfiniteScroll(searchController, _options) {
|
|
1460
1500
|
if (!this.node)
|
|
@@ -1479,6 +1519,65 @@
|
|
|
1479
1519
|
};
|
|
1480
1520
|
this.node.addEventListener("scroll", this._scrollListener);
|
|
1481
1521
|
}
|
|
1522
|
+
/**
|
|
1523
|
+
* Completely tear down the popup instance and release all resources.
|
|
1524
|
+
*
|
|
1525
|
+
* Responsibilities:
|
|
1526
|
+
* - Clear any pending timeouts and cancel animations/effects.
|
|
1527
|
+
* - Remove event listeners (scroll, mousedown) and disconnect ResizeObserver.
|
|
1528
|
+
* - Unmount and remove the DOM node; sever references to Effector/ModelManager.
|
|
1529
|
+
* - Dispose adapter/recycler and child components (OptionHandle, EmptyState, LoadingState).
|
|
1530
|
+
* - Reset flags and null out references to avoid memory leaks.
|
|
1531
|
+
*
|
|
1532
|
+
* Safe to call multiple times; all operations are guarded via optional chaining.
|
|
1533
|
+
*/
|
|
1534
|
+
detroy() {
|
|
1535
|
+
if (this._hideLoadHandle) {
|
|
1536
|
+
clearTimeout(this._hideLoadHandle);
|
|
1537
|
+
this._hideLoadHandle = null;
|
|
1538
|
+
}
|
|
1539
|
+
if (this.node && this._scrollListener) {
|
|
1540
|
+
this.node.removeEventListener("scroll", this._scrollListener);
|
|
1541
|
+
this._scrollListener = null;
|
|
1542
|
+
}
|
|
1543
|
+
try {
|
|
1544
|
+
this._resizeObser?.disconnect();
|
|
1545
|
+
}
|
|
1546
|
+
catch (_) { }
|
|
1547
|
+
this._resizeObser = null;
|
|
1548
|
+
try {
|
|
1549
|
+
this._effSvc?.setElement?.(null);
|
|
1550
|
+
}
|
|
1551
|
+
catch (_) { }
|
|
1552
|
+
this._effSvc = null;
|
|
1553
|
+
if (this.node) {
|
|
1554
|
+
try {
|
|
1555
|
+
const clone = this.node.cloneNode(true);
|
|
1556
|
+
this.node.replaceWith(clone);
|
|
1557
|
+
clone.remove();
|
|
1558
|
+
}
|
|
1559
|
+
catch (_) {
|
|
1560
|
+
this.node.remove();
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
this.node = null;
|
|
1564
|
+
this._optionsContainer = null;
|
|
1565
|
+
try {
|
|
1566
|
+
this._modelManager?.skipEvent?.(false);
|
|
1567
|
+
this.recyclerView?.clear?.();
|
|
1568
|
+
this.recyclerView = null;
|
|
1569
|
+
this.optionAdapter = null;
|
|
1570
|
+
this.node.remove();
|
|
1571
|
+
}
|
|
1572
|
+
catch (_) { }
|
|
1573
|
+
this._modelManager = null;
|
|
1574
|
+
this.optionHandle = null;
|
|
1575
|
+
this.emptyState = null;
|
|
1576
|
+
this.loadingState = null;
|
|
1577
|
+
this._parent = null;
|
|
1578
|
+
this.options = null;
|
|
1579
|
+
this.isCreated = false;
|
|
1580
|
+
}
|
|
1482
1581
|
/**
|
|
1483
1582
|
* Computes the parent panel's location and box metrics, including size, position,
|
|
1484
1583
|
* padding, and border, accounting for iOS visual viewport offsets.
|
|
@@ -1584,21 +1683,9 @@
|
|
|
1584
1683
|
* @param {object|null} [options=null] - Configuration (e.g., placeholder, accessibility IDs).
|
|
1585
1684
|
*/
|
|
1586
1685
|
constructor(options = null) {
|
|
1587
|
-
/**
|
|
1588
|
-
* @type {MountViewResult<any> | null}
|
|
1589
|
-
*/
|
|
1590
1686
|
this.nodeMounted = null;
|
|
1591
|
-
/**
|
|
1592
|
-
* @type {HTMLDivElement | null}
|
|
1593
|
-
*/
|
|
1594
1687
|
this.node = null;
|
|
1595
|
-
/**
|
|
1596
|
-
* @type {HTMLInputElement | null}
|
|
1597
|
-
*/
|
|
1598
1688
|
this.SearchInput = null;
|
|
1599
|
-
/**
|
|
1600
|
-
* @type {Function|null}
|
|
1601
|
-
*/
|
|
1602
1689
|
this.onSearch = null;
|
|
1603
1690
|
this.options = null;
|
|
1604
1691
|
this.onNavigate = null;
|
|
@@ -2005,7 +2092,9 @@
|
|
|
2005
2092
|
}
|
|
2006
2093
|
else {
|
|
2007
2094
|
this._resizeTimeout = setTimeout(() => {
|
|
2008
|
-
this.element
|
|
2095
|
+
if (this.element?.style) {
|
|
2096
|
+
this.element.style.transition = "none";
|
|
2097
|
+
}
|
|
2009
2098
|
}, duration);
|
|
2010
2099
|
}
|
|
2011
2100
|
Object.assign(this.element.style, styles);
|
|
@@ -2059,7 +2148,6 @@
|
|
|
2059
2148
|
constructor(options, targetElement = null, view = null) {
|
|
2060
2149
|
/** @type {TTarget | null} */
|
|
2061
2150
|
this.targetElement = null;
|
|
2062
|
-
/** @type {TView | null} */
|
|
2063
2151
|
this.view = null;
|
|
2064
2152
|
this.position = -1;
|
|
2065
2153
|
this.isInit = false;
|
|
@@ -2097,7 +2185,6 @@
|
|
|
2097
2185
|
constructor(options, targetElement) {
|
|
2098
2186
|
super(options, targetElement ?? null, null);
|
|
2099
2187
|
this.label = "";
|
|
2100
|
-
/** @type {OptionModel[]} */
|
|
2101
2188
|
this.items = [];
|
|
2102
2189
|
this.collapsed = false;
|
|
2103
2190
|
this._privOnCollapsedChanged = [];
|
|
@@ -2295,7 +2382,7 @@
|
|
|
2295
2382
|
* @type {boolean}
|
|
2296
2383
|
*/
|
|
2297
2384
|
set selectedNonTrigger(value) {
|
|
2298
|
-
const input = this.view?.
|
|
2385
|
+
const input = this.view?.view?.tags?.OptionInput;
|
|
2299
2386
|
const viewEl = this.view?.getView?.();
|
|
2300
2387
|
if (input)
|
|
2301
2388
|
input.checked = value;
|
|
@@ -2390,7 +2477,7 @@
|
|
|
2390
2477
|
onTargetChanged() {
|
|
2391
2478
|
if (!this.view)
|
|
2392
2479
|
return;
|
|
2393
|
-
const labelContent = this.view.
|
|
2480
|
+
const labelContent = this.view.view.tags.LabelContent;
|
|
2394
2481
|
if (labelContent) {
|
|
2395
2482
|
if (this.options.allowHtml) {
|
|
2396
2483
|
labelContent.innerHTML = this.text;
|
|
@@ -2399,7 +2486,7 @@
|
|
|
2399
2486
|
labelContent.textContent = this.textContent;
|
|
2400
2487
|
}
|
|
2401
2488
|
}
|
|
2402
|
-
const imageTag = this.view.
|
|
2489
|
+
const imageTag = this.view.view.tags.OptionImage;
|
|
2403
2490
|
if (imageTag && this.hasImage) {
|
|
2404
2491
|
imageTag.src = this.imageSrc;
|
|
2405
2492
|
imageTag.alt = this.text;
|
|
@@ -2576,7 +2663,6 @@
|
|
|
2576
2663
|
});
|
|
2577
2664
|
let currentGroup = null;
|
|
2578
2665
|
let position = 0;
|
|
2579
|
-
const changesToApply = [];
|
|
2580
2666
|
modelData.forEach((data) => {
|
|
2581
2667
|
if (data.tagName === "OPTGROUP") {
|
|
2582
2668
|
const dataVset = data;
|
|
@@ -2585,7 +2671,7 @@
|
|
|
2585
2671
|
// Label is used as key; keep original behavior.
|
|
2586
2672
|
const hasLabelChange = existingGroup.label !== dataVset.label;
|
|
2587
2673
|
if (hasLabelChange) {
|
|
2588
|
-
|
|
2674
|
+
existingGroup.update(dataVset);
|
|
2589
2675
|
}
|
|
2590
2676
|
existingGroup.position = position;
|
|
2591
2677
|
existingGroup.items = [];
|
|
@@ -2605,17 +2691,8 @@
|
|
|
2605
2691
|
const key = `${dataVset.value}::${dataVset.text}`;
|
|
2606
2692
|
const existingOption = oldOptionMap.get(key);
|
|
2607
2693
|
if (existingOption) {
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
if (hasSelectedChange || hasPositionChange) {
|
|
2611
|
-
changesToApply.push(() => {
|
|
2612
|
-
existingOption.update(dataVset);
|
|
2613
|
-
existingOption.position = position;
|
|
2614
|
-
});
|
|
2615
|
-
}
|
|
2616
|
-
else {
|
|
2617
|
-
existingOption.position = position;
|
|
2618
|
-
}
|
|
2694
|
+
existingOption.update(dataVset);
|
|
2695
|
+
existingOption.position = position;
|
|
2619
2696
|
const parentGroup = dataVset["__parentGroup"];
|
|
2620
2697
|
if (parentGroup && currentGroup) {
|
|
2621
2698
|
currentGroup.addItem(existingOption);
|
|
@@ -2642,11 +2719,6 @@
|
|
|
2642
2719
|
position++;
|
|
2643
2720
|
}
|
|
2644
2721
|
});
|
|
2645
|
-
if (changesToApply.length > 0) {
|
|
2646
|
-
requestAnimationFrame(() => {
|
|
2647
|
-
changesToApply.forEach((change) => change());
|
|
2648
|
-
});
|
|
2649
|
-
}
|
|
2650
2722
|
oldGroupMap.forEach((removedGroup) => {
|
|
2651
2723
|
removedGroup.view?.getView?.()?.remove?.();
|
|
2652
2724
|
});
|
|
@@ -2689,9 +2761,6 @@
|
|
|
2689
2761
|
* adapter instance, and recycler view instance.
|
|
2690
2762
|
*/
|
|
2691
2763
|
getResources() {
|
|
2692
|
-
if (!this._privAdapterHandle || !this._privRecyclerViewHandle) {
|
|
2693
|
-
throw new Error("ModelManager resources not loaded. Call load() first.");
|
|
2694
|
-
}
|
|
2695
2764
|
return {
|
|
2696
2765
|
modelList: this._privModelList,
|
|
2697
2766
|
adapter: this._privAdapterHandle,
|
|
@@ -2726,13 +2795,7 @@
|
|
|
2726
2795
|
* @param {HTMLDivElement|null} [viewElement=null] - The root element where the adapter will render items.
|
|
2727
2796
|
*/
|
|
2728
2797
|
constructor(viewElement = null) {
|
|
2729
|
-
/**
|
|
2730
|
-
* @type {HTMLDivElement|null}
|
|
2731
|
-
*/
|
|
2732
2798
|
this.viewElement = null;
|
|
2733
|
-
/**
|
|
2734
|
-
* @type {TAdapter|null}
|
|
2735
|
-
*/
|
|
2736
2799
|
this.adapter = null;
|
|
2737
2800
|
this.viewElement = viewElement;
|
|
2738
2801
|
}
|
|
@@ -2799,20 +2862,11 @@
|
|
|
2799
2862
|
* @param {object|null} options - Configuration options for the accessory box (e.g., layout and behavior).
|
|
2800
2863
|
*/
|
|
2801
2864
|
constructor(options = null) {
|
|
2802
|
-
/**
|
|
2803
|
-
* @type {MountViewResult<any> | null}
|
|
2804
|
-
*/
|
|
2805
2865
|
this.nodeMounted = null;
|
|
2806
|
-
/**
|
|
2807
|
-
* @type {HTMLDivElement | null}
|
|
2808
|
-
*/
|
|
2809
2866
|
this.node = null;
|
|
2810
2867
|
this.options = null;
|
|
2811
|
-
/** @type {HTMLDivElement | null} */
|
|
2812
2868
|
this.selectUIMask = null;
|
|
2813
|
-
/** @type {HTMLDivElement | null} */
|
|
2814
2869
|
this.parentMask = null;
|
|
2815
|
-
/** @type {ModelManager<MixedItem> | null} */
|
|
2816
2870
|
this.modelManager = null;
|
|
2817
2871
|
if (options)
|
|
2818
2872
|
this.init(options);
|
|
@@ -3513,7 +3567,6 @@
|
|
|
3513
3567
|
* @param {TItem[]} [items=[]] - Initial items to be managed by the adapter.
|
|
3514
3568
|
*/
|
|
3515
3569
|
constructor(items = []) {
|
|
3516
|
-
/** @type {TItem[]} */
|
|
3517
3570
|
this.items = [];
|
|
3518
3571
|
this.adapterKey = Libs.randomString(12);
|
|
3519
3572
|
this.isSkipEvent = false;
|
|
@@ -3550,7 +3603,7 @@
|
|
|
3550
3603
|
* @param {Function} callback - Function to execute before the property changes.
|
|
3551
3604
|
*/
|
|
3552
3605
|
onPropChanging(propName, callback) {
|
|
3553
|
-
Libs.
|
|
3606
|
+
Libs.callbackScheduler.on(`${propName}ing_${this.adapterKey}`, callback, { debounce: 1 });
|
|
3554
3607
|
}
|
|
3555
3608
|
/**
|
|
3556
3609
|
* Registers a post-change callback for a property change pipeline.
|
|
@@ -3560,7 +3613,7 @@
|
|
|
3560
3613
|
* @param {Function} callback - Function to execute after the property changes.
|
|
3561
3614
|
*/
|
|
3562
3615
|
onPropChanged(propName, callback) {
|
|
3563
|
-
Libs.
|
|
3616
|
+
Libs.callbackScheduler.on(`${propName}_${this.adapterKey}`, callback);
|
|
3564
3617
|
}
|
|
3565
3618
|
/**
|
|
3566
3619
|
* Triggers the post-change pipeline for a given property, passing optional parameters
|
|
@@ -3570,7 +3623,7 @@
|
|
|
3570
3623
|
* @param {...any} params - Parameters forwarded to the callbacks.
|
|
3571
3624
|
*/
|
|
3572
3625
|
changeProp(propName, ...params) {
|
|
3573
|
-
Libs.
|
|
3626
|
+
Libs.callbackScheduler.run(`${propName}_${this.adapterKey}`, ...params);
|
|
3574
3627
|
}
|
|
3575
3628
|
/**
|
|
3576
3629
|
* Triggers the pre-change pipeline for a given property, passing optional parameters
|
|
@@ -3580,7 +3633,7 @@
|
|
|
3580
3633
|
* @param {...any} params - Parameters forwarded to the callbacks.
|
|
3581
3634
|
*/
|
|
3582
3635
|
changingProp(propName, ...params) {
|
|
3583
|
-
Libs.
|
|
3636
|
+
Libs.callbackScheduler.run(`${propName}ing_${this.adapterKey}`, ...params);
|
|
3584
3637
|
}
|
|
3585
3638
|
/**
|
|
3586
3639
|
* Creates and returns a viewer instance for the given item within the specified parent container.
|
|
@@ -3660,9 +3713,7 @@
|
|
|
3660
3713
|
* @param {HTMLElement} parent - The parent element into which this view will render.
|
|
3661
3714
|
*/
|
|
3662
3715
|
constructor(parent) {
|
|
3663
|
-
/** @type {HTMLElement|null} */
|
|
3664
3716
|
this.parent = null;
|
|
3665
|
-
/** @type {MountViewResult<TTags> | null} */
|
|
3666
3717
|
this.view = null;
|
|
3667
3718
|
this.parent = parent;
|
|
3668
3719
|
}
|
|
@@ -3686,28 +3737,6 @@
|
|
|
3686
3737
|
throw new Error("View is not mounted. Did you forget to set this.view?");
|
|
3687
3738
|
return this.view.view;
|
|
3688
3739
|
}
|
|
3689
|
-
/**
|
|
3690
|
-
* Retrieves a single tagged element from the mounted view.
|
|
3691
|
-
*
|
|
3692
|
-
* @template K
|
|
3693
|
-
* @param {K} tag - The tag key corresponding to the desired element.
|
|
3694
|
-
* @returns {TTags[K]} - The element associated with the provided tag key.
|
|
3695
|
-
*/
|
|
3696
|
-
getTag(tag) {
|
|
3697
|
-
if (!this.view)
|
|
3698
|
-
throw new Error("View is not mounted. Did you forget to set this.view?");
|
|
3699
|
-
return this.view.tags[tag];
|
|
3700
|
-
}
|
|
3701
|
-
/**
|
|
3702
|
-
* Retrieves the full tag map for the mounted view.
|
|
3703
|
-
*
|
|
3704
|
-
* @returns {TTags} - An object map of all tagged elements.
|
|
3705
|
-
*/
|
|
3706
|
-
getTags() {
|
|
3707
|
-
if (!this.view)
|
|
3708
|
-
throw new Error("View is not mounted. Did you forget to set this.view?");
|
|
3709
|
-
return this.view.tags;
|
|
3710
|
-
}
|
|
3711
3740
|
}
|
|
3712
3741
|
|
|
3713
3742
|
/**
|
|
@@ -3716,7 +3745,6 @@
|
|
|
3716
3745
|
class GroupView extends View {
|
|
3717
3746
|
constructor() {
|
|
3718
3747
|
super(...arguments);
|
|
3719
|
-
/** @type {GroupViewResult} */
|
|
3720
3748
|
this.view = null;
|
|
3721
3749
|
}
|
|
3722
3750
|
/**
|
|
@@ -4164,7 +4192,7 @@
|
|
|
4164
4192
|
_handleGroupView(groupModel, groupView, position) {
|
|
4165
4193
|
super.onViewHolder(groupModel, groupView, position);
|
|
4166
4194
|
groupModel.view = groupView;
|
|
4167
|
-
const header = groupView.
|
|
4195
|
+
const header = groupView.view.tags.GroupHeader;
|
|
4168
4196
|
header.textContent = groupModel.label;
|
|
4169
4197
|
if (!groupModel.isInit) {
|
|
4170
4198
|
header.style.cursor = "pointer";
|
|
@@ -4216,7 +4244,7 @@
|
|
|
4216
4244
|
}
|
|
4217
4245
|
optionModel.view = optionViewer;
|
|
4218
4246
|
if (optionModel.hasImage) {
|
|
4219
|
-
const imageTag = optionViewer.
|
|
4247
|
+
const imageTag = optionViewer.view.tags.OptionImage;
|
|
4220
4248
|
if (imageTag) {
|
|
4221
4249
|
if (imageTag.src !== optionModel.imageSrc)
|
|
4222
4250
|
imageTag.src = optionModel.imageSrc;
|
|
@@ -4224,9 +4252,9 @@
|
|
|
4224
4252
|
imageTag.alt = optionModel.text;
|
|
4225
4253
|
}
|
|
4226
4254
|
}
|
|
4227
|
-
optionViewer.
|
|
4255
|
+
optionViewer.view.tags.LabelContent.innerHTML = optionModel.text;
|
|
4228
4256
|
if (!optionModel.isInit) {
|
|
4229
|
-
optionViewer.
|
|
4257
|
+
optionViewer.view.tags.OptionView.addEventListener("click", (ev) => {
|
|
4230
4258
|
ev.stopPropagation();
|
|
4231
4259
|
ev.preventDefault();
|
|
4232
4260
|
if (this.isSkipEvent)
|
|
@@ -4246,8 +4274,8 @@
|
|
|
4246
4274
|
}, 5);
|
|
4247
4275
|
}
|
|
4248
4276
|
});
|
|
4249
|
-
optionViewer.
|
|
4250
|
-
optionViewer.
|
|
4277
|
+
optionViewer.view.tags.OptionView.title = optionModel.textContent;
|
|
4278
|
+
optionViewer.view.tags.OptionView.addEventListener("mouseenter", () => {
|
|
4251
4279
|
if (this.isSkipEvent)
|
|
4252
4280
|
return;
|
|
4253
4281
|
this.setHighlight(this.flatOptions.indexOf(optionModel), false);
|
|
@@ -4470,10 +4498,8 @@
|
|
|
4470
4498
|
constructor(select = null, Selective = null) {
|
|
4471
4499
|
this.container = {};
|
|
4472
4500
|
this.oldValue = null;
|
|
4473
|
-
/** @type {HTMLDivElement|null} */
|
|
4474
4501
|
this.node = null;
|
|
4475
4502
|
this.options = null;
|
|
4476
|
-
/** @type {ModelManager<MixedItem, MixedAdapter> | null} */
|
|
4477
4503
|
this.optionModelManager = null;
|
|
4478
4504
|
this.isOpen = false;
|
|
4479
4505
|
this.hasLoadedOnce = false;
|
|
@@ -5050,6 +5076,9 @@
|
|
|
5050
5076
|
}
|
|
5051
5077
|
return flatOptions;
|
|
5052
5078
|
}
|
|
5079
|
+
detroy() {
|
|
5080
|
+
this.container.popup.detroy();
|
|
5081
|
+
}
|
|
5053
5082
|
}
|
|
5054
5083
|
|
|
5055
5084
|
/**
|
|
@@ -5058,9 +5087,7 @@
|
|
|
5058
5087
|
class ElementAdditionObserver {
|
|
5059
5088
|
constructor() {
|
|
5060
5089
|
this._isActive = false;
|
|
5061
|
-
/** @type {MutationObserver|null} */
|
|
5062
5090
|
this._observer = null;
|
|
5063
|
-
/** @type {Array<(el: T) => void>} */
|
|
5064
5091
|
this._actions = [];
|
|
5065
5092
|
}
|
|
5066
5093
|
/**
|
|
@@ -5145,9 +5172,9 @@
|
|
|
5145
5172
|
merged.on.load = (merged.on.load ?? []);
|
|
5146
5173
|
this.bindedQueries.set(query, merged);
|
|
5147
5174
|
const doneToken = Libs.randomString();
|
|
5148
|
-
Libs.
|
|
5175
|
+
Libs.callbackScheduler.on(doneToken, () => {
|
|
5149
5176
|
iEvents.callEvent([this.find(query)], ...merged.on.load);
|
|
5150
|
-
Libs.
|
|
5177
|
+
Libs.callbackScheduler.clear(doneToken);
|
|
5151
5178
|
merged.on.load = [];
|
|
5152
5179
|
});
|
|
5153
5180
|
const selectElements = Libs.getElements(query);
|
|
@@ -5156,7 +5183,7 @@
|
|
|
5156
5183
|
if (item.tagName === "SELECT") {
|
|
5157
5184
|
Libs.removeUnbinderMap(item);
|
|
5158
5185
|
if (this.applySelectBox(item, merged)) {
|
|
5159
|
-
Libs.
|
|
5186
|
+
Libs.callbackScheduler.run(doneToken);
|
|
5160
5187
|
}
|
|
5161
5188
|
}
|
|
5162
5189
|
})();
|
|
@@ -5281,6 +5308,8 @@
|
|
|
5281
5308
|
const bindMap = Libs.getBinderMap(selectElement);
|
|
5282
5309
|
if (!bindMap)
|
|
5283
5310
|
return;
|
|
5311
|
+
const popup = bindMap.container?.popup;
|
|
5312
|
+
popup?.detroy();
|
|
5284
5313
|
Libs.setUnbinderMap(selectElement, bindMap);
|
|
5285
5314
|
const wasObserving = !!this.EAObserver;
|
|
5286
5315
|
if (wasObserving)
|
|
@@ -5421,7 +5450,6 @@
|
|
|
5421
5450
|
|
|
5422
5451
|
/**
|
|
5423
5452
|
* Checks for a previously loaded global library instance by name.
|
|
5424
|
-
* If found (with `__loaded` flag), logs a warning and returns true; otherwise
|
|
5425
5453
|
* initializes a loading placeholder on `window[name]` and returns false.
|
|
5426
5454
|
*
|
|
5427
5455
|
* @param {string} LIB_NAME - The global namespace key to check on `window`.
|
|
@@ -5431,13 +5459,12 @@
|
|
|
5431
5459
|
if (typeof window === "undefined")
|
|
5432
5460
|
return false;
|
|
5433
5461
|
const existing = window[LIB_NAME];
|
|
5434
|
-
if (existing
|
|
5435
|
-
console.warn(`[${LIB_NAME}] Already loaded (v${existing.
|
|
5462
|
+
if (existing) {
|
|
5463
|
+
console.warn(`[${LIB_NAME}] Already loaded (v${existing.version}). ` +
|
|
5436
5464
|
`Using existing instance. Please remove duplicate <script> tags.`);
|
|
5437
5465
|
return true;
|
|
5438
5466
|
}
|
|
5439
5467
|
const base = existing ?? {};
|
|
5440
|
-
base.__loading = true;
|
|
5441
5468
|
window[LIB_NAME] = base;
|
|
5442
5469
|
return false;
|
|
5443
5470
|
}
|
|
@@ -5455,16 +5482,14 @@
|
|
|
5455
5482
|
if (typeof window === "undefined")
|
|
5456
5483
|
return;
|
|
5457
5484
|
const ns = (window[name] ?? {});
|
|
5458
|
-
ns.
|
|
5459
|
-
ns.__loading = false;
|
|
5460
|
-
ns.__version = version;
|
|
5485
|
+
ns.version = version;
|
|
5461
5486
|
Object.assign(ns, api);
|
|
5462
5487
|
Object.freeze(ns);
|
|
5463
5488
|
window[name] = ns;
|
|
5464
5489
|
console.log(`[${name}] v${version} loaded successfully`);
|
|
5465
5490
|
}
|
|
5466
5491
|
|
|
5467
|
-
const version = "1.1.
|
|
5492
|
+
const version = "1.1.2";
|
|
5468
5493
|
const name = "SelectiveUI";
|
|
5469
5494
|
const alreadyLoaded = checkDuplicate(name);
|
|
5470
5495
|
function getGlobal() {
|
|
@@ -5496,7 +5521,7 @@
|
|
|
5496
5521
|
* Destroys Selective instances associated with the given query.
|
|
5497
5522
|
* Proxies to a global loaded instance if available; otherwise uses local Selective.destroy.
|
|
5498
5523
|
*/
|
|
5499
|
-
function destroy(query) {
|
|
5524
|
+
function destroy(query = null) {
|
|
5500
5525
|
const global = getGlobal();
|
|
5501
5526
|
if (alreadyLoaded && global)
|
|
5502
5527
|
return global.destroy(query);
|
|
@@ -5523,11 +5548,13 @@
|
|
|
5523
5548
|
return Effector(element);
|
|
5524
5549
|
}
|
|
5525
5550
|
if (!alreadyLoaded) {
|
|
5526
|
-
|
|
5551
|
+
const api = { bind, find, destroy, rebind, effector, version };
|
|
5552
|
+
markLoaded(name, version, api);
|
|
5553
|
+
let domInitialized = false;
|
|
5527
5554
|
function init() {
|
|
5528
|
-
if (
|
|
5555
|
+
if (domInitialized)
|
|
5529
5556
|
return;
|
|
5530
|
-
|
|
5557
|
+
domInitialized = true;
|
|
5531
5558
|
document.addEventListener("mousedown", () => {
|
|
5532
5559
|
const sels = Libs.getBindedCommand();
|
|
5533
5560
|
if (sels.length > 0) {
|
|
@@ -5537,8 +5564,6 @@
|
|
|
5537
5564
|
}
|
|
5538
5565
|
});
|
|
5539
5566
|
Selective.Observer();
|
|
5540
|
-
const api = { bind, find, destroy, rebind, effector, version };
|
|
5541
|
-
markLoaded(name, version, api);
|
|
5542
5567
|
}
|
|
5543
5568
|
if (document.readyState === "loading") {
|
|
5544
5569
|
document.addEventListener("DOMContentLoaded", init);
|