selective-ui 1.2.0 → 1.2.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.esm.js +55 -34
- 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 +56 -35
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/ts/adapter/mixed-adapter.ts +17 -11
- package/src/ts/components/selectbox.ts +16 -9
- package/src/ts/core/base/virtual-recyclerview.ts +1 -0
- package/src/ts/core/search-controller.ts +1 -4
- package/src/ts/models/option-model.ts +22 -3
- package/src/ts/services/effector.ts +5 -5
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@ import { OptionView } from "../views/option-view";
|
|
|
6
6
|
import { MixedItem, VisibilityStats } from "../types/core/base/mixed-adapter.type";
|
|
7
7
|
import { IEventCallback } from "../types/utils/ievents.type";
|
|
8
8
|
import { ImagePosition, LabelHalign, LabelValign } from "../types/views/view.option.type";
|
|
9
|
+
import { Libs } from "../utils/libs";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @extends {Adapter<GroupModel|OptionModel>}
|
|
@@ -25,6 +26,21 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
25
26
|
constructor(items: MixedItem[] = []) {
|
|
26
27
|
super(items);
|
|
27
28
|
this._buildFlatStructure();
|
|
29
|
+
|
|
30
|
+
Libs.callbackScheduler.on(`sche_vis_${this.adapterKey}`, () => {
|
|
31
|
+
const visibleCount = this.flatOptions.filter((item) => item.visible).length;
|
|
32
|
+
const totalCount = this.flatOptions.length;
|
|
33
|
+
|
|
34
|
+
this._visibilityChangedCallbacks.forEach((callback) => {
|
|
35
|
+
callback({
|
|
36
|
+
visibleCount,
|
|
37
|
+
totalCount,
|
|
38
|
+
hasVisible: visibleCount > 0,
|
|
39
|
+
isEmpty: totalCount === 0,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
Libs.callbackScheduler.run(`sche_vis_proxy_${this.adapterKey}`);
|
|
43
|
+
}, {debounce: 10});
|
|
28
44
|
}
|
|
29
45
|
|
|
30
46
|
/**
|
|
@@ -289,17 +305,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
289
305
|
* Computes visible and total counts, then emits aggregated state.
|
|
290
306
|
*/
|
|
291
307
|
private _notifyVisibilityChanged(): void {
|
|
292
|
-
|
|
293
|
-
const totalCount = this.flatOptions.length;
|
|
294
|
-
|
|
295
|
-
this._visibilityChangedCallbacks.forEach((callback) => {
|
|
296
|
-
callback({
|
|
297
|
-
visibleCount,
|
|
298
|
-
totalCount,
|
|
299
|
-
hasVisible: visibleCount > 0,
|
|
300
|
-
isEmpty: totalCount === 0,
|
|
301
|
-
});
|
|
302
|
-
});
|
|
308
|
+
Libs.callbackScheduler.run(`sche_vis_${this.adapterKey}`);
|
|
303
309
|
}
|
|
304
310
|
|
|
305
311
|
/**
|
|
@@ -251,6 +251,7 @@ export class SelectBox {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
const optionAdapter = container.popup!.optionAdapter as MixedAdapter;
|
|
254
|
+
let hightlightTimer : ReturnType<typeof setTimeout> | null = null;
|
|
254
255
|
|
|
255
256
|
const searchHandle = (keyword: string, isTrigger: boolean) => {
|
|
256
257
|
if (!isTrigger && keyword === "") {
|
|
@@ -261,13 +262,17 @@ export class SelectBox {
|
|
|
261
262
|
searchController
|
|
262
263
|
.search(keyword)
|
|
263
264
|
.then((result: any) => {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
265
|
+
clearTimeout(hightlightTimer!);
|
|
266
|
+
Libs.callbackScheduler.on(`sche_vis_proxy_${optionAdapter.adapterKey}`, () => {
|
|
267
|
+
container.popup?.triggerResize?.();
|
|
268
|
+
|
|
269
|
+
if (result?.hasResults) {
|
|
270
|
+
hightlightTimer = setTimeout(() => {
|
|
271
|
+
optionAdapter.resetHighlight();
|
|
272
|
+
container.popup?.triggerResize?.();
|
|
273
|
+
}, options.animationtime ?? 0);
|
|
274
|
+
}
|
|
275
|
+
}, { debounce: 10 });
|
|
271
276
|
})
|
|
272
277
|
.catch((error: unknown) => {
|
|
273
278
|
console.error("Search error:", error);
|
|
@@ -279,15 +284,17 @@ export class SelectBox {
|
|
|
279
284
|
|
|
280
285
|
searchbox.onSearch = (keyword: string, isTrigger: boolean) => {
|
|
281
286
|
if (!searchController.compareSearchTrigger(keyword)) return;
|
|
287
|
+
if (searchHandleTimer) clearTimeout(searchHandleTimer);
|
|
282
288
|
|
|
283
289
|
if (searchController.isAjax()) {
|
|
284
|
-
if (searchHandleTimer) clearTimeout(searchHandleTimer);
|
|
285
290
|
container.popup?.showLoading?.();
|
|
286
291
|
searchHandleTimer = setTimeout(() => {
|
|
287
292
|
searchHandle(keyword, isTrigger);
|
|
288
293
|
}, options.delaysearchtime ?? 0);
|
|
289
294
|
} else {
|
|
290
|
-
|
|
295
|
+
searchHandleTimer = setTimeout(() => {
|
|
296
|
+
searchHandle(keyword, isTrigger);
|
|
297
|
+
}, 10);
|
|
291
298
|
}
|
|
292
299
|
};
|
|
293
300
|
|
|
@@ -223,10 +223,7 @@ export class SearchController {
|
|
|
223
223
|
let hasVisibleItems = false;
|
|
224
224
|
|
|
225
225
|
flatOptions.forEach((opt) => {
|
|
226
|
-
const
|
|
227
|
-
const textNA = Libs.string2normalize(text);
|
|
228
|
-
|
|
229
|
-
const isVisible = lower === "" || text.includes(lower) || textNA.includes(lowerNA);
|
|
226
|
+
const isVisible = lower === "" || opt.textToFind.includes(lowerNA);
|
|
230
227
|
|
|
231
228
|
opt.visible = isVisible;
|
|
232
229
|
if (isVisible) hasVisibleItems = true;
|
|
@@ -6,12 +6,12 @@ import { OptionView } from "../views/option-view";
|
|
|
6
6
|
import type { IEventCallback } from "../types/utils/ievents.type";
|
|
7
7
|
import type { OptionViewTags } from "../types/views/view.option.type";
|
|
8
8
|
import type { GroupModel } from "./group-model";
|
|
9
|
-
import {
|
|
9
|
+
import { SelectiveOptions } from "../types/utils/selective.type";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* @extends {Model<HTMLOptionElement, OptionViewTags, OptionView>}
|
|
12
|
+
* @extends {Model<HTMLOptionElement, OptionViewTags, OptionView, SelectiveOptions>}
|
|
13
13
|
*/
|
|
14
|
-
export class OptionModel extends Model<HTMLOptionElement, OptionViewTags, OptionView,
|
|
14
|
+
export class OptionModel extends Model<HTMLOptionElement, OptionViewTags, OptionView, SelectiveOptions> {
|
|
15
15
|
private _privOnSelected: Array<(evtToken: IEventCallback, el: OptionModel, selected: boolean) => void> = [];
|
|
16
16
|
|
|
17
17
|
private _privOnInternalSelected: Array<(evtToken: IEventCallback, el: OptionModel, selected: boolean) => void> = [];
|
|
@@ -24,6 +24,22 @@ export class OptionModel extends Model<HTMLOptionElement, OptionViewTags, Option
|
|
|
24
24
|
|
|
25
25
|
group: GroupModel | null = null;
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Constructs a Model instance with configuration options and optional bindings to a target element and view.
|
|
29
|
+
* Stores references for later updates and rendering.
|
|
30
|
+
*
|
|
31
|
+
* @param {SelectiveOptions} options - Configuration options for the model.
|
|
32
|
+
* @param {HTMLOptionElement|null} [targetElement=null] - The underlying element (e.g., <option> or group node).
|
|
33
|
+
* @param {OptionView|null} [view=null] - The associated view responsible for rendering the model.
|
|
34
|
+
*/
|
|
35
|
+
constructor(options: SelectiveOptions, targetElement: HTMLOptionElement | null = null, view: OptionView | null = null) {
|
|
36
|
+
super(options, targetElement, view);
|
|
37
|
+
|
|
38
|
+
(async () => {
|
|
39
|
+
this.textToFind = Libs.string2normalize(this.textContent.toLowerCase());
|
|
40
|
+
})();
|
|
41
|
+
}
|
|
42
|
+
|
|
27
43
|
/**
|
|
28
44
|
* Returns the image source from dataset (imgsrc or image), or an empty string if absent.
|
|
29
45
|
*
|
|
@@ -149,6 +165,8 @@ export class OptionModel extends Model<HTMLOptionElement, OptionViewTags, Option
|
|
|
149
165
|
return this.options.allowHtml ? Libs.stripHtml(this.text).trim() : this.text.trim();
|
|
150
166
|
}
|
|
151
167
|
|
|
168
|
+
textToFind: string;
|
|
169
|
+
|
|
152
170
|
/**
|
|
153
171
|
* Returns the dataset object of the underlying <option> element, or an empty object.
|
|
154
172
|
*
|
|
@@ -214,6 +232,7 @@ export class OptionModel extends Model<HTMLOptionElement, OptionViewTags, Option
|
|
|
214
232
|
* and synchronizes initial selected state to the view.
|
|
215
233
|
*/
|
|
216
234
|
onTargetChanged(): void {
|
|
235
|
+
this.textToFind = Libs.string2normalize(this.textContent.toLowerCase());
|
|
217
236
|
if (!this.view) return;
|
|
218
237
|
|
|
219
238
|
const labelContent = this.view.view.tags.LabelContent;
|
|
@@ -301,7 +301,7 @@ class EffectorImpl implements EffectorInterface {
|
|
|
301
301
|
resize(config: ResizeConfig): this {
|
|
302
302
|
if (!this.element) return this;
|
|
303
303
|
|
|
304
|
-
|
|
304
|
+
this.cancel();
|
|
305
305
|
|
|
306
306
|
const {
|
|
307
307
|
duration = 200,
|
|
@@ -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
|
+
setTimeout(() => {
|
|
330
330
|
const styles: Partial<CSSStyleDeclaration> & Record<string, string> = {
|
|
331
331
|
width: `${width}px`,
|
|
332
332
|
left: `${left}px`,
|
|
@@ -343,7 +343,7 @@ class EffectorImpl implements EffectorInterface {
|
|
|
343
343
|
} else {
|
|
344
344
|
this._resizeTimeout = setTimeout(() => {
|
|
345
345
|
if (this.element?.style) {
|
|
346
|
-
this.element.style.transition =
|
|
346
|
+
this.element.style.transition = null;
|
|
347
347
|
}
|
|
348
348
|
}, duration);
|
|
349
349
|
}
|
|
@@ -352,7 +352,7 @@ class EffectorImpl implements EffectorInterface {
|
|
|
352
352
|
|
|
353
353
|
if (animate && (isPositionChanged || heightDiff > 1)) {
|
|
354
354
|
this._resizeTimeout = setTimeout(() => {
|
|
355
|
-
this.element.style.transition =
|
|
355
|
+
this.element.style.transition = null;
|
|
356
356
|
if (isPositionChanged) delete this.element.style.transition;
|
|
357
357
|
onComplete?.();
|
|
358
358
|
}, duration);
|
|
@@ -360,7 +360,7 @@ class EffectorImpl implements EffectorInterface {
|
|
|
360
360
|
if (isPositionChanged) delete this.element.style.transition;
|
|
361
361
|
onComplete?.();
|
|
362
362
|
}
|
|
363
|
-
});
|
|
363
|
+
}, 20);
|
|
364
364
|
|
|
365
365
|
return this;
|
|
366
366
|
}
|