selective-ui 1.2.0 → 1.2.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.css +3 -1
- package/dist/selective-ui.css.map +1 -1
- package/dist/selective-ui.esm.js +487 -436
- 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 +488 -437
- package/dist/selective-ui.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/css/components/popup.css +3 -1
- package/src/ts/adapter/mixed-adapter.ts +56 -50
- package/src/ts/components/accessorybox.ts +49 -21
- package/src/ts/components/directive.ts +3 -3
- package/src/ts/components/empty-state.ts +7 -7
- package/src/ts/components/loading-state.ts +7 -7
- package/src/ts/components/option-handle.ts +17 -17
- package/src/ts/components/placeholder.ts +12 -12
- package/src/ts/components/popup.ts +93 -108
- package/src/ts/components/searchbox.ts +14 -14
- package/src/ts/components/selectbox.ts +41 -31
- package/src/ts/core/base/adapter.ts +12 -12
- package/src/ts/core/base/model.ts +12 -13
- package/src/ts/core/base/recyclerview.ts +7 -7
- package/src/ts/core/base/view.ts +6 -6
- package/src/ts/core/base/virtual-recyclerview.ts +51 -50
- package/src/ts/core/model-manager.ts +53 -53
- package/src/ts/core/search-controller.ts +70 -73
- package/src/ts/models/group-model.ts +21 -21
- package/src/ts/models/option-model.ts +50 -31
- package/src/ts/services/dataset-observer.ts +17 -17
- package/src/ts/services/ea-observer.ts +21 -21
- package/src/ts/services/effector.ts +30 -30
- package/src/ts/services/refresher.ts +1 -1
- package/src/ts/services/resize-observer.ts +29 -29
- package/src/ts/services/select-observer.ts +29 -29
- package/src/ts/types/components/popup.type.ts +15 -0
- package/src/ts/types/utils/istorage.type.ts +1 -1
- package/src/ts/utils/callback-scheduler.ts +4 -4
- package/src/ts/utils/ievents.ts +4 -4
- package/src/ts/utils/istorage.ts +5 -5
- package/src/ts/utils/libs.ts +38 -29
- package/src/ts/utils/selective.ts +11 -11
- package/src/ts/views/group-view.ts +7 -7
- package/src/ts/views/option-view.ts +51 -51
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
display: none;
|
|
11
11
|
position: fixed;
|
|
12
12
|
z-index: 9999;
|
|
13
|
-
overflow:
|
|
13
|
+
overflow: hidden;
|
|
14
14
|
flex-direction: column;
|
|
15
15
|
|
|
16
16
|
height: 0px;
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
border-style: var(--seui-popup-border-style);
|
|
23
23
|
border-width: var(--seui-popup-border-width);
|
|
24
24
|
border-radius: var(--seui-popup-border-radius);
|
|
25
|
+
|
|
26
|
+
transition: none;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
.selective-ui-options-container {
|
|
@@ -6,31 +6,47 @@ 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>}
|
|
12
13
|
*/
|
|
13
14
|
export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
14
|
-
isMultiple = false;
|
|
15
|
+
public isMultiple = false;
|
|
15
16
|
|
|
16
|
-
private
|
|
17
|
-
private
|
|
17
|
+
private visibilityChangedCallbacks: Array<(stats: VisibilityStats) => void> = [];
|
|
18
|
+
private currentHighlightIndex = -1;
|
|
18
19
|
|
|
19
|
-
private
|
|
20
|
+
private selectedItemSingle: OptionModel | null = null;
|
|
20
21
|
|
|
21
|
-
groups: GroupModel[] = [];
|
|
22
|
+
public groups: GroupModel[] = [];
|
|
22
23
|
|
|
23
|
-
flatOptions: OptionModel[] = [];
|
|
24
|
+
public flatOptions: OptionModel[] = [];
|
|
24
25
|
|
|
25
|
-
constructor(items: MixedItem[] = []) {
|
|
26
|
+
public constructor(items: MixedItem[] = []) {
|
|
26
27
|
super(items);
|
|
27
|
-
this.
|
|
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
|
/**
|
|
31
47
|
* Build flat list of all options for navigation
|
|
32
48
|
*/
|
|
33
|
-
private
|
|
49
|
+
private buildFlatStructure(): void {
|
|
34
50
|
this.flatOptions = [];
|
|
35
51
|
this.groups = [];
|
|
36
52
|
|
|
@@ -68,9 +84,9 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
68
84
|
item.position = position;
|
|
69
85
|
|
|
70
86
|
if (item instanceof GroupModel) {
|
|
71
|
-
this.
|
|
87
|
+
this.handleGroupView(item, viewer as GroupView, position);
|
|
72
88
|
} else if (item instanceof OptionModel) {
|
|
73
|
-
this.
|
|
89
|
+
this.handleOptionView(item, viewer as OptionView, position);
|
|
74
90
|
}
|
|
75
91
|
|
|
76
92
|
item.isInit = true;
|
|
@@ -84,7 +100,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
84
100
|
* @param {GroupView} groupView - The view instance that renders the group in the UI.
|
|
85
101
|
* @param {number} position - The position (index) of the group within a list.
|
|
86
102
|
*/
|
|
87
|
-
private
|
|
103
|
+
private handleGroupView(groupModel: GroupModel, groupView: GroupView, position: number): void {
|
|
88
104
|
super.onViewHolder(groupModel, groupView, position);
|
|
89
105
|
groupModel.view = groupView;
|
|
90
106
|
|
|
@@ -118,7 +134,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
118
134
|
optionViewer = new OptionView(itemsContainer);
|
|
119
135
|
}
|
|
120
136
|
|
|
121
|
-
this.
|
|
137
|
+
this.handleOptionView(optionModel, optionViewer, idx);
|
|
122
138
|
optionModel.isInit = true;
|
|
123
139
|
});
|
|
124
140
|
|
|
@@ -134,7 +150,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
134
150
|
* @param {OptionView} optionViewer - The view instance that renders the option in the UI.
|
|
135
151
|
* @param {number} position - The index of this option within its group's item list.
|
|
136
152
|
*/
|
|
137
|
-
private
|
|
153
|
+
private handleOptionView(optionModel: OptionModel, optionViewer: OptionView, position: number): void {
|
|
138
154
|
optionViewer.isMultiple = this.isMultiple;
|
|
139
155
|
optionViewer.hasImage = optionModel.hasImage;
|
|
140
156
|
|
|
@@ -178,7 +194,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
178
194
|
} else if (optionModel.selected !== true) {
|
|
179
195
|
this.changingProp("select");
|
|
180
196
|
setTimeout(() => {
|
|
181
|
-
if (this.
|
|
197
|
+
if (this.selectedItemSingle) this.selectedItemSingle.selected = false;
|
|
182
198
|
optionModel.selected = true;
|
|
183
199
|
}, 5);
|
|
184
200
|
}
|
|
@@ -196,18 +212,18 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
196
212
|
});
|
|
197
213
|
|
|
198
214
|
optionModel.onInternalSelected((_evtToken: IEventCallback, _el: OptionModel, selected: boolean) => {
|
|
199
|
-
if (selected) this.
|
|
215
|
+
if (selected) this.selectedItemSingle = optionModel;
|
|
200
216
|
this.changeProp("selected_internal");
|
|
201
217
|
});
|
|
202
218
|
|
|
203
219
|
optionModel.onVisibilityChanged((_evtToken: IEventCallback, model: OptionModel, _visible: boolean) => {
|
|
204
220
|
model.group?.updateVisibility();
|
|
205
|
-
this.
|
|
221
|
+
this.notifyVisibilityChanged();
|
|
206
222
|
});
|
|
207
223
|
}
|
|
208
224
|
|
|
209
225
|
if (optionModel.selected) {
|
|
210
|
-
this.
|
|
226
|
+
this.selectedItemSingle = optionModel;
|
|
211
227
|
optionModel.selectedNonTrigger = true;
|
|
212
228
|
}
|
|
213
229
|
}
|
|
@@ -220,7 +236,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
220
236
|
override setItems(items: MixedItem[]): void {
|
|
221
237
|
this.changingProp("items", items);
|
|
222
238
|
this.items = items;
|
|
223
|
-
this.
|
|
239
|
+
this.buildFlatStructure();
|
|
224
240
|
this.changeProp("items", items);
|
|
225
241
|
}
|
|
226
242
|
|
|
@@ -241,7 +257,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
241
257
|
*/
|
|
242
258
|
override updateData(items: MixedItem[]): void {
|
|
243
259
|
this.items = items;
|
|
244
|
-
this.
|
|
260
|
+
this.buildFlatStructure();
|
|
245
261
|
}
|
|
246
262
|
|
|
247
263
|
/**
|
|
@@ -249,7 +265,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
249
265
|
*
|
|
250
266
|
* @returns {OptionModel[]} - An array of selected option items from the flat list.
|
|
251
267
|
*/
|
|
252
|
-
getSelectedItems(): OptionModel[] {
|
|
268
|
+
public getSelectedItems(): OptionModel[] {
|
|
253
269
|
return this.flatOptions.filter((item) => item.selected);
|
|
254
270
|
}
|
|
255
271
|
|
|
@@ -258,7 +274,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
258
274
|
*
|
|
259
275
|
* @returns {OptionModel|undefined} - The first selected option or undefined if none are selected.
|
|
260
276
|
*/
|
|
261
|
-
getSelectedItem(): OptionModel | undefined {
|
|
277
|
+
public getSelectedItem(): OptionModel | undefined {
|
|
262
278
|
return this.flatOptions.find((item) => item.selected);
|
|
263
279
|
}
|
|
264
280
|
|
|
@@ -267,7 +283,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
267
283
|
*
|
|
268
284
|
* @param {boolean} isChecked - If true, select all; if false, deselect all.
|
|
269
285
|
*/
|
|
270
|
-
checkAll(isChecked: boolean): void {
|
|
286
|
+
public checkAll(isChecked: boolean): void {
|
|
271
287
|
if (!this.isMultiple) return;
|
|
272
288
|
this.flatOptions.forEach((item) => {
|
|
273
289
|
item.selected = isChecked;
|
|
@@ -280,32 +296,22 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
280
296
|
* @param {(stats: {visibleCount:number,totalCount:number,hasVisible:boolean,isEmpty:boolean}) => void} callback
|
|
281
297
|
* - Function to invoke when visibility stats change.
|
|
282
298
|
*/
|
|
283
|
-
onVisibilityChanged(callback: (stats: VisibilityStats) => void): void {
|
|
284
|
-
this.
|
|
299
|
+
public onVisibilityChanged(callback: (stats: VisibilityStats) => void): void {
|
|
300
|
+
this.visibilityChangedCallbacks.push(callback);
|
|
285
301
|
}
|
|
286
302
|
|
|
287
303
|
/**
|
|
288
304
|
* Notifies all registered visibility-change callbacks with up-to-date statistics.
|
|
289
305
|
* Computes visible and total counts, then emits aggregated state.
|
|
290
306
|
*/
|
|
291
|
-
private
|
|
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
|
-
});
|
|
307
|
+
private notifyVisibilityChanged(): void {
|
|
308
|
+
Libs.callbackScheduler.run(`sche_vis_${this.adapterKey}`);
|
|
303
309
|
}
|
|
304
310
|
|
|
305
311
|
/**
|
|
306
312
|
* Computes and returns current visibility statistics for options.
|
|
307
313
|
*/
|
|
308
|
-
getVisibilityStats(): VisibilityStats {
|
|
314
|
+
public getVisibilityStats(): VisibilityStats {
|
|
309
315
|
const visibleCount = this.flatOptions.filter((item) => item.visible).length;
|
|
310
316
|
const totalCount = this.flatOptions.length;
|
|
311
317
|
|
|
@@ -320,7 +326,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
320
326
|
/**
|
|
321
327
|
* Resets the highlight to the first visible option (index 0).
|
|
322
328
|
*/
|
|
323
|
-
resetHighlight(): void {
|
|
329
|
+
public resetHighlight(): void {
|
|
324
330
|
this.setHighlight(0);
|
|
325
331
|
}
|
|
326
332
|
|
|
@@ -330,12 +336,12 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
330
336
|
* @param {number} direction - Increment (+1) or decrement (-1) of the current visible index.
|
|
331
337
|
* @param {boolean} [isScrollToView=true] - Whether to scroll the highlighted item into view.
|
|
332
338
|
*/
|
|
333
|
-
navigate(direction: number, isScrollToView: boolean = true): void {
|
|
339
|
+
public navigate(direction: number, isScrollToView: boolean = true): void {
|
|
334
340
|
const visibleOptions = this.flatOptions.filter((opt) => opt.visible);
|
|
335
341
|
if (visibleOptions.length === 0) return;
|
|
336
342
|
|
|
337
343
|
let currentVisibleIndex = visibleOptions.findIndex(
|
|
338
|
-
(opt) => opt === this.flatOptions[this.
|
|
344
|
+
(opt) => opt === this.flatOptions[this.currentHighlightIndex]
|
|
339
345
|
);
|
|
340
346
|
if (currentVisibleIndex === -1) currentVisibleIndex = -1;
|
|
341
347
|
|
|
@@ -353,9 +359,9 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
353
359
|
* Triggers a click on the currently highlighted and visible option to select it.
|
|
354
360
|
* No-op if nothing is highlighted or the highlighted item is not visible.
|
|
355
361
|
*/
|
|
356
|
-
selectHighlighted(): void {
|
|
357
|
-
if (this.
|
|
358
|
-
const item = this.flatOptions[this.
|
|
362
|
+
public selectHighlighted(): void {
|
|
363
|
+
if (this.currentHighlightIndex > -1 && this.flatOptions[this.currentHighlightIndex]) {
|
|
364
|
+
const item = this.flatOptions[this.currentHighlightIndex];
|
|
359
365
|
if (item.visible) {
|
|
360
366
|
const viewEl = item.view?.getView?.();
|
|
361
367
|
if (viewEl) viewEl.click();
|
|
@@ -370,7 +376,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
370
376
|
* @param {number|OptionModel} target - Flat index or the specific OptionModel to highlight.
|
|
371
377
|
* @param {boolean} [isScrollToView=true] - Whether to scroll the highlighted item into view.
|
|
372
378
|
*/
|
|
373
|
-
setHighlight(target: number | OptionModel, isScrollToView: boolean = true): void {
|
|
379
|
+
public setHighlight(target: number | OptionModel, isScrollToView: boolean = true): void {
|
|
374
380
|
let index = 0;
|
|
375
381
|
|
|
376
382
|
if (typeof target === "number") {
|
|
@@ -382,8 +388,8 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
382
388
|
index = 0;
|
|
383
389
|
}
|
|
384
390
|
|
|
385
|
-
if (this.
|
|
386
|
-
this.flatOptions[this.
|
|
391
|
+
if (this.currentHighlightIndex > -1 && this.flatOptions[this.currentHighlightIndex]) {
|
|
392
|
+
this.flatOptions[this.currentHighlightIndex].highlighted = false;
|
|
387
393
|
}
|
|
388
394
|
|
|
389
395
|
for (let i = index; i < this.flatOptions.length; i++) {
|
|
@@ -391,7 +397,7 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
391
397
|
if (!item?.visible) continue;
|
|
392
398
|
|
|
393
399
|
item.highlighted = true;
|
|
394
|
-
this.
|
|
400
|
+
this.currentHighlightIndex = i;
|
|
395
401
|
|
|
396
402
|
if (isScrollToView) {
|
|
397
403
|
const el = item.view?.getView?.();
|
|
@@ -411,11 +417,11 @@ export class MixedAdapter extends Adapter<MixedItem, GroupView | OptionView> {
|
|
|
411
417
|
* Hook invoked whenever the highlight changes.
|
|
412
418
|
* Override to handle UI side effects (e.g., ARIA announcement, focus sync).
|
|
413
419
|
*/
|
|
414
|
-
onHighlightChange(index: number, id?: string): void { }
|
|
420
|
+
public onHighlightChange(index: number, id?: string): void { }
|
|
415
421
|
|
|
416
422
|
/**
|
|
417
423
|
* Hook invoked when a group's collapsed state changes.
|
|
418
424
|
* Override to handle side effects like analytics or layout adjustments.
|
|
419
425
|
*/
|
|
420
|
-
onCollapsedChange(model: GroupModel, collapsed: boolean): void { }
|
|
426
|
+
public onCollapsedChange(model: GroupModel, collapsed: boolean): void { }
|
|
421
427
|
}
|
|
@@ -11,24 +11,26 @@ import { Libs } from "../utils/libs";
|
|
|
11
11
|
* @class
|
|
12
12
|
*/
|
|
13
13
|
export class AccessoryBox {
|
|
14
|
-
nodeMounted: MountViewResult<any> | null = null;
|
|
14
|
+
private nodeMounted: MountViewResult<any> | null = null;
|
|
15
15
|
|
|
16
|
-
node: HTMLDivElement | null = null;
|
|
16
|
+
private node: HTMLDivElement | null = null;
|
|
17
17
|
|
|
18
|
-
options: SelectiveOptions | null = null;
|
|
18
|
+
private options: SelectiveOptions | null = null;
|
|
19
19
|
|
|
20
|
-
selectUIMask: HTMLDivElement | null = null;
|
|
20
|
+
private selectUIMask: HTMLDivElement | null = null;
|
|
21
21
|
|
|
22
|
-
parentMask: HTMLDivElement | null = null;
|
|
22
|
+
private parentMask: HTMLDivElement | null = null;
|
|
23
23
|
|
|
24
|
-
modelManager: ModelManager<MixedItem, MixedAdapter> | null = null;
|
|
24
|
+
private modelManager: ModelManager<MixedItem, MixedAdapter> | null = null;
|
|
25
|
+
|
|
26
|
+
private modelDatas: OptionModel[] = [];
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* Initializes the accessory box with optional configuration and immediately calls init() if provided.
|
|
28
30
|
*
|
|
29
31
|
* @param {object|null} options - Configuration options for the accessory box (e.g., layout and behavior).
|
|
30
32
|
*/
|
|
31
|
-
constructor(options: SelectiveOptions | null = null) {
|
|
33
|
+
public constructor(options: SelectiveOptions | null = null) {
|
|
32
34
|
if (options) this.init(options);
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -38,7 +40,7 @@ export class AccessoryBox {
|
|
|
38
40
|
*
|
|
39
41
|
* @param {SelectiveOptions} options - Configuration object for the accessory box.
|
|
40
42
|
*/
|
|
41
|
-
init(options: SelectiveOptions): void {
|
|
43
|
+
private init(options: SelectiveOptions): void {
|
|
42
44
|
this.nodeMounted = Libs.mountNode({
|
|
43
45
|
AccessoryBox: {
|
|
44
46
|
tag: {
|
|
@@ -60,7 +62,7 @@ export class AccessoryBox {
|
|
|
60
62
|
*
|
|
61
63
|
* @param {HTMLDivElement} selectUIMask - The overlay/mask element of the main Select UI.
|
|
62
64
|
*/
|
|
63
|
-
setRoot(selectUIMask: HTMLDivElement): void {
|
|
65
|
+
public setRoot(selectUIMask: HTMLDivElement): void {
|
|
64
66
|
this.selectUIMask = selectUIMask;
|
|
65
67
|
this.parentMask = selectUIMask.parentElement as HTMLDivElement | null;
|
|
66
68
|
|
|
@@ -71,8 +73,14 @@ export class AccessoryBox {
|
|
|
71
73
|
* Inserts the accessory box before or after the Select UI mask depending on the configured accessoryStyle.
|
|
72
74
|
* Keeps the accessory box aligned relative to the parent mask.
|
|
73
75
|
*/
|
|
74
|
-
refreshLocation(): void {
|
|
75
|
-
if (
|
|
76
|
+
public refreshLocation(): void {
|
|
77
|
+
if (
|
|
78
|
+
!this.parentMask ||
|
|
79
|
+
!this.node ||
|
|
80
|
+
!this.selectUIMask ||
|
|
81
|
+
!this.options
|
|
82
|
+
)
|
|
83
|
+
return;
|
|
76
84
|
|
|
77
85
|
const ref =
|
|
78
86
|
this.options.accessoryStyle === "top"
|
|
@@ -87,7 +95,9 @@ export class AccessoryBox {
|
|
|
87
95
|
*
|
|
88
96
|
* @param {ModelManager} modelManager - The model manager controlling option state.
|
|
89
97
|
*/
|
|
90
|
-
setModelManager(
|
|
98
|
+
public setModelManager(
|
|
99
|
+
modelManager: ModelManager<MixedItem, MixedAdapter> | null,
|
|
100
|
+
): void {
|
|
91
101
|
this.modelManager = modelManager;
|
|
92
102
|
}
|
|
93
103
|
|
|
@@ -97,14 +107,11 @@ export class AccessoryBox {
|
|
|
97
107
|
*
|
|
98
108
|
* @param {OptionModel[]} modelDatas - List of option models to render as accessory items.
|
|
99
109
|
*/
|
|
100
|
-
setModelData(modelDatas: OptionModel[]): void {
|
|
110
|
+
public setModelData(modelDatas: OptionModel[]): void {
|
|
101
111
|
if (!this.node || !this.options) return;
|
|
102
|
-
|
|
103
112
|
this.node.replaceChildren();
|
|
104
113
|
|
|
105
114
|
if (modelDatas.length > 0 && this.options.multiple) {
|
|
106
|
-
this.node.classList.remove("hide");
|
|
107
|
-
|
|
108
115
|
modelDatas.forEach((modelData) => {
|
|
109
116
|
Libs.mountNode(
|
|
110
117
|
{
|
|
@@ -120,7 +127,9 @@ export class AccessoryBox {
|
|
|
120
127
|
title: `${this.options!.textAccessoryDeselect}${modelData.textContent}`,
|
|
121
128
|
onclick: (evt: MouseEvent) => {
|
|
122
129
|
evt.preventDefault();
|
|
123
|
-
this.modelManager?.triggerChanging?.(
|
|
130
|
+
this.modelManager?.triggerChanging?.(
|
|
131
|
+
"select",
|
|
132
|
+
);
|
|
124
133
|
setTimeout(() => {
|
|
125
134
|
modelData.selected = false;
|
|
126
135
|
}, 10);
|
|
@@ -137,13 +146,32 @@ export class AccessoryBox {
|
|
|
137
146
|
},
|
|
138
147
|
},
|
|
139
148
|
},
|
|
140
|
-
this.node
|
|
149
|
+
this.node,
|
|
141
150
|
);
|
|
142
151
|
});
|
|
143
|
-
}
|
|
144
|
-
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
modelDatas = [];
|
|
145
155
|
}
|
|
146
156
|
|
|
157
|
+
this.modelDatas = modelDatas;
|
|
158
|
+
this.refreshDisplay();
|
|
147
159
|
iEvents.trigger(window, "resize");
|
|
148
160
|
}
|
|
149
|
-
|
|
161
|
+
|
|
162
|
+
private refreshDisplay(): void {
|
|
163
|
+
if (this.options?.accessoryVisible && this.modelDatas.length > 0 && this.options.multiple) {
|
|
164
|
+
this.show();
|
|
165
|
+
} else {
|
|
166
|
+
this.hide();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private show(): void {
|
|
171
|
+
this.node.classList.remove("hide");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private hide(): void {
|
|
175
|
+
this.node.classList.add("hide");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -8,14 +8,14 @@ export class Directive {
|
|
|
8
8
|
node: HTMLElement;
|
|
9
9
|
|
|
10
10
|
constructor() {
|
|
11
|
-
this.node = this.
|
|
11
|
+
this.node = this.init();
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Represents a directive button element used to toggle dropdown state.
|
|
16
16
|
* Initializes a clickable node with appropriate ARIA attributes for accessibility.
|
|
17
17
|
*/
|
|
18
|
-
private
|
|
18
|
+
private init(): HTMLElement {
|
|
19
19
|
// Libs.nodeCreator returns Element, but this node is always an HTMLElement in practice.
|
|
20
20
|
return Libs.nodeCreator({
|
|
21
21
|
node: "div",
|
|
@@ -30,7 +30,7 @@ export class Directive {
|
|
|
30
30
|
*
|
|
31
31
|
* @param {boolean} value - If true, adds the "drop-down" class; otherwise removes it.
|
|
32
32
|
*/
|
|
33
|
-
setDropdown(value: boolean): void {
|
|
33
|
+
public setDropdown(value: boolean): void {
|
|
34
34
|
this.node.classList.toggle("drop-down", !!value);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -6,15 +6,15 @@ import { Libs } from "../utils/libs";
|
|
|
6
6
|
* @class
|
|
7
7
|
*/
|
|
8
8
|
export class EmptyState {
|
|
9
|
-
node: HTMLDivElement | null = null;
|
|
9
|
+
public node: HTMLDivElement | null = null;
|
|
10
10
|
|
|
11
|
-
options: SelectiveOptions | null = null;
|
|
11
|
+
public options: SelectiveOptions | null = null;
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Represents an empty state component that displays a message when no data or search results are available.
|
|
15
15
|
* Provides methods to show/hide the state and check its visibility.
|
|
16
16
|
*/
|
|
17
|
-
constructor(options: SelectiveOptions | null = null) {
|
|
17
|
+
public constructor(options: SelectiveOptions | null = null) {
|
|
18
18
|
if (options) this.init(options);
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -23,7 +23,7 @@ export class EmptyState {
|
|
|
23
23
|
*
|
|
24
24
|
* @param {object} options - Configuration object containing text for "no data" and "not found" states.
|
|
25
25
|
*/
|
|
26
|
-
init(options: SelectiveOptions): void {
|
|
26
|
+
private init(options: SelectiveOptions): void {
|
|
27
27
|
this.options = options;
|
|
28
28
|
|
|
29
29
|
this.node = Libs.nodeCreator({
|
|
@@ -40,7 +40,7 @@ export class EmptyState {
|
|
|
40
40
|
* @param {"notfound" | "nodata"} [type="nodata"] - Determines which message to show:
|
|
41
41
|
* "notfound" for search results not found, "nodata" for no available data.
|
|
42
42
|
*/
|
|
43
|
-
show(type: EmptyStateType = "nodata"): void {
|
|
43
|
+
public show(type: EmptyStateType = "nodata"): void {
|
|
44
44
|
if (!this.node || !this.options) return;
|
|
45
45
|
|
|
46
46
|
const text = type === "notfound" ? this.options.textNotFound : this.options.textNoData;
|
|
@@ -52,7 +52,7 @@ export class EmptyState {
|
|
|
52
52
|
/**
|
|
53
53
|
* Hides the empty state element by adding the "hide" class.
|
|
54
54
|
*/
|
|
55
|
-
hide(): void {
|
|
55
|
+
public hide(): void {
|
|
56
56
|
if (!this.node) return;
|
|
57
57
|
this.node.classList.add("hide");
|
|
58
58
|
}
|
|
@@ -62,7 +62,7 @@ export class EmptyState {
|
|
|
62
62
|
*
|
|
63
63
|
* @returns {boolean} - True if visible, false otherwise.
|
|
64
64
|
*/
|
|
65
|
-
get isVisible(): boolean {
|
|
65
|
+
public get isVisible(): boolean {
|
|
66
66
|
return !!this.node && !this.node.classList.contains("hide");
|
|
67
67
|
}
|
|
68
68
|
}
|
|
@@ -5,15 +5,15 @@ import { Libs } from "../utils/libs";
|
|
|
5
5
|
* @class
|
|
6
6
|
*/
|
|
7
7
|
export class LoadingState {
|
|
8
|
-
node: HTMLDivElement | null = null;
|
|
8
|
+
public node: HTMLDivElement | null = null;
|
|
9
9
|
|
|
10
|
-
options: SelectiveOptions | null = null;
|
|
10
|
+
public options: SelectiveOptions | null = null;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Represents a loading state component that displays a loading message during data fetch or processing.
|
|
14
14
|
* Provides methods to show/hide the state and check its visibility.
|
|
15
15
|
*/
|
|
16
|
-
constructor(options: SelectiveOptions | null = null) {
|
|
16
|
+
public constructor(options: SelectiveOptions | null = null) {
|
|
17
17
|
if (options) this.init(options);
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -22,7 +22,7 @@ export class LoadingState {
|
|
|
22
22
|
*
|
|
23
23
|
* @param {object} options - Configuration object containing text for the loading message.
|
|
24
24
|
*/
|
|
25
|
-
init(options: SelectiveOptions): void {
|
|
25
|
+
private init(options: SelectiveOptions): void {
|
|
26
26
|
this.options = options;
|
|
27
27
|
|
|
28
28
|
this.node = Libs.nodeCreator({
|
|
@@ -39,7 +39,7 @@ export class LoadingState {
|
|
|
39
39
|
*
|
|
40
40
|
* @param {boolean} hasItems - If true, applies a "small" style for compact display.
|
|
41
41
|
*/
|
|
42
|
-
show(hasItems: boolean): void {
|
|
42
|
+
public show(hasItems: boolean): void {
|
|
43
43
|
if (!this.node || !this.options) return;
|
|
44
44
|
|
|
45
45
|
this.node.textContent = this.options.textLoading;
|
|
@@ -50,7 +50,7 @@ export class LoadingState {
|
|
|
50
50
|
/**
|
|
51
51
|
* Hides the loading state element by adding the "hide" class.
|
|
52
52
|
*/
|
|
53
|
-
hide(): void {
|
|
53
|
+
public hide(): void {
|
|
54
54
|
if (!this.node) return;
|
|
55
55
|
this.node.classList.add("hide");
|
|
56
56
|
}
|
|
@@ -60,7 +60,7 @@ export class LoadingState {
|
|
|
60
60
|
*
|
|
61
61
|
* @returns {boolean} - True if visible, false otherwise.
|
|
62
62
|
*/
|
|
63
|
-
get isVisible(): boolean {
|
|
63
|
+
public get isVisible(): boolean {
|
|
64
64
|
return !!this.node && !this.node.classList.contains("hide");
|
|
65
65
|
}
|
|
66
66
|
}
|