@dodlhuat/basix 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (430) hide show
  1. package/README.md +482 -0
  2. package/css/accordion.css +109 -0
  3. package/css/accordion.css.map +1 -0
  4. package/css/accordion.scss +78 -0
  5. package/css/alert.css +57 -0
  6. package/css/alert.css.map +1 -0
  7. package/css/alert.scss +86 -0
  8. package/css/button.css +69 -0
  9. package/css/button.css.map +1 -0
  10. package/css/button.scss +102 -0
  11. package/css/card.css +144 -0
  12. package/css/card.css.map +1 -0
  13. package/css/card.scss +66 -0
  14. package/css/carousel.css +118 -0
  15. package/css/carousel.css.map +1 -0
  16. package/css/carousel.scss +87 -0
  17. package/css/chart.css +159 -0
  18. package/css/chart.css.map +1 -0
  19. package/css/chart.scss +159 -0
  20. package/css/chat-bubbles.css +97 -0
  21. package/css/chat-bubbles.css.map +1 -0
  22. package/css/chat-bubbles.scss +68 -0
  23. package/css/checkbox.css +77 -0
  24. package/css/checkbox.css.map +1 -0
  25. package/css/checkbox.scss +55 -0
  26. package/css/chips.css +72 -0
  27. package/css/chips.css.map +1 -0
  28. package/css/chips.scss +52 -0
  29. package/css/code-viewer.css +97 -0
  30. package/css/code-viewer.css.map +1 -0
  31. package/css/code-viewer.scss +98 -0
  32. package/css/colors.css +63 -0
  33. package/css/colors.css.map +1 -0
  34. package/css/colors.scss +33 -0
  35. package/css/datepicker.css +264 -0
  36. package/css/datepicker.css.map +1 -0
  37. package/css/datepicker.scss +317 -0
  38. package/css/defaults.css +118 -0
  39. package/css/defaults.css.map +1 -0
  40. package/css/defaults.scss +91 -0
  41. package/css/dropdown.css +146 -0
  42. package/css/dropdown.css.map +1 -0
  43. package/css/dropdown.scss +137 -0
  44. package/css/editor.css +413 -0
  45. package/css/editor.scss +458 -0
  46. package/css/file-uploader.css +194 -0
  47. package/css/file-uploader.css.map +1 -0
  48. package/css/file-uploader.scss +195 -0
  49. package/css/flyout-menu.css +345 -0
  50. package/css/flyout-menu.css.map +1 -0
  51. package/css/flyout-menu.scss +355 -0
  52. package/css/form-builder.css +9 -0
  53. package/css/form-builder.css.map +1 -0
  54. package/css/form-builder.scss +11 -0
  55. package/css/form.css +130 -0
  56. package/css/form.css.map +1 -0
  57. package/css/form.scss +115 -0
  58. package/css/gallery.css +91 -0
  59. package/css/gallery.css.map +1 -0
  60. package/css/gallery.scss +63 -0
  61. package/css/grid.css +44 -0
  62. package/css/grid.css.map +1 -0
  63. package/css/grid.scss +41 -0
  64. package/css/guitar-chords.css +251 -0
  65. package/css/icons.css +327 -0
  66. package/css/icons.css.map +1 -0
  67. package/css/icons.scss +331 -0
  68. package/css/modal.css +97 -0
  69. package/css/modal.css.map +1 -0
  70. package/css/modal.scss +72 -0
  71. package/css/parameters.css +1 -0
  72. package/css/parameters.css.map +1 -0
  73. package/css/parameters.scss +4 -0
  74. package/css/placeholder.css +50 -0
  75. package/css/placeholder.css.map +1 -0
  76. package/css/placeholder.scss +28 -0
  77. package/css/progress.css +51 -0
  78. package/css/progress.css.map +1 -0
  79. package/css/progress.scss +32 -0
  80. package/css/properties.css +31 -0
  81. package/css/properties.css.map +1 -0
  82. package/css/properties.scss +31 -0
  83. package/css/push-menu.css +145 -0
  84. package/css/push-menu.css.map +1 -0
  85. package/css/push-menu.scss +127 -0
  86. package/css/radiobutton.css +91 -0
  87. package/css/radiobutton.css.map +1 -0
  88. package/css/radiobutton.scss +64 -0
  89. package/css/reset.css +46 -0
  90. package/css/reset.css.map +1 -0
  91. package/css/reset.scss +40 -0
  92. package/css/scrollbar.css +91 -0
  93. package/css/scrollbar.css.map +1 -0
  94. package/css/scrollbar.scss +69 -0
  95. package/css/spinner.css +118 -0
  96. package/css/spinner.css.map +1 -0
  97. package/css/spinner.scss +91 -0
  98. package/css/style.css +3735 -0
  99. package/css/style.css.map +1 -0
  100. package/css/style.min.css +1 -0
  101. package/css/style.scss +38 -0
  102. package/css/switch.css +66 -0
  103. package/css/switch.css.map +1 -0
  104. package/css/switch.scss +42 -0
  105. package/css/table.css +201 -0
  106. package/css/table.css.map +1 -0
  107. package/css/table.scss +178 -0
  108. package/css/tabs.css +135 -0
  109. package/css/tabs.css.map +1 -0
  110. package/css/tabs.scss +118 -0
  111. package/css/timeline.css +69 -0
  112. package/css/timeline.css.map +1 -0
  113. package/css/timeline.scss +69 -0
  114. package/css/timepicker.scss +72 -0
  115. package/css/toast.css +98 -0
  116. package/css/toast.css.map +1 -0
  117. package/css/toast.scss +81 -0
  118. package/css/tooltip.css +151 -0
  119. package/css/tooltip.css.map +1 -0
  120. package/css/tooltip.scss +122 -0
  121. package/css/tree.css +199 -0
  122. package/css/tree.css.map +1 -0
  123. package/css/tree.scss +192 -0
  124. package/css/typography.css +137 -0
  125. package/css/typography.css.map +1 -0
  126. package/css/typography.scss +100 -0
  127. package/css/virtual-dropdown.css +149 -0
  128. package/css/virtual-dropdown.css.map +1 -0
  129. package/css/virtual-dropdown.scss +142 -0
  130. package/fonts/MaterialSymbolsOutlined.woff2 +0 -0
  131. package/fonts/Outfit-VariableFont_wght.woff +0 -0
  132. package/fonts/Outfit-VariableFont_wght.woff2 +0 -0
  133. package/fonts/material-icons.woff2 +0 -0
  134. package/icons/activity-outline.svg +1 -0
  135. package/icons/alert-circle-outline.svg +1 -0
  136. package/icons/alert-triangle-outline.svg +1 -0
  137. package/icons/archive-outline.svg +1 -0
  138. package/icons/arrow-back-outline.svg +1 -0
  139. package/icons/arrow-circle-down-outline.svg +1 -0
  140. package/icons/arrow-circle-left-outline.svg +1 -0
  141. package/icons/arrow-circle-right-outline.svg +1 -0
  142. package/icons/arrow-circle-up-outline.svg +1 -0
  143. package/icons/arrow-down-outline.svg +1 -0
  144. package/icons/arrow-downward-outline.svg +1 -0
  145. package/icons/arrow-forward-outline.svg +1 -0
  146. package/icons/arrow-ios-back-outline.svg +1 -0
  147. package/icons/arrow-ios-downward-outline.svg +1 -0
  148. package/icons/arrow-ios-forward-outline.svg +1 -0
  149. package/icons/arrow-ios-upward-outline.svg +1 -0
  150. package/icons/arrow-left-outline.svg +1 -0
  151. package/icons/arrow-right-outline.svg +1 -0
  152. package/icons/arrow-up-outline.svg +1 -0
  153. package/icons/arrow-upward-outline.svg +1 -0
  154. package/icons/arrowhead-down-outline.svg +1 -0
  155. package/icons/arrowhead-left-outline.svg +1 -0
  156. package/icons/arrowhead-right-outline.svg +1 -0
  157. package/icons/arrowhead-up-outline.svg +1 -0
  158. package/icons/at-outline.svg +1 -0
  159. package/icons/attach-2-outline.svg +1 -0
  160. package/icons/attach-outline.svg +1 -0
  161. package/icons/award-outline.svg +1 -0
  162. package/icons/backspace-outline.svg +1 -0
  163. package/icons/bar-chart-2-outline.svg +1 -0
  164. package/icons/bar-chart-outline.svg +1 -0
  165. package/icons/battery-outline.svg +1 -0
  166. package/icons/behance-outline.svg +1 -0
  167. package/icons/bell-off-outline.svg +1 -0
  168. package/icons/bell-outline.svg +1 -0
  169. package/icons/bluetooth-outline.svg +1 -0
  170. package/icons/book-open-outline.svg +1 -0
  171. package/icons/book-outline.svg +1 -0
  172. package/icons/bookmark-outline.svg +1 -0
  173. package/icons/briefcase-outline.svg +1 -0
  174. package/icons/browser-outline.svg +1 -0
  175. package/icons/brush-outline.svg +1 -0
  176. package/icons/bulb-outline.svg +1 -0
  177. package/icons/calendar-outline.svg +1 -0
  178. package/icons/camera-outline.svg +1 -0
  179. package/icons/car-outline.svg +1 -0
  180. package/icons/cast-outline.svg +1 -0
  181. package/icons/charging-outline.svg +1 -0
  182. package/icons/checkmark-circle-2-outline.svg +1 -0
  183. package/icons/checkmark-circle-outline.svg +1 -0
  184. package/icons/checkmark-outline.svg +1 -0
  185. package/icons/checkmark-square-2-outline.svg +1 -0
  186. package/icons/checkmark-square-outline.svg +1 -0
  187. package/icons/chevron-down-outline.svg +1 -0
  188. package/icons/chevron-left-outline.svg +1 -0
  189. package/icons/chevron-right-outline.svg +1 -0
  190. package/icons/chevron-up-outline.svg +1 -0
  191. package/icons/clipboard-outline.svg +1 -0
  192. package/icons/clock-outline.svg +1 -0
  193. package/icons/close-circle-outline.svg +1 -0
  194. package/icons/close-outline.svg +1 -0
  195. package/icons/close-square-outline.svg +1 -0
  196. package/icons/cloud-download-outline.svg +1 -0
  197. package/icons/cloud-upload-outline.svg +1 -0
  198. package/icons/code-download-outline.svg +1 -0
  199. package/icons/code-outline.svg +1 -0
  200. package/icons/collapse-outline.svg +1 -0
  201. package/icons/color-palette-outline.svg +1 -0
  202. package/icons/color-picker-outline.svg +1 -0
  203. package/icons/compass-outline.svg +1 -0
  204. package/icons/copy-outline.svg +1 -0
  205. package/icons/corner-down-left-outline.svg +1 -0
  206. package/icons/corner-down-right-outline.svg +1 -0
  207. package/icons/corner-left-down-outline.svg +1 -0
  208. package/icons/corner-left-up-outline.svg +1 -0
  209. package/icons/corner-right-down-outline.svg +1 -0
  210. package/icons/corner-right-up-outline.svg +1 -0
  211. package/icons/corner-up-left-outline.svg +1 -0
  212. package/icons/corner-up-right-outline.svg +1 -0
  213. package/icons/credit-card-outline.svg +1 -0
  214. package/icons/crop-outline.svg +1 -0
  215. package/icons/cube-outline.svg +1 -0
  216. package/icons/diagonal-arrow-left-down-outline.svg +1 -0
  217. package/icons/diagonal-arrow-left-up-outline.svg +1 -0
  218. package/icons/diagonal-arrow-right-down-outline.svg +1 -0
  219. package/icons/diagonal-arrow-right-up-outline.svg +1 -0
  220. package/icons/done-all-outline.svg +1 -0
  221. package/icons/download-outline.svg +1 -0
  222. package/icons/droplet-off-outline.svg +1 -0
  223. package/icons/droplet-outline.svg +1 -0
  224. package/icons/edit-2-outline.svg +1 -0
  225. package/icons/edit-outline.svg +1 -0
  226. package/icons/email-outline.svg +1 -0
  227. package/icons/expand-outline.svg +1 -0
  228. package/icons/external-link-outline.svg +1 -0
  229. package/icons/eye-off-2-outline.svg +1 -0
  230. package/icons/eye-off-outline.svg +1 -0
  231. package/icons/eye-outline.svg +1 -0
  232. package/icons/facebook-outline.svg +1 -0
  233. package/icons/file-add-outline.svg +1 -0
  234. package/icons/file-outline.svg +1 -0
  235. package/icons/file-remove-outline.svg +1 -0
  236. package/icons/file-text-outline.svg +1 -0
  237. package/icons/film-outline.svg +1 -0
  238. package/icons/flag-outline.svg +1 -0
  239. package/icons/flash-off-outline.svg +1 -0
  240. package/icons/flash-outline.svg +1 -0
  241. package/icons/flip-2-outline.svg +1 -0
  242. package/icons/flip-outline.svg +1 -0
  243. package/icons/folder-add-outline.svg +1 -0
  244. package/icons/folder-outline.svg +1 -0
  245. package/icons/folder-remove-outline.svg +1 -0
  246. package/icons/funnel-outline.svg +1 -0
  247. package/icons/gift-outline.svg +1 -0
  248. package/icons/github-outline.svg +1 -0
  249. package/icons/globe-2-outline.svg +1 -0
  250. package/icons/globe-outline.svg +1 -0
  251. package/icons/google-outline.svg +1 -0
  252. package/icons/grid-outline.svg +1 -0
  253. package/icons/hard-drive-outline.svg +1 -0
  254. package/icons/hash-outline.svg +1 -0
  255. package/icons/headphones-outline.svg +1 -0
  256. package/icons/heart-outline.svg +1 -0
  257. package/icons/home-outline.svg +1 -0
  258. package/icons/image-outline.svg +1 -0
  259. package/icons/inbox-outline.svg +1 -0
  260. package/icons/info-outline.svg +1 -0
  261. package/icons/keypad-outline.svg +1 -0
  262. package/icons/layers-outline.svg +1 -0
  263. package/icons/layout-outline.svg +1 -0
  264. package/icons/link-2-outline.svg +1 -0
  265. package/icons/link-outline.svg +1 -0
  266. package/icons/linkedin-outline.svg +1 -0
  267. package/icons/list-outline.svg +1 -0
  268. package/icons/loader-outline.svg +1 -0
  269. package/icons/lock-outline.svg +1 -0
  270. package/icons/log-in-outline.svg +1 -0
  271. package/icons/log-out-outline.svg +1 -0
  272. package/icons/map-outline.svg +1 -0
  273. package/icons/maximize-outline.svg +1 -0
  274. package/icons/menu-2-outline.svg +1 -0
  275. package/icons/menu-arrow-outline.svg +1 -0
  276. package/icons/menu-outline.svg +1 -0
  277. package/icons/message-circle-outline.svg +1 -0
  278. package/icons/message-square-outline.svg +1 -0
  279. package/icons/mic-off-outline.svg +1 -0
  280. package/icons/mic-outline.svg +1 -0
  281. package/icons/minimize-outline.svg +1 -0
  282. package/icons/minus-circle-outline.svg +1 -0
  283. package/icons/minus-outline.svg +1 -0
  284. package/icons/minus-square-outline.svg +1 -0
  285. package/icons/monitor-outline.svg +1 -0
  286. package/icons/moon-outline.svg +1 -0
  287. package/icons/more-horizontal-outline.svg +1 -0
  288. package/icons/more-vertical-outline.svg +1 -0
  289. package/icons/move-outline.svg +1 -0
  290. package/icons/music-outline.svg +1 -0
  291. package/icons/navigation-2-outline.svg +1 -0
  292. package/icons/navigation-outline.svg +1 -0
  293. package/icons/npm-outline.svg +1 -0
  294. package/icons/options-2-outline.svg +1 -0
  295. package/icons/options-outline.svg +1 -0
  296. package/icons/pantone-outline.svg +1 -0
  297. package/icons/paper-plane-outline.svg +1 -0
  298. package/icons/pause-circle-outline.svg +1 -0
  299. package/icons/people-outline.svg +1 -0
  300. package/icons/percent-outline.svg +1 -0
  301. package/icons/person-add-outline.svg +1 -0
  302. package/icons/person-delete-outline.svg +1 -0
  303. package/icons/person-done-outline.svg +1 -0
  304. package/icons/person-outline.svg +1 -0
  305. package/icons/person-remove-outline.svg +1 -0
  306. package/icons/phone-call-outline.svg +1 -0
  307. package/icons/phone-missed-outline.svg +1 -0
  308. package/icons/phone-off-outline.svg +1 -0
  309. package/icons/phone-outline.svg +1 -0
  310. package/icons/pie-chart-outline.svg +1 -0
  311. package/icons/pin-outline.svg +1 -0
  312. package/icons/play-circle-outline.svg +1 -0
  313. package/icons/plus-circle-outline.svg +1 -0
  314. package/icons/plus-outline.svg +1 -0
  315. package/icons/plus-square-outline.svg +1 -0
  316. package/icons/power-outline.svg +1 -0
  317. package/icons/pricetags-outline.svg +1 -0
  318. package/icons/printer-outline.svg +1 -0
  319. package/icons/question-mark-circle-outline.svg +1 -0
  320. package/icons/question-mark-outline.svg +1 -0
  321. package/icons/radio-button-off-outline.svg +1 -0
  322. package/icons/radio-button-on-outline.svg +1 -0
  323. package/icons/radio-outline.svg +1 -0
  324. package/icons/recording-outline.svg +1 -0
  325. package/icons/refresh-outline.svg +1 -0
  326. package/icons/repeat-outline.svg +1 -0
  327. package/icons/rewind-left-outline.svg +1 -0
  328. package/icons/rewind-right-outline.svg +1 -0
  329. package/icons/save-outline.svg +1 -0
  330. package/icons/scissors-outline.svg +1 -0
  331. package/icons/search-outline.svg +1 -0
  332. package/icons/settings-2-outline.svg +1 -0
  333. package/icons/settings-outline.svg +1 -0
  334. package/icons/shake-outline.svg +1 -0
  335. package/icons/share-outline.svg +1 -0
  336. package/icons/shield-off-outline.svg +1 -0
  337. package/icons/shield-outline.svg +1 -0
  338. package/icons/shopping-bag-outline.svg +1 -0
  339. package/icons/shopping-cart-outline.svg +1 -0
  340. package/icons/shuffle-2-outline.svg +1 -0
  341. package/icons/shuffle-outline.svg +1 -0
  342. package/icons/skip-back-outline.svg +1 -0
  343. package/icons/skip-forward-outline.svg +1 -0
  344. package/icons/slash-outline.svg +1 -0
  345. package/icons/smartphone-outline.svg +1 -0
  346. package/icons/smiling-face-outline.svg +1 -0
  347. package/icons/speaker-outline.svg +1 -0
  348. package/icons/square-outline.svg +1 -0
  349. package/icons/star-outline.svg +1 -0
  350. package/icons/stop-circle-outline.svg +1 -0
  351. package/icons/sun-outline.svg +1 -0
  352. package/icons/swap-outline.svg +1 -0
  353. package/icons/sync-outline.svg +1 -0
  354. package/icons/text-outline.svg +1 -0
  355. package/icons/thermometer-minus-outline.svg +1 -0
  356. package/icons/thermometer-outline.svg +1 -0
  357. package/icons/thermometer-plus-outline.svg +1 -0
  358. package/icons/toggle-left-outline.svg +1 -0
  359. package/icons/toggle-right-outline.svg +1 -0
  360. package/icons/trash-2-outline.svg +1 -0
  361. package/icons/trash-outline.svg +1 -0
  362. package/icons/trending-down-outline.svg +1 -0
  363. package/icons/trending-up-outline.svg +1 -0
  364. package/icons/tv-outline.svg +1 -0
  365. package/icons/twitter-outline.svg +1 -0
  366. package/icons/umbrella-outline.svg +1 -0
  367. package/icons/undo-outline.svg +1 -0
  368. package/icons/unlock-outline.svg +1 -0
  369. package/icons/upload-outline.svg +1 -0
  370. package/icons/video-off-outline.svg +1 -0
  371. package/icons/video-outline.svg +1 -0
  372. package/icons/volume-down-outline.svg +1 -0
  373. package/icons/volume-mute-outline.svg +1 -0
  374. package/icons/volume-off-outline.svg +1 -0
  375. package/icons/volume-up-outline.svg +1 -0
  376. package/icons/wifi-off-outline.svg +1 -0
  377. package/icons/wifi-outline.svg +1 -0
  378. package/js/carousel.js +133 -0
  379. package/js/carousel.ts +173 -0
  380. package/js/chart.js +257 -0
  381. package/js/code-viewer.js +148 -0
  382. package/js/code-viewer.ts +188 -0
  383. package/js/datepicker.js +497 -0
  384. package/js/datepicker.ts +619 -0
  385. package/js/dropdown.js +122 -0
  386. package/js/dropdown.ts +180 -0
  387. package/js/editor.js +421 -0
  388. package/js/editor.ts +426 -0
  389. package/js/file-uploader.js +268 -0
  390. package/js/file-uploader.ts +350 -0
  391. package/js/flyout-menu.js +195 -0
  392. package/js/flyout-menu.ts +250 -0
  393. package/js/form-builder.js +107 -0
  394. package/js/gallery.js +177 -0
  395. package/js/gallery.ts +231 -0
  396. package/js/guitar-chords.js +268 -0
  397. package/js/index.js +720 -0
  398. package/js/index.ts +874 -0
  399. package/js/lazy-loader.js +121 -0
  400. package/js/modal.js +113 -0
  401. package/js/modal.ts +167 -0
  402. package/js/push-menu.js +101 -0
  403. package/js/push-menu.ts +130 -0
  404. package/js/request.js +51 -0
  405. package/js/scroll.js +27 -0
  406. package/js/scroll.ts +47 -0
  407. package/js/scrollbar.js +219 -0
  408. package/js/scrollbar.ts +308 -0
  409. package/js/select.js +158 -0
  410. package/js/select.ts +217 -0
  411. package/js/table.js +359 -0
  412. package/js/table.ts +453 -0
  413. package/js/tabs.js +225 -0
  414. package/js/tabs.ts +280 -0
  415. package/js/theme.js +194 -0
  416. package/js/theme.ts +225 -0
  417. package/js/timepicker.js +98 -0
  418. package/js/timepicker.ts +131 -0
  419. package/js/toast.js +93 -0
  420. package/js/toast.ts +138 -0
  421. package/js/tooltip.js +193 -0
  422. package/js/tooltip.ts +252 -0
  423. package/js/tree.js +162 -0
  424. package/js/tree.ts +218 -0
  425. package/js/tsconfig.json +18 -0
  426. package/js/utils.js +69 -0
  427. package/js/utils.ts +84 -0
  428. package/js/virtual-dropdown.js +277 -0
  429. package/js/virtual-dropdown.ts +366 -0
  430. package/package.json +38 -0
