@dodlhuat/basix 1.3.5 → 1.4.0

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 (82) hide show
  1. package/README.md +83 -7
  2. package/css/checkbox.scss +15 -2
  3. package/css/color-picker.scss +278 -0
  4. package/css/datepicker.scss +5 -3
  5. package/css/push-menu.scss +7 -0
  6. package/css/stepper.scss +1 -1
  7. package/css/style.css +291 -6
  8. package/css/style.css.map +1 -1
  9. package/css/style.min.css +1 -1
  10. package/css/style.min.css.map +1 -1
  11. package/css/style.scss +1 -0
  12. package/css/table.scss +33 -0
  13. package/js/bottom-sheet.d.ts +0 -2
  14. package/js/bottom-sheet.js +0 -2
  15. package/js/calendar.d.ts +9 -17
  16. package/js/calendar.js +4 -7
  17. package/js/carousel.d.ts +0 -2
  18. package/js/carousel.js +1 -3
  19. package/js/chart.d.ts +6 -14
  20. package/js/chart.js +3 -5
  21. package/js/code-viewer.d.ts +2 -1
  22. package/js/code-viewer.js +8 -2
  23. package/js/color-picker.d.ts +50 -0
  24. package/js/color-picker.js +291 -0
  25. package/js/context-menu.d.ts +0 -3
  26. package/js/context-menu.js +0 -3
  27. package/js/datepicker.d.ts +0 -4
  28. package/js/datepicker.js +0 -1
  29. package/js/docs-nav.js +1 -0
  30. package/js/dropdown.d.ts +1 -4
  31. package/js/dropdown.js +0 -1
  32. package/js/editor.d.ts +0 -4
  33. package/js/editor.js +3 -5
  34. package/js/file-uploader.d.ts +0 -4
  35. package/js/file-uploader.js +0 -1
  36. package/js/flyout-menu.d.ts +0 -2
  37. package/js/flyout-menu.js +0 -1
  38. package/js/gallery.d.ts +1 -4
  39. package/js/gallery.js +18 -23
  40. package/js/group-picker.d.ts +0 -5
  41. package/js/group-picker.js +6 -8
  42. package/js/lightbox.d.ts +1 -4
  43. package/js/lightbox.js +6 -10
  44. package/js/modal.d.ts +0 -2
  45. package/js/modal.js +0 -1
  46. package/js/popover.d.ts +0 -3
  47. package/js/popover.js +0 -2
  48. package/js/position.d.ts +0 -13
  49. package/js/position.js +0 -12
  50. package/js/push-menu.d.ts +0 -3
  51. package/js/push-menu.js +6 -11
  52. package/js/range-slider.d.ts +0 -1
  53. package/js/range-slider.js +2 -3
  54. package/js/scroll.d.ts +0 -2
  55. package/js/scroll.js +5 -6
  56. package/js/scrollbar.d.ts +2 -10
  57. package/js/scrollbar.js +25 -42
  58. package/js/select.d.ts +2 -3
  59. package/js/select.js +6 -9
  60. package/js/sidebar-nav.d.ts +4 -15
  61. package/js/sidebar-nav.js +21 -35
  62. package/js/stepper.d.ts +5 -2
  63. package/js/stepper.js +42 -6
  64. package/js/table.d.ts +1 -56
  65. package/js/table.js +4 -56
  66. package/js/tabs.d.ts +1 -32
  67. package/js/tabs.js +7 -39
  68. package/js/theme.d.ts +0 -44
  69. package/js/theme.js +0 -44
  70. package/js/timepicker.d.ts +1 -4
  71. package/js/timepicker.js +1 -2
  72. package/js/toast.d.ts +0 -2
  73. package/js/toast.js +0 -1
  74. package/js/tooltip.d.ts +0 -4
  75. package/js/tooltip.js +0 -1
  76. package/js/tree.d.ts +0 -3
  77. package/js/tree.js +9 -11
  78. package/js/utils.d.ts +0 -10
  79. package/js/utils.js +2 -42
  80. package/js/virtual-dropdown.d.ts +3 -5
  81. package/js/virtual-dropdown.js +17 -66
  82. package/package.json +1 -1
