selective-ui 1.1.6 → 1.2.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 (40) hide show
  1. package/dist/selective-ui.css +7 -1
  2. package/dist/selective-ui.css.map +1 -1
  3. package/dist/selective-ui.esm.js +794 -57
  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 +795 -58
  12. package/dist/selective-ui.umd.js.map +1 -1
  13. package/package.json +1 -1
  14. package/src/css/components/popup.css +7 -1
  15. package/src/ts/adapter/mixed-adapter.ts +29 -18
  16. package/src/ts/components/empty-state.ts +5 -4
  17. package/src/ts/components/loading-state.ts +4 -4
  18. package/src/ts/components/option-handle.ts +4 -4
  19. package/src/ts/components/popup.ts +35 -6
  20. package/src/ts/components/searchbox.ts +2 -0
  21. package/src/ts/components/selectbox.ts +23 -9
  22. package/src/ts/core/base/adapter.ts +8 -5
  23. package/src/ts/core/base/model.ts +19 -1
  24. package/src/ts/core/base/recyclerview.ts +3 -1
  25. package/src/ts/core/base/virtual-recyclerview.ts +763 -0
  26. package/src/ts/core/model-manager.ts +24 -16
  27. package/src/ts/core/search-controller.ts +5 -8
  28. package/src/ts/models/option-model.ts +22 -3
  29. package/src/ts/services/effector.ts +7 -7
  30. package/src/ts/types/components/state.box.type.ts +1 -18
  31. package/src/ts/types/core/base/adapter.type.ts +14 -0
  32. package/src/ts/types/core/base/model.type.ts +5 -0
  33. package/src/ts/types/core/base/recyclerview.type.ts +3 -1
  34. package/src/ts/types/core/base/view.type.ts +6 -0
  35. package/src/ts/types/core/base/virtual-recyclerview.type.ts +66 -0
  36. package/src/ts/types/utils/istorage.type.ts +1 -0
  37. package/src/ts/utils/istorage.ts +3 -2
  38. package/src/ts/utils/libs.ts +26 -25
  39. package/src/ts/utils/selective.ts +7 -7
  40. package/src/ts/views/option-view.ts +8 -8
@@ -3,6 +3,7 @@ import { GroupModel } from "../models/group-model";
3
3
  import { OptionModel } from "../models/option-model";
4
4
  import { ModelContract } from "../types/core/base/model.type";
5
5
  import { RecyclerViewContract } from "../types/core/base/recyclerview.type";
6
+ import { ViewContract } from "../types/core/base/view.type";
6
7
  import { SelectiveOptions } from "../types/utils/selective.type";
7
8
  import { Adapter } from "./base/adapter";
8
9
 
@@ -12,7 +13,7 @@ import { Adapter } from "./base/adapter";
12
13
  */
13
14
  export class ModelManager<
14
15
  TModel extends ModelContract<any, any>,
15
- TAdapter extends Adapter<TModel>
16
+ TAdapter extends Adapter<TModel, ViewContract<any>>
16
17
  > {
17
18
  private _privModelList: Array<GroupModel | OptionModel> = [];
18
19
 
@@ -120,7 +121,7 @@ export class ModelManager<
120
121
  const optionEl = data as HTMLOptionElement;
121
122
  const optionModel = new OptionModel(this.options, optionEl);
122
123
 
123
- const parentGroup = (optionEl as any)["__parentGroup"] as HTMLOptGroupElement | undefined;
124
+ const parentGroup = optionEl["__parentGroup"] as HTMLOptGroupElement | undefined;
124
125
 
125
126
  if (parentGroup && currentGroup && parentGroup === currentGroup.targetElement) {
126
127
  currentGroup.addItem(optionModel);
@@ -150,7 +151,7 @@ export class ModelManager<
150
151
  this._privAdapterHandle.syncFromSource(this._privModelList as unknown as TModel[]);
151
152
  }
152
153
 
153
- this.refresh();
154
+ this.refresh(false);
154
155
  }
155
156
 
156
157
  /**
@@ -159,25 +160,27 @@ export class ModelManager<
159
160
  */
