@dodlhuat/basix 1.3.1 → 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 +14 -8
  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 -67
  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 +5 -21
  16. package/css/datepicker.scss +6 -9
  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 +65 -58
  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 +9 -16
  36. package/css/sidebar-nav.scss +0 -12
  37. package/css/stepper.scss +0 -4
  38. package/css/style.css +108 -116
  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 +55 -39
  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 -1
  69. package/js/editor.js +14 -20
  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 +4 -0
  75. package/js/gallery.js +39 -50
  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 -1
  87. package/js/push-menu.js +25 -48
  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 +22 -5
  107. package/js/timepicker.js +160 -57
  108. package/js/toast.d.ts +3 -1
  109. package/js/toast.js +25 -22
  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/timepicker.js CHANGED
@@ -1,7 +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.handleStartChange = () => { this.handleChange(); };
4
- this.handleEndChange = () => { this.handleChange(); };
5
18
  const element = typeof elementOrSelector === 'string'
6
19
  ? (elementOrSelector.startsWith('#') || elementOrSelector.startsWith('.')
7
20
  ? document.querySelector(elementOrSelector)
@@ -14,28 +27,25 @@ class TimeSpanPicker {
14
27
  this.onChange = options?.onChange;
15
28
  this.uid = `tsp-${Math.random().toString(36).slice(2, 9)}`;
16
29
  this.fromString = options?.fromString ?? 'From';
17
- this.toString = options?.toString ?? 'To';
30
+ this.toLabel = options?.toString ?? 'To';
18
31
  this.render();
19
- this.startTimeInput = this.queryInput('.timespan-start');
20
- this.endTimeInput = this.queryInput('.timespan-end');
21
- if (options?.defaultStart) {
32
+ this.startTimeInput = this.queryEl('.timespan-start');
33
+ this.endTimeInput = this.queryEl('.timespan-end');
34
+ if (options?.defaultStart)
22
35
  this.startTimeInput.value = options.defaultStart;
23
- }
24
- if (options?.defaultEnd) {
36
+ if (options?.defaultEnd)
25
37
  this.endTimeInput.value = options.defaultEnd;
26
- }
27
38
  this.attachEventListeners();
28
- // Render initial state if defaults provided
39
+ this.attachBarListeners();
29
40
  if (options?.defaultStart || options?.defaultEnd) {
30
41
  this.updateUI();
31
42
  }
32
43
  }
33
- queryInput(selector) {
34
- const input = this.container.querySelector(selector);
35
- if (!input) {
36
- throw new Error(`Input with selector "${selector}" not found`);
37
- }
38
- return input;
44
+ queryEl(selector) {
45
+ const el = this.container.querySelector(selector);
46
+ if (!el)
47
+ throw new Error(`TimeSpanPicker: "${selector}" not found`);
48
+ return el;
39
49
  }
40
50
  render() {
41
51
  const startId = `${this.uid}-start`;
@@ -53,23 +63,129 @@ class TimeSpanPicker {
53
63
  </div>
54
64
 
55
65
  <div class="timespan-field timespan-field-end">
56
- <label for="${endId}">${this.toString}</label>
66
+ <label for="${endId}">${this.toLabel}</label>
57
67
  <input type="time" class="timespan-end" id="${endId}"/>
58
68
  </div>
59
69
  </div>
60
70
  <div class="timespan-bar" aria-hidden="true">
61
- <div class="timespan-bar-fill"></div>
71
+ <div class="timespan-bar-fill">
72
+ <div class="timespan-handle timespan-handle-start"></div>
73
+ <div class="timespan-handle timespan-handle-end"></div>
74
+ </div>
62
75
  </div>
63
76
  `;
64
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
+ };
65
86
  attachEventListeners() {
66
- this.startTimeInput.addEventListener('change', this.handleStartChange);
67
- this.endTimeInput.addEventListener('change', this.handleEndChange);
87
+ this.startTimeInput.addEventListener('change', this.handleChange);
88
+ this.endTimeInput.addEventListener('change', this.handleChange);
68
89
  }
90
+ attachBarListeners() {
91
+ this.pickerEl = this.queryEl('.timespan-picker');
92
+ this.durationEl = this.queryEl('.timespan-duration');
93
+ this.barEl = this.queryEl('.timespan-bar');
94
+ this.barFillEl = this.queryEl('.timespan-bar-fill');
95
+ this.startHandleEl = this.queryEl('.timespan-handle-start');
96
+ this.endHandleEl = this.queryEl('.timespan-handle-end');
97
+ this.startHandleEl.addEventListener('pointerdown', this.onStartHandleDown);
98
+ this.endHandleEl.addEventListener('pointerdown', this.onEndHandleDown);
99
+ this.barFillEl.addEventListener('pointerdown', this.onFillDown);
100
+ }
101
+ beginDrag(type, clickOffsetMins = 0, rect) {
102
+ const start = this.startTimeInput.value;
103
+ const end = this.endTimeInput.value;
104
+ if (!start || !end)
105
+ return;
106
+ const barRect = rect ?? this.barEl.getBoundingClientRect();
107
+ this.dragState = {
108
+ type,
109
+ barLeft: barRect.left,
110
+ barWidth: barRect.width,
111
+ startMins: this.toMinutes(start),
112
+ endMins: this.toMinutes(end),
113
+ clickOffsetMins,
114
+ };
115
+ this.barEl.classList.add('is-dragging');
116
+ document.addEventListener('pointermove', this.onPointerMove);
117
+ document.addEventListener('pointerup', this.onPointerUp);
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
+ };
69
177
  toMinutes(time) {
70
178
  const [h, m] = time.split(':').map(Number);
71
179
  return h * 60 + m;
72
180
  }
181
+ minutesToTime(minutes) {
182
+ const h = Math.floor(minutes / 60);
183
+ const m = minutes % 60;
184
+ return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}`;
185
+ }
186
+ snap(mins) {
187
+ return Math.round(mins / 5) * 5;
188
+ }
73
189
  formatDuration(minutes) {
74
190
  const h = Math.floor(minutes / 60);
75
191
  const m = minutes % 60;
@@ -80,44 +196,31 @@ class TimeSpanPicker {
80
196
  return `${m}m`;
81
197
  }
82
198
  updateUI() {
83
- const picker = this.container.querySelector('.timespan-picker');
84
- const durationEl = this.container.querySelector('.timespan-duration');
85
- const barFill = this.container.querySelector('.timespan-bar-fill');
86
199
  const start = this.startTimeInput.value;
87
200
  const end = this.endTimeInput.value;
88
201
  const isError = !!(start && end && start >= end);
89
- picker?.classList.toggle('is-error', isError);
202
+ this.pickerEl.classList.toggle('is-error', isError);
90
203
  if (isError) {
91
204
  this.endTimeInput.setCustomValidity('End time must be after start time');
92
- if (durationEl)
93
- durationEl.textContent = '!';
205
+ this.durationEl.textContent = '!';
206
+ this.barFillEl.classList.remove('is-active');
94
207
  return;
95
208
  }
96
209
  this.endTimeInput.setCustomValidity('');
97
- if (start && end && durationEl && barFill) {
210
+ if (start && end) {
98
211
  const startMins = this.toMinutes(start);
99
212
  const endMins = this.toMinutes(end);
100
213
  const duration = endMins - startMins;
101
- durationEl.textContent = this.formatDuration(duration);
102
- const startPct = ((startMins / 1440) * 100).toFixed(2);
103
- const widthPct = ((duration / 1440) * 100).toFixed(2);
104
- barFill.style.left = `${startPct}%`;
105
- barFill.style.width = `${widthPct}%`;
214
+ this.durationEl.textContent = this.formatDuration(duration);
215
+ this.barFillEl.style.left = `${((startMins / 1440) * 100).toFixed(2)}%`;
216
+ this.barFillEl.style.width = `${((duration / 1440) * 100).toFixed(2)}%`;
217
+ this.barFillEl.classList.add('is-active');
106
218
  }
107
219
  else {
108
- if (durationEl)
109
- durationEl.textContent = '';
110
- if (barFill) {
111
- barFill.style.left = '0';
112
- barFill.style.width = '0';
113
- }
114
- }
115
- }
116
- handleChange() {
117
- this.updateUI();
118
- const { start, end } = this.getValue();
119
- if (this.onChange && start && end) {
120
- this.onChange(start, end);
220
+ this.durationEl.textContent = '';
221
+ this.barFillEl.style.left = '0';
222
+ this.barFillEl.style.width = '0';
223
+ this.barFillEl.classList.remove('is-active');
121
224
  }
122
225
  }
123
226
  getValue() {
@@ -135,24 +238,24 @@ class TimeSpanPicker {
135
238
  this.startTimeInput.value = '';
136
239
  this.endTimeInput.value = '';
137
240
  this.endTimeInput.setCustomValidity('');
138
- const picker = this.container.querySelector('.timespan-picker');
139
- const durationEl = this.container.querySelector('.timespan-duration');
140
- const barFill = this.container.querySelector('.timespan-bar-fill');
141
- picker?.classList.remove('is-error');
142
- if (durationEl)
143
- durationEl.textContent = '';
144
- if (barFill) {
145
- barFill.style.left = '0';
146
- barFill.style.width = '0';
147
- }
241
+ this.pickerEl.classList.remove('is-error');
242
+ this.durationEl.textContent = '';
243
+ this.barFillEl.style.left = '0';
244
+ this.barFillEl.style.width = '0';
245
+ this.barFillEl.classList.remove('is-active');
148
246
  }
149
247
  isValid() {
150
248
  const { start, end } = this.getValue();
151
249
  return !!(start && end && start < end);
152
250
  }
153
251
  destroy() {
154
- this.startTimeInput.removeEventListener('change', this.handleStartChange);
155
- this.endTimeInput.removeEventListener('change', this.handleEndChange);
252
+ this.startTimeInput.removeEventListener('change', this.handleChange);
253
+ this.endTimeInput.removeEventListener('change', this.handleChange);
254
+ this.startHandleEl.removeEventListener('pointerdown', this.onStartHandleDown);
255
+ this.endHandleEl.removeEventListener('pointerdown', this.onEndHandleDown);
256
+ this.barFillEl.removeEventListener('pointerdown', this.onFillDown);
257
+ document.removeEventListener('pointermove', this.onPointerMove);
258
+ document.removeEventListener('pointerup', this.onPointerUp);
156
259
  }
157
260
  }
158
261
  export { TimeSpanPicker };
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;
@@ -18,8 +20,8 @@ declare class Toast {
18
20
  constructor(content: string, header?: string, type?: ToastType, closeable?: boolean);
19
21
  show(ms?: number): void;
20
22
  hide: () => void;
21
- private handleClose;
22
23
  private startTimer;
24
+ destroy(): void;
23
25
  private buildTemplate;
24
26
  }
25
27
  export { Toast };
package/js/toast.js CHANGED
@@ -1,27 +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
- const closeButton = this.toastElement?.querySelector('.close');
15
- if (closeButton) {
16
- closeButton.removeEventListener('click', this.handleClose);
17
- }
18
- this.toastElement?.remove();
19
- this.toastElement = null;
20
- }, 150);
21
- };
22
- this.handleClose = () => {
23
- this.hide();
24
- };
25
13
  if (typeof contentOrOptions === 'object') {
26
14
  this.content = contentOrOptions.content;
27
15
  this.header = contentOrOptions.header ?? '';
@@ -50,7 +38,7 @@ class Toast {
50
38
  this.toastElement?.classList.add('show');
51
39
  const closeButton = this.toastElement?.querySelector('.close');
52
40
  if (closeButton) {
53
- closeButton.addEventListener('click', this.handleClose);
41
+ closeButton.addEventListener('click', this.hide);
54
42
  }
55
43
  if (ms !== undefined && ms > 0) {
56
44
  this.startTimer(ms);
@@ -58,6 +46,18 @@ class Toast {
58
46
  });
59
47
  });
60
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
+ };
61
61
  startTimer(ms, elapsed = 0) {
62
62
  const stepSize = 250;
63
63
  if (elapsed >= ms) {
@@ -74,6 +74,9 @@ class Toast {
74
74
  }
75
75
  }, stepSize);
76
76
  }
77
+ destroy() {
78
+ this.hide();
79
+ }
77
80
  buildTemplate() {
78
81
  const parts = ['<div class="bar"></div>'];
79
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.1",
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
  }