package/js/table.ts ADDED
@@ -0,0 +1,453 @@
1
+ import { Select } from "./select.js";
2
+
3
+ interface TableColumn {
4
+ key: string;
5
+ label: string;
6
+ sortable?: boolean;
7
+ }
8
+
9
+ interface TableRow {
10
+ [key: string]: string | number | boolean;
11
+ }
12
+
13
+ interface TableOptions {
14
+ data?: TableRow[];
15
+ columns?: TableColumn[];
16
+ pageSize?: number;
17
+ }
18
+
19
+ type SortDirection = 'asc' | 'desc';
20
+
21
+ class Table {
22
+ private container: HTMLElement;
23
+ private data: TableRow[];
24
+ private columns: TableColumn[];
25
+ private pageSize: number;
26
+ private currentPage: number;
27
+ private sortColumn: string | null;
28
+ private sortDirection: SortDirection;
29
+ private filterText: string;
30
+ private tableBody!: HTMLTableSectionElement;
31
+ private tableHeader!: HTMLTableSectionElement;
32
+ private paginationContainer!: HTMLDivElement;
33
+
34
+ constructor(elementOrSelector: string | HTMLElement, options: TableOptions = {}) {
35
+ const element = typeof elementOrSelector === 'string'
36
+ ? document.querySelector<HTMLElement>(elementOrSelector)
37
+ : elementOrSelector;
38
+
39
+ if (!element) {
40
+ throw new Error(`Table: Element not found for selector "${elementOrSelector}"`);
41
+ }
42
+
43
+ this.container = element;
44
+ this.data = options.data || [];
45
+ this.columns = options.columns || [];
46
+ this.pageSize = options.pageSize || 10;
47
+ this.currentPage = 1;
48
+ this.sortColumn = null;
49
+ this.sortDirection = 'asc';
50
+ this.filterText = '';
51
+
52
+ if (this.data.length === 0 && this.container.querySelector('table')) {
53
+ this.parseTableFromDOM();
54
+ }
55
+
56
+ this.init();
57
+ }
58
+
59
+ /**
60
+ * Parses an existing HTML table in the DOM to extract data and columns
61
+ */
62
+ private parseTableFromDOM(): void {
63
+ const table = this.container.querySelector('table');
64
+ if (!table) return;
65
+
66
+ const thead = table.querySelector('thead');
67
+ const tbody = table.querySelector('tbody');
68
+
69
+ if (!thead || !tbody) return;
70
+
71
+ // Parse columns from header
72
+ const ths = thead.querySelectorAll('th');
73
+ this.columns = Array.from(ths).map((th, index) => ({
74
+ key: `col${index}`,
75
+ label: th.textContent?.trim() || '',
76
+ sortable: true
77
+ }));
78
+
79
+ // Parse data from body rows
80
+ const trs = tbody.querySelectorAll('tr');
81
+ this.data = Array.from(trs).map(tr => {
82
+ const row: TableRow = {};
83
+ const tds = tr.querySelectorAll('td');
84
+
85
+ tds.forEach((td, index) => {
86
+ if (this.columns[index]) {
87
+ row[this.columns[index].key] = td.textContent?.trim() || '';
88
+ }
89
+ });
90
+
91
+ return row;
92
+ });
93
+
94
+ // Clear the existing static table
95
+ this.container.innerHTML = '';
96
+ }
97
+
98
+ /**
99
+ * Initializes the table by rendering controls, structure, and content
100
+ */
101
+ private init(): void {
102
+ this.renderControls();
103
+ this.renderTableStructure();
104
+ this.render();
105
+ }
106
+
107
+ /**
108
+ * Renders the search and page size controls
109
+ */
110
+ private renderControls(): void {
111
+ const controlsDiv = document.createElement('div');
112
+ controlsDiv.className = 'table-controls';
113
+
114
+ // Search input
115
+ const searchInput = document.createElement('input');
116
+ searchInput.type = 'text';
117
+ searchInput.placeholder = 'Search...';
118
+ searchInput.className = 'search-input';
119
+ searchInput.addEventListener('input', (e) => {
120
+ this.handleSearch((e.target as HTMLInputElement).value);
121
+ });
122
+ controlsDiv.appendChild(searchInput);
123
+
124
+ // Page size selector
125
+ const selectGroup = document.createElement('div');
126
+ selectGroup.className = 'select-group';
127
+
128
+ const label = document.createElement('label');
129
+ label.textContent = 'Page Size';
130
+ selectGroup.appendChild(label);
131
+
132
+ const pageSizeSelect = document.createElement('select');
133
+ pageSizeSelect.className = 'page-size-select';
134
+
135
+ [5, 10, 20, 50].forEach(size => {
136
+ const option = document.createElement('option');
137
+ option.value = String(size);
138
+ option.textContent = `${size} per page`;
139
+ option.selected = size === this.pageSize;
140
+ pageSizeSelect.appendChild(option);
141
+ });
142
+
143
+ pageSizeSelect.addEventListener('change', (e) => {
144
+ this.handlePageSizeChange(parseInt((e.target as HTMLSelectElement).value, 10));
145
+ });
146
+
147
+ this.assignUniqueId(pageSizeSelect, 'page-size-select-0');
148
+ selectGroup.appendChild(pageSizeSelect);
149
+ controlsDiv.appendChild(selectGroup);
150
+
151
+ this.container.appendChild(controlsDiv);
152
+ new Select('#' + pageSizeSelect.id);
153
+ }
154
+
155
+ /**
156
+ * Creates the table structure (table, thead, tbody, pagination container)
157
+ */
158
+ private renderTableStructure(): void {
159
+ const wrapper = document.createElement('div');
160
+ wrapper.className = 'table-wrapper';
161
+
162
+ const table = document.createElement('table');
163
+ const thead = document.createElement('thead');
164
+ const tbody = document.createElement('tbody');
165
+
166
+ // Create header row
167
+ const tr = document.createElement('tr');
168
+ this.columns.forEach(col => {
169
+ const th = document.createElement('th');
170
+ th.textContent = col.label;
171
+ th.dataset.key = col.key;
172
+
173
+ if (col.sortable !== false) {
174
+ th.classList.add('sortable');
175
+ th.addEventListener('click', () => this.handleSort(col.key));
176
+ }
177
+
178
+ tr.appendChild(th);
179
+ });
180
+ thead.appendChild(tr);
181
+
182
+ table.appendChild(thead);
183
+ table.appendChild(tbody);
184
+ wrapper.appendChild(table);
185
+ this.container.appendChild(wrapper);
186
+
187
+ // Create pagination container
188
+ const paginationDiv = document.createElement('div');
189
+ paginationDiv.className = 'pagination';
190
+ this.container.appendChild(paginationDiv);
191
+
192
+ this.tableBody = tbody;
193
+ this.tableHeader = thead;
194
+ this.paginationContainer = paginationDiv;
195
+ }
196
+
197
+ /**
198
+ * Returns filtered and sorted data based on current state
199
+ */
200
+ private getFilteredAndSortedData(): TableRow[] {
201
+ let processedData = [...this.data];
202
+
203
+ // Apply filter
204
+ if (this.filterText) {
205
+ const lowerFilter = this.filterText.toLowerCase();
206
+ processedData = processedData.filter(row => {
207
+ return this.columns.some(col => {
208
+ const val = String(row[col.key] ?? '').toLowerCase();
209
+ return val.includes(lowerFilter);
210
+ });
211
+ });
212
+ }
213
+
214
+ // Apply sort
215
+ if (this.sortColumn) {
216
+ processedData.sort((a, b) => {
217
+ const valA = a[this.sortColumn!];
218
+ const valB = b[this.sortColumn!];
219
+
220
+ // Handle null/undefined values
221
+ if (valA == null && valB == null) return 0;
222
+ if (valA == null) return 1;
223
+ if (valB == null) return -1;
224
+
225
+ if (valA < valB) return this.sortDirection === 'asc' ? -1 : 1;
226
+ if (valA > valB) return this.sortDirection === 'asc' ? 1 : -1;
227
+ return 0;
228
+ });
229
+ }
230
+
231
+ return processedData;
232
+ }
233
+
234
+ /**
235
+ * Renders the table body, pagination, and header sort indicators
236
+ */
237
+ private render(): void {
238
+ const processedData = this.getFilteredAndSortedData();
239
+ const totalItems = processedData.length;
240
+ const totalPages = Math.ceil(totalItems / this.pageSize);
241
+
242
+ // Ensure current page is valid
243
+ if (this.currentPage > totalPages && totalPages > 0) {
244
+ this.currentPage = totalPages;
245
+ }
246
+ if (this.currentPage < 1 && totalPages > 0) {
247
+ this.currentPage = 1;
248
+ }
249
+
250
+ const startIndex = (this.currentPage - 1) * this.pageSize;
251
+ const endIndex = Math.min(startIndex + this.pageSize, totalItems);
252
+ const pageData = processedData.slice(startIndex, endIndex);
253
+
254
+ this.renderBody(pageData);
255
+ this.renderPagination(totalItems, totalPages, startIndex, endIndex);
256
+ this.updateHeaderSortIcons();
257
+ }
258
+
259
+ /**
260
+ * Renders the table body rows
261
+ */
262
+ private renderBody(data: TableRow[]): void {
263
+ this.tableBody.innerHTML = '';
264
+
265
+ if (data.length === 0) {
266
+ const tr = document.createElement('tr');
267
+ const td = document.createElement('td');
268
+ td.colSpan = this.columns.length;
269
+ td.textContent = 'No results found';
270
+ td.style.textAlign = 'center';
271
+ tr.appendChild(td);
272
+ this.tableBody.appendChild(tr);
273
+ return;
274
+ }
275
+
276
+ data.forEach(row => {
277
+ const tr = document.createElement('tr');
278
+ this.columns.forEach(col => {
279
+ const td = document.createElement('td');
280
+ td.textContent = String(row[col.key] ?? '');
281
+ td.setAttribute('data-label', col.label); // For mobile view
282
+ tr.appendChild(td);
283
+ });
284
+ this.tableBody.appendChild(tr);
285
+ });
286
+ }
287
+
288
+ /**
289
+ * Updates the sort direction indicators in table headers
290
+ */
291
+ private updateHeaderSortIcons(): void {
292
+ const ths = this.tableHeader.querySelectorAll('th');
293
+ ths.forEach(th => {
294
+ th.classList.remove('sort-asc', 'sort-desc');
295
+ if (th.dataset.key === this.sortColumn) {
296
+ th.classList.add(this.sortDirection === 'asc' ? 'sort-asc' : 'sort-desc');
297
+ }
298
+ });
299
+ }
300
+
301
+ /**
302
+ * Renders pagination controls and info
303
+ */
304
+ private renderPagination(
305
+ totalItems: number,
306
+ totalPages: number,
307
+ startIndex: number,
308
+ endIndex: number
309
+ ): void {
310
+ this.paginationContainer.innerHTML = '';
311
+
312
+ if (totalItems === 0) return;
313
+
314
+ // Info text
315
+ const info = document.createElement('div');
316
+ info.className = 'pagination-info';
317
+ info.textContent = `Showing ${startIndex + 1} to ${endIndex} of ${totalItems} entries`;
318
+ this.paginationContainer.appendChild(info);
319
+
320
+ // Pagination buttons
321
+ const buttonsDiv = document.createElement('div');
322
+ buttonsDiv.className = 'pagination-buttons';
323
+
324
+ // Previous button
325
+ const prevBtn = document.createElement('button');
326
+ prevBtn.className = 'page-btn';
327
+ prevBtn.textContent = 'Previous';
328
+ prevBtn.disabled = this.currentPage === 1;
329
+ prevBtn.addEventListener('click', () => this.setPage(this.currentPage - 1));
330
+ buttonsDiv.appendChild(prevBtn);
331
+
332
+ // Calculate page range to display (max 5 pages)
333
+ let startPage = Math.max(1, this.currentPage - 2);
334
+ let endPage = Math.min(totalPages, startPage + 4);
335
+
336
+ if (endPage - startPage < 4) {
337
+ startPage = Math.max(1, endPage - 4);
338
+ }
339
+
340
+ // Page number buttons
341
+ for (let i = startPage; i <= endPage; i++) {
342
+ const btn = document.createElement('button');
343
+ btn.className = `page-btn ${i === this.currentPage ? 'active' : ''}`;
344
+ btn.textContent = String(i);
345
+ btn.addEventListener('click', () => this.setPage(i));
346
+ buttonsDiv.appendChild(btn);
347
+ }
348
+
349
+ // Next button
350
+ const nextBtn = document.createElement('button');
351
+ nextBtn.className = 'page-btn';
352
+ nextBtn.textContent = 'Next';
353
+ nextBtn.disabled = this.currentPage === totalPages;
354
+ nextBtn.addEventListener('click', () => this.setPage(this.currentPage + 1));
355
+ buttonsDiv.appendChild(nextBtn);
356
+
357
+ this.paginationContainer.appendChild(buttonsDiv);
358
+ }
359
+
360
+ /**
361
+ * Handles search input changes
362
+ */
363
+ private handleSearch(text: string): void {
364
+ this.filterText = text;
365
+ this.currentPage = 1; // Reset to first page on search
366
+ this.render();
367
+ }
368
+
369
+ /**
370
+ * Handles column header clicks for sorting
371
+ */
372
+ private handleSort(key: string): void {
373
+ if (this.sortColumn === key) {
374
+ // Toggle sort direction
375
+ this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc';
376
+ } else {
377
+ this.sortColumn = key;
378
+ this.sortDirection = 'asc';
379
+ }
380
+ this.render();
381
+ }
382
+
383
+ /**
384
+ * Handles page size changes
385
+ */
386
+ private handlePageSizeChange(size: number): void {
387
+ this.pageSize = size;
388
+ this.currentPage = 1;
389
+ this.render();
390
+ }
391
+
392
+ /**
393
+ * Sets the current page and re-renders
394
+ */
395
+ private setPage(page: number): void {
396
+ this.currentPage = page;
397
+ this.render();
398
+ }
399
+
400
+ /**
401
+ * Assigns a unique ID to an element, incrementing if necessary
402
+ */
403
+ private assignUniqueId(element: HTMLElement, baseId: string): string | null {
404
+ if (!element || !baseId) return null;
405
+
406
+ let id = baseId;
407
+ let counter = 1;
408
+
409
+ // If baseId already ends with a number, extract it
410
+ const match = baseId.match(/^(.*?)(\d+)$/);
411
+ if (match) {
412
+ id = match[1];
413
+ counter = parseInt(match[2], 10);
414
+ }
415
+
416
+ let uniqueId = baseId;
417
+
418
+ while (document.getElementById(uniqueId)) {
419
+ counter++;
420
+ uniqueId = `${id}${counter}`;
421
+ }
422
+
423
+ element.id = uniqueId;
424
+ return uniqueId;
425
+ }
426
+
427
+ /**
428
+ * Public API: Updates the table data and re-renders
429
+ */
430
+ public setData(data: TableRow[]): void {
431
+ this.data = data;
432
+ this.currentPage = 1;
433
+ this.render();
434
+ }
435
+
436
+ /**
437
+ * Public API: Updates the columns and re-renders
438
+ */
439
+ public setColumns(columns: TableColumn[]): void {
440
+ this.columns = columns;
441
+ this.container.innerHTML = '';
442
+ this.init();
443
+ }
444
+
445
+ /**
446
+ * Public API: Gets the current filtered and sorted data
447
+ */
448
+ public getData(): TableRow[] {
449
+ return this.getFilteredAndSortedData();
450
+ }
451
+ }
452
+
453
+ export { Table, TableRow, TableColumn, TableOptions };
package/js/tabs.js ADDED
@@ -0,0 +1,225 @@
1
+ class Tabs {
2
+ constructor(elementOrSelector, options = {}) {
3
+ const element = typeof elementOrSelector === 'string'
4
+ ? document.querySelector(elementOrSelector)
5
+ : elementOrSelector;
6
+ if (!element) {
7
+ throw new Error(`Tabs: Element not found for selector "${elementOrSelector}"`);
8
+ }
9
+ this.container = element;
10
+ // Set default options
11
+ const layout = options.layout || 'horizontal';
12
+ this.options = {
13
+ layout,
14
+ defaultTab: options.defaultTab ?? 0,
15
+ menuPos: options.menuPos || (layout === 'vertical' ? 'left' : 'top'),
16
+ onChange: options.onChange
17
+ };
18
+ this.currentTab = this.options.defaultTab;
19
+ this.tabItems = document.querySelectorAll('.tab-item'); // Will be set in init
20
+ this.tabPanels = document.querySelectorAll('.tab-panel'); // Will be set in init
21
+ this.init();
22
+ }
23
+ /**
24
+ * Initializes the tabs component
25
+ */
26
+ init() {
27
+ // Apply layout class
28
+ if (this.options.layout === 'vertical') {
29
+ this.container.classList.add('tabs-vertical');
30
+ }
31
+ this.tabItems = this.container.querySelectorAll('.tab-item');
32
+ this.tabPanels = this.container.querySelectorAll('.tab-panel');
33
+ // Validate that we have tabs and panels
34
+ if (this.tabItems.length === 0) {
35
+ console.warn('No tab items found in container');
36
+ return;
37
+ }
38
+ if (this.tabPanels.length === 0) {
39
+ console.warn('No tab panels found in container');
40
+ return;
41
+ }
42
+ if (this.tabItems.length !== this.tabPanels.length) {
43
+ console.warn('Number of tab items does not match number of tab panels');
44
+ }
45
+ this.bindEvents();
46
+ this.activateTab(this.options.defaultTab);
47
+ }
48
+ /**
49
+ * Binds click events to tab items
50
+ */
51
+ bindEvents() {
52
+ this.tabItems.forEach((item, index) => {
53
+ item.addEventListener('click', (e) => {
54
+ e.preventDefault();
55
+ this.activateTab(index);
56
+ });
57
+ // Add keyboard navigation for accessibility
58
+ item.addEventListener('keydown', (e) => {
59
+ const keyEvent = e;
60
+ this.handleKeyboardNavigation(keyEvent, index);
61
+ });
62
+ // Set ARIA attributes
63
+ item.setAttribute('role', 'tab');
64
+ item.setAttribute('tabindex', index === this.options.defaultTab ? '0' : '-1');
65
+ item.setAttribute('aria-selected', index === this.options.defaultTab ? 'true' : 'false');
66
+ });
67
+ // Set ARIA attributes for panels
68
+ this.tabPanels.forEach((panel, index) => {
69
+ panel.setAttribute('role', 'tabpanel');
70
+ panel.setAttribute('aria-hidden', index === this.options.defaultTab ? 'false' : 'true');
71
+ });
72
+ }
73
+ /**
74
+ * Handles keyboard navigation (Arrow keys, Home, End)
75
+ */
76
+ handleKeyboardNavigation(e, currentIndex) {
77
+ let newIndex = currentIndex;
78
+ const isVertical = this.options.layout === 'vertical';
79
+ switch (e.key) {
80
+ case 'ArrowLeft':
81
+ if (!isVertical) {
82
+ newIndex = currentIndex > 0 ? currentIndex - 1 : this.tabItems.length - 1;
83
+ e.preventDefault();
84
+ }
85
+ break;
86
+ case 'ArrowRight':
87
+ if (!isVertical) {
88
+ newIndex = currentIndex < this.tabItems.length - 1 ? currentIndex + 1 : 0;
89
+ e.preventDefault();
90
+ }
91
+ break;
92
+ case 'ArrowUp':
93
+ if (isVertical) {
94
+ newIndex = currentIndex > 0 ? currentIndex - 1 : this.tabItems.length - 1;
95
+ e.preventDefault();
96
+ }
97
+ break;
98
+ case 'ArrowDown':
99
+ if (isVertical) {
100
+ newIndex = currentIndex < this.tabItems.length - 1 ? currentIndex + 1 : 0;
101
+ e.preventDefault();
102
+ }
103
+ break;
104
+ case 'Home':
105
+ newIndex = 0;
106
+ e.preventDefault();
107
+ break;
108
+ case 'End':
109
+ newIndex = this.tabItems.length - 1;
110
+ e.preventDefault();
111
+ break;
112
+ default:
113
+ return;
114
+ }
115
+ if (newIndex !== currentIndex) {
116
+ this.activateTab(newIndex);
117
+ this.tabItems[newIndex].focus();
118
+ }
119
+ }
120
+ /**
121
+ * Activates a tab by index
122
+ */
123
+ activateTab(index) {
124
+ if (index < 0 || index >= this.tabItems.length) {
125
+ console.warn(`Invalid tab index: ${index}`);
126
+ return;
127
+ }
128
+ // Remove active class from all
129
+ this.tabItems.forEach((item, i) => {
130
+ item.classList.remove('active');
131
+ item.setAttribute('tabindex', '-1');
132
+ item.setAttribute('aria-selected', 'false');
133
+ });
134
+ this.tabPanels.forEach((panel) => {
135
+ panel.classList.remove('active');
136
+ panel.setAttribute('aria-hidden', 'true');
137
+ });
138
+ // Add active class to selected
139
+ this.tabItems[index].classList.add('active');
140
+ this.tabItems[index].setAttribute('tabindex', '0');
141
+ this.tabItems[index].setAttribute('aria-selected', 'true');
142
+ this.tabPanels[index].classList.add('active');
143
+ this.tabPanels[index].setAttribute('aria-hidden', 'false');
144
+ const previousTab = this.currentTab;
145
+ this.currentTab = index;
146
+ // Call onChange callback if provided
147
+ if (this.options.onChange && previousTab !== index) {
148
+ this.options.onChange(index);
149
+ }
150
+ }
151
+ /**
152
+ * Public API: Programmatically activate a tab
153
+ */
154
+ goToTab(index) {
155
+ this.activateTab(index);
156
+ // Focus the tab for keyboard users
157
+ if (this.tabItems[index]) {
158
+ this.tabItems[index].focus();
159
+ }
160
+ }
161
+ /**
162
+ * Public API: Get the currently active tab index
163
+ */
164
+ getCurrentTab() {
165
+ return this.currentTab;
166
+ }
167
+ /**
168
+ * Public API: Get the total number of tabs
169
+ */
170
+ getTabCount() {
171
+ return this.tabItems.length;
172
+ }
173
+ /**
174
+ * Public API: Enable a tab
175
+ */
176
+ enableTab(index) {
177
+ if (index < 0 || index >= this.tabItems.length)
178
+ return;
179
+ const tab = this.tabItems[index];
180
+ tab.classList.remove('disabled');
181
+ tab.removeAttribute('aria-disabled');
182
+ tab.style.pointerEvents = '';
183
+ }
184
+ /**
185
+ * Public API: Disable a tab
186
+ */
187
+ disableTab(index) {
188
+ if (index < 0 || index >= this.tabItems.length)
189
+ return;
190
+ const tab = this.tabItems[index];
191
+ tab.classList.add('disabled');
192
+ tab.setAttribute('aria-disabled', 'true');
193
+ tab.style.pointerEvents = 'none';
194
+ // If disabling the current tab, switch to the first enabled tab
195
+ if (index === this.currentTab) {
196
+ const firstEnabled = Array.from(this.tabItems).findIndex((item) => !item.classList.contains('disabled'));
197
+ if (firstEnabled !== -1) {
198
+ this.activateTab(firstEnabled);
199
+ }
200
+ }
201
+ }
202
+ /**
203
+ * Public API: Destroy the tabs instance and clean up
204
+ */
205
+ destroy() {
206
+ // Remove event listeners by cloning and replacing nodes
207
+ this.tabItems.forEach((item) => {
208
+ const newItem = item.cloneNode(true);
209
+ item.parentNode?.replaceChild(newItem, item);
210
+ });
211
+ // Remove classes
212
+ this.container.classList.remove('tabs-vertical');
213
+ // Remove ARIA attributes
214
+ this.tabItems.forEach((item) => {
215
+ item.removeAttribute('role');
216
+ item.removeAttribute('tabindex');
217
+ item.removeAttribute('aria-selected');
218
+ });
219
+ this.tabPanels.forEach((panel) => {
220
+ panel.removeAttribute('role');
221
+ panel.removeAttribute('aria-hidden');
222
+ });
223
+ }
224
+ }
225
+ export { Tabs };