160
161
  notify(): void {
161
162
  if (!this._privAdapterHandle) return;
162
- this.refresh();
163
+ this.refresh(false);
163
164
  }
164
165
 
165
166
  /**
166
167
  * Initializes adapter and recycler view instances, attaches them to a container element,
167
168
  * and applies optional configuration overrides for adapter and recyclerView.
168
169
  */
169
- load(
170
+
171
+ load<TExtra extends object = {}>(
170
172
  viewElement: HTMLElement,
171
173
  adapterOpt: Partial<TAdapter> = {},
172
- recyclerViewOpt: Partial<RecyclerViewContract<TAdapter>> = {}
174
+ recyclerViewOpt: Partial<RecyclerViewContract<TAdapter>> & TExtra = {} as any
173
175
  ): void {
176
+
174
177
  this._privAdapterHandle = new this._privAdapter(this._privModelList as unknown as TModel[]);
175
178
  Object.assign(this._privAdapterHandle, adapterOpt);
176
179
 
177
180
  this._privRecyclerViewHandle = new this._privRecyclerView(viewElement);
178
- this._privRecyclerViewHandle.setAdapter(this._privAdapterHandle);
179
-
180
181
  Object.assign(this._privRecyclerViewHandle, recyclerViewOpt);
182
+
183
+ this._privRecyclerViewHandle.setAdapter(this._privAdapterHandle);
181
184
  }
182
185
 
183
186
  /**
@@ -240,7 +243,7 @@ export class ModelManager<
240
243
  existingOption.update(dataVset);
241
244
  existingOption.position = position;
242
245
 
243
- const parentGroup = (dataVset as any)["__parentGroup"] as HTMLOptGroupElement | undefined;
246
+ const parentGroup = dataVset["__parentGroup"] as HTMLOptGroupElement | undefined;
244
247
 
245
248
  if (parentGroup && currentGroup) {
246
249
  currentGroup.addItem(existingOption);
@@ -255,7 +258,7 @@ export class ModelManager<
255
258
  const newOption = new OptionModel(this.options, dataVset);
256
259
  newOption.position = position;
257
260
 
258
- const parentGroup = (dataVset as any)["__parentGroup"] as HTMLOptGroupElement | undefined;
261
+ const parentGroup = dataVset["__parentGroup"] as HTMLOptGroupElement | undefined;
259
262
 
260
263
  if (parentGroup && currentGroup) {
261
264
  currentGroup.addItem(newOption);
@@ -269,12 +272,15 @@ export class ModelManager<
269
272
  }
270
273
  });
271
274
 
275
+ let isUpdate = true;
272
276
  oldGroupMap.forEach((removedGroup) => {
273
- removedGroup.view?.getView?.()?.remove?.();
277
+ isUpdate = false;
278
+ removedGroup.remove();
274
279
  });
275
280
 
276
281
  oldOptionMap.forEach((removedOption) => {
277
- removedOption.view?.getView?.()?.remove?.();
282
+ isUpdate = false;
283
+ removedOption.remove();
278
284
  });
279
285
 
280
286
  this._privModelList = newModels;
@@ -284,7 +290,7 @@ export class ModelManager<
284
290
  }
285
291
 
286
292
  this.onUpdated();
287
- this.refresh();
293
+ this.refresh(isUpdate);
288
294
  }
289
295
 
290
296
  /**
@@ -299,16 +305,18 @@ export class ModelManager<
299
305
  * @param {boolean} value - True to skip events; false to restore normal behavior.
300
306
  */
301
307
  skipEvent(value: boolean): void {
302
- if (this._privAdapterHandle) (this._privAdapterHandle as any).isSkipEvent = value;
308
+ if (this._privAdapterHandle) this._privAdapterHandle.isSkipEvent = value;
303
309
  }
304
310
 
305
311
  /**
306
312
  * Re-renders the recycler view if present and invokes the post-refresh hook.
307
313
  * No-op if the recycler view is not initialized.
314
+ *
315
+ * @param isUpdate - Indicates if this refresh is due to an update operation.
308
316
  */
309
- refresh(): void {
317
+ refresh(isUpdate: boolean): void {
310
318
  if (!this._privRecyclerViewHandle) return;
311
- this._privRecyclerViewHandle.refresh();
319
+ this._privRecyclerViewHandle.refresh(isUpdate);
312
320
  this.onUpdated();
313
321
  }
314
322
 
@@ -91,7 +91,7 @@ export class SearchController {
91
91
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
92
92
  });
93
93
  } else {
94
- const params = new URLSearchParams(payload as any).toString();
94
+ const params = new URLSearchParams(payload).toString();
95
95
  response = await fetch(`${cfg.url}?${params}`);
96
96
  }
