selective-ui 1.3.1 → 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 +271 -559
- 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 +273 -561
- 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 +258 -83
- 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "selective-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "An overlay for the HTML select element.",
|
|
5
5
|
"author": "Huỳnh Công Xuân Mai",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,28 +37,28 @@
|
|
|
37
37
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@rollup/plugin-commonjs": "^29.0.
|
|
40
|
+
"@rollup/plugin-commonjs": "^29.0.2",
|
|
41
41
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
42
42
|
"@rollup/plugin-replace": "^6.0.3",
|
|
43
|
-
"@rollup/plugin-terser": "^0.
|
|
43
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
44
44
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
45
45
|
"@testing-library/dom": "^10.4.1",
|
|
46
46
|
"@testing-library/jest-dom": "^6.9.1",
|
|
47
47
|
"@types/jest": "^30.0.0",
|
|
48
|
-
"autoprefixer": "^10.4.
|
|
49
|
-
"cssnano": "^7.1.
|
|
50
|
-
"jest": "^30.
|
|
51
|
-
"jest-environment-jsdom": "^30.
|
|
48
|
+
"autoprefixer": "^10.4.27",
|
|
49
|
+
"cssnano": "^7.1.4",
|
|
50
|
+
"jest": "^30.3.0",
|
|
51
|
+
"jest-environment-jsdom": "^30.3.0",
|
|
52
52
|
"mutation-observer": "^1.0.3",
|
|
53
|
-
"postcss": "^8.5.
|
|
54
|
-
"rimraf": "^6.1.
|
|
55
|
-
"rollup": "^4.
|
|
53
|
+
"postcss": "^8.5.8",
|
|
54
|
+
"rimraf": "^6.1.3",
|
|
55
|
+
"rollup": "^4.60.1",
|
|
56
56
|
"rollup-plugin-brotli": "^3.1.0",
|
|
57
57
|
"rollup-plugin-postcss": "^4.0.2",
|
|
58
|
-
"ts-jest": "^29.4.
|
|
58
|
+
"ts-jest": "^29.4.9",
|
|
59
59
|
"ts-node": "^10.9.2",
|
|
60
60
|
"tslib": "^2.8.1",
|
|
61
|
-
"typescript": "^
|
|
61
|
+
"typescript": "^6.0.2"
|
|
62
62
|
},
|
|
63
63
|
"keywords": [
|
|
64
64
|
"select",
|
|
@@ -3,9 +3,16 @@ import { GroupModel } from "../models/group-model";
|
|
|
3
3
|
import { OptionModel } from "../models/option-model";
|
|
4
4
|
import { GroupView } from "../views/group-view";
|
|
5
5
|
import { OptionView } from "../views/option-view";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
MixedItem,
|
|
8
|
+
VisibilityStats,
|
|
9
|
+
} from "../types/core/base/mixed-adapter.type";
|
|
7
10
|
import { IEventCallback } from "../types/utils/ievents.type";
|
|
8
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
ImagePosition,
|
|
13
|
+
LabelHalign,
|
|
14
|
+
LabelValign,
|
|
15
|
+
} from "../types/views/view.option.type";
|
|
9
16
|
import { Libs } from "../utils/libs";
|
|
10
17
|
import { LifecycleState } from "../types/core/base/lifecycle.type";
|
|
11
18
|
import { Lifecycle } from "../core/base/lifecycle";
|
|
@@ -72,25 +79,27 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
72
79
|
*
|
|
73
80
|
* @internal
|
|
74
81
|
*/
|
|
75
|
-
public options
|
|
82
|
+
public options?: SelectiveOptions;
|
|
76
83
|
|
|
77
84
|
/**
|
|
78
85
|
* Subscribers for aggregated visibility statistics.
|
|
79
86
|
* Fired via a debounced scheduler to avoid repeated recomputation during batch updates.
|
|
80
87
|
*/
|
|
81
|
-
private visibilityChangedCallbacks: Array<
|
|
88
|
+
private visibilityChangedCallbacks: Array<
|
|
89
|
+
(stats: VisibilityStats) => void
|
|
90
|
+
> = [];
|
|
82
91
|
|
|
83
92
|
/**
|
|
84
93
|
* Flat index of the currently highlighted option.
|
|
85
94
|
* `-1` indicates "no highlight".
|
|
86
95
|
*/
|
|
87
|
-
private currentHighlightIndex = -1;
|
|
96
|
+
private currentHighlightIndex: number = -1;
|
|
88
97
|
|
|
89
98
|
/**
|
|
90
99
|
* Cached pointer to the selected option in single-select mode.
|
|
91
100
|
* Used to efficiently clear previous selection when selecting a new option.
|
|
92
101
|
*/
|
|
93
|
-
private selectedItemSingle
|
|
102
|
+
private selectedItemSingle?: OptionModel;
|
|
94
103
|
|
|
95
104
|
/** Top-level group models (if any). */
|
|
96
105
|
public groups: GroupModel[] = [];
|
|
@@ -127,11 +136,13 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
127
136
|
* @returns {void}
|
|
128
137
|
* @override
|
|
129
138
|
*/
|
|
130
|
-
public override init() {
|
|
139
|
+
public override init(): void {
|
|
131
140
|
Libs.callbackScheduler.on(
|
|
132
141
|
`sche_vis_${this.adapterKey}`,
|
|
133
142
|
() => {
|
|
134
|
-
const visibleCount = this.flatOptions.filter(
|
|
143
|
+
const visibleCount = this.flatOptions.filter(
|
|
144
|
+
(item) => item.visible,
|
|
145
|
+
).length;
|
|
135
146
|
const totalCount = this.flatOptions.length;
|
|
136
147
|
|
|
137
148
|
this.visibilityChangedCallbacks.forEach((callback) => {
|
|
@@ -146,7 +157,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
146
157
|
// Proxy hook; allows other listeners to chain after visibility aggregation.
|
|
147
158
|
Libs.callbackScheduler.run(`sche_vis_proxy_${this.adapterKey}`);
|
|
148
159
|
},
|
|
149
|
-
{ debounce: 10 }
|
|
160
|
+
{ debounce: 10 },
|
|
150
161
|
);
|
|
151
162
|
|
|
152
163
|
super.init();
|
|
@@ -187,8 +198,12 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
187
198
|
* @returns {GroupView | OptionView} A view instance matching the item type.
|
|
188
199
|
* @override
|
|
189
200
|
*/
|
|
190
|
-
override viewHolder(
|
|
191
|
-
|
|
201
|
+
override viewHolder(
|
|
202
|
+
parent: HTMLElement,
|
|
203
|
+
item: MixedItem,
|
|
204
|
+
): GroupView | OptionView {
|
|
205
|
+
if (item instanceof GroupModel)
|
|
206
|
+
return new GroupView(parent, this.options);
|
|
192
207
|
return new OptionView(parent, this.options);
|
|
193
208
|
}
|
|
194
209
|
|
|
@@ -200,12 +215,16 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
200
215
|
* - Performs one-time listener binding guarded by `item.isInit`.
|
|
201
216
|
*
|
|
202
217
|
* @param {MixedItem} item - {@link GroupModel} or {@link OptionModel}.
|
|
203
|
-
* @param {GroupView | OptionView
|
|
218
|
+
* @param {GroupView | OptionView} viewer - The view instance that will render the model.
|
|
204
219
|
* @param {number} position - Position in the top-level mixed list.
|
|
205
220
|
* @returns {void}
|
|
206
221
|
* @override
|
|
207
222
|
*/
|
|
208
|
-
override onViewHolder(
|
|
223
|
+
override onViewHolder(
|
|
224
|
+
item: MixedItem,
|
|
225
|
+
viewer?: GroupView | OptionView,
|
|
226
|
+
position?: number,
|
|
227
|
+
): void {
|
|
209
228
|
item.position = position;
|
|
210
229
|
|
|
211
230
|
if (item instanceof GroupModel) {
|
|
@@ -237,7 +256,11 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
237
256
|
* @param {number} position - Group index in the top-level list.
|
|
238
257
|
* @returns {void}
|
|
239
258
|
*/
|
|
240
|
-
private handleGroupView(
|
|
259
|
+
private handleGroupView(
|
|
260
|
+
groupModel: GroupModel,
|
|
261
|
+
groupView: GroupView,
|
|
262
|
+
position: number,
|
|
263
|
+
): void {
|
|
241
264
|
super.onViewHolder(groupModel, groupView, position);
|
|
242
265
|
groupModel.view = groupView;
|
|
243
266
|
|
|
@@ -251,16 +274,23 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
251
274
|
groupModel.toggleCollapse();
|
|
252
275
|
});
|
|
253
276
|
|
|
254
|
-
groupModel.onCollapsedChanged(
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
277
|
+
groupModel.onCollapsedChanged(
|
|
278
|
+
(
|
|
279
|
+
evtToken: IEventCallback,
|
|
280
|
+
model: GroupModel,
|
|
281
|
+
collapsed: boolean,
|
|
282
|
+
) => {
|
|
283
|
+
void evtToken;
|
|
284
|
+
|
|
285
|
+
model.items.forEach((optItem) => {
|
|
286
|
+
const optView = optItem.view?.getView?.();
|
|
287
|
+
if (optView)
|
|
288
|
+
optView.style.display = collapsed ? "none" : "";
|
|
289
|
+
});
|
|
261
290
|
|
|
262
|
-
|
|
263
|
-
|
|
291
|
+
this.onCollapsedChange(model, collapsed);
|
|
292
|
+
},
|
|
293
|
+
);
|
|
264
294
|
}
|
|
265
295
|
|
|
266
296
|
const itemsContainer = groupView.getItemsContainer();
|
|
@@ -307,7 +337,11 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
307
337
|
* @param {number} position - Option index within its group list (or rendering context).
|
|
308
338
|
* @returns {void}
|
|
309
339
|
*/
|
|
310
|
-
private handleOptionView(
|
|
340
|
+
private handleOptionView(
|
|
341
|
+
optionModel: OptionModel,
|
|
342
|
+
optionViewer: OptionView,
|
|
343
|
+
position: number,
|
|
344
|
+
): void {
|
|
311
345
|
optionViewer.isMultiple = this.isMultiple;
|
|
312
346
|
optionViewer.hasImage = optionModel.hasImage;
|
|
313
347
|
|
|
@@ -329,8 +363,10 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
329
363
|
if (optionModel.hasImage) {
|
|
330
364
|
const imageTag = optionViewer.view.tags.OptionImage;
|
|
331
365
|
if (imageTag) {
|
|
332
|
-
if (imageTag.src !== optionModel.imageSrc)
|
|
333
|
-
|
|
366
|
+
if (imageTag.src !== optionModel.imageSrc)
|
|
367
|
+
imageTag.src = optionModel.imageSrc;
|
|
368
|
+
if (imageTag.alt !== optionModel.text)
|
|
369
|
+
imageTag.alt = optionModel.text;
|
|
334
370
|
}
|
|
335
371
|
}
|
|
336
372
|
|
|
@@ -338,45 +374,73 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
338
374
|
optionViewer.view.tags.LabelContent.innerHTML = optionModel.text;
|
|
339
375
|
|
|
340
376
|
if (!optionModel.isInit) {
|
|
341
|
-
optionViewer.view.tags.OptionView.addEventListener(
|
|
342
|
-
|
|
343
|
-
ev
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
if (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
377
|
+
optionViewer.view.tags.OptionView.addEventListener(
|
|
378
|
+
"click",
|
|
379
|
+
async (ev: MouseEvent) => {
|
|
380
|
+
ev.stopPropagation();
|
|
381
|
+
ev.preventDefault();
|
|
382
|
+
|
|
383
|
+
if (this.isSkipEvent) return;
|
|
384
|
+
|
|
385
|
+
if (this.isMultiple) {
|
|
386
|
+
await this.changingProp("select");
|
|
387
|
+
optionModel.selected = !optionModel.selected;
|
|
388
|
+
} else if (optionModel.selected !== true) {
|
|
389
|
+
await this.changingProp("select");
|
|
390
|
+
if (this.selectedItemSingle)
|
|
391
|
+
this.selectedItemSingle.selected = false;
|
|
392
|
+
optionModel.selected = true;
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
);
|
|
356
396
|
|
|
357
397
|
optionViewer.view.tags.OptionView.title = optionModel.textContent;
|
|
358
398
|
|
|
359
|
-
optionViewer.view.tags.OptionView.addEventListener(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
399
|
+
optionViewer.view.tags.OptionView.addEventListener(
|
|
400
|
+
"mouseenter",
|
|
401
|
+
() => {
|
|
402
|
+
if (this.isSkipEvent) return;
|
|
403
|
+
this.setHighlight(
|
|
404
|
+
this.flatOptions.indexOf(optionModel),
|
|
405
|
+
false,
|
|
406
|
+
);
|
|
407
|
+
},
|
|
408
|
+
);
|
|
363
409
|
|
|
364
410
|
// External selection notification (user-facing semantics).
|
|
365
|
-
optionModel.onSelected(
|
|
366
|
-
|
|
367
|
-
|
|
411
|
+
optionModel.onSelected(
|
|
412
|
+
(
|
|
413
|
+
_evtToken: IEventCallback,
|
|
414
|
+
_el: OptionModel,
|
|
415
|
+
_selected: boolean,
|
|
416
|
+
) => {
|
|
417
|
+
this.changeProp("selected");
|
|
418
|
+
},
|
|
419
|
+
);
|
|
368
420
|
|
|
369
421
|
// Internal selection notification (non-trigger semantics).
|
|
370
|
-
optionModel.onInternalSelected(
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
422
|
+
optionModel.onInternalSelected(
|
|
423
|
+
(
|
|
424
|
+
_evtToken: IEventCallback,
|
|
425
|
+
_el: OptionModel,
|
|
426
|
+
selected: boolean,
|
|
427
|
+
) => {
|
|
428
|
+
if (selected) this.selectedItemSingle = optionModel;
|
|
429
|
+
this.changeProp("selected_internal");
|
|
430
|
+
},
|
|
431
|
+
);
|
|
374
432
|
|
|
375
433
|
// Visibility changes affect group visibility and aggregated visibility stats.
|
|
376
|
-
optionModel.onVisibilityChanged(
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
434
|
+
optionModel.onVisibilityChanged(
|
|
435
|
+
(
|
|
436
|
+
_evtToken: IEventCallback,
|
|
437
|
+
model: OptionModel,
|
|
438
|
+
_visible: boolean,
|
|
439
|
+
) => {
|
|
440
|
+
model.group?.updateVisibility();
|
|
441
|
+
this.notifyVisibilityChanged();
|
|
442
|
+
},
|
|
443
|
+
);
|
|
380
444
|
}
|
|
381
445
|
|
|
382
446
|
// Ensure single-select cache and suppress re-trigger when model is already selected.
|
|
@@ -453,7 +517,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
453
517
|
|
|
454
518
|
Libs.callbackScheduler.clear(`sche_vis_${this.adapterKey}`);
|
|
455
519
|
|
|
456
|
-
this.groups.forEach(group => {
|
|
520
|
+
this.groups.forEach((group) => {
|
|
457
521
|
group.destroy();
|
|
458
522
|
});
|
|
459
523
|
|
|
@@ -506,7 +570,9 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
506
570
|
* @param {(stats: VisibilityStats) => void} callback - Invoked with `{ visibleCount, totalCount, hasVisible, isEmpty }`.
|
|
507
571
|
* @returns {void}
|
|
508
572
|
*/
|
|
509
|
-
public onVisibilityChanged(
|
|
573
|
+
public onVisibilityChanged(
|
|
574
|
+
callback: (stats: VisibilityStats) => void,
|
|
575
|
+
): void {
|
|
510
576
|
this.visibilityChangedCallbacks.push(callback);
|
|
511
577
|
}
|
|
512
578
|
|
|
@@ -525,7 +591,9 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
525
591
|
* @returns {VisibilityStats} Aggregated stats: `{ visibleCount, totalCount, hasVisible, isEmpty }`.
|
|
526
592
|
*/
|
|
527
593
|
public getVisibilityStats(): VisibilityStats {
|
|
528
|
-
const visibleCount = this.flatOptions.filter(
|
|
594
|
+
const visibleCount = this.flatOptions.filter(
|
|
595
|
+
(item) => item.visible,
|
|
596
|
+
).length;
|
|
529
597
|
const totalCount = this.flatOptions.length;
|
|
530
598
|
|
|
531
599
|
return {
|
|
@@ -560,7 +628,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
560
628
|
if (visibleOptions.length === 0) return;
|
|
561
629
|
|
|
562
630
|
let currentVisibleIndex = visibleOptions.findIndex(
|
|
563
|
-
(opt) => opt === this.flatOptions[this.currentHighlightIndex]
|
|
631
|
+
(opt) => opt === this.flatOptions[this.currentHighlightIndex],
|
|
564
632
|
);
|
|
565
633
|
if (currentVisibleIndex === -1) currentVisibleIndex = -1;
|
|
566
634
|
|
|
@@ -589,7 +657,10 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
589
657
|
* @returns {void}
|
|
590
658
|
*/
|
|
591
659
|
public selectHighlighted(): void {
|
|
592
|
-
if (
|
|
660
|
+
if (
|
|
661
|
+
this.currentHighlightIndex > -1 &&
|
|
662
|
+
this.flatOptions[this.currentHighlightIndex]
|
|
663
|
+
) {
|
|
593
664
|
const item = this.flatOptions[this.currentHighlightIndex];
|
|
594
665
|
if (item.visible) {
|
|
595
666
|
const viewEl = item.view?.getView?.();
|
|
@@ -614,7 +685,10 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
614
685
|
* @param {boolean} [isScrollToView=true] - Whether to scroll the highlighted item into view.
|
|
615
686
|
* @returns {void}
|
|
616
687
|
*/
|
|
617
|
-
public setHighlight(
|
|
688
|
+
public setHighlight(
|
|
689
|
+
target: number | OptionModel,
|
|
690
|
+
isScrollToView: boolean = true,
|
|
691
|
+
): void {
|
|
618
692
|
let index = 0;
|
|
619
693
|
|
|
620
694
|
if (typeof target === "number") {
|
|
@@ -626,7 +700,10 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
626
700
|
index = 0;
|
|
627
701
|
}
|
|
628
702
|
|
|
629
|
-
if (
|
|
703
|
+
if (
|
|
704
|
+
this.currentHighlightIndex > -1 &&
|
|
705
|
+
this.flatOptions[this.currentHighlightIndex]
|
|
706
|
+
) {
|
|
630
707
|
this.flatOptions[this.currentHighlightIndex].highlighted = false;
|
|
631
708
|
}
|
|
632
709
|
|
|
@@ -640,10 +717,12 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
640
717
|
if (isScrollToView) {
|
|
641
718
|
const el = item.view?.getView?.();
|
|
642
719
|
if (el) {
|
|
643
|
-
el.scrollIntoView({ block:
|
|
720
|
+
el.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
644
721
|
} else {
|
|
645
722
|
// If virtualized, ensure the item is rendered before trying to scroll.
|
|
646
|
-
this.recyclerView?.ensureRendered?.(i, {
|
|
723
|
+
this.recyclerView?.ensureRendered?.(i, {
|
|
724
|
+
scrollIntoView: true,
|
|
725
|
+
});
|
|
647
726
|
}
|
|
648
727
|
}
|
|
649
728
|
|
|
@@ -664,7 +743,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
664
743
|
* @param {string} [id] - Optional DOM id of the highlighted view element (when available).
|
|
665
744
|
* @returns {void}
|
|
666
745
|
*/
|
|
667
|
-
public onHighlightChange(index: number, id?: string): void {
|
|
746
|
+
public onHighlightChange(index: number, id?: string): void {}
|
|
668
747
|
|
|
669
748
|
/**
|
|
670
749
|
* Hook called whenever a group's collapsed state changes.
|
|
@@ -678,5 +757,5 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
678
757
|
* @param {boolean} collapsed - New collapsed state.
|
|
679
758
|
* @returns {void}
|
|
680
759
|
*/
|
|
681
|
-
public onCollapsedChange(model: GroupModel, collapsed: boolean): void {
|
|
682
|
-
}
|
|
760
|
+
public onCollapsedChange(model: GroupModel, collapsed: boolean): void {}
|
|
761
|
+
}
|
|
@@ -61,13 +61,13 @@ export class AccessoryBox extends Lifecycle {
|
|
|
61
61
|
* Mounted structure returned by the node mounting helper.
|
|
62
62
|
* Contains the root element (`view`) and any tag handles (if present).
|
|
63
63
|
*/
|
|
64
|
-
private nodeMounted
|
|
64
|
+
private nodeMounted?: MountViewResult;
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* Root DOM element of the accessory box (hidden by default).
|
|
68
68
|
* Created during {@link init} and removed during {@link destroy}.
|
|
69
69
|
*/
|
|
70
|
-
private node
|
|
70
|
+
private node?: HTMLDivElement;
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
73
|
* Component configuration (texts, behavior, placement).
|
|
@@ -77,25 +77,25 @@ export class AccessoryBox extends Lifecycle {
|
|
|
77
77
|
* - `multiple` (multi-select mode)
|
|
78
78
|
* - `textAccessoryDeselect` (a11y label prefix)
|
|
79
79
|
*/
|
|
80
|
-
private options
|
|
80
|
+
private options?: SelectiveOptions;
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
83
|
* The Select UI mask element used as the positioning reference.
|
|
84
84
|
* Provided by {@link setRoot}.
|
|
85
85
|
*/
|
|
86
|
-
private selectUIMask
|
|
86
|
+
private selectUIMask?: HTMLDivElement;
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
89
|
* Parent container that hosts both the Select UI mask and the accessory box.
|
|
90
90
|
* Computed from `selectUIMask.parentElement`.
|
|
91
91
|
*/
|
|
92
|
-
private parentMask
|
|
92
|
+
private parentMask?: HTMLDivElement;
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
95
|
* ModelManager used to run selection pipelines and coordinate state updates.
|
|
96
96
|
* This component does not own selection state; it delegates to the model layer.
|
|
97
97
|
*/
|
|
98
|
-
private modelManager
|
|
98
|
+
private modelManager?: ModelManager<MixedItem, MixedAdapter>;
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
101
|
* Current selected option models rendered as chips.
|
|
@@ -106,9 +106,9 @@ export class AccessoryBox extends Lifecycle {
|
|
|
106
106
|
/**
|
|
107
107
|
* Creates an AccessoryBox and optionally initializes it with configuration.
|
|
108
108
|
*
|
|
109
|
-
* @param {SelectiveOptions
|
|
109
|
+
* @param {SelectiveOptions} [options=null] - Configuration controlling placement/visibility and texts.
|
|
110
110
|
*/
|
|
111
|
-
public constructor(options
|
|
111
|
+
public constructor(options?: SelectiveOptions) {
|
|
112
112
|
super();
|
|
113
113
|
if (options) this.initialize(options);
|
|
114
114
|
}
|
|
@@ -211,7 +211,8 @@ export class AccessoryBox extends Lifecycle {
|
|
|
211
211
|
!this.node ||
|
|
212
212
|
!this.selectUIMask ||
|
|
213
213
|
!this.options
|
|
214
|
-
)
|
|
214
|
+
)
|
|
215
|
+
return;
|
|
215
216
|
|
|
216
217
|
const ref =
|
|
217
218
|
this.options.accessoryStyle === "top"
|
|
@@ -281,7 +282,9 @@ export class AccessoryBox extends Lifecycle {
|
|
|
281
282
|
title: `${this.options!.textAccessoryDeselect}${modelData.textContent}`,
|
|
282
283
|
onclick: async (evt: MouseEvent) => {
|
|
283
284
|
evt.preventDefault();
|
|
284
|
-
await this.modelManager?.triggerChanging?.(
|
|
285
|
+
await this.modelManager?.triggerChanging?.(
|
|
286
|
+
"select",
|
|
287
|
+
);
|
|
285
288
|
modelData.selected = false;
|
|
286
289
|
},
|
|
287
290
|
},
|
|
@@ -392,4 +395,4 @@ export class AccessoryBox extends Lifecycle {
|
|
|
392
395
|
|
|
393
396
|
super.destroy();
|
|
394
397
|
}
|
|
395
|
-
}
|
|
398
|
+
}
|
|
@@ -49,7 +49,7 @@ export class OptionHandle extends Lifecycle {
|
|
|
49
49
|
*
|
|
50
50
|
* @internal
|
|
51
51
|
*/
|
|
52
|
-
private nodeMounted
|
|
52
|
+
private nodeMounted?: MountViewResult;
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Root element of this control.
|
|
@@ -57,7 +57,7 @@ export class OptionHandle extends Lifecycle {
|
|
|
57
57
|
* Created during {@link initialize}. This node is used by {@link show}/{@link hide}
|
|
58
58
|
* and removed during {@link destroy}.
|
|
59
59
|
*/
|
|
60
|
-
public node
|
|
60
|
+
public node?: HTMLDivElement;
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Configuration snapshot used for:
|
|
@@ -68,7 +68,7 @@ export class OptionHandle extends Lifecycle {
|
|
|
68
68
|
*
|
|
69
69
|
* @internal
|
|
70
70
|
*/
|
|
71
|
-
private options
|
|
71
|
+
private options?: SelectiveOptions;
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
* Callback list invoked when the "Select all" control is activated.
|
|
@@ -99,7 +99,7 @@ export class OptionHandle extends Lifecycle {
|
|
|
99
99
|
*
|
|
100
100
|
* @param options - Feature flags and labels for the two actions.
|
|
101
101
|
*/
|
|
102
|
-
public constructor(options
|
|
102
|
+
public constructor(options?: SelectiveOptions) {
|
|
103
103
|
super();
|
|
104
104
|
if (options) this.initialize(options);
|
|
105
105
|
}
|
|
@@ -172,7 +172,10 @@ export class OptionHandle extends Lifecycle {
|
|
|
172
172
|
*/
|
|
173
173
|
private available(): boolean {
|
|
174
174
|
if (!this.options) return false;
|
|
175
|
-
return
|
|
175
|
+
return (
|
|
176
|
+
Libs.string2Boolean(this.options.multiple) &&
|
|
177
|
+
Libs.string2Boolean(this.options.selectall)
|
|
178
|
+
);
|
|
176
179
|
}
|
|
177
180
|
|
|
178
181
|
/**
|
|
@@ -231,7 +234,7 @@ export class OptionHandle extends Lifecycle {
|
|
|
231
234
|
*
|
|
232
235
|
* @param action - Callback invoked on activation; ignored when not a function.
|
|
233
236
|
*/
|
|
234
|
-
public onSelectAll(action
|
|
237
|
+
public onSelectAll(action?: (...args: unknown[]) => unknown): void {
|
|
235
238
|
if (typeof action === "function") {
|
|
236
239
|
this.actionOnSelectAll.push(action);
|
|
237
240
|
}
|
|
@@ -248,7 +251,7 @@ export class OptionHandle extends Lifecycle {
|
|
|
248
251
|
*
|
|
249
252
|
* @param action - Callback invoked on activation; ignored when not a function.
|
|
250
253
|
*/
|
|
251
|
-
public onDeSelectAll(action
|
|
254
|
+
public onDeSelectAll(action?: (...args: unknown[]) => unknown): void {
|
|
252
255
|
if (typeof action === "function") {
|
|
253
256
|
this.actionOnDeSelectAll.push(action);
|
|
254
257
|
}
|
|
@@ -277,8 +280,8 @@ export class OptionHandle extends Lifecycle {
|
|
|
277
280
|
this.options = null;
|
|
278
281
|
this.actionOnSelectAll = null;
|
|
279
282
|
this.actionOnDeSelectAll = null;
|
|
280
|
-
this.node = null
|
|
283
|
+
this.node = null;
|
|
281
284
|
|
|
282
285
|
super.destroy();
|
|
283
286
|
}
|
|
284
|
-
}
|
|
287
|
+
}
|
|
@@ -38,7 +38,7 @@ export class PlaceHolder extends Lifecycle {
|
|
|
38
38
|
* Created during {@link initialize}. Removed from the DOM during {@link destroy}.
|
|
39
39
|
* `null` before initialization and after destruction.
|
|
40
40
|
*/
|
|
41
|
-
public node
|
|
41
|
+
public node?: HTMLElement;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Configuration snapshot used to render and optionally persist placeholder content.
|
|
@@ -51,7 +51,7 @@ export class PlaceHolder extends Lifecycle {
|
|
|
51
51
|
*
|
|
52
52
|
* @internal
|
|
53
53
|
*/
|
|
54
|
-
private options
|
|
54
|
+
private options?: SelectiveOptions;
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Creates a new {@link PlaceHolder}.
|
|
@@ -62,7 +62,7 @@ export class PlaceHolder extends Lifecycle {
|
|
|
62
62
|
*
|
|
63
63
|
* @param options - Select UI options containing placeholder content and rendering flags.
|
|
64
64
|
*/
|
|
65
|
-
constructor(options
|
|
65
|
+
constructor(options?: SelectiveOptions) {
|
|
66
66
|
super();
|
|
67
67
|
if (options) this.initialize(options);
|
|
68
68
|
}
|
|
@@ -144,11 +144,11 @@ export class PlaceHolder extends Lifecycle {
|
|
|
144
144
|
if (this.is(LifecycleState.DESTROYED)) {
|
|
145
145
|
return;
|
|
146
146
|
}
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
this.node?.remove();
|
|
149
149
|
this.node = null;
|
|
150
150
|
this.options = null;
|
|
151
151
|
|
|
152
152
|
super.destroy();
|
|
153
153
|
}
|
|
154
|
-
}
|
|
154
|
+
}
|