selective-ui 1.2.2 → 1.2.3
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 +74 -73
- 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.js +2 -2
- package/dist/selective-ui.min.js.br +0 -0
- package/dist/selective-ui.umd.js +75 -74
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/ts/adapter/mixed-adapter.ts +11 -15
- package/src/ts/components/accessorybox.ts +3 -5
- package/src/ts/components/popup.ts +1 -1
- package/src/ts/components/selectbox.ts +0 -7
- package/src/ts/core/base/adapter.ts +11 -11
- package/src/ts/core/base/virtual-recyclerview.ts +5 -2
- package/src/ts/core/model-manager.ts +14 -7
- package/src/ts/core/search-controller.ts +0 -2
- package/src/ts/services/effector.ts +2 -2
- package/src/ts/services/select-observer.ts +1 -8
- package/src/ts/utils/callback-scheduler.ts +38 -18
package/package.json
CHANGED
|
@@ -180,23 +180,19 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
180
180
|
optionViewer.view.tags.LabelContent.innerHTML = optionModel.text;
|
|
181
181
|
|
|
182
182
|
if (!optionModel.isInit) {
|
|
183
|
-
optionViewer.view.tags.OptionView.addEventListener("click", (ev: MouseEvent) => {
|
|
183
|
+
optionViewer.view.tags.OptionView.addEventListener("click", async (ev: MouseEvent) => {
|
|
184
184
|
ev.stopPropagation();
|
|
185
185
|
ev.preventDefault();
|
|
186
186
|
|
|
187
187
|
if (this.isSkipEvent) return;
|
|
188
188
|
|
|
189
189
|
if (this.isMultiple) {
|
|
190
|
-
this.changingProp("select");
|
|
191
|
-
|
|
192
|
-
optionModel.selected = !optionModel.selected;
|
|
193
|
-
}, 5);
|
|
190
|
+
await this.changingProp("select");
|
|
191
|
+
optionModel.selected = !optionModel.selected;
|
|
194
192
|
} else if (optionModel.selected !== true) {
|
|
195
|
-
this.changingProp("select");
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
optionModel.selected = true;
|
|
199
|
-
}, 5);
|
|
193
|
+
await this.changingProp("select");
|
|
194
|
+
if (this.selectedItemSingle) this.selectedItemSingle.selected = false;
|
|
195
|
+
optionModel.selected = true;
|
|
200
196
|
}
|
|
201
197
|
});
|
|
202
198
|
|
|
@@ -233,11 +229,11 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
233
229
|
*
|
|
234
230
|
* @param {Array<GroupModel|OptionModel>} items - The new collection of items to be displayed.
|
|
235
231
|
*/
|
|
236
|
-
override setItems(items: MixedItem[]): void {
|
|
237
|
-
this.changingProp("items", items);
|
|
232
|
+
override async setItems(items: MixedItem[]): Promise<void> {
|
|
233
|
+
await this.changingProp("items", items);
|
|
238
234
|
this.items = items;
|
|
239
235
|
this.buildFlatStructure();
|
|
240
|
-
this.changeProp("items", items);
|
|
236
|
+
await this.changeProp("items", items);
|
|
241
237
|
}
|
|
242
238
|
|
|
243
239
|
/**
|
|
@@ -245,8 +241,8 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
245
241
|
*
|
|
246
242
|
* @param {Array<GroupModel|OptionModel>} items - The new collection of items to sync.
|
|
247
243
|
*/
|
|
248
|
-
override syncFromSource(items: MixedItem[]): void {
|
|
249
|
-
this.setItems(items);
|
|
244
|
+
override async syncFromSource(items: MixedItem[]): Promise<void> {
|
|
245
|
+
await this.setItems(items);
|
|
250
246
|
}
|
|
251
247
|
|
|
252
248
|
/**
|
|
@@ -125,14 +125,12 @@ export class AccessoryBox {
|
|
|
125
125
|
role: "button",
|
|
126
126
|
ariaLabel: `${this.options!.textAccessoryDeselect}${modelData.textContent}`,
|
|
127
127
|
title: `${this.options!.textAccessoryDeselect}${modelData.textContent}`,
|
|
128
|
-
onclick: (evt: MouseEvent) => {
|
|
128
|
+
onclick: async (evt: MouseEvent) => {
|
|
129
129
|
evt.preventDefault();
|
|
130
|
-
this.modelManager?.triggerChanging?.(
|
|
130
|
+
await this.modelManager?.triggerChanging?.(
|
|
131
131
|
"select",
|
|
132
132
|
);
|
|
133
|
-
|
|
134
|
-
modelData.selected = false;
|
|
135
|
-
}, 10);
|
|
133
|
+
modelData.selected = false;
|
|
136
134
|
},
|
|
137
135
|
},
|
|
138
136
|
},
|
|
@@ -240,13 +240,6 @@ export class SelectBox {
|
|
|
240
240
|
}
|
|
241
241
|
};
|
|
242
242
|
|
|
243
|
-
// Custom event (manual refresh)
|
|
244
|
-
select.addEventListener("options:changed", () => {
|
|
245
|
-
optionModelManager.update(Libs.parseSelectToArray(select));
|
|
246
|
-
this.getAction()?.refreshMask();
|
|
247
|
-
container.popup?.triggerResize?.();
|
|
248
|
-
});
|
|
249
|
-
|
|
250
243
|
// AJAX setup (if provided)
|
|
251
244
|
if (options.ajax) {
|
|
252
245
|
searchController.setAjax(options.ajax);
|
|
@@ -64,7 +64,7 @@ export class Adapter<
|
|
|
64
64
|
* @param {Function} callback - Function to execute before the property changes.
|
|
65
65
|
*/
|
|
66
66
|
public onPropChanging(propName: string, callback: (...args: unknown[]) => void): void {
|
|
67
|
-
Libs.callbackScheduler.on(`${propName}ing_${this.adapterKey}`, callback, { debounce:
|
|
67
|
+
Libs.callbackScheduler.on(`${propName}ing_${this.adapterKey}`, callback, { debounce: 0 });
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
@@ -75,7 +75,7 @@ export class Adapter<
|
|
|
75
75
|
* @param {Function} callback - Function to execute after the property changes.
|
|
76
76
|
*/
|
|
77
77
|
public onPropChanged(propName: string, callback: (...args: unknown[]) => void): void {
|
|
78
|
-
Libs.callbackScheduler.on(`${propName}_${this.adapterKey}`, callback);
|
|
78
|
+
Libs.callbackScheduler.on(`${propName}_${this.adapterKey}`, callback, { debounce: 0 });
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
@@ -85,8 +85,8 @@ export class Adapter<
|
|
|
85
85
|
* @param {string} propName - The property name to emit (e.g., "items").
|
|
86
86
|
* @param {...any} params - Parameters forwarded to the callbacks.
|
|
87
87
|
*/
|
|
88
|
-
public changeProp(propName: string, ...params: unknown[]): void {
|
|
89
|
-
Libs.callbackScheduler.run(`${propName}_${this.adapterKey}`, ...params)
|
|
88
|
+
public changeProp(propName: string, ...params: unknown[]): Promise<void> {
|
|
89
|
+
return Libs.callbackScheduler.run(`${propName}_${this.adapterKey}`, ...params) as Promise<void>;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/**
|
|
@@ -96,8 +96,8 @@ export class Adapter<
|
|
|
96
96
|
* @param {string} propName - The property name to emit (e.g., "items").
|
|
97
97
|
* @param {...any} params - Parameters forwarded to the callbacks.
|
|
98
98
|
*/
|
|
99
|
-
public changingProp(propName: string, ...params: unknown[]): void {
|
|
100
|
-
Libs.callbackScheduler.run(`${propName}ing_${this.adapterKey}`, ...params)
|
|
99
|
+
public changingProp(propName: string, ...params: unknown[]): Promise<void> {
|
|
100
|
+
return Libs.callbackScheduler.run(`${propName}ing_${this.adapterKey}`, ...params) as Promise<void>;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/**
|
|
@@ -129,10 +129,10 @@ export class Adapter<
|
|
|
129
129
|
*
|
|
130
130
|
* @param {TItem[]} items - The new list of items to set.
|
|
131
131
|
*/
|
|
132
|
-
public setItems(items: TItem[]): void {
|
|
133
|
-
this.changingProp("items", items);
|
|
132
|
+
public async setItems(items: TItem[]): Promise<void> {
|
|
133
|
+
await this.changingProp("items", items);
|
|
134
134
|
this.items = items;
|
|
135
|
-
this.changeProp("items", items);
|
|
135
|
+
await this.changeProp("items", items);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
/**
|
|
@@ -141,8 +141,8 @@ export class Adapter<
|
|
|
141
141
|
*
|
|
142
142
|
* @param {TItem[]} items - The source list of items to synchronize.
|
|
143
143
|
*/
|
|
144
|
-
public syncFromSource(items: TItem[]): void {
|
|
145
|
-
this.setItems(items);
|
|
144
|
+
public async syncFromSource(items: TItem[]): Promise<void> {
|
|
145
|
+
await this.setItems(items);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
/**
|
|
@@ -384,7 +384,7 @@ export class VirtualRecyclerView<
|
|
|
384
384
|
private containerTopInScroll(): number {
|
|
385
385
|
const a = this.viewElement!.getBoundingClientRect();
|
|
386
386
|
const b = this.scrollEl.getBoundingClientRect();
|
|
387
|
-
return a.top - b.top + this.scrollEl.scrollTop;
|
|
387
|
+
return Math.max(0, a.top - b.top + this.scrollEl.scrollTop);
|
|
388
388
|
}
|
|
389
389
|
|
|
390
390
|
/**
|
|
@@ -670,7 +670,10 @@ export class VirtualRecyclerView<
|
|
|
670
670
|
const maxScroll = Math.max(0, this.scrollEl.scrollHeight - this.scrollEl.clientHeight);
|
|
671
671
|
const clamped = Math.min(Math.max(0, targetScroll), maxScroll);
|
|
672
672
|
|
|
673
|
-
|
|
673
|
+
const heightChanged = Math.abs(anchorTopNew - anchorTop) > 1;
|
|
674
|
+
const scrollDiff = Math.abs(this.scrollEl.scrollTop - clamped);
|
|
675
|
+
|
|
676
|
+
if (heightChanged && scrollDiff > 0.5 && scrollDiff < 100) {
|
|
674
677
|
this.scrollEl.scrollTop = clamped;
|
|
675
678
|
}
|
|
676
679
|
} finally {
|
|
@@ -29,6 +29,8 @@ export class ModelManager<
|
|
|
29
29
|
|
|
30
30
|
private options: SelectiveOptions = null;
|
|
31
31
|
|
|
32
|
+
private oldPosition = 0;
|
|
33
|
+
|
|
32
34
|
/**
|
|
33
35
|
* Constructs a ModelManager with configuration options used by created models and components.
|
|
34
36
|
*
|
|
@@ -142,13 +144,13 @@ export class ModelManager<
|
|
|
142
144
|
*
|
|
143
145
|
* @param {Array<HTMLOptGroupElement|HTMLOptionElement>} modelData - New source elements to rebuild models from.
|
|
144
146
|
*/
|
|
145
|
-
public replace(modelData: Array<HTMLOptGroupElement | HTMLOptionElement>): void {
|
|
147
|
+
public async replace(modelData: Array<HTMLOptGroupElement | HTMLOptionElement>): Promise<void> {
|
|
146
148
|
this.lastFingerprint = null;
|
|
147
149
|
this.createModelResources(modelData);
|
|
148
150
|
|
|
149
151
|
if (this.privAdapterHandle) {
|
|
150
152
|
// Adapter expects TModel[], but this manager's list is GroupModel|OptionModel.
|
|
151
|
-
this.privAdapterHandle.syncFromSource(this.privModelList as unknown as TModel[]);
|
|
153
|
+
await this.privAdapterHandle.syncFromSource(this.privModelList as unknown as TModel[]);
|
|
152
154
|
}
|
|
153
155
|
|
|
154
156
|
this.refresh(false);
|
|
@@ -273,6 +275,11 @@ export class ModelManager<
|
|
|
273
275
|
});
|
|
274
276
|
|
|
275
277
|
let isUpdate = true;
|
|
278
|
+
if (this.oldPosition == 0) {
|
|
279
|
+
isUpdate = false;
|
|
280
|
+
}
|
|
281
|
+
this.oldPosition = position;
|
|
282
|
+
|
|
276
283
|
oldGroupMap.forEach((removedGroup) => {
|
|
277
284
|
isUpdate = false;
|
|
278
285
|
removedGroup.remove();
|
|
@@ -289,7 +296,7 @@ export class ModelManager<
|
|
|
289
296
|
this.privAdapterHandle.updateData(this.privModelList as unknown as TModel[]);
|
|
290
297
|
}
|
|
291
298
|
|
|
292
|
-
this.onUpdated();
|
|
299
|
+
// this.onUpdated();
|
|
293
300
|
this.refresh(isUpdate);
|
|
294
301
|
}
|
|
295
302
|
|
|
@@ -340,15 +347,15 @@ export class ModelManager<
|
|
|
340
347
|
* Triggers the adapter's pre-change pipeline for a named event,
|
|
341
348
|
* enabling observers to react before a change is applied.
|
|
342
349
|
*/
|
|
343
|
-
public triggerChanging(event_name: string): void {
|
|
344
|
-
this.privAdapterHandle?.changingProp(event_name);
|
|
350
|
+
public triggerChanging(event_name: string): Promise<void> {
|
|
351
|
+
return this.privAdapterHandle?.changingProp(event_name);
|
|
345
352
|
}
|
|
346
353
|
|
|
347
354
|
/**
|
|
348
355
|
* Triggers the adapter's post-change pipeline for a named event,
|
|
349
356
|
* notifying observers after a change has been applied.
|
|
350
357
|
*/
|
|
351
|
-
public triggerChanged(event_name: string): void {
|
|
352
|
-
this.privAdapterHandle?.changeProp(event_name);
|
|
358
|
+
public triggerChanged(event_name: string): Promise<void> {
|
|
359
|
+
return this.privAdapterHandle?.changeProp(event_name);
|
|
353
360
|
}
|
|
354
361
|
}
|
|
@@ -326,7 +326,7 @@ class EffectorImpl implements EffectorInterface {
|
|
|
326
326
|
this.element.style.transition = `top ${duration}ms ease-out, height ${duration}ms ease-out, max-height ${duration}ms ease-out;`;
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
|
|
329
|
+
requestAnimationFrame(() => {
|
|
330
330
|
const styles: Partial<CSSStyleDeclaration> & Record<string, string> = {
|
|
331
331
|
width: `${width}px`,
|
|
332
332
|
left: `${left}px`,
|
|
@@ -360,7 +360,7 @@ class EffectorImpl implements EffectorInterface {
|
|
|
360
360
|
if (isPositionChanged) delete this.element.style.transition;
|
|
361
361
|
onComplete?.();
|
|
362
362
|
}
|
|
363
|
-
}
|
|
363
|
+
});
|
|
364
364
|
|
|
365
365
|
return this;
|
|
366
366
|
}
|
|
@@ -16,7 +16,7 @@ export class SelectObserver {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Initializes the SelectObserver for a given <select> element.
|
|
19
|
-
* Captures the initial snapshot, sets up a MutationObserver
|
|
19
|
+
* Captures the initial snapshot, sets up a MutationObserver.
|
|
20
20
|
* Changes are debounced to prevent excessive calls.
|
|
21
21
|
*
|
|
22
22
|
* @param {HTMLSelectElement} select - The <select> element to observe.
|
|
@@ -31,13 +31,6 @@ export class SelectObserver {
|
|
|
31
31
|
this.handleChange();
|
|
32
32
|
}, this._DEBOUNCE_DELAY);
|
|
33
33
|
});
|
|
34
|
-
|
|
35
|
-
select.addEventListener("options:changed", () => {
|
|
36
|
-
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
37
|
-
this.debounceTimer = setTimeout(() => {
|
|
38
|
-
this.handleChange();
|
|
39
|
-
}, this._DEBOUNCE_DELAY);
|
|
40
|
-
});
|
|
41
34
|
}
|
|
42
35
|
|
|
43
36
|
/**
|
|
@@ -84,12 +84,18 @@ export class CallbackScheduler {
|
|
|
84
84
|
* - If an entry has `once = true`, it is removed after execution by setting its slot to `undefined`.
|
|
85
85
|
* (The list is not spliced to preserve indices.)
|
|
86
86
|
*/
|
|
87
|
-
public run(key: TimerKey, ...params: any[]): void {
|
|
87
|
+
public run(key: TimerKey, ...params: any[]): Promise<void> | void {
|
|
88
88
|
const executes = this.executeStored.get(key);
|
|
89
|
-
if (!executes)
|
|
89
|
+
if (!executes || executes.length === 0) {
|
|
90
|
+
return Promise.resolve();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!this.timerRunner.has(key)) {
|
|
94
|
+
this.timerRunner.set(key, new Map());
|
|
95
|
+
}
|
|
90
96
|
|
|
91
|
-
if (!this.timerRunner.has(key)) this.timerRunner.set(key, new Map());
|
|
92
97
|
const runner = this.timerRunner.get(key)!;
|
|
98
|
+
const tasks: Promise<void>[] = [];
|
|
93
99
|
|
|
94
100
|
for (let i = 0; i < executes.length; i++) {
|
|
95
101
|
const entry = executes[i];
|
|
@@ -98,22 +104,36 @@ export class CallbackScheduler {
|
|
|
98
104
|
const prev = runner.get(i);
|
|
99
105
|
if (prev) clearTimeout(prev);
|
|
100
106
|
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
107
|
+
const task = new Promise<void>((resolve) => {
|
|
108
|
+
const timer = setTimeout(async () => {
|
|
109
|
+
try {
|
|
110
|
+
const resp = entry.callback(
|
|
111
|
+
params.length > 0 ? params : null
|
|
112
|
+
) as any;
|
|
113
|
+
|
|
114
|
+
if (resp instanceof Promise) {
|
|
115
|
+
await resp;
|
|
116
|
+
}
|
|
117
|
+
} catch {} finally {
|
|
118
|
+
if (entry.once) {
|
|
119
|
+
executes[i] = undefined;
|
|
120
|
+
|
|
121
|
+
const current = runner.get(i);
|
|
122
|
+
if (current) clearTimeout(current);
|
|
123
|
+
runner.delete(i);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
resolve();
|
|
127
|
+
}
|
|
128
|
+
}, entry.timeout);
|
|
129
|
+
|
|
130
|
+
runner.set(i, timer);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
tasks.push(task);
|
|
116
134
|
}
|
|
135
|
+
|
|
136
|
+
return Promise.all(tasks).then(() => void 0);
|
|
117
137
|
}
|
|
118
138
|
|
|
119
139
|
/**
|