@dodlhuat/basix 1.1.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (352) hide show
  1. package/README.md +706 -482
  2. package/css/accordion.scss +86 -87
  3. package/css/alert.scss +137 -137
  4. package/css/badge.scss +104 -0
  5. package/css/bottom-sheet.scss +192 -0
  6. package/css/breadcrumb.scss +158 -0
  7. package/css/button.scss +48 -0
  8. package/css/calendar.scss +957 -0
  9. package/css/card.scss +65 -65
  10. package/css/chart.scss +270 -157
  11. package/css/chat-bubbles.scss +134 -68
  12. package/css/chips.scss +109 -19
  13. package/css/colors.scss +32 -32
  14. package/css/context-menu.scss +182 -0
  15. package/css/datepicker.scss +336 -336
  16. package/css/defaults.scss +90 -90
  17. package/css/docs.scss +529 -0
  18. package/css/editor.scss +664 -461
  19. package/css/file-uploader.scss +1 -1
  20. package/css/flyout-menu.scss +361 -361
  21. package/css/form.scss +124 -0
  22. package/css/gallery.scss +65 -6
  23. package/css/grid.scss +41 -40
  24. package/css/group-picker.scss +345 -0
  25. package/css/guitar-chords.css +250 -250
  26. package/css/icons.scss +330 -330
  27. package/css/parameters.scss +3 -3
  28. package/css/placeholder.scss +33 -33
  29. package/css/popover.scss +206 -0
  30. package/css/progress.scss +76 -32
  31. package/css/properties.scss +51 -36
  32. package/css/push-menu.scss +302 -174
  33. package/css/reset.scss +39 -39
  34. package/css/scrollbar.scss +62 -5
  35. package/css/sidebar-nav.scss +92 -0
  36. package/css/spinner.scss +65 -65
  37. package/css/stepper.scss +248 -0
  38. package/css/style.css +4603 -273
  39. package/css/style.css.map +1 -1
  40. package/css/style.min.css +1 -1
  41. package/css/style.scss +51 -39
  42. package/css/table.scss +199 -199
  43. package/css/tabs.scss +154 -123
  44. package/css/timeline.scss +83 -38
  45. package/css/timepicker.scss +100 -5
  46. package/css/toast.scss +81 -81
  47. package/css/typography.scss +194 -161
  48. package/css/virtual-dropdown.scss +35 -29
  49. package/js/bottom-sheet.js +173 -0
  50. package/js/bottom-sheet.ts +222 -0
  51. package/js/calendar.js +532 -0
  52. package/js/calendar.ts +706 -0
  53. package/js/carousel.js +26 -13
  54. package/js/chart.js +573 -257
  55. package/js/chart.ts +692 -0
  56. package/js/code-viewer.js +10 -10
  57. package/js/code-viewer.ts +188 -188
  58. package/js/context-menu.js +212 -0
  59. package/js/context-menu.ts +252 -0
  60. package/js/datepicker.ts +627 -627
  61. package/js/docs-nav.js +204 -0
  62. package/js/dropdown.ts +179 -179
  63. package/js/editor.js +96 -38
  64. package/js/editor.ts +483 -425
  65. package/js/file-uploader.js +1 -0
  66. package/js/file-uploader.ts +1 -0
  67. package/js/flyout-menu.js +14 -14
  68. package/js/flyout-menu.ts +249 -249
  69. package/js/form-builder.js +106 -106
  70. package/js/gallery.js +13 -6
  71. package/js/gallery.ts +245 -236
  72. package/js/group-picker.js +342 -0
  73. package/js/group-picker.ts +447 -0
  74. package/js/guitar-chords.js +268 -268
  75. package/js/lazy-loader.js +121 -121
  76. package/js/modal.ts +166 -166
  77. package/js/popover.js +163 -0
  78. package/js/popover.ts +219 -0
  79. package/js/position.js +108 -0
  80. package/js/position.ts +111 -0
  81. package/js/push-menu.js +226 -113
  82. package/js/push-menu.ts +284 -145
  83. package/js/request.js +50 -50
  84. package/js/scroll.ts +47 -47
  85. package/js/scrollbar.js +13 -0
  86. package/js/scrollbar.ts +324 -307
  87. package/js/select.ts +216 -216
  88. package/js/sidebar-nav.js +41 -0
  89. package/js/sidebar-nav.ts +66 -0
  90. package/js/stepper.js +80 -0
  91. package/js/stepper.ts +104 -0
  92. package/js/table.ts +452 -452
  93. package/js/tabs.ts +279 -279
  94. package/js/theme.js +17 -6
  95. package/js/theme.ts +234 -224
  96. package/js/timepicker.js +21 -8
  97. package/js/toast.ts +137 -137
  98. package/js/tooltip.js +6 -60
  99. package/js/tooltip.ts +184 -251
  100. package/js/tsconfig.json +18 -18
  101. package/js/utils.ts +83 -83
  102. package/js/virtual-dropdown.js +25 -25
  103. package/js/virtual-dropdown.ts +365 -365
  104. package/package.json +39 -39
  105. package/fonts/Outfit-VariableFont_wght.woff +0 -0
  106. package/fonts/material-icons.woff2 +0 -0
  107. package/icons/activity-outline.svg +0 -1
  108. package/icons/alert-circle-outline.svg +0 -1
  109. package/icons/alert-triangle-outline.svg +0 -1
  110. package/icons/archive-outline.svg +0 -1
  111. package/icons/arrow-back-outline.svg +0 -1
  112. package/icons/arrow-circle-down-outline.svg +0 -1
  113. package/icons/arrow-circle-left-outline.svg +0 -1
  114. package/icons/arrow-circle-right-outline.svg +0 -1
  115. package/icons/arrow-circle-up-outline.svg +0 -1
  116. package/icons/arrow-down-outline.svg +0 -1
  117. package/icons/arrow-downward-outline.svg +0 -1
  118. package/icons/arrow-forward-outline.svg +0 -1
  119. package/icons/arrow-ios-back-outline.svg +0 -1
  120. package/icons/arrow-ios-downward-outline.svg +0 -1
  121. package/icons/arrow-ios-forward-outline.svg +0 -1
  122. package/icons/arrow-ios-upward-outline.svg +0 -1
  123. package/icons/arrow-left-outline.svg +0 -1
  124. package/icons/arrow-right-outline.svg +0 -1
  125. package/icons/arrow-up-outline.svg +0 -1
  126. package/icons/arrow-upward-outline.svg +0 -1
  127. package/icons/arrowhead-down-outline.svg +0 -1
  128. package/icons/arrowhead-left-outline.svg +0 -1
  129. package/icons/arrowhead-right-outline.svg +0 -1
  130. package/icons/arrowhead-up-outline.svg +0 -1
  131. package/icons/at-outline.svg +0 -1
  132. package/icons/attach-2-outline.svg +0 -1
  133. package/icons/attach-outline.svg +0 -1
  134. package/icons/award-outline.svg +0 -1
  135. package/icons/backspace-outline.svg +0 -1
  136. package/icons/bar-chart-2-outline.svg +0 -1
  137. package/icons/bar-chart-outline.svg +0 -1
  138. package/icons/battery-outline.svg +0 -1
  139. package/icons/behance-outline.svg +0 -1
  140. package/icons/bell-off-outline.svg +0 -1
  141. package/icons/bell-outline.svg +0 -1
  142. package/icons/bluetooth-outline.svg +0 -1
  143. package/icons/book-open-outline.svg +0 -1
  144. package/icons/book-outline.svg +0 -1
  145. package/icons/bookmark-outline.svg +0 -1
  146. package/icons/briefcase-outline.svg +0 -1
  147. package/icons/browser-outline.svg +0 -1
  148. package/icons/brush-outline.svg +0 -1
  149. package/icons/bulb-outline.svg +0 -1
  150. package/icons/calendar-outline.svg +0 -1
  151. package/icons/camera-outline.svg +0 -1
  152. package/icons/car-outline.svg +0 -1
  153. package/icons/cast-outline.svg +0 -1
  154. package/icons/charging-outline.svg +0 -1
  155. package/icons/checkmark-circle-2-outline.svg +0 -1
  156. package/icons/checkmark-circle-outline.svg +0 -1
  157. package/icons/checkmark-outline.svg +0 -1
  158. package/icons/checkmark-square-2-outline.svg +0 -1
  159. package/icons/checkmark-square-outline.svg +0 -1
  160. package/icons/chevron-down-outline.svg +0 -1
  161. package/icons/chevron-left-outline.svg +0 -1
  162. package/icons/chevron-right-outline.svg +0 -1
  163. package/icons/chevron-up-outline.svg +0 -1
  164. package/icons/clipboard-outline.svg +0 -1
  165. package/icons/clock-outline.svg +0 -1
  166. package/icons/close-circle-outline.svg +0 -1
  167. package/icons/close-outline.svg +0 -1
  168. package/icons/close-square-outline.svg +0 -1
  169. package/icons/cloud-download-outline.svg +0 -1
  170. package/icons/cloud-upload-outline.svg +0 -1
  171. package/icons/code-download-outline.svg +0 -1
  172. package/icons/code-outline.svg +0 -1
  173. package/icons/collapse-outline.svg +0 -1
  174. package/icons/color-palette-outline.svg +0 -1
  175. package/icons/color-picker-outline.svg +0 -1
  176. package/icons/compass-outline.svg +0 -1
  177. package/icons/copy-outline.svg +0 -1
  178. package/icons/corner-down-left-outline.svg +0 -1
  179. package/icons/corner-down-right-outline.svg +0 -1
  180. package/icons/corner-left-down-outline.svg +0 -1
  181. package/icons/corner-left-up-outline.svg +0 -1
  182. package/icons/corner-right-down-outline.svg +0 -1
  183. package/icons/corner-right-up-outline.svg +0 -1
  184. package/icons/corner-up-left-outline.svg +0 -1
  185. package/icons/corner-up-right-outline.svg +0 -1
  186. package/icons/credit-card-outline.svg +0 -1
  187. package/icons/crop-outline.svg +0 -1
  188. package/icons/cube-outline.svg +0 -1
  189. package/icons/diagonal-arrow-left-down-outline.svg +0 -1
  190. package/icons/diagonal-arrow-left-up-outline.svg +0 -1
  191. package/icons/diagonal-arrow-right-down-outline.svg +0 -1
  192. package/icons/diagonal-arrow-right-up-outline.svg +0 -1
  193. package/icons/done-all-outline.svg +0 -1
  194. package/icons/download-outline.svg +0 -1
  195. package/icons/droplet-off-outline.svg +0 -1
  196. package/icons/droplet-outline.svg +0 -1
  197. package/icons/edit-2-outline.svg +0 -1
  198. package/icons/edit-outline.svg +0 -1
  199. package/icons/email-outline.svg +0 -1
  200. package/icons/expand-outline.svg +0 -1
  201. package/icons/external-link-outline.svg +0 -1
  202. package/icons/eye-off-2-outline.svg +0 -1
  203. package/icons/eye-off-outline.svg +0 -1
  204. package/icons/eye-outline.svg +0 -1
  205. package/icons/facebook-outline.svg +0 -1
  206. package/icons/file-add-outline.svg +0 -1
  207. package/icons/file-outline.svg +0 -1
  208. package/icons/file-remove-outline.svg +0 -1
  209. package/icons/file-text-outline.svg +0 -1
  210. package/icons/film-outline.svg +0 -1
  211. package/icons/flag-outline.svg +0 -1
  212. package/icons/flash-off-outline.svg +0 -1
  213. package/icons/flash-outline.svg +0 -1
  214. package/icons/flip-2-outline.svg +0 -1
  215. package/icons/flip-outline.svg +0 -1
  216. package/icons/folder-add-outline.svg +0 -1
  217. package/icons/folder-outline.svg +0 -1
  218. package/icons/folder-remove-outline.svg +0 -1
  219. package/icons/funnel-outline.svg +0 -1
  220. package/icons/gift-outline.svg +0 -1
  221. package/icons/github-outline.svg +0 -1
  222. package/icons/globe-2-outline.svg +0 -1
  223. package/icons/globe-outline.svg +0 -1
  224. package/icons/google-outline.svg +0 -1
  225. package/icons/grid-outline.svg +0 -1
  226. package/icons/hard-drive-outline.svg +0 -1
  227. package/icons/hash-outline.svg +0 -1
  228. package/icons/headphones-outline.svg +0 -1
  229. package/icons/heart-outline.svg +0 -1
  230. package/icons/home-outline.svg +0 -1
  231. package/icons/image-outline.svg +0 -1
  232. package/icons/inbox-outline.svg +0 -1
  233. package/icons/info-outline.svg +0 -1
  234. package/icons/keypad-outline.svg +0 -1
  235. package/icons/layers-outline.svg +0 -1
  236. package/icons/layout-outline.svg +0 -1
  237. package/icons/link-2-outline.svg +0 -1
  238. package/icons/link-outline.svg +0 -1
  239. package/icons/linkedin-outline.svg +0 -1
  240. package/icons/list-outline.svg +0 -1
  241. package/icons/loader-outline.svg +0 -1
  242. package/icons/lock-outline.svg +0 -1
  243. package/icons/log-in-outline.svg +0 -1
  244. package/icons/log-out-outline.svg +0 -1
  245. package/icons/map-outline.svg +0 -1
  246. package/icons/maximize-outline.svg +0 -1
  247. package/icons/menu-2-outline.svg +0 -1
  248. package/icons/menu-arrow-outline.svg +0 -1
  249. package/icons/menu-outline.svg +0 -1
  250. package/icons/message-circle-outline.svg +0 -1
  251. package/icons/message-square-outline.svg +0 -1
  252. package/icons/mic-off-outline.svg +0 -1
  253. package/icons/mic-outline.svg +0 -1
  254. package/icons/minimize-outline.svg +0 -1
  255. package/icons/minus-circle-outline.svg +0 -1
  256. package/icons/minus-outline.svg +0 -1
  257. package/icons/minus-square-outline.svg +0 -1
  258. package/icons/monitor-outline.svg +0 -1
  259. package/icons/moon-outline.svg +0 -1
  260. package/icons/more-horizontal-outline.svg +0 -1
  261. package/icons/more-vertical-outline.svg +0 -1
  262. package/icons/move-outline.svg +0 -1
  263. package/icons/music-outline.svg +0 -1
  264. package/icons/navigation-2-outline.svg +0 -1
  265. package/icons/navigation-outline.svg +0 -1
  266. package/icons/npm-outline.svg +0 -1
  267. package/icons/options-2-outline.svg +0 -1
  268. package/icons/options-outline.svg +0 -1
  269. package/icons/pantone-outline.svg +0 -1
  270. package/icons/paper-plane-outline.svg +0 -1
  271. package/icons/pause-circle-outline.svg +0 -1
  272. package/icons/people-outline.svg +0 -1
  273. package/icons/percent-outline.svg +0 -1
  274. package/icons/person-add-outline.svg +0 -1
  275. package/icons/person-delete-outline.svg +0 -1
  276. package/icons/person-done-outline.svg +0 -1
  277. package/icons/person-outline.svg +0 -1
  278. package/icons/person-remove-outline.svg +0 -1
  279. package/icons/phone-call-outline.svg +0 -1
  280. package/icons/phone-missed-outline.svg +0 -1
  281. package/icons/phone-off-outline.svg +0 -1
  282. package/icons/phone-outline.svg +0 -1
  283. package/icons/pie-chart-outline.svg +0 -1
  284. package/icons/pin-outline.svg +0 -1
  285. package/icons/play-circle-outline.svg +0 -1
  286. package/icons/plus-circle-outline.svg +0 -1
  287. package/icons/plus-outline.svg +0 -1
  288. package/icons/plus-square-outline.svg +0 -1
  289. package/icons/power-outline.svg +0 -1
  290. package/icons/pricetags-outline.svg +0 -1
  291. package/icons/printer-outline.svg +0 -1
  292. package/icons/question-mark-circle-outline.svg +0 -1
  293. package/icons/question-mark-outline.svg +0 -1
  294. package/icons/radio-button-off-outline.svg +0 -1
  295. package/icons/radio-button-on-outline.svg +0 -1
  296. package/icons/radio-outline.svg +0 -1
  297. package/icons/recording-outline.svg +0 -1
  298. package/icons/refresh-outline.svg +0 -1
  299. package/icons/repeat-outline.svg +0 -1
  300. package/icons/rewind-left-outline.svg +0 -1
  301. package/icons/rewind-right-outline.svg +0 -1
  302. package/icons/save-outline.svg +0 -1
  303. package/icons/scissors-outline.svg +0 -1
  304. package/icons/search-outline.svg +0 -1
  305. package/icons/settings-2-outline.svg +0 -1
  306. package/icons/settings-outline.svg +0 -1
  307. package/icons/shake-outline.svg +0 -1
  308. package/icons/share-outline.svg +0 -1
  309. package/icons/shield-off-outline.svg +0 -1
  310. package/icons/shield-outline.svg +0 -1
  311. package/icons/shopping-bag-outline.svg +0 -1
  312. package/icons/shopping-cart-outline.svg +0 -1
  313. package/icons/shuffle-2-outline.svg +0 -1
  314. package/icons/shuffle-outline.svg +0 -1
  315. package/icons/skip-back-outline.svg +0 -1
  316. package/icons/skip-forward-outline.svg +0 -1
  317. package/icons/slash-outline.svg +0 -1
  318. package/icons/smartphone-outline.svg +0 -1
  319. package/icons/smiling-face-outline.svg +0 -1
  320. package/icons/speaker-outline.svg +0 -1
  321. package/icons/square-outline.svg +0 -1
  322. package/icons/star-outline.svg +0 -1
  323. package/icons/stop-circle-outline.svg +0 -1
  324. package/icons/sun-outline.svg +0 -1
  325. package/icons/swap-outline.svg +0 -1
  326. package/icons/sync-outline.svg +0 -1
  327. package/icons/text-outline.svg +0 -1
  328. package/icons/thermometer-minus-outline.svg +0 -1
  329. package/icons/thermometer-outline.svg +0 -1
  330. package/icons/thermometer-plus-outline.svg +0 -1
  331. package/icons/toggle-left-outline.svg +0 -1
  332. package/icons/toggle-right-outline.svg +0 -1
  333. package/icons/trash-2-outline.svg +0 -1
  334. package/icons/trash-outline.svg +0 -1
  335. package/icons/trending-down-outline.svg +0 -1
  336. package/icons/trending-up-outline.svg +0 -1
  337. package/icons/tv-outline.svg +0 -1
  338. package/icons/twitter-outline.svg +0 -1
  339. package/icons/umbrella-outline.svg +0 -1
  340. package/icons/undo-outline.svg +0 -1
  341. package/icons/unlock-outline.svg +0 -1
  342. package/icons/upload-outline.svg +0 -1
  343. package/icons/video-off-outline.svg +0 -1
  344. package/icons/video-outline.svg +0 -1
  345. package/icons/volume-down-outline.svg +0 -1
  346. package/icons/volume-mute-outline.svg +0 -1
  347. package/icons/volume-off-outline.svg +0 -1
  348. package/icons/volume-up-outline.svg +0 -1
  349. package/icons/wifi-off-outline.svg +0 -1
  350. package/icons/wifi-outline.svg +0 -1
  351. package/js/index.js +0 -718
  352. package/js/index.ts +0 -873
