selective-ui 1.4.0 → 1.4.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.
Files changed (71) hide show
  1. package/dist/selective-ui.css +2 -2
  2. package/dist/selective-ui.css.map +1 -1
  3. package/dist/selective-ui.esm.js +407 -573
  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 +409 -575
  12. package/dist/selective-ui.umd.js.map +1 -1
  13. package/package.json +12 -12
  14. package/src/css/views/option-view.css +2 -2
  15. package/src/ts/adapter/mixed-adapter.ts +149 -71
  16. package/src/ts/components/accessorybox.ts +14 -11
  17. package/src/ts/components/directive.ts +1 -1
  18. package/src/ts/components/option-handle.ts +12 -9
  19. package/src/ts/components/placeholder.ts +5 -5
  20. package/src/ts/components/popup/empty-state.ts +5 -5
  21. package/src/ts/components/popup/loading-state.ts +5 -5
  22. package/src/ts/components/popup/popup.ts +138 -76
  23. package/src/ts/components/searchbox.ts +17 -13
  24. package/src/ts/components/selectbox.ts +260 -84
  25. package/src/ts/core/base/adapter.ts +61 -14
  26. package/src/ts/core/base/fenwick.ts +3 -2
  27. package/src/ts/core/base/lifecycle.ts +14 -4
  28. package/src/ts/core/base/model.ts +17 -15
  29. package/src/ts/core/base/recyclerview.ts +7 -5
  30. package/src/ts/core/base/view.ts +10 -5
  31. package/src/ts/core/base/virtual-recyclerview.ts +178 -45
  32. package/src/ts/core/model-manager.ts +48 -21
  33. package/src/ts/core/search-controller.ts +174 -56
  34. package/src/ts/global.ts +5 -8
  35. package/src/ts/index.ts +2 -2
  36. package/src/ts/models/group-model.ts +33 -8
  37. package/src/ts/models/option-model.ts +88 -20
  38. package/src/ts/services/dataset-observer.ts +6 -3
  39. package/src/ts/services/ea-observer.ts +1 -1
  40. package/src/ts/services/effector.ts +22 -12
  41. package/src/ts/services/refresher.ts +14 -4
  42. package/src/ts/services/resize-observer.ts +24 -11
  43. package/src/ts/services/select-observer.ts +2 -2
  44. package/src/ts/types/components/popup.type.ts +18 -1
  45. package/src/ts/types/components/searchbox.type.ts +43 -30
  46. package/src/ts/types/components/state.box.type.ts +1 -1
  47. package/src/ts/types/core/base/adapter.type.ts +13 -5
  48. package/src/ts/types/core/base/lifecycle.type.ts +1 -2
  49. package/src/ts/types/core/base/model.type.ts +3 -3
  50. package/src/ts/types/core/base/recyclerview.type.ts +7 -5
  51. package/src/ts/types/core/base/view.type.ts +6 -6
  52. package/src/ts/types/core/base/virtual-recyclerview.type.ts +45 -46
  53. package/src/ts/types/core/search-controller.type.ts +18 -2
  54. package/src/ts/types/css.d.ts +1 -0
  55. package/src/ts/types/plugins/plugin.type.ts +2 -2
  56. package/src/ts/types/services/effector.type.ts +25 -25
  57. package/src/ts/types/services/resize-observer.type.ts +23 -12
  58. package/src/ts/types/utils/callback-scheduler.type.ts +2 -2
  59. package/src/ts/types/utils/ievents.type.ts +1 -1
  60. package/src/ts/types/utils/istorage.type.ts +62 -60
  61. package/src/ts/types/utils/libs.type.ts +19 -17
  62. package/src/ts/types/utils/selective.type.ts +6 -3
  63. package/src/ts/types/views/view.group.type.ts +9 -5
  64. package/src/ts/types/views/view.option.type.ts +39 -17
  65. package/src/ts/utils/callback-scheduler.ts +12 -7
  66. package/src/ts/utils/ievents.ts +12 -5
  67. package/src/ts/utils/istorage.ts +5 -3
  68. package/src/ts/utils/libs.ts +122 -43
  69. package/src/ts/utils/selective.ts +15 -8
  70. package/src/ts/views/group-view.ts +11 -9
  71. package/src/ts/views/option-view.ts +37 -18
@@ -3,19 +3,19 @@
3
3
  * Includes attributes, styles, events, and accessibility properties.
