@dodlhuat/basix 1.3.2 → 1.3.3

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 (118) hide show
  1. package/README.md +13 -7
  2. package/css/accordion.scss +0 -5
  3. package/css/badge.scss +1 -6
  4. package/css/bottom-sheet.scss +3 -8
  5. package/css/breadcrumb.scss +6 -15
  6. package/css/button.scss +2 -1
  7. package/css/calendar.scss +0 -54
  8. package/css/card.scss +0 -5
  9. package/css/carousel.scss +0 -3
  10. package/css/chart.scss +0 -25
  11. package/css/chat-bubbles.scss +0 -15
  12. package/css/checkbox.scss +3 -2
  13. package/css/chips.scss +3 -7
  14. package/css/code-viewer.scss +1 -5
  15. package/css/context-menu.scss +4 -6
  16. package/css/datepicker.scss +4 -7
  17. package/css/docs.scss +0 -4
  18. package/css/dropdown.scss +1 -1
  19. package/css/editor.scss +1 -23
  20. package/css/file-uploader.scss +2 -2
  21. package/css/flyout-menu.scss +66 -44
  22. package/css/form.scss +0 -28
  23. package/css/gallery.scss +2 -3
  24. package/css/group-picker.scss +5 -35
  25. package/css/icons.scss +0 -3
  26. package/css/lightbox.scss +2 -4
  27. package/css/mixins.scss +8 -0
  28. package/css/modal.scss +3 -3
  29. package/css/parameters.scss +6 -1
  30. package/css/popover.scss +3 -15
  31. package/css/progress.scss +0 -6
  32. package/css/push-menu.scss +3 -28
  33. package/css/radiobutton.scss +2 -1
  34. package/css/range-slider.scss +1 -7
  35. package/css/scrollbar.scss +2 -6
  36. package/css/sidebar-nav.scss +0 -12
  37. package/css/stepper.scss +0 -4
  38. package/css/style.css +63 -68
  39. package/css/style.css.map +1 -1
  40. package/css/style.min.css +1 -1
  41. package/css/style.min.css.map +1 -1
  42. package/css/style.scss +1 -1
  43. package/css/table.scss +0 -4
  44. package/css/tabs.scss +0 -2
  45. package/css/timeline.scss +1 -13
  46. package/css/timepicker.scss +6 -7
  47. package/css/toast.scss +1 -1
  48. package/css/tooltip.scss +1 -5
  49. package/css/tree.scss +1 -1
  50. package/css/typography.scss +3 -3
  51. package/css/virtual-dropdown.scss +3 -28
  52. package/js/bottom-sheet.d.ts +3 -1
  53. package/js/bottom-sheet.js +26 -27
  54. package/js/calendar.d.ts +7 -0
  55. package/js/calendar.js +14 -33
  56. package/js/carousel.d.ts +2 -0
  57. package/js/carousel.js +13 -5
  58. package/js/chart.d.ts +4 -0
  59. package/js/chart.js +13 -31
  60. package/js/code-viewer.d.ts +1 -0
  61. package/js/code-viewer.js +4 -0
  62. package/js/context-menu.d.ts +9 -2
  63. package/js/context-menu.js +17 -14
  64. package/js/datepicker.d.ts +4 -0
  65. package/js/datepicker.js +26 -11
  66. package/js/dropdown.d.ts +3 -3
  67. package/js/dropdown.js +6 -9
  68. package/js/editor.d.ts +1 -0
  69. package/js/editor.js +9 -3
  70. package/js/file-uploader.d.ts +4 -0
  71. package/js/file-uploader.js +52 -43
  72. package/js/flyout-menu.d.ts +5 -3
  73. package/js/flyout-menu.js +23 -46
  74. package/js/gallery.d.ts +3 -0
  75. package/js/gallery.js +22 -24
  76. package/js/group-picker.d.ts +5 -0
  77. package/js/group-picker.js +12 -17
  78. package/js/lightbox.d.ts +3 -0
  79. package/js/lightbox.js +12 -6
  80. package/js/modal.d.ts +3 -1
  81. package/js/modal.js +14 -11
  82. package/js/popover.d.ts +2 -0
  83. package/js/popover.js +26 -30
  84. package/js/position.d.ts +2 -0
  85. package/js/position.js +1 -5
  86. package/js/push-menu.d.ts +2 -0
  87. package/js/push-menu.js +22 -35
  88. package/js/range-slider.d.ts +1 -0
  89. package/js/range-slider.js +5 -3
  90. package/js/scroll.d.ts +2 -0
  91. package/js/scroll.js +1 -0
  92. package/js/scrollbar.d.ts +2 -0
  93. package/js/scrollbar.js +24 -36
  94. package/js/select.d.ts +1 -0
  95. package/js/select.js +5 -10
  96. package/js/sidebar-nav.d.ts +2 -0
  97. package/js/sidebar-nav.js +8 -0
  98. package/js/stepper.d.ts +2 -0
  99. package/js/stepper.js +7 -1
  100. package/js/table.d.ts +4 -0
  101. package/js/table.js +15 -22
  102. package/js/tabs.d.ts +2 -0
  103. package/js/tabs.js +6 -14
  104. package/js/theme.d.ts +1 -0
  105. package/js/theme.js +5 -13
  106. package/js/timepicker.d.ts +3 -0
  107. package/js/timepicker.js +81 -67
  108. package/js/toast.d.ts +3 -0
  109. package/js/toast.js +24 -15
  110. package/js/tooltip.d.ts +2 -0
  111. package/js/tooltip.js +21 -19
  112. package/js/tree.d.ts +3 -0
  113. package/js/tree.js +13 -0
  114. package/js/utils.d.ts +1 -3
  115. package/js/utils.js +0 -3
  116. package/js/virtual-dropdown.d.ts +3 -0
  117. package/js/virtual-dropdown.js +25 -0
  118. package/package.json +2 -2