97
97
 
@@ -168,7 +168,7 @@ export class SearchController {
168
168
  clear(): void {
169
169
  this._paginationState.currentKeyword = "";
170
170
 
171
- const { modelList } = this._modelManager.getResources() as any;
171
+ const { modelList } = this._modelManager.getResources();
172
172
  const flatOptions: OptionModel[] = [];
173
173
 
174
174
  for (const m of modelList as MixedItem[]) {
@@ -212,7 +212,7 @@ export class SearchController {
212
212
  const lower = String(keyword ?? "").toLowerCase();
213
213
  const lowerNA = Libs.string2normalize(lower);
214
214
 
215
- const { modelList } = this._modelManager.getResources() as any;
215
+ const { modelList } = this._modelManager.getResources();
216
216
 
217
217
  const flatOptions: OptionModel[] = [];
218
218
  for (const m of modelList as MixedItem[]) {
@@ -223,10 +223,7 @@ export class SearchController {
223
223
  let hasVisibleItems = false;
224
224
 
225
225
  flatOptions.forEach((opt) => {
226
- const text = String(opt.textContent ?? opt.text ?? "").toLowerCase();
227
- const textNA = Libs.string2normalize(text);
228
-
229
- const isVisible = lower === "" || text.includes(lower) || textNA.includes(lowerNA);
226
+ const isVisible = lower === "" || opt.textToFind.includes(lowerNA);
230
227
 
231
228
  opt.visible = isVisible;
232
229
  if (isVisible) hasVisibleItems = true;
@@ -291,7 +288,7 @@ export class SearchController {
291
288
  signal: this._abortController.signal,
292
289
  });
293
290
  } else {
294
- const params = new URLSearchParams(payload as any).toString();
291
+ const params = new URLSearchParams(payload).toString();
295
292
  response = await fetch(`${cfg.url}?${params}`, { signal: this._abortController.signal });
296
293
  }
297
294
 
@@ -6,12 +6,12 @@ import { OptionView } from "../views/option-view";
6
6
  import type { IEventCallback } from "../types/utils/ievents.type";
7
7
  import type { OptionViewTags } from "../types/views/view.option.type";
8
8
  import type { GroupModel } from "./group-model";
9
- import { DefaultConfig } from "../types/utils/istorage.type";
9
+ import { SelectiveOptions } from "../types/utils/selective.type";
10
10
 
11
11
  /**
12
- * @extends {Model<HTMLOptionElement, OptionViewTags, OptionView>}
12
+ * @extends {Model<HTMLOptionElement, OptionViewTags, OptionView, SelectiveOptions>}
13
13
  */
14
- export class OptionModel extends Model<HTMLOptionElement, OptionViewTags, OptionView, DefaultConfig> {
14
+ export class OptionModel extends Model<HTMLOptionElement, OptionViewTags, OptionView, SelectiveOptions> {
15
15
  private _privOnSelected: Array<(evtToken: IEventCallback, el: OptionModel, selected: boolean) => void> = [];
16
16
 
17
17
  private _privOnInternalSelected: Array<(evtToken: IEventCallback, el: OptionModel, selected: boolean) => void> = [];
@@ -24,6 +24,22 @@ export class OptionModel extends Model<HTMLOptionElement, OptionViewTags, Option
24
24
 
25
25
  group: GroupModel | null = null;
26
26
 
27
+ /**
28
+ * Constructs a Model instance with configuration options and optional bindings to a target element and view.
29
+ * Stores references for later updates and rendering.
30
+ *
31
+ * @param {SelectiveOptions} options - Configuration options for the model.
32
+ * @param {HTMLOptionElement|null} [targetElement=null] - The underlying element (e.g., <option> or group node).
33
+ * @param {OptionView|null} [view=null] - The associated view responsible for rendering the model.
34
+ */
35
+ constructor(options: SelectiveOptions, targetElement: HTMLOptionElement | null = null, view: OptionView | null = null) {
36
+ super(options, targetElement, view);
37
+
38
+ (async () => {
39
+ this.textToFind = Libs.string2normalize(this.textContent.toLowerCase());
40
+ })();
41
+ }
42
+
27
43
  /**
28
44
  * Returns the image source from dataset (imgsrc or image), or an empty string if absent.
29
45
  *
@@ -149,6 +165,8 @@ export class OptionModel extends Model<HTMLOptionElement, OptionViewTags, Option
149
165
  return this.options.allowHtml ? Libs.stripHtml(this.text).trim() : this.text.trim();
150
166
  }
151
167
 
168
+ textToFind: string;
169
+
152
170
  /**
153
171
  * Returns the dataset object of the underlying <option> element, or an empty object.
154
172
  *
@@ -214,6 +232,7 @@ export class OptionModel extends Model<HTMLOptionElement, OptionViewTags, Option
214
232
  * and synchronizes initial selected state to the view.
215
233
  */
216
234
  onTargetChanged(): void {
235
+ this.textToFind = Libs.string2normalize(this.textContent.toLowerCase());
217
236
  if (!this.view) return;
218
237
 
219
238
  const labelContent = this.view.view.tags.LabelContent;
@@ -301,7 +301,7 @@ class EffectorImpl implements EffectorInterface {
301
301
  resize(config: ResizeConfig): this {
302
302
  if (!this.element) return this;
303
303
 
304
- if (this._resizeTimeout) clearTimeout(this._resizeTimeout);
304
+ this.cancel();
305
305
 
306
306
  const {
307
307
  duration = 200,
@@ -326,7 +326,7 @@ class EffectorImpl implements EffectorInterface {
326
326
  this.element.style.transition = `top ${duration}ms ease-out, height ${duration}ms ease-out, max-height ${duration}ms ease-out;`;
327
327
  }
328
328
 
329
- requestAnimationFrame(() => {
329
+ setTimeout(() => {
330
330
  const styles: Partial<CSSStyleDeclaration> & Record<string, string> = {
331
331
  width: `${width}px`,
332
332
  left: `${left}px`,
@@ -343,7 +343,7 @@ class EffectorImpl implements EffectorInterface {
343
343
  } else {
344
344
  this._resizeTimeout = setTimeout(() => {
345
345
  if (this.element?.style) {
346
- this.element.style.transition = "none";
346
+ this.element.style.transition = null;
347
347
  }
348
348
  }, duration);
349
349
  }
@@ -352,15 +352,15 @@ class EffectorImpl implements EffectorInterface {
352
352
 
353
353
  if (animate && (isPositionChanged || heightDiff > 1)) {
354
354
  this._resizeTimeout = setTimeout(() => {
355
- this.element.style.transition = "none";
356
- if (isPositionChanged) delete (this.element.style as any).transition;
355
+ this.element.style.transition = null;
356
+ if (isPositionChanged) delete this.element.style.transition;
357
357
  onComplete?.();
358
358
  }, duration);
359
359
  } else {
360
- if (isPositionChanged) delete (this.element.style as any).transition;
360
+ if (isPositionChanged) delete this.element.style.transition;
361
361
  onComplete?.();
362
362
  }
363
- });
363
+ }, 20);
364
364
 
365
365
  return this;
366
366
  }
@@ -3,21 +3,4 @@
3
3
  * - "notfound": No matching results found.
4
4
  * - "nodata": No data available to display.
5
5
  */
6
- export type EmptyStateType = "notfound" | "nodata";
7
-
8
- /**
9
- * Configuration options for displaying empty states.
10
- */
11
- export interface EmptyStateOptions {
12
- textNoData: string; // Text to display when there is no data
13
- textNotFound: string; // Text to display when no results are found
14
- [key: string]: unknown; // Allows additional custom properties
15
- }
16
-
17
- /**
18
- * Configuration options for displaying loading state.
19
- */
20
- export interface LoadingStateOptions {
21
- textLoading: string; // Text to display while data is loading
22
- [key: string]: unknown; // Allows additional custom properties
23
- }
6
+ export type EmptyStateType = "notfound" | "nodata";
@@ -18,6 +18,20 @@ export interface AdapterContract<TItem extends ModelContract<any, any>> {
18
18
  */
19
19
  adapterKey: string;
20
20
 
21
+ /**
22
+ * Reference to the associated RecyclerView instance.
23
+ *
24
+ * Acts as the rendering host and lifecycle coordinator for this adapter.
25
+ * The adapter uses this reference to:
26
+ * - Access recycler-level configuration and state
27
+ * - Request layout, rebind, or invalidation operations
28
+ * - Coordinate virtual scrolling, recycling, or view reuse strategies
29
+ *
30
+ * The concrete type is intentionally left open to avoid tight coupling
31
+ * between the adapter contract and a specific RecyclerView implementation.
32
+ */
33
+ recyclerView: any;
34
+
21
35
  /**
22
36
  * Replace the current list of items with a new list.
23
37
  * Implementations should also trigger a re-render when appropriate.
@@ -32,4 +32,9 @@ export interface ModelContract<TTarget, TView> {
32
32
  * Indicates whether the model has been initialized.
33
33
  */
34
34
  isInit: boolean;
35
+
36
+ /**
37
+ * Indicates whether the model has been vissibly.
38
+ */
39
+ visible?: boolean;
35
40
  }
@@ -48,6 +48,8 @@ export interface RecyclerViewContract<TAdapter extends AdapterContract<any>> {
48
48
  /**
49
49
  * Refresh the rendered views to reflect current adapter data/state.
50
50
  * May perform diffing, partial updates, or full re-binding.
51
+ *
52
+ * @param isUpdate - Indicates if this refresh is due to an update operation.
51
53
  */
52
- refresh(): void;
54
+ refresh(isUpdate: boolean): void;
53
55
  }
@@ -27,6 +27,12 @@ export interface ViewContract<TTags extends Record<string, HTMLElement>> {
27
27
  */
28
28
  render(): void;
29
29
 
30
+ /**
31
+ * Update the view.
32
+ * Implementations typically refresh displayed data without a full re-render.
33
+ */
34
+ update(): void;
35
+
30
36
  /**
31
37
  * Get the root HTMLElement for the mounted view.
32
38
  *
@@ -0,0 +1,66 @@
1
+
2
+ /**
3
+ * Configuration options for the virtualized recycler view.
4
+ * Controls which element is used for scrolling, sizing heuristics, and how many
5
+ * extra items are rendered outside the viewport to reduce popping.
6
+ */
7
+ export type VirtualOptions = {
8
+ /**
9
+ * The scroll container element used to measure scroll position and viewport.
10
+ * If omitted, the implementation may default to the nearest scrollable parent
11
+ * or the window (depending on your mount strategy).
12
+ */
13
+ scrollEl?: HTMLElement;
14
+
15
+ /**
16
+ * Estimated height (in pixels) for a single item.
17
+ * Used as a heuristic before real measurements are available (especially when
18
+ * `dynamicHeights` is enabled) to compute the total scrollable size.
19
+ */
20
+ estimateItemHeight?: number;
21
+
22
+ /**
23
+ * Number of extra items to render above and below the visible range.
24
+ * Higher values reduce blanking during fast scroll at the cost of more DOM work.
25
+ */
26
+ overscan?: number;
27
+
28
+ /**
29
+ * Enables measuring and caching actual item heights.
30
+ * When `true`, the recycler view supports variable-height items instead of
31
+ * assuming a fixed height for all items.
32
+ */
33
+ dynamicHeights?: boolean;
34
+
35
+ /**
36
+ * Enables adaptive estimation based on observed item measurements.
37
+ * When `true`, `estimateItemHeight` may be refined over time to improve
38
+ * scrollbar accuracy and reduce layout jumps.
39
+ */
40
+ adaptiveEstimate?: boolean;
41
+ };
42
+
43
+ /**
44
+ * Tag map for the virtual recycler view DOM structure.
45
+ * These nodes are typically produced by `mountView`/`mountNode` and used to
46
+ * manipulate padding and host the rendered item elements.
47
+ */
48
+ export type VirtualRecyclerViewTags = {
49
+ /**
50
+ * Top spacer element that simulates the height of items scrolled past.
51
+ * Its height is adjusted to keep the visible items aligned with the scroll offset.
52
+ */
53
+ PadTop: HTMLDivElement;
54
+
55
+ /**
56
+ * Container that hosts the currently rendered (visible + overscan) item elements.
57
+ * Items are inserted/updated within this element during virtualization.
58
+ */
59
+ ItemsHost: HTMLDivElement;
60
+
61
+ /**
62
+ * Bottom spacer element that simulates the height of items not yet rendered.
63
+ * Its height is adjusted to represent remaining content below the visible range.
64
+ */
65
+ PadBottom: HTMLDivElement;
66
+ };
@@ -21,6 +21,7 @@ export type StorageEvents = {
21
21
  */
22
22
  export interface DefaultConfig {
23
23
  showPanel?: boolean; // Whether to show the panel initially
24
+ virtualScroll?: boolean; // Enable virtual scroll
24
25
  accessoryStyle?: string; // CSS style for accessory elements
25
26
  multiple?: boolean; // Enable multiple selection
26
27
  minWidth?: string; // Minimum width of the component
@@ -6,6 +6,7 @@ import { BinderMap, DefaultConfig } from "../types/utils/istorage.type";
6
6
  export class iStorage {
7
7
  defaultConfig: DefaultConfig = {
8
8
  showPanel: true,
9
+ virtualScroll: true,
9
10
  accessoryStyle: "top",
10
11
  multiple: false,
11
12
  minWidth: "50px",
@@ -59,10 +60,10 @@ export class iStorage {
59
60
  };
60
61
 
61
62
  /** Bound instance map (keyed by select element). */
62
- bindedMap: Map<HTMLSelectElement, BinderMap> = new Map();
63
+ bindedMap: Map<HTMLSelectElement|HTMLElement, BinderMap> = new Map();
63
64
 
64
65
  /** Unbind cache map (keyed by select element). */
65
- unbindedMap: Map<HTMLSelectElement, BinderMap> = new Map();
66
+ unbindedMap: Map<HTMLSelectElement|HTMLElement, BinderMap> = new Map();
66
67
 
67
68
  /** List of bound selectors/commands. */
68
69
  bindedCommand: string[] = [];
@@ -2,6 +2,7 @@ import { BinderMap } from "../types/utils/istorage.type";
2
2
  import { MountViewResult, NodeSpec } from "../types/utils/libs.type";
3
3
  import { iStorage } from "./istorage";
4
4
  import { CallbackScheduler } from "./callback-scheduler";
5
+ import { SelectiveOptions } from "../types/utils/selective.type";
5
6
 
6
7
  /**
7
8
  * @class
@@ -88,8 +89,8 @@ export class Libs {
88
89
  }
89
90
 
90
91
  // NodeList or array-like
91
- if ((queryCommon as any) instanceof NodeList || Array.isArray(queryCommon)) {
92
- return Array.from(queryCommon as any);
92
+ if (queryCommon instanceof NodeList || Array.isArray(queryCommon)) {
93
+ return Array.from(queryCommon);
93
94
  }
94
95
 
95
96
  return [];
@@ -131,7 +132,7 @@ export class Libs {
131
132
  (["style", "dataset"] as const).forEach((property) => {
132
133
  const value = nodeOption[property];
133
134
  if (value && typeof value === "object") {
134
- Object.assign((element_creation as any)[property], value);
135
+ Object.assign(element_creation[property], value);
135
136
  }
136
137
  delete nodeOption[property];
137
138
  });
@@ -176,7 +177,7 @@ export class Libs {
176
177
  if (value === null) {
177
178
  element_creation.removeAttribute(key);
178
179
  } else {
179
- (element_creation as any)[key] = value;
180
+ element_creation[key] = value;
180
181
  }
181
182
  });
182
183
 
@@ -260,18 +261,18 @@ export class Libs {
260
261
  * matching element properties or data-* attributes when present.
261
262
  *
262
263
  * @param {HTMLElement} element - Source element providing overrides.
263
- * @param {object} options - Default configuration to be merged.
264
- * @returns {object} - Final configuration after element overrides.
264
+ * @param {SelectiveOptions} options - Default configuration to be merged.
265
+ * @returns {SelectiveOptions} - Final configuration after element overrides.
265
266
  */
266
- static buildConfig<T extends Record<string, any>>(element: HTMLElement, options: T): T {
267
- const myOptions = this.jsCopyObject(options);
267
+ static buildConfig(element: HTMLElement, options: SelectiveOptions): SelectiveOptions {
268
+ const myOptions = this.jsCopyObject<SelectiveOptions>(options);
268
269
 
269
270
  for (const optionKey in myOptions) {
270
- const propValue = (element as any)[optionKey];
271
+ const propValue = element[optionKey];
271
272
  if (propValue) {
272
- (myOptions as any)[optionKey] = propValue;
273
+ myOptions[optionKey] = propValue;
273
274
  } else if (typeof element?.dataset?.[optionKey] !== "undefined") {
274
- (myOptions as any)[optionKey] = element.dataset[optionKey];
275
+ myOptions[optionKey] = element.dataset[optionKey];
275
276
  }
276
277
  }
277
278
 
@@ -298,10 +299,10 @@ export class Libs {
298
299
  const cfgVar = cfg[optionKey];
299
300
  for (const actKey in cfgVar) {
300
301
  // Keep original behavior (push), do not change semantics.
301
- (level0 as any)[optionKey][actKey].push((cfgVar as any)[actKey]);
302
+ level0[optionKey][actKey].push(cfgVar[actKey]);
302
303
  }
303
304
  } else {
304
- (level0 as any)[optionKey] = (cfg as any)[optionKey];
305
+ level0[optionKey] = cfg[optionKey];
305
306
  }
306
307
  }
307
308
  }
@@ -343,7 +344,7 @@ export class Libs {
343
344
  * @returns {boolean} - True if an entry existed and was removed.
344
345
  */
345
346
  static removeBinderMap(element: HTMLElement): boolean {
346
- return this.iStorage.bindedMap.delete(element as any);
347
+ return this.iStorage.bindedMap.delete(element);
347
348
  }
348
349
 
349
350
  /**
@@ -353,17 +354,17 @@ export class Libs {
353
354
  * @returns {BinderMap | null} - The stored binder map value or undefined if absent.
354
355
  */
355
356
  static getBinderMap(item: HTMLElement): BinderMap | null {
356
- return this.iStorage.bindedMap.get(item as any);
357
+ return this.iStorage.bindedMap.get(item);
357
358
  }
358
359
 
359
360
  /**
360
361
  * Sets or updates the binder map entry for a given element.
361
362
  *
362
363
  * @param {HTMLElement} item - Element key to associate with the binder map.
363
- * @param {unknown} bindMap - Value to store in the binder map.
364
+ * @param {BinderMap} bindMap - Value to store in the binder map.
364
365
  */
365
- static setBinderMap(item: HTMLElement, bindMap: unknown): void {
366
- this.iStorage.bindedMap.set(item as any, bindMap as any);
366
+ static setBinderMap(item: HTMLElement, bindMap: BinderMap): void {
367
+ this.iStorage.bindedMap.set(item, bindMap);
367
368
  }
368
369
 
369
370
  /**
@@ -373,7 +374,7 @@ export class Libs {
373
374
  * @returns {boolean} - True if an entry existed and was removed.
374
375
  */
375
376
  static removeUnbinderMap(element: HTMLElement): boolean {
376
- return this.iStorage.unbindedMap.delete(element as any);
377
+ return this.iStorage.unbindedMap.delete(element);
377
378
  }
378
379
 
379
380
  /**
@@ -383,17 +384,17 @@ export class Libs {
383
384
  * @returns {unknown} - The stored unbinder map value or undefined if absent.
384
385
  */
385
386
  static getUnbinderMap(item: HTMLElement): unknown {
386
- return this.iStorage.unbindedMap.get(item as any);
387
+ return this.iStorage.unbindedMap.get(item);
387
388
  }
388
389
 
389
390
  /**
390
391
  * Sets or updates the unbinder map entry for a given element.
391
392
  *
392
393
  * @param {HTMLElement} item - Element key to associate with the unbinder map.
393
- * @param {unknown} bindMap - Value to store in the unbinder map.
394
+ * @param {BinderMap} bindMap - Value to store in the unbinder map.
394
395
  */
395
- static setUnbinderMap(item: HTMLElement, bindMap: unknown): void {
396
- this.iStorage.unbindedMap.set(item as any, bindMap as any);
396
+ static setUnbinderMap(item: HTMLElement, bindMap: BinderMap): void {
397
+ this.iStorage.unbindedMap.set(item, bindMap);
397
398
  }
398
399
 
399
400
  /**
@@ -430,7 +431,7 @@ export class Libs {
430
431
  .replace(/\`\>/g, ">")
431
432
  .trim();
432
433
 
433
- const doc = (globalThis as any)?.document as Document | undefined;
434
+ const doc = globalThis?.document as Document | undefined;
434
435
 
435
436
  if (!doc || typeof doc.createElement !== "function") {
436
437
  s = s
@@ -510,7 +511,7 @@ export class Libs {
510
511
  result.push(group);
511
512
 
512
513
  Array.from(group.children).forEach((option) => {
513
- (option as any).__parentGroup = group;
514
+ option["__parentGroup"] = group;
514
515
  result.push(option as HTMLOptionElement);
515
516
  });
516
517
  } else if (child.tagName === "OPTION") {
@@ -30,7 +30,7 @@ export class Selective {
30
30
 
31
31
  const doneToken = Libs.randomString();
32
32
  Libs.callbackScheduler.on(doneToken, () => {
33
- iEvents.callEvent([this.find(query)], ...(merged.on!.load as any[]));
33
+ iEvents.callEvent([this.find(query)], ...(merged.on!.load));
34
34
  Libs.callbackScheduler.clear(doneToken);
35
35
  merged.on!.load = [];
36
36
  });
@@ -194,7 +194,7 @@ export class Selective {
194
194
  selectElement.style.display = "";
195
195
  selectElement.style.visibility = "";
196
196
  selectElement.disabled = false;
197
- delete (selectElement as any).dataset.selectiveId;
197
+ delete selectElement.dataset.selectiveId;
198
198
 
199
199
  if (wrapper && wrapper.parentNode) {
200
200
  wrapper.parentNode.replaceChild(selectElement, wrapper);
@@ -245,12 +245,12 @@ export class Selective {
245
245
  const bindMap: BinderMap = { options: options_cfg };
246
246
  Libs.setBinderMap(selectElement, bindMap);
247
247
 
248
- const selectBox = new SelectBox(selectElement, this as any);
249
- bindMap.container = (selectBox as any).container;
250
- bindMap.action = (selectBox as any).getAction();
251
- bindMap.self = selectBox as any;
248
+ const selectBox = new SelectBox(selectElement, this);
249
+ bindMap.container = selectBox.container;
250
+ bindMap.action = selectBox.getAction();
251
+ bindMap.self = selectBox;
252
252
 
253
- (selectBox as any).container.view.addEventListener("mouseup", () => {
253
+ selectBox.container.view.addEventListener("mouseup", () => {
254
254
  bindMap.action?.toggle?.();
255
255
  });
256
256