4
4
  */
5
5
  export type NodeSpec = {
6
- node: string; // Tag name of the node (e.g., "div", "span")
7
- classList?: string | string[]; // CSS classes to apply
8
- style?: Partial<CSSStyleDeclaration>; // Inline styles for the node
9
- dataset?: Record<string, string>; // Data attributes (e.g., data-* values)
10
- role?: string; // ARIA role for accessibility
11
- ariaLive?: string; // ARIA live region setting
12
- ariaLabelledby?: string; // ARIA labelledby reference
13
- ariaControls?: string; // ARIA controls reference
14
- ariaHaspopup?: string; // ARIA haspopup attribute
15
- ariaMultiselectable?: string; // ARIA multiselectable attribute
16
- ariaAutocomplete?: string; // ARIA autocomplete attribute
17
- event?: Record<string, EventListener>; // Event listeners mapped by event name
18
- [key: string]: unknown; // Allow additional custom properties
6
+ node: string; // Tag name of the node (e.g., "div", "span")
7
+ classList?: string | string[]; // CSS classes to apply
8
+ style?: Partial<CSSStyleDeclaration>; // Inline styles for the node
9
+ dataset?: Record<string, string>; // Data attributes (e.g., data-* values)
10
+ role?: string; // ARIA role for accessibility
11
+ ariaLive?: string; // ARIA live region setting
12
+ ariaLabelledby?: string; // ARIA labelledby reference
13
+ ariaControls?: string; // ARIA controls reference
14
+ ariaHaspopup?: string; // ARIA haspopup attribute
15
+ ariaMultiselectable?: string; // ARIA multiselectable attribute
16
+ ariaAutocomplete?: string; // ARIA autocomplete attribute
17
+ event?: Record<string, EventListener>; // Event listeners mapped by event name
18
+ [key: string]: unknown; // Allow additional custom properties
19
19
  };
20
20
 
21
21
  /**
@@ -24,7 +24,9 @@ export type NodeSpec = {
24
24
  *
25
25
  * @template TTags - A map of tag names to their corresponding HTMLElement instances.
26
26
  */
27
- export type MountViewResult<TTags extends Record<string, HTMLElement> = Record<string, HTMLElement>> = {
28
- view: HTMLElement | null; // Root element of the mounted view
29
- tags: TTags & { id: string }; // Tag map with an additional unique ID
30
- };
27
+ export type MountViewResult<
28
+ TTags extends Record<string, HTMLElement> = Record<string, HTMLElement>,
29
+ > = {
30
+ view?: HTMLElement; // Root element of the mounted view
31
+ tags: TTags & { id: string }; // Tag map with an additional unique ID
32
+ };
@@ -7,9 +7,12 @@ import type { SelectivePlugin } from "../plugins/plugin.type";
7
7
  * Extends DefaultConfig with additional internal identifiers.
8
8
  */
9
9
  export type SelectiveOptions = DefaultConfig & {
10
- SEID?: string; // Unique Selective Element ID
11
- SEID_LIST?: string; // ID for the list container
12
- SEID_HOLDER?: string; // ID for the holder element
10
+ /** Unique Selective Element ID */
11
+ SEID?: string;
12
+ /** ID for the list container */
13
+ SEID_LIST?: string;
14
+ /** ID for the holder element */
15
+ SEID_HOLDER?: string;
13
16
  };
14
17
 
15
18
  /**
@@ -5,9 +5,12 @@ import { MountViewResult } from "../utils/libs.type";
5
5
  * These tags correspond to key sections of the group UI.
6
6
  */
7
7
  export type GroupViewTags = {
8
- GroupView: HTMLDivElement; // Root container for the group view
9
- GroupHeader: HTMLDivElement; // Header section displaying the group title
10
- GroupItems: HTMLDivElement; // Container for the group's items
8
+ /** Root container for the group view */
9
+ GroupView: HTMLDivElement;
10
+ /** Header section displaying the group title */
11
+ GroupHeader: HTMLDivElement;
12
+ /** Container for the group's items */
13
+ GroupItems: HTMLDivElement;
11
14
  };
12
15
 
13
16
  /**
@@ -15,5 +18,6 @@ export type GroupViewTags = {
15
18
  * Extends MountViewResult with a guaranteed root element (`view`).
16
19
  */
