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.
Files changed (70) hide show
  1. package/dist/selective-ui.css +0 -6
  2. package/dist/selective-ui.css.map +1 -1
  3. package/dist/selective-ui.esm.js +252 -553
  4. package/dist/selective-ui.esm.js.map +1 -1
  5. package/dist/selective-ui.esm.min.js +2 -2
  6. package/dist/selective-ui.esm.min.js.br +0 -0
  7. package/dist/selective-ui.min.css +1 -1
  8. package/dist/selective-ui.min.css.br +0 -0
  9. package/dist/selective-ui.min.js +2 -2
  10. package/dist/selective-ui.min.js.br +0 -0
  11. package/dist/selective-ui.umd.js +254 -555
  12. package/dist/selective-ui.umd.js.map +1 -1
  13. package/package.json +12 -12
  14. package/src/ts/adapter/mixed-adapter.ts +147 -68
  15. package/src/ts/components/accessorybox.ts +14 -11
  16. package/src/ts/components/directive.ts +1 -1
  17. package/src/ts/components/option-handle.ts +12 -9
  18. package/src/ts/components/placeholder.ts +5 -5
  19. package/src/ts/components/popup/empty-state.ts +5 -5
  20. package/src/ts/components/popup/loading-state.ts +5 -5
  21. package/src/ts/components/popup/popup.ts +138 -76
  22. package/src/ts/components/searchbox.ts +17 -13
  23. package/src/ts/components/selectbox.ts +242 -81
  24. package/src/ts/core/base/adapter.ts +39 -14
  25. package/src/ts/core/base/fenwick.ts +3 -2
  26. package/src/ts/core/base/lifecycle.ts +14 -4
  27. package/src/ts/core/base/model.ts +17 -15
  28. package/src/ts/core/base/recyclerview.ts +7 -5
  29. package/src/ts/core/base/view.ts +10 -5
  30. package/src/ts/core/base/virtual-recyclerview.ts +89 -37
  31. package/src/ts/core/model-manager.ts +48 -21
  32. package/src/ts/core/search-controller.ts +174 -56
  33. package/src/ts/global.ts +5 -8
  34. package/src/ts/index.ts +2 -2
  35. package/src/ts/models/group-model.ts +33 -8
  36. package/src/ts/models/option-model.ts +60 -19
  37. package/src/ts/services/dataset-observer.ts +6 -3
  38. package/src/ts/services/ea-observer.ts +1 -1
  39. package/src/ts/services/effector.ts +22 -12
  40. package/src/ts/services/refresher.ts +7 -3
  41. package/src/ts/services/resize-observer.ts +24 -11
  42. package/src/ts/services/select-observer.ts +2 -2
  43. package/src/ts/types/components/popup.type.ts +18 -1
  44. package/src/ts/types/components/searchbox.type.ts +43 -30
  45. package/src/ts/types/components/state.box.type.ts +1 -1
  46. package/src/ts/types/core/base/adapter.type.ts +13 -5
  47. package/src/ts/types/core/base/lifecycle.type.ts +1 -2
  48. package/src/ts/types/core/base/model.type.ts +3 -3
  49. package/src/ts/types/core/base/recyclerview.type.ts +7 -5
  50. package/src/ts/types/core/base/view.type.ts +6 -6
  51. package/src/ts/types/core/base/virtual-recyclerview.type.ts +45 -46
  52. package/src/ts/types/core/search-controller.type.ts +18 -2
  53. package/src/ts/types/css.d.ts +1 -0
  54. package/src/ts/types/plugins/plugin.type.ts +2 -2
  55. package/src/ts/types/services/effector.type.ts +25 -25
  56. package/src/ts/types/services/resize-observer.type.ts +23 -12
  57. package/src/ts/types/utils/callback-scheduler.type.ts +2 -2
  58. package/src/ts/types/utils/ievents.type.ts +1 -1
  59. package/src/ts/types/utils/istorage.type.ts +62 -60
  60. package/src/ts/types/utils/libs.type.ts +19 -17
  61. package/src/ts/types/utils/selective.type.ts +6 -3
  62. package/src/ts/types/views/view.group.type.ts +9 -5
  63. package/src/ts/types/views/view.option.type.ts +39 -17
  64. package/src/ts/utils/callback-scheduler.ts +12 -7
  65. package/src/ts/utils/ievents.ts +12 -5
  66. package/src/ts/utils/istorage.ts +5 -3
  67. package/src/ts/utils/libs.ts +122 -43
  68. package/src/ts/utils/selective.ts +15 -8
  69. package/src/ts/views/group-view.ts +11 -9
  70. package/src/ts/views/option-view.ts +37 -18
@@ -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: iStorage | null = null;
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 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
55
+ const characters =
56
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
56
57
  const charactersLength = characters.length;
