@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/table.js CHANGED
@@ -1,7 +1,19 @@
1
1
  import { Select } from "./select.js";
2
+ /** Dynamic data table with sorting, filtering, and pagination. */
2
3
  class Table {
4
+ container;
5
+ data;
6
+ columns;
7
+ pageSize;
8
+ currentPage;
9
+ sortColumn;
10
+ sortDirection;
11
+ filterText;
12
+ tableBody;
13
+ tableHeader;
14
+ paginationContainer;
15
+ abortController = new AbortController();
3
16
  constructor(elementOrSelector, options = {}) {
4
- this.abortController = new AbortController();
5
17
  const element = typeof elementOrSelector === 'string'
6
18
  ? document.querySelector(elementOrSelector)
7
19
  : elementOrSelector;
@@ -32,14 +44,12 @@ class Table {
32
44
  const tbody = table.querySelector('tbody');
33
45
  if (!thead || !tbody)
34
46
  return;
35
- // Parse columns from header
36
47
  const ths = thead.querySelectorAll('th');
37
48
  this.columns = Array.from(ths).map((th, index) => ({
38
49
  key: `col${index}`,
39
50
  label: th.textContent?.trim() || '',
40
51
  sortable: true
41
52
  }));
42
- // Parse data from body rows
43
53
  const trs = tbody.querySelectorAll('tr');
44
54
  this.data = Array.from(trs).map(tr => {
45
55
  const row = {};
@@ -51,7 +61,6 @@ class Table {
51
61
  });
52
62
  return row;
53
63
  });
54
- // Clear the existing static table
55
64
  this.container.innerHTML = '';
56
65
  }
57
66
  /**
@@ -68,7 +77,6 @@ class Table {
68
77
  renderControls() {
69
78
  const controlsDiv = document.createElement('div');
70
79
  controlsDiv.className = 'table-controls';
71
- // Search input
72
80
  const searchInput = document.createElement('input');
73
81
  searchInput.type = 'text';
74
82
  searchInput.placeholder = 'Search...';
@@ -77,7 +85,6 @@ class Table {
77
85
  this.handleSearch(e.target.value);
78
86
  }, { signal: this.abortController.signal });
79
87
  controlsDiv.appendChild(searchInput);
80
- // Page size selector
81
88
  const selectGroup = document.createElement('div');
82
89
  selectGroup.className = 'select-group';
83
90
  const label = document.createElement('label');
@@ -110,7 +117,6 @@ class Table {
110
117
  const table = document.createElement('table');
111
118
  const thead = document.createElement('thead');
112
119
  const tbody = document.createElement('tbody');
113
- // Create header row
114
120
  const tr = document.createElement('tr');
115
121
  this.columns.forEach(col => {
116
122
  const th = document.createElement('th');
@@ -127,7 +133,6 @@ class Table {
127
133
  table.appendChild(tbody);
128
134
  wrapper.appendChild(table);
129
135
  this.container.appendChild(wrapper);
130
- // Create pagination container
131
136
  const paginationDiv = document.createElement('div');
132
137
  paginationDiv.className = 'pagination';
133
138
  this.container.appendChild(paginationDiv);
@@ -140,7 +145,6 @@ class Table {
140
145
  */
141
146
  getFilteredAndSortedData() {
142
147
  let processedData = [...this.data];
143
- // Apply filter
144
148
  if (this.filterText) {
145
149
  const lowerFilter = this.filterText.toLowerCase();
146
150
  processedData = processedData.filter(row => {
@@ -150,12 +154,10 @@ class Table {
150
154
  });
151
155
  });
152
156
  }
153
- // Apply sort
154
157
  if (this.sortColumn) {
155
158
  processedData.sort((a, b) => {
156
159
  const valA = a[this.sortColumn];
157
160
  const valB = b[this.sortColumn];
158
- // Handle null/undefined values
159
161
  if (valA == null && valB == null)
160
162
  return 0;
161
163
  if (valA == null)
@@ -178,7 +180,6 @@ class Table {
178
180
  const processedData = this.getFilteredAndSortedData();
179
181
  const totalItems = processedData.length;
180
182
  const totalPages = Math.ceil(totalItems / this.pageSize);
181
- // Ensure current page is valid
182
183
  if (this.currentPage > totalPages && totalPages > 0) {
183
184
  this.currentPage = totalPages;
184
185
  }
@@ -212,7 +213,7 @@ class Table {
212
213
  this.columns.forEach(col => {
213
214
  const td = document.createElement('td');
214
215
  td.textContent = String(row[col.key] ?? '');
215
- td.setAttribute('data-label', col.label); // For mobile view
216
+ td.setAttribute('data-label', col.label);
216
217
  tr.appendChild(td);
217
218
  });
218
219
  this.tableBody.appendChild(tr);
@@ -237,28 +238,23 @@ class Table {
237
238
  this.paginationContainer.innerHTML = '';
238
239
  if (totalItems === 0)
239
240
  return;
240
- // Info text
241
241
  const info = document.createElement('div');
242
242
  info.className = 'pagination-info';
243
243
  info.textContent = `Showing ${startIndex + 1} to ${endIndex} of ${totalItems} entries`;
244
244
  this.paginationContainer.appendChild(info);
245
- // Pagination buttons
246
245
  const buttonsDiv = document.createElement('div');
247
246
  buttonsDiv.className = 'pagination-buttons';
248
- // Previous button
249
247
  const prevBtn = document.createElement('button');
250
248
  prevBtn.className = 'page-btn';
251
249
  prevBtn.textContent = 'Previous';
252
250
  prevBtn.disabled = this.currentPage === 1;
253
251
  prevBtn.addEventListener('click', () => this.setPage(this.currentPage - 1));
254
252
  buttonsDiv.appendChild(prevBtn);
255
- // Calculate page range to display (max 5 pages)
256
253
  let startPage = Math.max(1, this.currentPage - 2);
257
254
  let endPage = Math.min(totalPages, startPage + 4);
258
255
  if (endPage - startPage < 4) {
259
256
  startPage = Math.max(1, endPage - 4);
260
257
  }
261
- // Page number buttons
262
258
  for (let i = startPage; i <= endPage; i++) {
263
259
  const btn = document.createElement('button');
264
260
  btn.className = `page-btn ${i === this.currentPage ? 'active' : ''}`;
@@ -266,7 +262,6 @@ class Table {
266
262
  btn.addEventListener('click', () => this.setPage(i));
267
263
  buttonsDiv.appendChild(btn);
268
264
  }
269
- // Next button
270
265
  const nextBtn = document.createElement('button');
271
266
  nextBtn.className = 'page-btn';
272
267
  nextBtn.textContent = 'Next';
@@ -280,7 +275,7 @@ class Table {
280
275
  */
281
276
  handleSearch(text) {
282
277
  this.filterText = text;
283
- this.currentPage = 1; // Reset to first page on search
278
+ this.currentPage = 1;
284
279
  this.render();
285
280
  }
286
281
  /**
@@ -288,7 +283,6 @@ class Table {
288
283
  */
289
284
  handleSort(key) {
290
285
  if (this.sortColumn === key) {
291
- // Toggle sort direction
292
286
  this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
293
287
  }
294
288
  else {
@@ -320,7 +314,6 @@ class Table {
320
314
  return null;
321
315
  let id = baseId;
322
316
  let counter = 1;
323
- // If baseId already ends with a number, extract it
324
317
  const match = baseId.match(/^(.*?)(\d+)$/);
325
318
  if (match) {
326
319
  id = match[1];
package/js/tabs.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  type TabLayout = 'horizontal' | 'vertical';
2
2
  type MenuPosition = 'top' | 'bottom' | 'left' | 'right';
3
+ /** Configuration options for a Tabs instance. */
3
4
  interface TabsOptions {
4
5
  layout?: TabLayout;
5
6
  defaultTab?: number;
6
7
  menuPos?: MenuPosition;
7
8
  onChange?: (index: number) => void;
8
9
  }
10
+ /** Tabbed content component with horizontal/vertical layouts and keyboard navigation. */
9
11
  declare class Tabs {
10
12
  private container;
11
13
  private options;
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;
@@ -16,17 +19,31 @@ declare class TimeSpanPicker {
16
19
  private onChange?;
17
20
  private readonly uid;
18
21
  private fromString;
19
- private toString;
22
+ private toLabel;
23
+ private pickerEl;
24
+ private durationEl;
25
+ private barEl;
26
+ private barFillEl;
27
+ private startHandleEl;
28
+ private endHandleEl;
29
+ private dragState;
20
30
  constructor(elementOrSelector: string | HTMLElement, options?: TimeSpanPickerOptions);
21
- private queryInput;
31
+ private queryEl;
22
32
  private render;
23
- private readonly handleStartChange;
24
- private readonly handleEndChange;
33
+ private readonly handleChange;
25
34
  private attachEventListeners;
35
+ private attachBarListeners;
36
+ private beginDrag;
37
+ private readonly onStartHandleDown;
38
+ private readonly onEndHandleDown;
39
+ private readonly onFillDown;
40
+ private readonly onPointerMove;
41
+ private readonly onPointerUp;
26
42
  private toMinutes;
43
+ private minutesToTime;
44
+ private snap;
27
45
  private formatDuration;
28
46
  private updateUI;
29
- private handleChange;
30
47
  getValue(): TimeSpan;
31
48
  setValue(start: string, end: string): void;
32
49
  reset(): void;