17
20
  export type GroupViewResult = MountViewResult<GroupViewTags> & {
18
- view: Element; // The root element of the mounted group view
19
- };
21
+ /** The root element of the mounted group view */
22
+ view: Element;
23
+ };
@@ -5,11 +5,16 @@ import { MountViewResult } from "../utils/libs.type";
5
5
  * These tags correspond to key elements of the option UI.
6
6
  */
7
7
  export type OptionViewTags = {
8
- OptionView: HTMLDivElement; // Root container for the option view
9
- OptionInput: HTMLInputElement; // Input element (checkbox or radio)
10
- OptionLabel: HTMLLabelElement; // Label element for the option
11
- LabelContent: HTMLDivElement; // Container for label text content
12
- OptionImage: HTMLImageElement; // Image element for options with images
8
+ /** Root container for the option view */
9
+ OptionView: HTMLDivElement;
10
+ /** Input element (checkbox or radio) */
11
+ OptionInput: HTMLInputElement;
12
+ /** Label element for the option */
13
+ OptionLabel: HTMLLabelElement;
14
+ /** Container for label text content */
15
+ LabelContent: HTMLDivElement;
16
+ /** Image element for options with images */
17
+ OptionImage: HTMLImageElement;
13
18
  };
14
19
 
15
20
  /**
@@ -17,7 +22,8 @@ export type OptionViewTags = {
17
22
  * Extends MountViewResult with a guaranteed root element (`view`).
18
23
  */
19
24
  export type OptionViewResult = MountViewResult<OptionViewTags> & {
20
- view: Element; // The root element of the mounted option view
25
+ /** The root element of the mounted option view */
26
+ view: Element;
21
27
  };
22
28
 
23
29
  /**
@@ -39,20 +45,36 @@ export type LabelHalign = "left" | "center" | "right";
39
45
  * Configuration options for rendering an option.
40
46
  */
41
47
  export type OptionConfig = {
42
- isMultiple: boolean; // Indicates if multiple selection is allowed
43
- hasImage: boolean; // Indicates if the option includes an image
44
- imagePosition: ImagePosition; // Position of the image relative to the label
45
- imageWidth: string; // Width of the image
46
- imageHeight: string; // Height of the image
47
- imageBorderRadius: string; // Border radius for the image
48
- labelValign: LabelValign; // Vertical alignment of the label
49
- labelHalign: LabelHalign; // Horizontal alignment of the label
48
+ /** Indicates if multiple selection is allowed */
49
+ isMultiple: boolean;
50
+ /** Indicates if the option includes an image */
51
+ hasImage: boolean;
52
+ /** Position of the image relative to the label */
53
+ imagePosition: ImagePosition;
54
+ /** Width of the image */
55
+ imageWidth: string;
56
+ /** Height of the image */
57
+ imageHeight: string;
58
+ /** Border radius for the image */
59
+ imageBorderRadius: string;
60
+ /** Vertical alignment of the label */
61
+ labelValign: LabelValign;
62
+ /** Horizontal alignment of the label */
63
+ labelHalign: LabelHalign;
50
64
  };
51
65
 
52
66
  /**
53
67
  * Partial configuration patch for updating specific option properties.
54
68
  * Includes only image and label alignment-related properties.
55
69
  */
56
- export type OptionConfigPatch = Partial<Pick<OptionConfig,
57
- "imageWidth" | "imageHeight" | "imageBorderRadius" | "imagePosition" | "labelValign" | "labelHalign"
58
- >>;
70
+ export type OptionConfigPatch = Partial<
71
+ Pick<
72
+ OptionConfig,
73
+ | "imageWidth"
74
+ | "imageHeight"
75
+ | "imageBorderRadius"
76
+ | "imagePosition"
77
+ | "labelValign"
78
+ | "labelHalign"
79
+ >
80
+ >;
@@ -1,4 +1,8 @@
1
- import { StoredEntry, TimerKey, TimerOptions } from "../types/utils/callback-scheduler.type";
1
+ import {
2
+ StoredEntry,
3
+ TimerKey,
4
+ TimerOptions,
5
+ } from "../types/utils/callback-scheduler.type";
2
6
 
