selective-ui 1.4.0 → 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 +252 -553
- 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 +254 -555
- 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 +242 -81
- 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/src/ts/utils/libs.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { SelectiveOptions } from "../types/utils/selective.type";
|
|
|
8
8
|
* @class
|
|
9
9
|
*/
|
|
10
10
|
export class Libs {
|
|
11
|
-
private static _iStorage
|
|
11
|
+
private static _iStorage?: iStorage;
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Retrieves the shared iStorage instance (lazy-initialized singleton).
|
|
@@ -52,10 +52,13 @@ export class Libs {
|
|
|
52
52
|
*/
|
|
53
53
|
public static randomString(length: number = 6): string {
|
|
54
54
|
let result = "";
|
|
55
|
-
const characters =
|
|
55
|
+
const characters =
|
|
56
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
56
57
|
const charactersLength = characters.length;
|
|
57
58
|
for (let i = 0; i < length; i++) {
|
|
58
|
-
result += characters.charAt(
|
|
59
|
+
result += characters.charAt(
|
|
60
|
+
Math.floor(Math.random() * charactersLength),
|
|
61
|
+
);
|
|
59
62
|
}
|
|
60
63
|
return result;
|
|
61
64
|
}
|
|
@@ -75,12 +78,13 @@ export class Libs {
|
|
|
75
78
|
| HTMLElement
|
|
76
79
|
| ArrayLike<HTMLElement>
|
|
77
80
|
| null
|
|
78
|
-
| undefined
|
|
81
|
+
| undefined,
|
|
79
82
|
): T {
|
|
80
83
|
if (!queryCommon) return [] as T;
|
|
81
84
|
|
|
82
85
|
if (typeof queryCommon === "string") {
|
|
83
|
-
const nodeList =
|
|
86
|
+
const nodeList =
|
|
87
|
+
document.querySelectorAll<HTMLElement>(queryCommon);
|
|
84
88
|
return Array.from(nodeList) as T;
|
|
85
89
|
}
|
|
86
90
|
|
|
@@ -102,9 +106,15 @@ export class Libs {
|
|
|
102
106
|
* @param {NodeSpec} data - Specification describing the element to create.
|
|
103
107
|
* @returns {HTMLElement} - The created element.
|
|
104
108
|
*/
|
|
105
|
-
public static nodeCreator<T extends HTMLElement>(
|
|
109
|
+
public static nodeCreator<T extends HTMLElement>(
|
|
110
|
+
data: Partial<NodeSpec> = {},
|
|
111
|
+
): T {
|
|
106
112
|
const nodeName = (data.node ?? "div") as string;
|
|
107
|
-
return this.nodeCloner<T>(
|
|
113
|
+
return this.nodeCloner<T>(
|
|
114
|
+
document.createElement(nodeName),
|
|
115
|
+
data as NodeSpec,
|
|
116
|
+
true,
|
|
117
|
+
);
|
|
108
118
|
}
|
|
109
119
|
|
|
110
120
|
/**
|
|
@@ -116,10 +126,16 @@ export class Libs {
|
|
|
116
126
|
* @param {boolean} systemNodeCreate - If true, do not clone; use original node.
|
|
117
127
|
* @returns {HTMLElement} - The processed element.
|
|
118
128
|
*/
|
|
119
|
-
public static nodeCloner<T extends HTMLElement>(
|
|
129
|
+
public static nodeCloner<T extends HTMLElement>(
|
|
130
|
+
node: HTMLElement = document.documentElement,
|
|
131
|
+
_nodeOption?: NodeSpec,
|
|
132
|
+
systemNodeCreate = false,
|
|
133
|
+
): T {
|
|
120
134
|
const nodeOption: Record<string, unknown> = { ...(_nodeOption ?? {}) };
|
|
121
135
|
|
|
122
|
-
const element_creation: T = systemNodeCreate
|
|
136
|
+
const element_creation: T = systemNodeCreate
|
|
137
|
+
? (node as T)
|
|
138
|
+
: (node.cloneNode(true) as T);
|
|
123
139
|
|
|
124
140
|
const classList = nodeOption.classList;
|
|
125
141
|
if (typeof classList === "string") {
|
|
@@ -142,32 +158,52 @@ export class Libs {
|
|
|
142
158
|
delete nodeOption.role;
|
|
143
159
|
}
|
|
144
160
|
if (nodeOption.ariaLive) {
|
|
145
|
-
element_creation.setAttribute(
|
|
161
|
+
element_creation.setAttribute(
|
|
162
|
+
"aria-live",
|
|
163
|
+
String(nodeOption.ariaLive),
|
|
164
|
+
);
|
|
146
165
|
delete nodeOption.ariaLive;
|
|
147
166
|
}
|
|
148
167
|
if (nodeOption.ariaLabelledby) {
|
|
149
|
-
element_creation.setAttribute(
|
|
168
|
+
element_creation.setAttribute(
|
|
169
|
+
"aria-labelledby",
|
|
170
|
+
String(nodeOption.ariaLabelledby),
|
|
171
|
+
);
|
|
150
172
|
delete nodeOption.ariaLabelledby;
|
|
151
173
|
}
|
|
152
174
|
if (nodeOption.ariaControls) {
|
|
153
|
-
element_creation.setAttribute(
|
|
175
|
+
element_creation.setAttribute(
|
|
176
|
+
"aria-controls",
|
|
177
|
+
String(nodeOption.ariaControls),
|
|
178
|
+
);
|
|
154
179
|
delete nodeOption.ariaControls;
|
|
155
180
|
}
|
|
156
181
|
if (nodeOption.ariaHaspopup) {
|
|
157
|
-
element_creation.setAttribute(
|
|
182
|
+
element_creation.setAttribute(
|
|
183
|
+
"aria-haspopup",
|
|
184
|
+
String(nodeOption.ariaHaspopup),
|
|
185
|
+
);
|
|
158
186
|
delete nodeOption.ariaHaspopup;
|
|
159
187
|
}
|
|
160
188
|
if (nodeOption.ariaMultiselectable) {
|
|
161
|
-
element_creation.setAttribute(
|
|
189
|
+
element_creation.setAttribute(
|
|
190
|
+
"aria-multiselectable",
|
|
191
|
+
String(nodeOption.ariaMultiselectable),
|
|
192
|
+
);
|
|
162
193
|
delete nodeOption.ariaMultiselectable;
|
|
163
194
|
}
|
|
164
195
|
if (nodeOption.ariaAutocomplete) {
|
|
165
|
-
element_creation.setAttribute(
|
|
196
|
+
element_creation.setAttribute(
|
|
197
|
+
"aria-autocomplete",
|
|
198
|
+
String(nodeOption.ariaAutocomplete),
|
|
199
|
+
);
|
|
166
200
|
delete nodeOption.ariaAutocomplete;
|
|
167
201
|
}
|
|
168
202
|
|
|
169
203
|
if (nodeOption.event && typeof nodeOption.event === "object") {
|
|
170
|
-
Object.entries(
|
|
204
|
+
Object.entries(
|
|
205
|
+
nodeOption.event as Record<string, EventListener>,
|
|
206
|
+
).forEach(([key, value]) => {
|
|
171
207
|
element_creation.addEventListener(key, value);
|
|
172
208
|
});
|
|
173
209
|
delete nodeOption.event;
|
|
@@ -198,21 +234,29 @@ export class Libs {
|
|
|
198
234
|
*/
|
|
199
235
|
public static mountNode<TTags extends Record<string, any>>(
|
|
200
236
|
rawObj: Record<string, any>,
|
|
201
|
-
parentE
|
|
237
|
+
parentE?: HTMLElement,
|
|
202
238
|
isPrepend = false,
|
|
203
239
|
isRecusive = false,
|
|
204
|
-
recursiveTemp: any = {}
|
|
240
|
+
recursiveTemp: any = {},
|
|
205
241
|
): TTags {
|
|
206
242
|
let view: HTMLElement | null = null;
|
|
207
243
|
|
|
208
244
|
for (const key in rawObj) {
|
|
209
245
|
const singleObj = rawObj[key];
|
|
210
|
-
const tag: HTMLElement =
|
|
211
|
-
|
|
246
|
+
const tag: HTMLElement = singleObj?.tag?.tagName
|
|
247
|
+
? (singleObj.tag as HTMLElement)
|
|
248
|
+
: (this.nodeCreator(singleObj.tag) as HTMLElement);
|
|
212
249
|
|
|
213
250
|
recursiveTemp[key] = tag;
|
|
214
251
|
|
|
215
|
-
if (singleObj?.child)
|
|
252
|
+
if (singleObj?.child)
|
|
253
|
+
this.mountNode<TTags>(
|
|
254
|
+
singleObj.child,
|
|
255
|
+
tag,
|
|
256
|
+
false,
|
|
257
|
+
false,
|
|
258
|
+
recursiveTemp,
|
|
259
|
+
);
|
|
216
260
|
|
|
217
261
|
if (parentE) {
|
|
218
262
|
if (isPrepend) parentE.prepend(tag);
|
|
@@ -240,7 +284,10 @@ export class Libs {
|
|
|
240
284
|
* @param {SelectiveOptions} options - Default configuration to be merged.
|
|
241
285
|
* @returns {SelectiveOptions} - Final configuration after element overrides.
|
|
242
286
|
*/
|
|
243
|
-
public static buildConfig(
|
|
287
|
+
public static buildConfig(
|
|
288
|
+
element: HTMLElement,
|
|
289
|
+
options: SelectiveOptions,
|
|
290
|
+
): SelectiveOptions {
|
|
244
291
|
const myOptions = this.jsCopyObject<SelectiveOptions>(options);
|
|
245
292
|
|
|
246
293
|
for (const optionKey in myOptions) {
|
|
@@ -248,13 +295,14 @@ export class Libs {
|
|
|
248
295
|
if (propValue) {
|
|
249
296
|
if (typeof myOptions[optionKey] === "boolean") {
|
|
250
297
|
myOptions[optionKey] = this.string2Boolean(propValue);
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
298
|
+
} else {
|
|
253
299
|
myOptions[optionKey] = propValue;
|
|
254
300
|
}
|
|
255
301
|
} else if (typeof element?.dataset?.[optionKey] !== "undefined") {
|
|
256
302
|
if (typeof myOptions[optionKey] === "boolean") {
|
|
257
|
-
myOptions[optionKey] = this.string2Boolean(
|
|
303
|
+
myOptions[optionKey] = this.string2Boolean(
|
|
304
|
+
element.dataset[optionKey],
|
|
305
|
+
);
|
|
258
306
|
} else {
|
|
259
307
|
myOptions[optionKey] = element.dataset[optionKey];
|
|
260
308
|
}
|
|
@@ -271,7 +319,9 @@ export class Libs {
|
|
|
271
319
|
* @param {...object} params - Config objects in priority order (leftmost is base).
|
|
272
320
|
* @returns {object} - Merged configuration object.
|
|
273
321
|
*/
|
|
274
|
-
public static mergeConfig<T extends Record<string, any>>(
|
|
322
|
+
public static mergeConfig<T extends Record<string, any>>(
|
|
323
|
+
...params: T[]
|
|
324
|
+
): T {
|
|
275
325
|
if (params.length === 0) return {} as T;
|
|
276
326
|
if (params.length === 1) return this.jsCopyObject(params[0]);
|
|
277
327
|
|
|
@@ -338,7 +388,9 @@ export class Libs {
|
|
|
338
388
|
* @param {HTMLElement} item - HTMLElement key whose binder map is requested.
|
|
339
389
|
* @returns {BinderMap | any} - The stored binder map value or undefined if absent.
|
|
340
390
|
*/
|
|
341
|
-
public static getBinderMap<T extends BinderMap | any>(
|
|
391
|
+
public static getBinderMap<T extends BinderMap | any>(
|
|
392
|
+
item: HTMLElement,
|
|
393
|
+
): T {
|
|
342
394
|
return this.iStorage.bindedMap.get(item) as T;
|
|
343
395
|
}
|
|
344
396
|
|
|
@@ -425,26 +477,34 @@ export class Libs {
|
|
|
425
477
|
.replace(/<iframe\b[^>]*>[\s\S]*?<\/iframe>/gi, "")
|
|
426
478
|
.replace(/<(object|embed|link)\b[^>]*>[\s\S]*?<\/\1>/gi, "");
|
|
427
479
|
s = s.replace(/\son[a-z]+\s*=\s*(['"]).*?\1/gi, "");
|
|
428
|
-
s = s.replace(
|
|
480
|
+
s = s.replace(
|
|
481
|
+
/\s([a-z-:]+)\s*=\s*(['"])\s*javascript:[^'"]*\2/gi,
|
|
482
|
+
"",
|
|
483
|
+
);
|
|
429
484
|
return s;
|
|
430
485
|
}
|
|
431
486
|
|
|
432
487
|
const tmp = doc.createElement("div");
|
|
433
488
|
tmp.innerHTML = s;
|
|
434
489
|
|
|
435
|
-
tmp.querySelectorAll(
|
|
490
|
+
tmp.querySelectorAll(
|
|
491
|
+
"script, style, iframe, object, embed, link",
|
|
492
|
+
).forEach((n) => n.remove());
|
|
436
493
|
|
|
437
494
|
tmp.querySelectorAll("*").forEach((n) => {
|
|
438
495
|
for (const a of Array.from(n.attributes)) {
|
|
439
496
|
const name = a.name ?? "";
|
|
440
497
|
const value = a.value ?? "";
|
|
441
|
-
|
|
498
|
+
|
|
442
499
|
if (/^on/i.test(name)) {
|
|
443
500
|
n.removeAttribute(name);
|
|
444
501
|
return;
|
|
445
502
|
}
|
|
446
|
-
|
|
447
|
-
if (
|
|
503
|
+
|
|
504
|
+
if (
|
|
505
|
+
/^(href|src|xlink:href)$/i.test(name) &&
|
|
506
|
+
/^javascript:/i.test(value)
|
|
507
|
+
) {
|
|
448
508
|
n.removeAttribute(name);
|
|
449
509
|
}
|
|
450
510
|
}
|
|
@@ -476,7 +536,10 @@ export class Libs {
|
|
|
476
536
|
*/
|
|
477
537
|
public static string2normalize(str: unknown): string {
|
|
478
538
|
if (str == null) return "";
|
|
479
|
-
const s = String(str)
|
|
539
|
+
const s = String(str)
|
|
540
|
+
.toLowerCase()
|
|
541
|
+
.normalize("NFD")
|
|
542
|
+
.replace(/[\u0300-\u036f]/g, "");
|
|
480
543
|
return s.replace(/đ/g, "d").replace(/Đ/g, "d");
|
|
481
544
|
}
|
|
482
545
|
|
|
@@ -491,7 +554,9 @@ export class Libs {
|
|
|
491
554
|
* @param {HTMLSelectElement} selectElement - The source select element.
|
|
492
555
|
* @returns {Array<HTMLOptGroupElement | HTMLOptionElement>} Flattened node list.
|
|
493
556
|
*/
|
|
494
|
-
public static parseSelectToArray(
|
|
557
|
+
public static parseSelectToArray(
|
|
558
|
+
selectElement: HTMLSelectElement,
|
|
559
|
+
): Array<HTMLOptGroupElement | HTMLOptionElement> {
|
|
495
560
|
const result: Array<HTMLOptGroupElement | HTMLOptionElement> = [];
|
|
496
561
|
const children = selectElement.children;
|
|
497
562
|
|
|
@@ -501,15 +566,19 @@ export class Libs {
|
|
|
501
566
|
const group = child as HTMLOptGroupElement;
|
|
502
567
|
result.push(group);
|
|
503
568
|
|
|
504
|
-
for (
|
|
569
|
+
for (
|
|
570
|
+
let optionIndex = 0;
|
|
571
|
+
optionIndex < group.children.length;
|
|
572
|
+
optionIndex++
|
|
573
|
+
) {
|
|
505
574
|
const option = group.children[optionIndex];
|
|
506
575
|
option["__parentGroup"] = group;
|
|
507
576
|
result.push(option as HTMLOptionElement);
|
|
508
|
-
}
|
|
577
|
+
}
|
|
509
578
|
} else if (child.tagName === "OPTION") {
|
|
510
579
|
result.push(child as HTMLOptionElement);
|
|
511
580
|
}
|
|
512
|
-
}
|
|
581
|
+
}
|
|
513
582
|
|
|
514
583
|
return result;
|
|
515
584
|
}
|
|
@@ -522,7 +591,10 @@ export class Libs {
|
|
|
522
591
|
*/
|
|
523
592
|
public static IsIOS(): boolean {
|
|
524
593
|
const ua = navigator.userAgent;
|
|
525
|
-
return
|
|
594
|
+
return (
|
|
595
|
+
/iP(hone|ad|od)/.test(ua) ||
|
|
596
|
+
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)
|
|
597
|
+
);
|
|
526
598
|
}
|
|
527
599
|
|
|
528
600
|
/**
|
|
@@ -535,18 +607,25 @@ export class Libs {
|
|
|
535
607
|
public static any2px(value: string): string {
|
|
536
608
|
const v = String(value).trim();
|
|
537
609
|
if (v.endsWith("px")) return v;
|
|
538
|
-
if (v.endsWith("vh"))
|
|
539
|
-
|
|
610
|
+
if (v.endsWith("vh"))
|
|
611
|
+
return (window.innerHeight * parseFloat(v)) / 100 + "px";
|
|
612
|
+
if (v.endsWith("vw"))
|
|
613
|
+
return (window.innerWidth * parseFloat(v)) / 100 + "px";
|
|
540
614
|
|
|
541
615
|
// rem/em: use computed font-size of document root
|
|
542
|
-
const fs = parseFloat(
|
|
616
|
+
const fs = parseFloat(
|
|
617
|
+
getComputedStyle(document.documentElement).fontSize,
|
|
618
|
+
);
|
|
543
619
|
if (v.endsWith("rem")) return fs * parseFloat(v) + "px";
|
|
544
620
|
|
|
545
621
|
// fallback: DOM measure
|
|
546
|
-
const el = this.nodeCreator({
|
|
622
|
+
const el = this.nodeCreator({
|
|
623
|
+
node: "div",
|
|
624
|
+
style: { height: v, opacity: "0" },
|
|
625
|
+
});
|
|
547
626
|
document.body.appendChild(el);
|
|
548
627
|
const px = el.offsetHeight + "px";
|
|
549
628
|
el.remove();
|
|
550
629
|
return px;
|
|
551
630
|
}
|
|
552
|
-
}
|
|
631
|
+
}
|
|
@@ -2,7 +2,10 @@ import { Libs } from "./libs";
|
|
|
2
2
|
import { iEvents } from "./ievents";
|
|
3
3
|
import { SelectBox } from "../components/selectbox";
|
|
4
4
|
import { ElementAdditionObserver } from "../services/ea-observer";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
SelectiveActionApi,
|
|
7
|
+
SelectiveOptions,
|
|
8
|
+
} from "../types/utils/selective.type";
|
|
6
9
|
import { BinderMap, PropertiesType } from "../types/utils/istorage.type";
|
|
7
10
|
import { Lifecycle } from "../core/base/lifecycle";
|
|
8
11
|
import { LifecycleState } from "../types/core/base/lifecycle.type";
|
|
@@ -172,7 +175,9 @@ export class Selective extends Lifecycle {
|
|
|
172
175
|
|
|
173
176
|
// Ensure hooks exist
|
|
174
177
|
merged.on = merged.on ?? {};
|
|
175
|
-
merged.on.load = (merged.on.load ?? []) as Array<
|
|
178
|
+
merged.on.load = (merged.on.load ?? []) as Array<
|
|
179
|
+
(...args: any[]) => void
|
|
180
|
+
>;
|
|
176
181
|
|
|
177
182
|
this.bindedQueries.set(query, merged);
|
|
178
183
|
|
|
@@ -531,7 +536,11 @@ export class Selective extends Lifecycle {
|
|
|
531
536
|
* @returns {void}
|
|
532
537
|
*/
|
|
533
538
|
private destroyElement(selectElement: HTMLSelectElement): void {
|
|
534
|
-
const bindMap = Libs.getBinderMap<BinderMap<
|
|
539
|
+
const bindMap = Libs.getBinderMap<BinderMap<
|
|
540
|
+
{ element: HTMLElement },
|
|
541
|
+
Record<string, any>,
|
|
542
|
+
SelectBox
|
|
543
|
+
> | null>(selectElement);
|
|
535
544
|
if (!bindMap) return;
|
|
536
545
|
|
|
537
546
|
const selfBox = bindMap.self;
|
|
@@ -543,7 +552,8 @@ export class Selective extends Lifecycle {
|
|
|
543
552
|
|
|
544
553
|
bindMap.self?.deInit?.();
|
|
545
554
|
|
|
546
|
-
const wrapper =
|
|
555
|
+
const wrapper =
|
|
556
|
+
bindMap.container?.element ?? selectElement.parentElement;
|
|
547
557
|
|
|
548
558
|
selectElement.style.display = "";
|
|
549
559
|
selectElement.style.visibility = "";
|
|
@@ -627,10 +637,7 @@ export class Selective extends Lifecycle {
|
|
|
627
637
|
}
|
|
628
638
|
|
|
629
639
|
const SEID = Libs.randomString(8);
|
|
630
|
-
const options_cfg = Libs.buildConfig(
|
|
631
|
-
selectElement,
|
|
632
|
-
options,
|
|
633
|
-
);
|
|
640
|
+
const options_cfg = Libs.buildConfig(selectElement, options);
|
|
634
641
|
|
|
635
642
|
options_cfg.SEID = SEID;
|
|
636
643
|
options_cfg.SEID_LIST = `seui-${SEID}-optionlist`;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { View } from "../core/base/view";
|
|
2
2
|
import { Libs } from "../utils/libs";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
GroupViewTags,
|
|
5
|
+
GroupViewResult,
|
|
6
|
+
} from "../types/views/view.group.type";
|
|
4
7
|
import { SelectiveOptions } from "../types/utils/selective.type";
|
|
5
8
|
|
|
6
9
|
/**
|
|
@@ -53,7 +56,6 @@ import { SelectiveOptions } from "../types/utils/selective.type";
|
|
|
53
56
|
* @see {@link View}
|
|
54
57
|
*/
|
|
55
58
|
export class GroupView extends View<GroupViewTags> {
|
|
56
|
-
|
|
57
59
|
/**
|
|
58
60
|
* Strongly-typed reference to the mounted group view structure.
|
|
59
61
|
*
|
|
@@ -67,7 +69,7 @@ export class GroupView extends View<GroupViewTags> {
|
|
|
67
69
|
*
|
|
68
70
|
* @public
|
|
69
71
|
*/
|
|
70
|
-
public view
|
|
72
|
+
public view?: GroupViewResult;
|
|
71
73
|
|
|
72
74
|
/**
|
|
73
75
|
* Parsed configuration (bound from the `<select>` element via binder map).
|
|
@@ -77,7 +79,7 @@ export class GroupView extends View<GroupViewTags> {
|
|
|
77
79
|
*
|
|
78
80
|
* @internal
|
|
79
81
|
*/
|
|
80
|
-
private options
|
|
82
|
+
private options?: SelectiveOptions;
|
|
81
83
|
|
|
82
84
|
/**
|
|
83
85
|
* Creates a new GroupView bound to the given parent element.
|
|
@@ -190,10 +192,10 @@ export class GroupView extends View<GroupViewTags> {
|
|
|
190
192
|
* - Safe to call multiple times with same value (idempotent).
|
|
191
193
|
*
|
|
192
194
|
* @public
|
|
193
|
-
* @param {string
|
|
195
|
+
* @param {string} [label=null] - New label to display; `null` preserves current label.
|
|
194
196
|
* @returns {void}
|
|
195
197
|
*/
|
|
196
|
-
public updateLabel(label
|
|
198
|
+
public updateLabel(label?: string): void {
|
|
197
199
|
if (!this.view) return;
|
|
198
200
|
|
|
199
201
|
const headerEl = this.view.tags.GroupHeader;
|
|
@@ -243,7 +245,7 @@ export class GroupView extends View<GroupViewTags> {
|
|
|
243
245
|
|
|
244
246
|
const items = this.view.tags.GroupItems;
|
|
245
247
|
const visibleItems = Array.from(items.children).filter(
|
|
246
|
-
child => !child.classList.contains("hide")
|
|
248
|
+
(child) => !child.classList.contains("hide"),
|
|
247
249
|
);
|
|
248
250
|
|
|
249
251
|
this.view.view.classList.toggle("hide", visibleItems.length === 0);
|
|
@@ -275,7 +277,7 @@ export class GroupView extends View<GroupViewTags> {
|
|
|
275
277
|
this.view.view.classList.toggle("collapsed", collapsed);
|
|
276
278
|
this.view.tags.GroupHeader.setAttribute(
|
|
277
279
|
"aria-expanded",
|
|
278
|
-
collapsed ? "false" : "true"
|
|
280
|
+
collapsed ? "false" : "true",
|
|
279
281
|
);
|
|
280
282
|
}
|
|
281
|
-
}
|
|
283
|
+
}
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
OptionViewTags,
|
|
5
5
|
OptionViewResult,
|
|
6
6
|
OptionConfig,
|
|
7
|
-
OptionConfigPatch
|
|
7
|
+
OptionConfigPatch,
|
|
8
8
|
} from "../types/views/view.option.type";
|
|
9
9
|
import { SelectiveOptions } from "../types/utils/selective.type";
|
|
10
10
|
|
|
@@ -77,7 +77,6 @@ import { SelectiveOptions } from "../types/utils/selective.type";
|
|
|
77
77
|
* @see {@link View}
|
|
78
78
|
*/
|
|
79
79
|
export class OptionView extends View<OptionViewTags> {
|
|
80
|
-
|
|
81
80
|
/**
|
|
82
81
|
* Strongly-typed reference to the mounted option view structure.
|
|
83
82
|
*
|
|
@@ -91,7 +90,7 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
91
90
|
*
|
|
92
91
|
* @public
|
|
93
92
|
*/
|
|
94
|
-
public view
|
|
93
|
+
public view?: OptionViewResult;
|
|
95
94
|
|
|
96
95
|
/**
|
|
97
96
|
* Parsed configuration (bound from the `<select>` element via binder map).
|
|
@@ -101,7 +100,7 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
101
100
|
*
|
|
102
101
|
* @internal
|
|
103
102
|
*/
|
|
104
|
-
private options
|
|
103
|
+
private options?: SelectiveOptions;
|
|
105
104
|
|
|
106
105
|
/**
|
|
107
106
|
* Internal configuration object (Proxy target).
|
|
@@ -116,7 +115,7 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
116
115
|
*
|
|
117
116
|
* @private
|
|
118
117
|
*/
|
|
119
|
-
private config
|
|
118
|
+
private config?: OptionConfig;
|
|
120
119
|
|
|
121
120
|
/**
|
|
122
121
|
* Reactive Proxy wrapper around {@link config}.
|
|
@@ -131,7 +130,7 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
131
130
|
*
|
|
132
131
|
* @private
|
|
133
132
|
*/
|
|
134
|
-
private configProxy
|
|
133
|
+
private configProxy?: OptionConfig;
|
|
135
134
|
|
|
136
135
|
/**
|
|
137
136
|
* Flag indicating whether the initial render has completed.
|
|
@@ -324,7 +323,7 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
324
323
|
* - Each changed property triggers {@link applyPartialChange} individually.
|
|
325
324
|
*
|
|
326
325
|
* @public
|
|
327
|
-
* @param {OptionConfigPatch
|
|
326
|
+
* @param {OptionConfigPatch} config - Partial configuration patch; `null` is no-op.
|
|
328
327
|
* @returns {void}
|
|
329
328
|
*/
|
|
330
329
|
public set optionConfig(config: OptionConfigPatch | null) {
|
|
@@ -332,22 +331,40 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
332
331
|
|
|
333
332
|
const changes: OptionConfigPatch = {};
|
|
334
333
|
|
|
335
|
-
if (
|
|
334
|
+
if (
|
|
335
|
+
config.imageWidth !== undefined &&
|
|
336
|
+
config.imageWidth !== this.config.imageWidth
|
|
337
|
+
)
|
|
336
338
|
changes.imageWidth = config.imageWidth;
|
|
337
339
|
|
|
338
|
-
if (
|
|
340
|
+
if (
|
|
341
|
+
config.imageHeight !== undefined &&
|
|
342
|
+
config.imageHeight !== this.config.imageHeight
|
|
343
|
+
)
|
|
339
344
|
changes.imageHeight = config.imageHeight;
|
|
340
345
|
|
|
341
|
-
if (
|
|
346
|
+
if (
|
|
347
|
+
config.imageBorderRadius !== undefined &&
|
|
348
|
+
config.imageBorderRadius !== this.config.imageBorderRadius
|
|
349
|
+
)
|
|
342
350
|
changes.imageBorderRadius = config.imageBorderRadius;
|
|
343
351
|
|
|
344
|
-
if (
|
|
352
|
+
if (
|
|
353
|
+
config.imagePosition !== undefined &&
|
|
354
|
+
config.imagePosition !== this.config.imagePosition
|
|
355
|
+
)
|
|
345
356
|
changes.imagePosition = config.imagePosition;
|
|
346
357
|
|
|
347
|
-
if (
|
|
358
|
+
if (
|
|
359
|
+
config.labelValign !== undefined &&
|
|
360
|
+
config.labelValign !== this.config.labelValign
|
|
361
|
+
)
|
|
348
362
|
changes.labelValign = config.labelValign;
|
|
349
363
|
|
|
350
|
-
if (
|
|
364
|
+
if (
|
|
365
|
+
config.labelHalign !== undefined &&
|
|
366
|
+
config.labelHalign !== this.config.labelHalign
|
|
367
|
+
)
|
|
351
368
|
changes.labelHalign = config.labelHalign;
|
|
352
369
|
|
|
353
370
|
if (Object.keys(changes).length > 0) {
|
|
@@ -485,7 +502,7 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
485
502
|
private applyPartialChange<K extends keyof OptionConfig>(
|
|
486
503
|
prop: K,
|
|
487
504
|
newValue: OptionConfig[K],
|
|
488
|
-
oldValue: OptionConfig[K]
|
|
505
|
+
oldValue: OptionConfig[K],
|
|
489
506
|
): void {
|
|
490
507
|
const v = this.view;
|
|
491
508
|
if (!v || !v.view) return;
|
|
@@ -540,9 +557,11 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
540
557
|
const img = v.tags?.OptionImage;
|
|
541
558
|
if (img) {
|
|
542
559
|
const styleProp =
|
|
543
|
-
prop === "imageWidth"
|
|
544
|
-
|
|
545
|
-
|
|
560
|
+
prop === "imageWidth"
|
|
561
|
+
? "width"
|
|
562
|
+
: prop === "imageHeight"
|
|
563
|
+
? "height"
|
|
564
|
+
: "borderRadius";
|
|
546
565
|
|
|
547
566
|
img.style[styleProp] = String(newValue);
|
|
548
567
|
}
|
|
@@ -609,4 +628,4 @@ export class OptionView extends View<OptionViewTags> {
|
|
|
609
628
|
|
|
610
629
|
v.tags.OptionImage = image;
|
|
611
630
|
}
|
|
612
|
-
}
|
|
631
|
+
}
|