package/js/tabs.js CHANGED
@@ -1,4 +1,10 @@
1
+ /** Tabbed content component with horizontal/vertical layouts and keyboard navigation. */
1
2
  class Tabs {
3
+ container;
4
+ options;
5
+ tabItems;
6
+ tabPanels;
7
+ currentTab;
2
8
  constructor(elementOrSelector, options = {}) {
3
9
  const element = typeof elementOrSelector === 'string'
4
10
  ? document.querySelector(elementOrSelector)
@@ -7,7 +13,6 @@ class Tabs {
7
13
  throw new Error(`Tabs: Element not found for selector "${elementOrSelector}"`);
8
14
  }
9
15
  this.container = element;
10
- // Set default options
11
16
  const layout = options.layout || 'horizontal';
12
17
  this.options = {
13
18
  layout,
@@ -24,13 +29,11 @@ class Tabs {
24
29
  * Initializes the tabs component
25
30
  */
26
31
  init() {
27
- // Apply layout class
28
32
  if (this.options.layout === 'vertical') {
29
33
  this.container.classList.add('tabs-vertical');
30
34
  }
31
35
  this.tabItems = this.container.querySelectorAll('.tab-item');
32
36
  this.tabPanels = this.container.querySelectorAll('.tab-panel');
33
- // Validate that we have tabs and panels
34
37
  if (this.tabItems.length === 0) {
35
38
  console.warn('No tab items found in container');
36
39
  return;
@@ -54,17 +57,14 @@ class Tabs {
54
57
  e.preventDefault();
55
58
  this.activateTab(index);
56
59
  });
57
- // Add keyboard navigation for accessibility
58
60
  item.addEventListener('keydown', (e) => {
59
61
  const keyEvent = e;
60
62
  this.handleKeyboardNavigation(keyEvent, index);
61
63
  });
62
- // Set ARIA attributes
63
64
  item.setAttribute('role', 'tab');
64
65
  item.setAttribute('tabindex', index === this.options.defaultTab ? '0' : '-1');
65
66
  item.setAttribute('aria-selected', index === this.options.defaultTab ? 'true' : 'false');
66
67
  });
67
- // Set ARIA attributes for panels
68
68
  this.tabPanels.forEach((panel, index) => {
69
69
  panel.setAttribute('role', 'tabpanel');
70
70
  panel.setAttribute('aria-hidden', index === this.options.defaultTab ? 'false' : 'true');
@@ -125,7 +125,6 @@ class Tabs {
125
125
  console.warn(`Invalid tab index: ${index}`);
126
126
  return;
127
127
  }
128
- // Remove active class from all
129
128
  this.tabItems.forEach((item, i) => {
130
129
  item.classList.remove('active');
131
130
  item.setAttribute('tabindex', '-1');
@@ -135,7 +134,6 @@ class Tabs {
135
134
  panel.classList.remove('active');
136
135
  panel.setAttribute('aria-hidden', 'true');
137
136
  });
138
- // Add active class to selected
139
137
  this.tabItems[index].classList.add('active');
140
138
  this.tabItems[index].setAttribute('tabindex', '0');
141
139
  this.tabItems[index].setAttribute('aria-selected', 'true');
@@ -143,7 +141,6 @@ class Tabs {
143
141
  this.tabPanels[index].setAttribute('aria-hidden', 'false');
144
142
  const previousTab = this.currentTab;
145
143
  this.currentTab = index;
146
- // Call onChange callback if provided
147
144
  if (this.options.onChange && previousTab !== index) {
148
145
  this.options.onChange(index);
149
146
  }
@@ -153,7 +150,6 @@ class Tabs {
153
150
  */
154
151
  goToTab(index) {
155
152
  this.activateTab(index);
156
- // Focus the tab for keyboard users
157
153
  if (this.tabItems[index]) {
158
154
  this.tabItems[index].focus();
159
155
  }
@@ -191,7 +187,6 @@ class Tabs {
191
187
  tab.classList.add('disabled');
192
188
  tab.setAttribute('aria-disabled', 'true');
193
189
  tab.style.pointerEvents = 'none';
194
- // If disabling the current tab, switch to the first enabled tab
195
190
  if (index === this.currentTab) {
196
191
  const firstEnabled = Array.from(this.tabItems).findIndex((item) => !item.classList.contains('disabled'));
197
192
  if (firstEnabled !== -1) {
@@ -203,14 +198,11 @@ class Tabs {
203
198
  * Public API: Destroy the tabs instance and clean up
204
199
  */
205
200
  destroy() {
206
- // Remove event listeners by cloning and replacing nodes
207
201
  this.tabItems.forEach((item) => {
208
202
  const newItem = item.cloneNode(true);
209
203
  item.parentNode?.replaceChild(newItem, item);
210
204
  });
211
- // Remove classes
212
205
  this.container.classList.remove('tabs-vertical');
213
- // Remove ARIA attributes
214
206
  this.tabItems.forEach((item) => {
215
207
  item.removeAttribute('role');
216
208
  item.removeAttribute('tabindex');
package/js/theme.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  type ThemeMode = 'light' | 'dark';
2
+ /** Static class for managing light/dark theme switching with system preference and localStorage persistence. */
2
3
  declare class Theme {
3
4
  private static readonly STORAGE_KEY;
4
5
  private static root;
package/js/theme.js CHANGED
@@ -1,14 +1,17 @@
1
+ /** Static class for managing light/dark theme switching with system preference and localStorage persistence. */
1
2
  class Theme {
3
+ static STORAGE_KEY = 'theme';
4
+ static root;
5
+ static elements = null;
6
+ static mediaQuery = null;
2
7
  /**
3
8
  * Initializes the theme system with toggle functionality and system preference detection
4
9
  */
5
10
  static init() {
6
11
  this.root = document.documentElement;
7
- // Get DOM elements
8
12
  const toggleBtn = document.getElementById('theme-toggle');
9
13
  const icon = document.getElementById('theme-icon');
10
14
  const status = document.getElementById('status');
11
- // Validate required elements
12
15
  if (!toggleBtn || !icon) {
13
16
  console.error('Theme toggle: missing DOM elements', { toggleBtn, icon });
14
17
  if (status) {
@@ -17,16 +20,13 @@ class Theme {
17
20
  return;
18
21
  }
19
22
  this.elements = { toggleBtn, icon, status };
20
- // Initialize media query
21
23
  if (window.matchMedia) {
22
24
  this.mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
23
25
  }
24
- // Apply initial theme
25
26
  const savedTheme = this.getSavedTheme();
26
27
  const systemTheme = this.getSystemTheme();
27
28
  const initialTheme = savedTheme || systemTheme;
28
29
  this.applyTheme(initialTheme);
29
- // Bind event listeners
30
30
  this.bindToggleClick();
31
31
  this.bindKeyboardShortcut();
32
32
  this.bindSystemThemeChange();
@@ -77,10 +77,8 @@ class Theme {
77
77
  this.root.setAttribute('data-theme', theme);
78
78
  const isDark = theme === 'dark';
79
79
  const { toggleBtn, icon } = this.elements;
80
- // Update button state
81
80
  toggleBtn.setAttribute('aria-pressed', String(isDark));
82
81
  toggleBtn.setAttribute('aria-label', `Switch to ${isDark ? 'light' : 'dark'} mode`);
83
- // Update icon — SVG sprite via <use> or font icon via class
84
82
  const useEl = icon.querySelector('use');
85
83
  if (useEl) {
86
84
  const iconName = isDark ? icon.dataset.iconDark : icon.dataset.iconLight;
@@ -151,17 +149,14 @@ class Theme {
151
149
  if (!this.mediaQuery)
152
150
  return;
153
151
  const handler = (e) => {
154
- // Only apply system theme if user hasn't saved a preference
155
152
  if (!this.getSavedTheme()) {
156
153
  const matches = 'matches' in e ? e.matches : e.matches;
157
154
  this.applyTheme(matches ? 'dark' : 'light');
158
155
  }
159
156
  };
160
- // Modern API
161
157
  if ('addEventListener' in this.mediaQuery) {
162
158
  this.mediaQuery.addEventListener('change', handler);
163
159
  }
164
- // Legacy API (deprecated but still supported in older browsers)
165
160
  else if ('addListener' in this.mediaQuery) {
166
161
  this.mediaQuery.addListener(handler);
167
162
  }
@@ -199,7 +194,4 @@ class Theme {
199
194
  return this.getSavedTheme() !== null;
200
195
  }
201
196
  }
202
- Theme.STORAGE_KEY = 'theme';
203
- Theme.elements = null;
204
- Theme.mediaQuery = null;
205
197
  export { Theme };
@@ -1,7 +1,9 @@
1
+ /** A start/end time pair as HH:MM strings. */
1
2
  interface TimeSpan {
2
3
  start: string;
3
4
  end: string;
4
5
  }
6
+ /** Configuration options for a TimeSpanPicker instance. */
5
7
  interface TimeSpanPickerOptions {
6
8
  onChange?: (start: string, end: string) => void;
7
9
  defaultStart?: string;
@@ -9,6 +11,7 @@ interface TimeSpanPickerOptions {
9
11
  fromString?: string;
10
12
  toString?: string;
11
13
  }
14
+ /** Interactive time-range picker with a draggable bar and dual time inputs. */
12
15
  declare class TimeSpanPicker {
13
16
  private container;
14
17
  private startTimeInput;
package/js/timepicker.js CHANGED
@@ -1,72 +1,20 @@
1
+ /** Interactive time-range picker with a draggable bar and dual time inputs. */
1
2
  class TimeSpanPicker {
3
+ container;
4
+ startTimeInput;
5
+ endTimeInput;
6
+ onChange;
7
+ uid;
8
+ fromString;
9
+ toLabel;
10
+ pickerEl;
11
+ durationEl;
12
+ barEl;
13
+ barFillEl;
14
+ startHandleEl;
15
+ endHandleEl;
16
+ dragState = null;
2
17
  constructor(elementOrSelector, options) {
3
- this.dragState = null;
4
- this.handleChange = () => {
5
- this.updateUI();
6
- const start = this.startTimeInput.value;
7
- const end = this.endTimeInput.value;
8
- if (this.onChange && start && end) {
9
- this.onChange(start, end);
10
- }
11
- };
12
- this.onStartHandleDown = (e) => {
13
- e.stopPropagation();
14
- e.preventDefault();
15
- this.beginDrag('start');
16
- };
17
- this.onEndHandleDown = (e) => {
18
- e.stopPropagation();
19
- e.preventDefault();
20
- this.beginDrag('end');
21
- };
22
- this.onFillDown = (e) => {
23
- if (e.target.classList.contains('timespan-handle'))
24
- return;
25
- e.preventDefault();
26
- const start = this.startTimeInput.value;
27
- if (!start)
28
- return;
29
- const rect = this.barEl.getBoundingClientRect();
30
- const clickMins = ((e.clientX - rect.left) / rect.width) * 1440;
31
- this.beginDrag('move', clickMins - this.toMinutes(start), rect);
32
- };
33
- this.onPointerMove = (e) => {
34
- if (!this.dragState)
35
- return;
36
- e.preventDefault();
37
- const { type, barLeft, barWidth, startMins, endMins, clickOffsetMins } = this.dragState;
38
- const pct = Math.max(0, Math.min(1, (e.clientX - barLeft) / barWidth));
39
- const rawMins = pct * 1440;
40
- let start = this.startTimeInput.value;
41
- let end = this.endTimeInput.value;
42
- if (type === 'start') {
43
- start = this.minutesToTime(Math.max(0, Math.min(endMins - 5, this.snap(rawMins))));
44
- this.startTimeInput.value = start;
45
- }
46
- else if (type === 'end') {
47
- end = this.minutesToTime(Math.max(startMins + 5, Math.min(1440, this.snap(rawMins))));
48
- this.endTimeInput.value = end;
49
- }
50
- else {
51
- const duration = endMins - startMins;
52
- const newStartMins = Math.max(0, Math.min(1440 - duration, this.snap(rawMins - clickOffsetMins)));
53
- start = this.minutesToTime(newStartMins);
54
- end = this.minutesToTime(newStartMins + duration);
55
- this.startTimeInput.value = start;
56
- this.endTimeInput.value = end;
57
- }
58
- this.updateUI();
59
- if (this.onChange)
60
- this.onChange(start, end);
61
- };
62
- this.onPointerUp = () => {
63
- if (!this.dragState)
64
- return;
65
- this.dragState = null;
66
- this.barEl.classList.remove('is-dragging');
67
- document.removeEventListener('pointermove', this.onPointerMove);
68
- document.removeEventListener('pointerup', this.onPointerUp);
69
- };
70
18
  const element = typeof elementOrSelector === 'string'
71
19
  ? (elementOrSelector.startsWith('#') || elementOrSelector.startsWith('.')
72
20
  ? document.querySelector(elementOrSelector)
@@ -127,6 +75,14 @@ class TimeSpanPicker {
127
75
  </div>
128
76
  `;
129
77
  }
78
+ handleChange = () => {
79
+ this.updateUI();
80
+ const start = this.startTimeInput.value;
81
+ const end = this.endTimeInput.value;
82
+ if (this.onChange && start && end) {
83
+ this.onChange(start, end);
84
+ }
85
+ };
130
86
  attachEventListeners() {
131
87
  this.startTimeInput.addEventListener('change', this.handleChange);
132
88
  this.endTimeInput.addEventListener('change', this.handleChange);
@@ -160,6 +116,64 @@ class TimeSpanPicker {
160
116
  document.addEventListener('pointermove', this.onPointerMove);
161
117
  document.addEventListener('pointerup', this.onPointerUp);
162
118
  }
119
+ onStartHandleDown = (e) => {
120
+ e.stopPropagation();
121
+ e.preventDefault();
122
+ this.beginDrag('start');
123
+ };
124
+ onEndHandleDown = (e) => {
125
+ e.stopPropagation();
126
+ e.preventDefault();
127
+ this.beginDrag('end');
128
+ };
129
+ onFillDown = (e) => {
130
+ if (e.target.classList.contains('timespan-handle'))
131
+ return;
132
+ e.preventDefault();
133
+ const start = this.startTimeInput.value;
134
+ if (!start)
135
+ return;
136
+ const rect = this.barEl.getBoundingClientRect();
137
+ const clickMins = ((e.clientX - rect.left) / rect.width) * 1440;
138
+ this.beginDrag('move', clickMins - this.toMinutes(start), rect);
139
+ };
140
+ onPointerMove = (e) => {
141
+ if (!this.dragState)
142
+ return;
143
+ e.preventDefault();
144
+ const { type, barLeft, barWidth, startMins, endMins, clickOffsetMins } = this.dragState;
145
+ const pct = Math.max(0, Math.min(1, (e.clientX - barLeft) / barWidth));
146
+ const rawMins = pct * 1440;
147
+ let start = this.startTimeInput.value;
148
+ let end = this.endTimeInput.value;
149
+ if (type === 'start') {
150
+ start = this.minutesToTime(Math.max(0, Math.min(endMins - 5, this.snap(rawMins))));
151
+ this.startTimeInput.value = start;
152
+ }
153
+ else if (type === 'end') {
154
+ end = this.minutesToTime(Math.max(startMins + 5, Math.min(1440, this.snap(rawMins))));
155
+ this.endTimeInput.value = end;
156
+ }
157
+ else {
158
+ const duration = endMins - startMins;
159
+ const newStartMins = Math.max(0, Math.min(1440 - duration, this.snap(rawMins - clickOffsetMins)));
160
+ start = this.minutesToTime(newStartMins);
161
+ end = this.minutesToTime(newStartMins + duration);
162
+ this.startTimeInput.value = start;
163
+ this.endTimeInput.value = end;
164
+ }
165
+ this.updateUI();
166
+ if (this.onChange)
167
+ this.onChange(start, end);
168
+ };
169
+ onPointerUp = () => {
170
+ if (!this.dragState)
171
+ return;
172
+ this.dragState = null;
173
+ this.barEl.classList.remove('is-dragging');
174
+ document.removeEventListener('pointermove', this.onPointerMove);
175
+ document.removeEventListener('pointerup', this.onPointerUp);
176
+ };
163
177
  toMinutes(time) {
164
178
  const [h, m] = time.split(':').map(Number);
165
179
  return h * 60 + m;
package/js/toast.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  type ToastType = 'success' | 'error' | 'warning' | 'info';
2
+ /** Options for creating a Toast notification. */
2
3
  interface ToastOptions {
3
4
  content: string;
4
5
  header?: string;
5
6
  type?: ToastType;
6
7
  closeable?: boolean;
7
8
  }
9
+ /** Dismissible notification banner with optional auto-hide timer and progress bar. */
8
10
  declare class Toast {
9
11
  private readonly content;
10
12
  private readonly header;
@@ -19,6 +21,7 @@ declare class Toast {
19
21
  show(ms?: number): void;
20
22
  hide: () => void;
21
23
  private startTimer;
24
+ destroy(): void;
22
25
  private buildTemplate;
23
26
  }
24
27
  export { Toast };
package/js/toast.js CHANGED
@@ -1,21 +1,15 @@
1
1
  import { escapeHtml } from './utils.js';
2
+ /** Dismissible notification banner with optional auto-hide timer and progress bar. */
2
3
  class Toast {
4
+ content;
5
+ header;
6
+ type;
7
+ closeable;
8
+ closureIcon = '<div class="icon icon-close close"></div>';
9
+ template;
10
+ toastElement = null;
11
+ timerId = null;
3
12
  constructor(contentOrOptions, header = '', type, closeable = true) {
4
- this.closureIcon = '<div class="icon icon-close close"></div>';
5
- this.toastElement = null;
6
- this.timerId = null;
7
- this.hide = () => {
8
- if (this.timerId !== null) {
9
- clearTimeout(this.timerId);
10
- this.timerId = null;
11
- }
12
- this.toastElement?.classList.remove('show');
13
- setTimeout(() => {
14
- this.toastElement?.querySelector('.close')?.removeEventListener('click', this.hide);
15
- this.toastElement?.remove();
16
- this.toastElement = null;
17
- }, 150);
18
- };
19
13
  if (typeof contentOrOptions === 'object') {
20
14
  this.content = contentOrOptions.content;
21
15
  this.header = contentOrOptions.header ?? '';
@@ -52,6 +46,18 @@ class Toast {
52
46
  });
53
47
  });
54
48
  }
49
+ hide = () => {
50
+ if (this.timerId !== null) {
51
+ clearTimeout(this.timerId);
52
+ this.timerId = null;
53
+ }
54
+ this.toastElement?.classList.remove('show');
55
+ setTimeout(() => {
56
+ this.toastElement?.querySelector('.close')?.removeEventListener('click', this.hide);
57
+ this.toastElement?.remove();
58
+ this.toastElement = null;
59
+ }, 150);
60
+ };
55
61
  startTimer(ms, elapsed = 0) {
56
62
  const stepSize = 250;
57
63
  if (elapsed >= ms) {
@@ -68,6 +74,9 @@ class Toast {
68
74
  }
69
75
  }, stepSize);
70
76
  }
77
+ destroy() {
78
+ this.hide();
79
+ }
71
80
  buildTemplate() {
72
81
  const parts = ['<div class="bar"></div>'];
73
82
  if (this.closeable) {
package/js/tooltip.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /** Configuration options for a Tooltip instance. */
1
2
  interface TooltipOptions {
2
3
  position?: 'top' | 'bottom' | 'left' | 'right' | 'auto';
3
4
  offset?: number;
@@ -7,6 +8,7 @@ interface TooltipOptions {
7
8
  * Defaults to false — content is treated as plain text and escaped. */
8
9
  isHtml?: boolean;
9
10
  }
11
+ /** Lightweight tooltip that positions itself relative to a trigger element. */
10
12
  declare class Tooltip {
11
13
  private static activeTooltip;
12
14
  private static idCounter;
package/js/tooltip.js CHANGED
@@ -1,22 +1,15 @@
1
- // tooltip.ts
2
1
  import { computePosition } from './position.js';
2
+ /** Lightweight tooltip that positions itself relative to a trigger element. */
3
3
  class Tooltip {
4
+ static activeTooltip = null;
5
+ static idCounter = 0;
6
+ trigger;
7
+ content;
8
+ options;
9
+ tooltipElement = null;
10
+ showTimeout = null;
11
+ isVisible = false;
4
12
  constructor(trigger, content, options = {}) {
5
- this.tooltipElement = null;
6
- this.showTimeout = null;
7
- this.isVisible = false;
8
- this.handleMouseEnter = () => {
9
- this.show();
10
- };
11
- this.handleMouseLeave = () => {
12
- this.hide();
13
- };
14
- this.handleFocus = () => {
15
- this.show();
16
- };
17
- this.handleBlur = () => {
18
- this.hide();
19
- };
20
13
  this.trigger = trigger;
21
14
  this.content = content;
22
15
  this.options = {
@@ -38,7 +31,6 @@ class Tooltip {
38
31
  new Tooltip(trigger, content, { position, className, isHtml: false });
39
32
  }
40
33
  });
41
- // Also support content from separate elements
42
34
  const advancedTriggers = document.querySelectorAll('[data-tooltip-id]');
43
35
  advancedTriggers.forEach(trigger => {
44
36
  const contentId = trigger.getAttribute('data-tooltip-id');
@@ -127,6 +119,18 @@ class Tooltip {
127
119
  this.trigger.addEventListener('focus', this.handleFocus);
128
120
  this.trigger.addEventListener('blur', this.handleBlur);
129
121
  }
122
+ handleMouseEnter = () => {
123
+ this.show();
124
+ };
125
+ handleMouseLeave = () => {
126
+ this.hide();
127
+ };
128
+ handleFocus = () => {
129
+ this.show();
130
+ };
131
+ handleBlur = () => {
132
+ this.hide();
133
+ };
130
134
  destroy() {
131
135
  this.hide();
132
136
  this.trigger.removeEventListener('mouseenter', this.handleMouseEnter);
@@ -143,6 +147,4 @@ class Tooltip {
143
147
  this.trigger.removeAttribute('data-previous-describedby');
144
148
  }
145
149
  }
146
- Tooltip.activeTooltip = null;
147
- Tooltip.idCounter = 0;
148
150
  export { Tooltip };
package/js/tree.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  type NodeType = 'file' | 'folder';
2
+ /** Configuration options for a TreeComponent instance. */
2
3
  interface TreeOptions {
3
4
  onSelect?: (node: TreeNode) => void;
4
5
  }
6
+ /** Represents a single node in a tree structure, either a file or folder. */
5
7
  declare class TreeNode {
6
8
  label: string;
7
9
  type: NodeType;
@@ -12,6 +14,7 @@ declare class TreeNode {
12
14
  childrenContainer: HTMLUListElement | null;
13
15
  constructor(label: string, type?: NodeType, children?: TreeNode[]);
14
16
  }
17
+ /** Renders an interactive collapsible tree view from a list of TreeNode objects. */
15
18
  declare class TreeComponent {
16
19
  private container;
17
20
  private data;
package/js/tree.js CHANGED
@@ -1,4 +1,12 @@
1
+ /** Represents a single node in a tree structure, either a file or folder. */
1
2
  class TreeNode {
3
+ label;
4
+ type;
5
+ children;
6
+ expanded;
7
+ selected;
8
+ element;
9
+ childrenContainer;
2
10
  constructor(label, type = 'file', children = []) {
3
11
  this.label = label;
4
12
  this.type = type;
@@ -9,7 +17,12 @@ class TreeNode {
9
17
  this.childrenContainer = null;
10
18
  }
11
19
  }
20
+ /** Renders an interactive collapsible tree view from a list of TreeNode objects. */
12
21
  class TreeComponent {
22
+ container;
23
+ data;
24
+ selectedNode;
25
+ options;
13
26
  constructor(elementOrSelector, data, options = {}) {
14
27
  const container = typeof elementOrSelector === 'string'
15
28
  ? document.querySelector(elementOrSelector)
package/js/utils.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- /**
2
- * Utility functions for DOM manipulation and element handling
3
- */
1
+ /** Shape of the `utils` helper object. */
4
2
  interface Utils {
5
3
  ready(fn: () => void): void;
6
4
  value(element: HTMLElement): string;
package/js/utils.js CHANGED
@@ -1,6 +1,3 @@
1
- /**
2
- * Utility functions for DOM manipulation and element handling
3
- */
4
1
  const utils = {
5
2
  /**
6
3
  * Execute a function when the DOM is ready
@@ -1,7 +1,9 @@
1
+ /** A single selectable option for a VirtualDropdown. */
1
2
  interface DropdownOption {
2
3
  label: string;
3
4
  value: string | number;
4
5
  }
6
+ /** Configuration for a VirtualDropdown instance. */
5
7
  interface VirtualDropdownConfig {
6
8
  container: string | HTMLElement;
7
9
  options: DropdownOption[];
@@ -12,6 +14,7 @@ interface VirtualDropdownConfig {
12
14
  itemHeight?: number;
13
15
  onSelect?: (selectedValues: Array<string | number>) => void;
14
16
  }
17
+ /** Virtualised dropdown that renders only visible items for performance with large option lists. */
15
18
  declare class VirtualDropdown {
16
19
  private readonly container;
17
20
  private readonly options;
@@ -1,5 +1,30 @@
1
1
  import { escapeHtml } from './utils.js';
2
+ /** Virtualised dropdown that renders only visible items for performance with large option lists. */
2
3
  class VirtualDropdown {
4
+ container;
5
+ options;
6
+ multiSelect;
7
+ searchable;
8
+ placeholder;
9
+ renderLimit;
10
+ itemHeight;
11
+ onSelect;
12
+ // Unique CSS anchor name for this instance — prevents conflicts when
13
+ // multiple dropdowns exist on the same page.
14
+ anchorName;
15
+ trigger;
16
+ triggerText;
17
+ menu;
18
+ listWrapper;
19
+ scroller;
20
+ spacer;
21
+ content;
22
+ searchInput;
23
+ selectedValues;
24
+ filteredOptions;
25
+ isOpen;
26
+ scrollTop;
27
+ boundHandlers;
3
28
  constructor(config) {
4
29
  const containerElement = typeof config.container === 'string'
5
30
  ? document.querySelector(config.container)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dodlhuat/basix",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "Basix is intended as a starter for the rapid development of a design. Each design element can be added individually to include only the data required. It is using plain javascript / typescript and therefore is not dependent on any plugin.",
5
5
  "exports": {
6
6
  "./css/*": "./css/*",
@@ -33,6 +33,6 @@
33
33
  },
34
34
  "homepage": "https://github.com/dodlhuat/basix#readme",
35
35
  "devDependencies": {
36
- "typescript": "^5.9.3"
36
+ "typescript": "^6.0.0"
37
37
  }
38
38
  }