3
7
  /**
4
8
  * CallbackScheduler
@@ -82,14 +86,14 @@ export class CallbackScheduler {
82
86
  *
83
87
  * @public
84
88
  * @param {TimerKey} key - Group identifier for callbacks.
85
- * @param {(payload: any[] | null) => void} callback - Function to execute after debounce timeout.
89
+ * @param {(payload?: any[]) => void} callback - Function to execute after debounce timeout.
86
90
  * @param {TimerOptions} [options={}] - Scheduling options (`debounce`, `once`).
87
91
  * @returns {void}
88
92
  */
89
93
  public on(
90
94
  key: TimerKey,
91
- callback: (payload: any[] | null) => void,
92
- options: TimerOptions = {}
95
+ callback: (payload?: any[]) => void,
96
+ options: TimerOptions = {},
93
97
  ): void {
94
98
  const timeout = options.debounce ?? 50;
95
99
  const once = options.once ?? false;
@@ -176,13 +180,14 @@ export class CallbackScheduler {
176
180
  const timer = setTimeout(async () => {
177
181
  try {
178
182
  const resp = entry.callback(
179
- params.length > 0 ? params : null
183
+ params.length > 0 ? params : null,
180
184
  ) as any;
181
185
 
182
186
  if (resp instanceof Promise) {
183
187
  await resp;
184
188
  }
185
- } catch {} finally {
189
+ } catch {
190
+ } finally {
186
191
  if (entry.once) {
187
192
  executes[i] = undefined;
188
193
 
@@ -230,4 +235,4 @@ export class CallbackScheduler {
230
235
  this.off(k);
231
236
  }
232
237
  }
233
- }
238
+ }
@@ -1,4 +1,8 @@
1
- import { IEventCallback, IEventHandler, IEventToken } from "../types/utils/ievents.type";
1
+ import {
2
+ IEventCallback,
3
+ IEventHandler,
4
+ IEventToken,
5
+ } from "../types/utils/ievents.type";
2
6
 
3
7
  /**
4
8
  * iEvents
@@ -58,7 +62,10 @@ export class iEvents {
58
62
  * - `token`: immutable view of the dispatch state.
59
63
  * - `callback`: controller passed into handlers to modify dispatch flow.
60
64
  */
61
- public static buildEventToken(): { token: IEventToken; callback: IEventCallback } {
65
+ public static buildEventToken(): {
66
+ token: IEventToken;
67
+ callback: IEventCallback;
68
+ } {
62
69
  const privToken = { isContinue: true, isCancel: false };
63
70
 
64
71
  const token: IEventToken = {
@@ -104,7 +111,7 @@ export class iEvents {
104
111
  * @returns The {@link IEventToken} describing the final dispatch state.
105
112
  */
106
113
  public static callEvent<TParams extends unknown[]>(
107
- params: TParams | null,
114
+ params?: TParams,
108
115
  ...handles: Array<IEventHandler<TParams> | unknown>
109
116
  ): IEventToken {
110
117
  const { token, callback } = this.buildEventToken();
@@ -139,7 +146,7 @@ export class iEvents {
139
146
  public static trigger(
140
147
  element: HTMLElement | Window | Document,
141
148
  eventType: string,
142
- opts: EventInit = { bubbles: true, cancelable: true }
149
+ opts: EventInit = { bubbles: true, cancelable: true },
143
150
  ): Event {
144
151
  const evt = new Event(eventType, opts);
145
152
  element.dispatchEvent(evt);
@@ -167,4 +174,4 @@ export class iEvents {
167
174
  (fn as (...args: TParams) => unknown)(...params);
168
175
  }
169
176
  }
170
- }
177
+ }
@@ -61,11 +61,13 @@ export class iStorage {
61
61
  };
62
62
 
63
63
  /** Bound instance map (keyed by select element). */
64
- public bindedMap: Map<HTMLSelectElement|HTMLElement, BinderMap> = new Map();
64
+ public bindedMap: Map<HTMLSelectElement | HTMLElement, BinderMap> =
65
+ new Map();
65
66
 
66
67
  /** Unbind cache map (keyed by select element). */
67
- public unbindedMap: Map<HTMLSelectElement|HTMLElement, BinderMap> = new Map();
68
+ public unbindedMap: Map<HTMLSelectElement | HTMLElement, BinderMap> =
69
+ new Map();
68
70
 
69
71
  /** List of bound selectors/commands. */
70
72
  public bindedCommand: string[] = [];
71
- }
73
+ }
@@ -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
+ }