57
58
  for (let i = 0; i < length; i++) {
58
- result += characters.charAt(Math.floor(Math.random() * charactersLength));
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 = document.querySelectorAll<HTMLElement>(queryCommon);
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>(data: Partial<NodeSpec> = {}): T {
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>(document.createElement(nodeName), data as NodeSpec, true);
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>(node: HTMLElement = document.documentElement, _nodeOption: NodeSpec | null = null, systemNodeCreate = false): T {
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 ? node as T : node.cloneNode(true) as T;
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("aria-live", String(nodeOption.ariaLive));
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("aria-labelledby", String(nodeOption.ariaLabelledby));
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("aria-controls", String(nodeOption.ariaControls));
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("aria-haspopup", String(nodeOption.ariaHaspopup));
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("aria-multiselectable", String(nodeOption.ariaMultiselectable));
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("aria-autocomplete", String(nodeOption.ariaAutocomplete));
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(nodeOption.event as Record<string, EventListener>).forEach(([key, value]) => {
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: HTMLElement | null = null,
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
- singleObj?.tag?.tagName ? (singleObj.tag as HTMLElement) : (this.nodeCreator(singleObj.tag) as HTMLElement);
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) this.mountNode<TTags>(singleObj.child, tag, false, false, recursiveTemp);
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(element: HTMLElement, options: SelectiveOptions): SelectiveOptions {
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(element.dataset[optionKey]);
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>>(...params: T[]): T {
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>(item: HTMLElement): T {
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(/\s([a-z-:]+)\s*=\s*(['"])\s*javascript:[^'"]*\2/gi, "");
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("script, style, iframe, object, embed, link").forEach((n) => n.remove());
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 (/^(href|src|xlink:href)$/i.test(name) && /^javascript:/i.test(value)) {
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).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
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(selectElement: HTMLSelectElement): Array<HTMLOptGroupElement | HTMLOptionElement> {
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 (let optionIndex = 0; optionIndex < group.children.length; optionIndex++) {
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 /iP(hone|ad|od)/.test(ua) || (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
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")) return (window.innerHeight * parseFloat(v)) / 100 + "px";
539
- if (v.endsWith("vw")) return (window.innerWidth * parseFloat(v)) / 100 + "px";
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(getComputedStyle(document.documentElement).fontSize);
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({ node: "div", style: { height: v, opacity: "0" } });
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 { SelectiveActionApi, SelectiveOptions } from "../types/utils/selective.type";
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<(...args: any[]) => void>;
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<{element: HTMLElement}, Record<string, any>, SelectBox> | null>(selectElement);
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 = (bindMap.container?.element) ?? selectElement.parentElement;
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 { GroupViewTags, GroupViewResult } from "../types/views/view.group.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: GroupViewResult | null = null;
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: SelectiveOptions | null = null;
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 | null} [label=null] - New label to display; `null` preserves current label.
195
+ * @param {string} [label=null] - New label to display; `null` preserves current label.
194
196
  * @returns {void}
195
197
  */
196
- public updateLabel(label: string | null = null): void {
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: OptionViewResult | null = null;
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: SelectiveOptions | null = null;
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: OptionConfig | null = null;
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: OptionConfig | null = null;
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 | null} config - Partial configuration patch; `null` is no-op.
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 (config.imageWidth !== undefined && config.imageWidth !== this.config.imageWidth)
334
+ if (
335
+ config.imageWidth !== undefined &&
336
+ config.imageWidth !== this.config.imageWidth
337
+ )
336
338
  changes.imageWidth = config.imageWidth;
337
339
 
338
- if (config.imageHeight !== undefined && config.imageHeight !== this.config.imageHeight)
340
+ if (
341
+ config.imageHeight !== undefined &&
342
+ config.imageHeight !== this.config.imageHeight
343
+ )
339
344
  changes.imageHeight = config.imageHeight;
340
345
 
341
- if (config.imageBorderRadius !== undefined && config.imageBorderRadius !== this.config.imageBorderRadius)
346
+ if (
347
+ config.imageBorderRadius !== undefined &&
348
+ config.imageBorderRadius !== this.config.imageBorderRadius
349
+ )
342
350
  changes.imageBorderRadius = config.imageBorderRadius;
343
351
 
344
- if (config.imagePosition !== undefined && config.imagePosition !== this.config.imagePosition)
352
+ if (
353
+ config.imagePosition !== undefined &&
354
+ config.imagePosition !== this.config.imagePosition
355
+ )
345
356
  changes.imagePosition = config.imagePosition;
346
357
 
347
- if (config.labelValign !== undefined && config.labelValign !== this.config.labelValign)
358
+ if (
359
+ config.labelValign !== undefined &&
360
+ config.labelValign !== this.config.labelValign
361
+ )
348
362
  changes.labelValign = config.labelValign;
349
363
 
350
- if (config.labelHalign !== undefined && config.labelHalign !== this.config.labelHalign)
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" ? "width" :
544
- prop === "imageHeight" ? "height" :
545
- "borderRadius";
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
+ }