package/js/tabs.ts CHANGED
@@ -1,280 +1,280 @@
1
- type TabLayout = 'horizontal' | 'vertical';
2
- type MenuPosition = 'top' | 'bottom' | 'left' | 'right';
3
-
4
- interface TabsOptions {
5
- layout?: TabLayout;
6
- defaultTab?: number;
7
- menuPos?: MenuPosition;
8
- onChange?: (index: number) => void;
9
- }
10
-
11
- class Tabs {
12
- private container: HTMLElement;
13
- private options: Required<Omit<TabsOptions, 'onChange'>> & Pick<TabsOptions, 'onChange'>;
14
- private tabItems: NodeListOf<Element>;
15
- private tabPanels: NodeListOf<Element>;
16
- private currentTab: number;
17
-
18
- constructor(elementOrSelector: string | HTMLElement, options: TabsOptions = {}) {
19
- const element = typeof elementOrSelector === 'string'
20
- ? document.querySelector<HTMLElement>(elementOrSelector)
21
- : elementOrSelector;
22
-
23
- if (!element) {
24
- throw new Error(`Tabs: Element not found for selector "${elementOrSelector}"`);
25
- }
26
-
27
- this.container = element;
28
-
29
- // Set default options
30
- const layout = options.layout || 'horizontal';
31
- this.options = {
32
- layout,
33
- defaultTab: options.defaultTab ?? 0,
34
- menuPos: options.menuPos || (layout === 'vertical' ? 'left' : 'top'),
35
- onChange: options.onChange
36
- };
37
-
38
- this.currentTab = this.options.defaultTab;
39
- this.tabItems = document.querySelectorAll('.tab-item'); // Will be set in init
40
- this.tabPanels = document.querySelectorAll('.tab-panel'); // Will be set in init
41
-
42
- this.init();
43
- }
44
-
45
- /**
46
- * Initializes the tabs component
47
- */
48
- private init(): void {
49
- // Apply layout class
50
- if (this.options.layout === 'vertical') {
51
- this.container.classList.add('tabs-vertical');
52
- }
53
-
54
- this.tabItems = this.container.querySelectorAll('.tab-item');
55
- this.tabPanels = this.container.querySelectorAll('.tab-panel');
56
-
57
- // Validate that we have tabs and panels
58
- if (this.tabItems.length === 0) {
59
- console.warn('No tab items found in container');
60
- return;
61
- }
62
-
63
- if (this.tabPanels.length === 0) {
64
- console.warn('No tab panels found in container');
65
- return;
66
- }
67
-
68
- if (this.tabItems.length !== this.tabPanels.length) {
69
- console.warn('Number of tab items does not match number of tab panels');
70
- }
71
-
72
- this.bindEvents();
73
- this.activateTab(this.options.defaultTab);
74
- }
75
-
76
- /**
77
- * Binds click events to tab items
78
- */
79
- private bindEvents(): void {
80
- this.tabItems.forEach((item, index) => {
81
- item.addEventListener('click', (e: Event) => {
82
- e.preventDefault();
83
- this.activateTab(index);
84
- });
85
-
86
- // Add keyboard navigation for accessibility
87
- item.addEventListener('keydown', (e: Event) => {
88
- const keyEvent = e as KeyboardEvent;
89
- this.handleKeyboardNavigation(keyEvent, index);
90
- });
91
-
92
- // Set ARIA attributes
93
- item.setAttribute('role', 'tab');
94
- item.setAttribute('tabindex', index === this.options.defaultTab ? '0' : '-1');
95
- item.setAttribute('aria-selected', index === this.options.defaultTab ? 'true' : 'false');
96
- });
97
-
98
- // Set ARIA attributes for panels
99
- this.tabPanels.forEach((panel, index) => {
100
- panel.setAttribute('role', 'tabpanel');
101
- panel.setAttribute('aria-hidden', index === this.options.defaultTab ? 'false' : 'true');
102
- });
103
- }
104
-
105
- /**
106
- * Handles keyboard navigation (Arrow keys, Home, End)
107
- */
108
- private handleKeyboardNavigation(e: KeyboardEvent, currentIndex: number): void {
109
- let newIndex = currentIndex;
110
- const isVertical = this.options.layout === 'vertical';
111
-
112
- switch (e.key) {
113
- case 'ArrowLeft':
114
- if (!isVertical) {
115
- newIndex = currentIndex > 0 ? currentIndex - 1 : this.tabItems.length - 1;
116
- e.preventDefault();
117
- }
118
- break;
119
- case 'ArrowRight':
120
- if (!isVertical) {
121
- newIndex = currentIndex < this.tabItems.length - 1 ? currentIndex + 1 : 0;
122
- e.preventDefault();
123
- }
124
- break;
125
- case 'ArrowUp':
126
- if (isVertical) {
127
- newIndex = currentIndex > 0 ? currentIndex - 1 : this.tabItems.length - 1;
128
- e.preventDefault();
129
- }
130
- break;
131
- case 'ArrowDown':
132
- if (isVertical) {
133
- newIndex = currentIndex < this.tabItems.length - 1 ? currentIndex + 1 : 0;
134
- e.preventDefault();
135
- }
136
- break;
137
- case 'Home':
138
- newIndex = 0;
139
- e.preventDefault();
140
- break;
141
- case 'End':
142
- newIndex = this.tabItems.length - 1;
143
- e.preventDefault();
144
- break;
145
- default:
146
- return;
147
- }
148
-
149
- if (newIndex !== currentIndex) {
150
- this.activateTab(newIndex);
151
- (this.tabItems[newIndex] as HTMLElement).focus();
152
- }
153
- }
154
-
155
- /**
156
- * Activates a tab by index
157
- */
158
- private activateTab(index: number): void {
159
- if (index < 0 || index >= this.tabItems.length) {
160
- console.warn(`Invalid tab index: ${index}`);
161
- return;
162
- }
163
-
164
- // Remove active class from all
165
- this.tabItems.forEach((item, i) => {
166
- item.classList.remove('active');
167
- item.setAttribute('tabindex', '-1');
168
- item.setAttribute('aria-selected', 'false');
169
- });
170
-
171
- this.tabPanels.forEach((panel) => {
172
- panel.classList.remove('active');
173
- panel.setAttribute('aria-hidden', 'true');
174
- });
175
-
176
- // Add active class to selected
177
- this.tabItems[index].classList.add('active');
178
- this.tabItems[index].setAttribute('tabindex', '0');
179
- this.tabItems[index].setAttribute('aria-selected', 'true');
180
-
181
- this.tabPanels[index].classList.add('active');
182
- this.tabPanels[index].setAttribute('aria-hidden', 'false');
183
-
184
- const previousTab = this.currentTab;
185
- this.currentTab = index;
186
-
187
- // Call onChange callback if provided
188
- if (this.options.onChange && previousTab !== index) {
189
- this.options.onChange(index);
190
- }
191
- }
192
-
193
- /**
194
- * Public API: Programmatically activate a tab
195
- */
196
- public goToTab(index: number): void {
197
- this.activateTab(index);
198
-
199
- // Focus the tab for keyboard users
200
- if (this.tabItems[index]) {
201
- (this.tabItems[index] as HTMLElement).focus();
202
- }
203
- }
204
-
205
- /**
206
- * Public API: Get the currently active tab index
207
- */
208
- public getCurrentTab(): number {
209
- return this.currentTab;
210
- }
211
-
212
- /**
213
- * Public API: Get the total number of tabs
214
- */
215
- public getTabCount(): number {
216
- return this.tabItems.length;
217
- }
218
-
219
- /**
220
- * Public API: Enable a tab
221
- */
222
- public enableTab(index: number): void {
223
- if (index < 0 || index >= this.tabItems.length) return;
224
-
225
- const tab = this.tabItems[index] as HTMLElement;
226
- tab.classList.remove('disabled');
227
- tab.removeAttribute('aria-disabled');
228
- tab.style.pointerEvents = '';
229
- }
230
-
231
- /**
232
- * Public API: Disable a tab
233
- */
234
- public disableTab(index: number): void {
235
- if (index < 0 || index >= this.tabItems.length) return;
236
-
237
- const tab = this.tabItems[index] as HTMLElement;
238
- tab.classList.add('disabled');
239
- tab.setAttribute('aria-disabled', 'true');
240
- tab.style.pointerEvents = 'none';
241
-
242
- // If disabling the current tab, switch to the first enabled tab
243
- if (index === this.currentTab) {
244
- const firstEnabled = Array.from(this.tabItems).findIndex(
245
- (item) => !item.classList.contains('disabled')
246
- );
247
- if (firstEnabled !== -1) {
248
- this.activateTab(firstEnabled);
249
- }
250
- }
251
- }
252
-
253
- /**
254
- * Public API: Destroy the tabs instance and clean up
255
- */
256
- public destroy(): void {
257
- // Remove event listeners by cloning and replacing nodes
258
- this.tabItems.forEach((item) => {
259
- const newItem = item.cloneNode(true);
260
- item.parentNode?.replaceChild(newItem, item);
261
- });
262
-
263
- // Remove classes
264
- this.container.classList.remove('tabs-vertical');
265
-
266
- // Remove ARIA attributes
267
- this.tabItems.forEach((item) => {
268
- item.removeAttribute('role');
269
- item.removeAttribute('tabindex');
270
- item.removeAttribute('aria-selected');
271
- });
272
-
273
- this.tabPanels.forEach((panel) => {
274
- panel.removeAttribute('role');
275
- panel.removeAttribute('aria-hidden');
276
- });
277
- }
278
- }
279
-
1
+ type TabLayout = 'horizontal' | 'vertical';
2
+ type MenuPosition = 'top' | 'bottom' | 'left' | 'right';
3
+
4
+ interface TabsOptions {
5
+ layout?: TabLayout;
6
+ defaultTab?: number;
7
+ menuPos?: MenuPosition;
8
+ onChange?: (index: number) => void;
9
+ }
10
+
11
+ class Tabs {
12
+ private container: HTMLElement;
13
+ private options: Required<Omit<TabsOptions, 'onChange'>> & Pick<TabsOptions, 'onChange'>;
14
+ private tabItems: NodeListOf<Element>;
15
+ private tabPanels: NodeListOf<Element>;
16
+ private currentTab: number;
17
+
18
+ constructor(elementOrSelector: string | HTMLElement, options: TabsOptions = {}) {
19
+ const element = typeof elementOrSelector === 'string'
20
+ ? document.querySelector<HTMLElement>(elementOrSelector)
21
+ : elementOrSelector;
22
+
23
+ if (!element) {
24
+ throw new Error(`Tabs: Element not found for selector "${elementOrSelector}"`);
25
+ }
26
+
27
+ this.container = element;
28
+
29
+ // Set default options
30
+ const layout = options.layout || 'horizontal';
31
+ this.options = {
32
+ layout,
33
+ defaultTab: options.defaultTab ?? 0,
34
+ menuPos: options.menuPos || (layout === 'vertical' ? 'left' : 'top'),
35
+ onChange: options.onChange
36
+ };
37
+
38
+ this.currentTab = this.options.defaultTab;
39
+ this.tabItems = document.querySelectorAll('.tab-item'); // Will be set in init
40
+ this.tabPanels = document.querySelectorAll('.tab-panel'); // Will be set in init
41
+
42
+ this.init();
43
+ }
44
+
45
+ /**
46
+ * Initializes the tabs component
47
+ */
48
+ private init(): void {
49
+ // Apply layout class
50
+ if (this.options.layout === 'vertical') {
51
+ this.container.classList.add('tabs-vertical');
52
+ }
53
+
54
+ this.tabItems = this.container.querySelectorAll('.tab-item');
55
+ this.tabPanels = this.container.querySelectorAll('.tab-panel');
56
+
57
+ // Validate that we have tabs and panels
58
+ if (this.tabItems.length === 0) {
59
+ console.warn('No tab items found in container');
60
+ return;
61
+ }
62
+
63
+ if (this.tabPanels.length === 0) {
64
+ console.warn('No tab panels found in container');
65
+ return;
66
+ }
67
+
68
+ if (this.tabItems.length !== this.tabPanels.length) {
69
+ console.warn('Number of tab items does not match number of tab panels');
70
+ }
71
+
72
+ this.bindEvents();
73
+ this.activateTab(this.options.defaultTab);
74
+ }
75
+
76
+ /**
77
+ * Binds click events to tab items
78
+ */
79
+ private bindEvents(): void {
80
+ this.tabItems.forEach((item, index) => {
81
+ item.addEventListener('click', (e: Event) => {
82
+ e.preventDefault();
83
+ this.activateTab(index);
84
+ });
85
+
86
+ // Add keyboard navigation for accessibility
87
+ item.addEventListener('keydown', (e: Event) => {
88
+ const keyEvent = e as KeyboardEvent;
89
+ this.handleKeyboardNavigation(keyEvent, index);
90
+ });
91
+
92
+ // Set ARIA attributes
93
+ item.setAttribute('role', 'tab');
94
+ item.setAttribute('tabindex', index === this.options.defaultTab ? '0' : '-1');
95
+ item.setAttribute('aria-selected', index === this.options.defaultTab ? 'true' : 'false');
96
+ });
97
+
98
+ // Set ARIA attributes for panels
99
+ this.tabPanels.forEach((panel, index) => {
100
+ panel.setAttribute('role', 'tabpanel');
101
+ panel.setAttribute('aria-hidden', index === this.options.defaultTab ? 'false' : 'true');
102
+ });
103
+ }
104
+
105
+ /**
106
+ * Handles keyboard navigation (Arrow keys, Home, End)
107
+ */
108
+ private handleKeyboardNavigation(e: KeyboardEvent, currentIndex: number): void {
109
+ let newIndex = currentIndex;
110
+ const isVertical = this.options.layout === 'vertical';
111
+
112
+ switch (e.key) {
113
+ case 'ArrowLeft':
114
+ if (!isVertical) {
115
+ newIndex = currentIndex > 0 ? currentIndex - 1 : this.tabItems.length - 1;
116
+ e.preventDefault();
117
+ }
118
+ break;
119
+ case 'ArrowRight':
120
+ if (!isVertical) {
121
+ newIndex = currentIndex < this.tabItems.length - 1 ? currentIndex + 1 : 0;
122
+ e.preventDefault();
123
+ }
124
+ break;
125
+ case 'ArrowUp':
126
+ if (isVertical) {
127
+ newIndex = currentIndex > 0 ? currentIndex - 1 : this.tabItems.length - 1;
128
+ e.preventDefault();
129
+ }
130
+ break;
131
+ case 'ArrowDown':
132
+ if (isVertical) {
133
+ newIndex = currentIndex < this.tabItems.length - 1 ? currentIndex + 1 : 0;
134
+ e.preventDefault();
135
+ }
136
+ break;
137
+ case 'Home':
138
+ newIndex = 0;
139
+ e.preventDefault();
140
+ break;
141
+ case 'End':
142
+ newIndex = this.tabItems.length - 1;
143
+ e.preventDefault();
144
+ break;
145
+ default:
146
+ return;
147
+ }
148
+
149
+ if (newIndex !== currentIndex) {
150
+ this.activateTab(newIndex);
151
+ (this.tabItems[newIndex] as HTMLElement).focus();
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Activates a tab by index
157
+ */
158
+ private activateTab(index: number): void {
159
+ if (index < 0 || index >= this.tabItems.length) {
160
+ console.warn(`Invalid tab index: ${index}`);
161
+ return;
162
+ }
163
+
164
+ // Remove active class from all
165
+ this.tabItems.forEach((item, i) => {
166
+ item.classList.remove('active');
167
+ item.setAttribute('tabindex', '-1');
168
+ item.setAttribute('aria-selected', 'false');
169
+ });
170
+
171
+ this.tabPanels.forEach((panel) => {
172
+ panel.classList.remove('active');
173
+ panel.setAttribute('aria-hidden', 'true');
174
+ });
175
+
176
+ // Add active class to selected
177
+ this.tabItems[index].classList.add('active');
178
+ this.tabItems[index].setAttribute('tabindex', '0');
179
+ this.tabItems[index].setAttribute('aria-selected', 'true');
180
+
181
+ this.tabPanels[index].classList.add('active');
182
+ this.tabPanels[index].setAttribute('aria-hidden', 'false');
183
+
184
+ const previousTab = this.currentTab;
185
+ this.currentTab = index;
186
+
187
+ // Call onChange callback if provided
188
+ if (this.options.onChange && previousTab !== index) {
189
+ this.options.onChange(index);
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Public API: Programmatically activate a tab
195
+ */
196
+ public goToTab(index: number): void {
197
+ this.activateTab(index);
198
+
199
+ // Focus the tab for keyboard users
200
+ if (this.tabItems[index]) {
201
+ (this.tabItems[index] as HTMLElement).focus();
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Public API: Get the currently active tab index
207
+ */
208
+ public getCurrentTab(): number {
209
+ return this.currentTab;
210
+ }
211
+
212
+ /**
213
+ * Public API: Get the total number of tabs
214
+ */
215
+ public getTabCount(): number {
216
+ return this.tabItems.length;
217
+ }
218
+
219
+ /**
220
+ * Public API: Enable a tab
221
+ */
222
+ public enableTab(index: number): void {
223
+ if (index < 0 || index >= this.tabItems.length) return;
224
+
225
+ const tab = this.tabItems[index] as HTMLElement;
226
+ tab.classList.remove('disabled');
227
+ tab.removeAttribute('aria-disabled');
228
+ tab.style.pointerEvents = '';
229
+ }
230
+
231
+ /**
232
+ * Public API: Disable a tab
233
+ */
234
+ public disableTab(index: number): void {
235
+ if (index < 0 || index >= this.tabItems.length) return;
236
+
237
+ const tab = this.tabItems[index] as HTMLElement;
238
+ tab.classList.add('disabled');
239
+ tab.setAttribute('aria-disabled', 'true');
240
+ tab.style.pointerEvents = 'none';
241
+
242
+ // If disabling the current tab, switch to the first enabled tab
243
+ if (index === this.currentTab) {
244
+ const firstEnabled = Array.from(this.tabItems).findIndex(
245
+ (item) => !item.classList.contains('disabled')
246
+ );
247
+ if (firstEnabled !== -1) {
248
+ this.activateTab(firstEnabled);
249
+ }
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Public API: Destroy the tabs instance and clean up
255
+ */
256
+ public destroy(): void {
257
+ // Remove event listeners by cloning and replacing nodes
258
+ this.tabItems.forEach((item) => {
259
+ const newItem = item.cloneNode(true);
260
+ item.parentNode?.replaceChild(newItem, item);
261
+ });
262
+
263
+ // Remove classes
264
+ this.container.classList.remove('tabs-vertical');
265
+
266
+ // Remove ARIA attributes
267
+ this.tabItems.forEach((item) => {
268
+ item.removeAttribute('role');
269
+ item.removeAttribute('tabindex');
270
+ item.removeAttribute('aria-selected');
271
+ });
272
+
273
+ this.tabPanels.forEach((panel) => {
274
+ panel.removeAttribute('role');
275
+ panel.removeAttribute('aria-hidden');
276
+ });
277
+ }
278
+ }
279
+
280
280
  export { Tabs };
package/js/theme.js CHANGED
@@ -80,14 +80,25 @@ class Theme {
80
80
  // Update button state
81
81
  toggleBtn.setAttribute('aria-pressed', String(isDark));
82
82
  toggleBtn.setAttribute('aria-label', `Switch to ${isDark ? 'light' : 'dark'} mode`);
83
- // Update icon classes
84
- if (isDark) {
85
- icon.classList.remove('icon-light');
86
- icon.classList.add('icon-dark');
83
+ // Update icon — SVG sprite via <use> or font icon via class
84
+ const useEl = icon.querySelector('use');
85
+ if (useEl) {
86
+ const iconName = isDark ? icon.dataset.iconDark : icon.dataset.iconLight;
87
+ if (iconName) {
88
+ const existingHref = useEl.getAttribute('href') ?? '';
89
+ const basePath = existingHref.includes('#') ? existingHref.split('#')[0] : '';
90
+ useEl.setAttribute('href', `${basePath}#${iconName}`);
91
+ }
87
92
  }
88
93
  else {
89
- icon.classList.remove('icon-dark');
90
- icon.classList.add('icon-light');
94
+ if (isDark) {
95
+ icon.classList.remove('icon-light');
96
+ icon.classList.add('icon-dark');
97
+ }
98
+ else {
99
+ icon.classList.remove('icon-dark');
100
+ icon.classList.add('icon-light');
101
+ }
91
102
  }
92
103
  }
93
104
  /**