package/js/tabs.js CHANGED
@@ -1,10 +1,10 @@
1
- /** Tabbed content component with horizontal/vertical layouts and keyboard navigation. */
2
1
  class Tabs {
3
2
  container;
4
3
  options;
5
4
  tabItems;
6
5
  tabPanels;
7
6
  currentTab;
7
+ abortController = new AbortController();
8
8
  constructor(elementOrSelector, options = {}) {
9
9
  const element = typeof elementOrSelector === 'string'
10
10
  ? document.querySelector(elementOrSelector)
@@ -13,11 +13,11 @@ class Tabs {
13
13
  throw new Error(`Tabs: Element not found for selector "${elementOrSelector}"`);
14
14
  }
15
15
  this.container = element;
16
- const layout = options.layout || 'horizontal';
16
+ const layout = options.layout ?? 'horizontal';
17
17
  this.options = {
18
18
  layout,
19
19
  defaultTab: options.defaultTab ?? 0,
20
- menuPos: options.menuPos || (layout === 'vertical' ? 'left' : 'top'),
20
+ menuPos: options.menuPos ?? (layout === 'vertical' ? 'left' : 'top'),
21
21
  onChange: options.onChange
22
22
  };
23
23
  this.currentTab = this.options.defaultTab;
@@ -25,9 +25,6 @@ class Tabs {
25
25
  this.tabPanels = this.container.querySelectorAll('.tab-panel');
26
26
  this.init();
27
27
  }
28
- /**
29
- * Initializes the tabs component
30
- */
31
28
  init() {
32
29
  if (this.options.layout === 'vertical') {
33
30
  this.container.classList.add('tabs-vertical');
@@ -48,19 +45,17 @@ class Tabs {
48
45
  this.bindEvents();
49
46
  this.activateTab(this.options.defaultTab);
50
47
  }
51
- /**
52
- * Binds click events to tab items
53
- */
54
48
  bindEvents() {
49
+ const sig = { signal: this.abortController.signal };
55
50
  this.tabItems.forEach((item, index) => {
56
51
  item.addEventListener('click', (e) => {
57
52
  e.preventDefault();
58
53
  this.activateTab(index);
59
- });
54
+ }, sig);
60
55
  item.addEventListener('keydown', (e) => {
61
56
  const keyEvent = e;
62
57
  this.handleKeyboardNavigation(keyEvent, index);
63
- });
58
+ }, sig);
64
59
  item.setAttribute('role', 'tab');
65
60
  item.setAttribute('tabindex', index === this.options.defaultTab ? '0' : '-1');
66
61
  item.setAttribute('aria-selected', index === this.options.defaultTab ? 'true' : 'false');
@@ -70,9 +65,6 @@ class Tabs {
70
65
  panel.setAttribute('aria-hidden', index === this.options.defaultTab ? 'false' : 'true');
71
66
  });
72
67
  }
73
- /**
74
- * Handles keyboard navigation (Arrow keys, Home, End)
75
- */
76
68
  handleKeyboardNavigation(e, currentIndex) {
77
69
  let newIndex = currentIndex;
78
70
  const isVertical = this.options.layout === 'vertical';
@@ -117,9 +109,6 @@ class Tabs {
117
109
  this.tabItems[newIndex].focus();
118
110
  }
119
111
  }
120
- /**
121
- * Activates a tab by index
122
- */
123
112
  activateTab(index) {
124
113
  if (index < 0 || index >= this.tabItems.length) {
125
114
  console.warn(`Invalid tab index: ${index}`);
@@ -145,30 +134,18 @@ class Tabs {
145
134
  this.options.onChange(index);
146
135
  }
147
136
  }
148
- /**
149
- * Public API: Programmatically activate a tab
150
- */
151
137
  goToTab(index) {
152
138
  this.activateTab(index);
153
139
  if (this.tabItems[index]) {
154
140
  this.tabItems[index].focus();
155
141
  }
156
142
  }
157
- /**
158
- * Public API: Get the currently active tab index
159
- */
160
143
  getCurrentTab() {
161
144
  return this.currentTab;
162
145
  }
163
- /**
164
- * Public API: Get the total number of tabs
165
- */
166
146
  getTabCount() {
167
147
  return this.tabItems.length;
168
148
  }
169
- /**
170
- * Public API: Enable a tab
171
- */
172
149
  enableTab(index) {
173
150
  if (index < 0 || index >= this.tabItems.length)
174
151
  return;
@@ -177,9 +154,6 @@ class Tabs {
177
154
  tab.removeAttribute('aria-disabled');
178
155
  tab.style.pointerEvents = '';
179
156
  }
180
- /**
181
- * Public API: Disable a tab
182
- */
183
157
  disableTab(index) {
184
158
  if (index < 0 || index >= this.tabItems.length)
185
159
  return;
@@ -194,14 +168,8 @@ class Tabs {
194
168
  }
195
169
  }
196
170
  }
197
- /**
198
- * Public API: Destroy the tabs instance and clean up
199
- */
200
171
  destroy() {
201
- this.tabItems.forEach((item) => {
202
- const newItem = item.cloneNode(true);
203
- item.parentNode?.replaceChild(newItem, item);
204
- });
172
+ this.abortController.abort();
205
173
  this.container.classList.remove('tabs-vertical');
206
174
  this.tabItems.forEach((item) => {
207
175
  item.removeAttribute('role');
package/js/theme.d.ts CHANGED
@@ -1,66 +1,22 @@
1
1
  type ThemeMode = 'light' | 'dark';
2
- /** Static class for managing light/dark theme switching with system preference and localStorage persistence. */
3
2
  declare class Theme {
4
3
  private static readonly STORAGE_KEY;
5
4
  private static root;
6
5
  private static elements;
7
6
  private static mediaQuery;
8
- /**
9
- * Initializes the theme system with toggle functionality and system preference detection
10
- */
11
7
  static init(): void;
12
- /**
13
- * Safely retrieves the saved theme from localStorage
14
- */
15
8
  private static getSavedTheme;
16
- /**
17
- * Safely saves the theme to localStorage
18
- */
19
9
  private static saveTheme;
20
- /**
21
- * Gets the system-preferred theme
22
- */
23
10
  private static getSystemTheme;
24
- /**
25
- * Gets the current active theme
26
- */
27
11
  private static getCurrentTheme;
28
- /**
29
- * Applies a theme to the document
30
- */
31
12
  private static applyTheme;
32
- /**
33
- * Toggles between light and dark theme
34
- */
35
13
  private static toggleTheme;
36
- /**
37
- * Binds click event to toggle button
38
- */
39
14
  private static bindToggleClick;
40
- /**
41
- * Binds keyboard shortcut (Ctrl/Cmd+J) for theme toggle
42
- */
43
15
  private static bindKeyboardShortcut;
44
- /**
45
- * Binds listener for system theme changes
46
- * Only applies if user hasn't explicitly saved a preference
47
- */
48
16
  private static bindSystemThemeChange;
49
- /**
50
- * Public API: Get the current theme
51
- */
52
17
  static getTheme(): ThemeMode;
53
- /**
54
- * Public API: Set the theme programmatically
55
- */
56
18
  static setTheme(theme: ThemeMode): void;
57
- /**
58
- * Public API: Reset to system preference
59
- */
60
19
  static resetToSystem(): void;
61
- /**
62
- * Public API: Check if user has a saved preference
63
- */
64
20
  static hasSavedPreference(): boolean;
65
21
  }
66
22
  export { Theme };
package/js/theme.js CHANGED
@@ -1,12 +1,8 @@
1
- /** Static class for managing light/dark theme switching with system preference and localStorage persistence. */
2
1
  class Theme {
3
2
  static STORAGE_KEY = 'theme';
4
3
  static root;
5
4
  static elements = null;
6
5
  static mediaQuery = null;
7
- /**
8
- * Initializes the theme system with toggle functionality and system preference detection
9
- */
10
6
  static init() {
11
7
  this.root = document.documentElement;
12
8
  const toggleBtn = document.getElementById('theme-toggle');
@@ -31,9 +27,6 @@ class Theme {
31
27
  this.bindKeyboardShortcut();
32
28
  this.bindSystemThemeChange();
33
29
  }
34
- /**
35
- * Safely retrieves the saved theme from localStorage
36
- */
37
30
  static getSavedTheme() {
38
31
  try {
39
32
  const saved = localStorage.getItem(this.STORAGE_KEY);
@@ -44,9 +37,6 @@ class Theme {
44
37
  return null;
45
38
  }
46
39
  }
47
- /**
48
- * Safely saves the theme to localStorage
49
- */
50
40
  static saveTheme(theme) {
51
41
  try {
52
42
  localStorage.setItem(this.STORAGE_KEY, theme);
@@ -55,22 +45,13 @@ class Theme {
55
45
  console.warn('localStorage.setItem failed', e);
56
46
  }
57
47
  }
58
- /**
59
- * Gets the system-preferred theme
60
- */
61
48
  static getSystemTheme() {
62
49
  return this.mediaQuery?.matches ? 'dark' : 'light';
63
50
  }
64
- /**
65
- * Gets the current active theme
66
- */
67
51
  static getCurrentTheme() {
68
52
  const current = this.root.getAttribute('data-theme');
69
53
  return current === 'dark' ? 'dark' : 'light';
70
54
  }
71
- /**
72
- * Applies a theme to the document
73
- */
74
55
  static applyTheme(theme) {
75
56
  if (!this.elements)
76
57
  return;
@@ -99,9 +80,6 @@ class Theme {
99
80
  }
100
81
  }
101
82
  }
102
- /**
103
- * Toggles between light and dark theme
104
- */
105
83
  static toggleTheme() {
106
84
  if (!this.elements)
107
85
  return;
@@ -118,9 +96,6 @@ class Theme {
118
96
  }
119
97
  }
120
98
  }
121
- /**
122
- * Binds click event to toggle button
123
- */
124
99
  static bindToggleClick() {
125
100
  if (!this.elements)
126
101
  return;
@@ -128,9 +103,6 @@ class Theme {
128
103
  this.toggleTheme();
129
104
  });
130
105
  }
131
- /**
132
- * Binds keyboard shortcut (Ctrl/Cmd+J) for theme toggle
133
- */
134
106
  static bindKeyboardShortcut() {
135
107
  window.addEventListener('keydown', (ev) => {
136
108
  const isMac = /Mac|iPhone|iPod|iPad/i.test(navigator.platform);
@@ -141,10 +113,6 @@ class Theme {
141
113
  }
142
114
  });
143
115
  }
144
- /**
145
- * Binds listener for system theme changes
146
- * Only applies if user hasn't explicitly saved a preference
147
- */
148
116
  static bindSystemThemeChange() {
149
117
  if (!this.mediaQuery)
150
118
  return;
@@ -161,22 +129,13 @@ class Theme {
161
129
  this.mediaQuery.addListener(handler);
162
130
  }
163
131
  }
164
- /**
165
- * Public API: Get the current theme
166
- */
167
132
  static getTheme() {
168
133
  return this.getCurrentTheme();
169
134
  }
170
- /**
171
- * Public API: Set the theme programmatically
172
- */
173
135
  static setTheme(theme) {
174
136
  this.saveTheme(theme);
175
137
  this.applyTheme(theme);
176
138
  }
177
- /**
178
- * Public API: Reset to system preference
179
- */
180
139
  static resetToSystem() {
181
140
  try {
182
141
  localStorage.removeItem(this.STORAGE_KEY);
@@ -187,9 +146,6 @@ class Theme {
187
146
  console.warn('Failed to reset theme', e);
188
147
  }
189
148
  }
190
- /**
191
- * Public API: Check if user has a saved preference
192
- */
193
149
  static hasSavedPreference() {
194
150
  return this.getSavedTheme() !== null;
195
151
  }
@@ -1,17 +1,14 @@
1
- /** A start/end time pair as HH:MM strings. */
2
1
  interface TimeSpan {
3
2
  start: string;
4
3
  end: string;
5
4
  }
6
- /** Configuration options for a TimeSpanPicker instance. */
7
5
  interface TimeSpanPickerOptions {
8
6
  onChange?: (start: string, end: string) => void;
9
7
  defaultStart?: string;
10
8
  defaultEnd?: string;
11
9
  fromString?: string;
12
- toString?: string;
10
+ toLabel?: string;
13
11
  }
14
- /** Interactive time-range picker with a draggable bar and dual time inputs. */
15
12
  declare class TimeSpanPicker {
16
13
  private container;
17
14
  private startTimeInput;
package/js/timepicker.js CHANGED
@@ -1,4 +1,3 @@
1
- /** Interactive time-range picker with a draggable bar and dual time inputs. */
2
1
  class TimeSpanPicker {
3
2
  container;
4
3
  startTimeInput;
@@ -27,7 +26,7 @@ class TimeSpanPicker {
27
26
  this.onChange = options?.onChange;
28
27
  this.uid = `tsp-${Math.random().toString(36).slice(2, 9)}`;
29
28
  this.fromString = options?.fromString ?? 'From';
30
- this.toLabel = options?.toString ?? 'To';
29
+ this.toLabel = options?.toLabel ?? 'To';
31
30
  this.render();
32
31
  this.startTimeInput = this.queryEl('.timespan-start');
33
32
  this.endTimeInput = this.queryEl('.timespan-end');
package/js/toast.d.ts CHANGED
@@ -1,12 +1,10 @@
1
1
  type ToastType = 'success' | 'error' | 'warning' | 'info';
2
- /** Options for creating a Toast notification. */
3
2
  interface ToastOptions {
4
3
  content: string;
5
4
  header?: string;
6
5
  type?: ToastType;
7
6
  closeable?: boolean;
8
7
  }
9
- /** Dismissible notification banner with optional auto-hide timer and progress bar. */
10
8
  declare class Toast {
11
9
  private readonly content;
12
10
  private readonly header;
package/js/toast.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { escapeHtml } from './utils.js';
2
- /** Dismissible notification banner with optional auto-hide timer and progress bar. */
3
2
  class Toast {
4
3
  content;
5
4
  header;
package/js/tooltip.d.ts CHANGED
@@ -1,14 +1,10 @@
1
- /** Configuration options for a Tooltip instance. */
2
1
  interface TooltipOptions {
3
2
  position?: 'top' | 'bottom' | 'left' | 'right' | 'auto';
4
3
  offset?: number;
5
4
  delay?: number;
6
5
  className?: string;
7
- /** Set to true when content is trusted HTML (e.g. from data-tooltip-id).
8
- * Defaults to false — content is treated as plain text and escaped. */
9
6
  isHtml?: boolean;
10
7
  }
11
- /** Lightweight tooltip that positions itself relative to a trigger element. */
12
8
  declare class Tooltip {
13
9
  private static activeTooltip;
14
10
  private static idCounter;
package/js/tooltip.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { computePosition } from './position.js';
2
- /** Lightweight tooltip that positions itself relative to a trigger element. */
3
2
  class Tooltip {
4
3
  static activeTooltip = null;
5
4
  static idCounter = 0;
package/js/tree.d.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  type NodeType = 'file' | 'folder';
2
- /** Configuration options for a TreeComponent instance. */
3
2
  interface TreeOptions {
4
3
  onSelect?: (node: TreeNode) => void;
5
4
  }
6
- /** Represents a single node in a tree structure, either a file or folder. */
7
5
  declare class TreeNode {
8
6
  label: string;
9
7
  type: NodeType;
@@ -14,7 +12,6 @@ declare class TreeNode {
14
12
  childrenContainer: HTMLUListElement | null;
15
13
  constructor(label: string, type?: NodeType, children?: TreeNode[]);
16
14
  }
17
- /** Renders an interactive collapsible tree view from a list of TreeNode objects. */
18
15
  declare class TreeComponent {
19
16
  private container;
20
17
  private data;
package/js/tree.js CHANGED
@@ -1,4 +1,3 @@
1
- /** Represents a single node in a tree structure, either a file or folder. */
2
1
  class TreeNode {
3
2
  label;
4
3
  type;
@@ -17,7 +16,6 @@ class TreeNode {
17
16
  this.childrenContainer = null;
18
17
  }
19
18
  }
20
- /** Renders an interactive collapsible tree view from a list of TreeNode objects. */
21
19
  class TreeComponent {
22
20
  container;
23
21
  data;
@@ -81,18 +79,18 @@ class TreeComponent {
81
79
  iconDiv.className = 'tree-icon';
82
80
  if (type === 'folder') {
83
81
  iconDiv.innerHTML = `
84
- <svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
85
- <path d="M9 18l6-6-6-6" stroke-linecap="round" stroke-linejoin="round"/>
86
- </svg>
87
- `;
82
+ <svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
83
+ <path d="M9 18l6-6-6-6" stroke-linecap="round" stroke-linejoin="round"/>
84
+ </svg>
85
+ `;
88
86
  }
89
87
  else {
90
88
  iconDiv.innerHTML = `
91
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
92
- <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" stroke-linecap="round" stroke-linejoin="round"/>
93
- <path d="M13 2v7h7" stroke-linecap="round" stroke-linejoin="round"/>
94
- </svg>
95
- `;
89
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
90
+ <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" stroke-linecap="round" stroke-linejoin="round"/>
91
+ <path d="M13 2v7h7" stroke-linecap="round" stroke-linejoin="round"/>
92
+ </svg>
93
+ `;
96
94
  }
97
95
  return iconDiv;
98
96
  }
package/js/utils.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- /** Shape of the `utils` helper object. */
2
1
  interface Utils {
3
2
  ready(fn: () => void): void;
4
3
  value(element: HTMLElement): string;
@@ -8,15 +7,6 @@ interface Utils {
8
7
  isHidden(element: HTMLElement): boolean;
9
8
  }
10
9
  declare const utils: Utils;
11
- /**
12
- * Escape a plain-text string so it is safe to inject into innerHTML.
13
- * Use this whenever inserting user-controlled strings into HTML templates.
14
- */
15
10
  declare function escapeHtml(text: string): string;
16
- /**
17
- * Sanitize an HTML string by removing dangerous elements and attributes
18
- * (script, iframe, on* handlers, javascript: hrefs) while preserving safe markup.
19
- * Use this when a component intentionally accepts rich HTML from callers.
20
- */
21
11
  declare function sanitizeHtml(html: string): string;
22
12
  export { utils, escapeHtml, sanitizeHtml };
package/js/utils.js CHANGED
@@ -1,22 +1,12 @@
1
1
  const utils = {
2
- /**
3
- * Execute a function when the DOM is ready
4
- * @param fn - Callback function to execute
5
- */
6
2
  ready(fn) {
7
- if (document.readyState === "complete" || document.readyState === "interactive") {
3
+ if (document.readyState === 'complete' || document.readyState === 'interactive') {
8
4
  setTimeout(fn, 1);
9
5
  }
10
6
  else {
11
- document.addEventListener("DOMContentLoaded", fn);
7
+ document.addEventListener('DOMContentLoaded', fn);
12
8
  }
13
9
  },
14
- /**
15
- * Get the value of an element from various sources
16
- * Priority: value attribute > data-value attribute > innerText
17
- * @param element - HTML element to get value from
18
- * @returns The element's value as a string
19
- */
20
10
  value(element) {
21
11
  if (element.hasAttribute('value')) {
22
12
  return element.getAttribute('value') || '';
@@ -26,57 +16,27 @@ const utils = {
26
16
  }
27
17
  return element.innerText;
28
18
  },
29
- /**
30
- * Get the text content of an element
31
- * @param element - HTML element to get text from
32
- * @returns The element's inner text
33
- */
34
19
  text(element) {
35
20
  return element.innerText;
36
21
  },
37
- /**
38
- * Get an attribute value from an element
39
- * @param element - HTML element to get attribute from
40
- * @param attribute - Name of the attribute to retrieve
41
- * @returns The attribute value or undefined if not present
42
- */
43
22
  attribute(element, attribute) {
44
23
  if (element.hasAttribute(attribute)) {
45
24
  return element.getAttribute(attribute) || undefined;
46
25
  }
47
26
  return undefined;
48
27
  },
49
- /**
50
- * Check if an element is a NodeList
51
- * @param element - Element or NodeList to check
52
- * @returns True if the element is a NodeList
53
- */
54
28
  isList(element) {
55
29
  return NodeList.prototype.isPrototypeOf(element);
56
30
  },
57
- /**
58
- * Check if an element is hidden
59
- * @param element - HTML element to check
60
- * @returns True if the element is hidden
61
- */
62
31
  isHidden(element) {
63
32
  return element.offsetParent === null;
64
33
  }
65
34
  };
66
- /**
67
- * Escape a plain-text string so it is safe to inject into innerHTML.
68
- * Use this whenever inserting user-controlled strings into HTML templates.
69
- */
70
35
  function escapeHtml(text) {
71
36
  const div = document.createElement('div');
72
37
  div.textContent = text;
73
38
  return div.innerHTML;
74
39
  }
75
- /**
76
- * Sanitize an HTML string by removing dangerous elements and attributes
77
- * (script, iframe, on* handlers, javascript: hrefs) while preserving safe markup.
78
- * Use this when a component intentionally accepts rich HTML from callers.
79
- */
80
40
  function sanitizeHtml(html) {
81
41
  const parser = new DOMParser();
82
42
  const doc = parser.parseFromString(html, 'text/html');
@@ -1,9 +1,7 @@
1
- /** A single selectable option for a VirtualDropdown. */
2
1
  interface DropdownOption {
3
2
  label: string;
4
3
  value: string | number;
5
4
  }
6
- /** Configuration for a VirtualDropdown instance. */
7
5
  interface VirtualDropdownConfig {
8
6
  container: string | HTMLElement;
9
7
  options: DropdownOption[];
@@ -14,7 +12,6 @@ interface VirtualDropdownConfig {
14
12
  itemHeight?: number;
15
13
  onSelect?: (selectedValues: Array<string | number>) => void;
16
14
  }
17
- /** Virtualised dropdown that renders only visible items for performance with large option lists. */
18
15
  declare class VirtualDropdown {
19
16
  private readonly container;
20
17
  private readonly options;
@@ -37,7 +34,7 @@ declare class VirtualDropdown {
37
34
  private filteredOptions;
38
35
  private isOpen;
39
36
  private scrollTop;
40
- private boundHandlers;
37
+ private abortController;
41
38
  constructor(config: VirtualDropdownConfig);
42
39
  private init;
43
40
  private renderBase;
@@ -55,4 +52,5 @@ declare class VirtualDropdown {
55
52
  clearSelection(): void;
56
53
  destroy(): void;
57
54
  }
58
- export { VirtualDropdown, DropdownOption, VirtualDropdownConfig };
55
+ export { VirtualDropdown };
56
+ export type { DropdownOption, VirtualDropdownConfig };