@ulu/frontend 0.1.0-beta.11 → 0.1.0-beta.110

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 (342) hide show
  1. package/CHANGELOG.md +669 -0
  2. package/GEMINI.md +9 -0
  3. package/README.dev.md +3 -3
  4. package/README.md +14 -4
  5. package/dist/ulu-frontend.min.css +1 -1
  6. package/dist/ulu-frontend.min.js +36 -29
  7. package/docs-dev/.nojekyll +1 -0
  8. package/docs-dev/assets/main.js +832 -421
  9. package/docs-dev/assets/placeholder/icon-calendar.svg +1 -0
  10. package/docs-dev/assets/placeholder/icon-check.svg +1 -0
  11. package/docs-dev/assets/style.css +629 -233
  12. package/docs-dev/changelog/index.html +7445 -0
  13. package/docs-dev/changelog/updates-and-changes/index.html +5109 -0
  14. package/docs-dev/demos/accordion/index.html +1442 -364
  15. package/docs-dev/demos/badge/index.html +5835 -0
  16. package/docs-dev/demos/badge-stack/index.html +5816 -0
  17. package/docs-dev/demos/basic-hero/index.html +111 -0
  18. package/docs-dev/demos/breadcrumb/index.html +5870 -0
  19. package/docs-dev/demos/breakpoints-manager/index.html +5846 -0
  20. package/docs-dev/demos/button/index.html +1490 -356
  21. package/docs-dev/demos/button-group/index.html +5832 -0
  22. package/docs-dev/demos/button-verbose/index.html +5838 -0
  23. package/docs-dev/demos/callout/index.html +1469 -349
  24. package/docs-dev/demos/captioned-figure/index.html +1446 -353
  25. package/docs-dev/demos/card/index.html +1518 -786
  26. package/docs-dev/demos/card-grid/index.html +5957 -0
  27. package/docs-dev/demos/card-new/index.html +5088 -0
  28. package/docs-dev/demos/card-old/index.html +5223 -0
  29. package/docs-dev/demos/card.1/index.html +5223 -0
  30. package/docs-dev/demos/card.TRASH/index.html +5541 -0
  31. package/docs-dev/demos/counter-list/index.html +5840 -0
  32. package/docs-dev/demos/css-icons/index.html +1466 -350
  33. package/docs-dev/demos/data-grid/index.html +1446 -353
  34. package/docs-dev/demos/data-table/index.html +1621 -395
  35. package/docs-dev/demos/details-group/index.html +5867 -0
  36. package/docs-dev/demos/donut-chart/index.html +5874 -0
  37. package/docs-dev/demos/file-save/index.html +1446 -353
  38. package/docs-dev/demos/flipcard/index.html +1446 -353
  39. package/docs-dev/demos/form-theme/index.html +1453 -360
  40. package/docs-dev/demos/headline-label/index.html +5802 -0
  41. package/docs-dev/demos/hero/index.html +12 -4
  42. package/docs-dev/demos/image-grid/index.html +12 -4
  43. package/docs-dev/demos/index.html +1447 -354
  44. package/docs-dev/demos/list-inline/index.html +1446 -353
  45. package/docs-dev/demos/list-lines/index.html +1446 -353
  46. package/docs-dev/demos/menu-stack/index.html +1550 -389
  47. package/docs-dev/demos/modals/index.html +1616 -400
  48. package/docs-dev/demos/nav-strip/index.html +1446 -353
  49. package/docs-dev/demos/overlay-section/index.html +1532 -369
  50. package/docs-dev/demos/panel/index.html +5862 -0
  51. package/docs-dev/demos/popovers/index.html +1710 -375
  52. package/docs-dev/demos/print/index.html +1446 -353
  53. package/docs-dev/demos/progress-bar/index.html +5964 -0
  54. package/docs-dev/demos/progress-circle/index.html +6137 -0
  55. package/docs-dev/demos/progress-donut/index.html +6107 -0
  56. package/docs-dev/demos/pull-quote/index.html +1446 -353
  57. package/docs-dev/demos/rail/index.html +5910 -0
  58. package/docs-dev/demos/rule/index.html +1455 -350
  59. package/docs-dev/demos/scroll-slider/index.html +72 -106
  60. package/docs-dev/demos/scrollpoints/index.html +1447 -354
  61. package/docs-dev/demos/skeleton/index.html +5858 -0
  62. package/docs-dev/demos/slider/index.html +12 -4
  63. package/docs-dev/demos/spoke-spinner/index.html +1446 -353
  64. package/docs-dev/demos/sticky-list/index.html +5823 -0
  65. package/docs-dev/demos/tabs/index.html +1474 -345
  66. package/docs-dev/demos/tag/index.html +1463 -358
  67. package/docs-dev/demos/theme-toggle/index.html +5879 -0
  68. package/docs-dev/demos/tile-grid-overlay/index.html +12 -4
  69. package/docs-dev/demos/tiles/index.html +1446 -353
  70. package/docs-dev/demos/tooltip/index.html +1446 -353
  71. package/docs-dev/demos/wysiwyg/index.html +5852 -0
  72. package/docs-dev/guide/building-stylesheet/index.html +1446 -353
  73. package/docs-dev/guide/developing-ulu-scss-module/index.html +1446 -353
  74. package/docs-dev/guide/index.html +1446 -353
  75. package/docs-dev/guide/updates-and-changes/index.html +5033 -0
  76. package/docs-dev/index.html +1446 -353
  77. package/docs-dev/javascript/events/index.html +1141 -940
  78. package/docs-dev/javascript/index.html +1446 -353
  79. package/docs-dev/javascript/settings/index.html +5629 -0
  80. package/docs-dev/javascript/ui-breakpoints/index.html +1270 -719
  81. package/docs-dev/javascript/ui-collapsible/index.html +1135 -824
  82. package/docs-dev/javascript/ui-details-group/index.html +5382 -0
  83. package/docs-dev/javascript/ui-dialog/index.html +1167 -841
  84. package/docs-dev/javascript/ui-flipcard/index.html +833 -2058
  85. package/docs-dev/javascript/ui-grid/index.html +835 -1962
  86. package/docs-dev/javascript/ui-modal-builder/index.html +988 -1919
  87. package/docs-dev/javascript/ui-overflow-scroller/index.html +811 -1912
  88. package/docs-dev/javascript/ui-overflow-scroller-pager/index.html +1643 -928
  89. package/docs-dev/javascript/ui-page/index.html +1643 -928
  90. package/docs-dev/javascript/ui-popover/index.html +1484 -773
  91. package/docs-dev/javascript/ui-print/index.html +1637 -930
  92. package/docs-dev/javascript/ui-print-details/index.html +1643 -928
  93. package/docs-dev/javascript/ui-programmatic-modal/index.html +1643 -928
  94. package/docs-dev/javascript/ui-proxy-click/index.html +1395 -595
  95. package/docs-dev/javascript/ui-resizer/index.html +1986 -1006
  96. package/docs-dev/javascript/ui-scroll-slider/index.html +1514 -767
  97. package/docs-dev/javascript/ui-scrollpoint/index.html +1649 -941
  98. package/docs-dev/javascript/ui-slider/index.html +1723 -817
  99. package/docs-dev/javascript/ui-tabs/index.html +1537 -859
  100. package/docs-dev/javascript/ui-theme-toggle/index.html +5634 -0
  101. package/docs-dev/javascript/ui-tooltip/index.html +1649 -938
  102. package/docs-dev/javascript/utils-class-logger/index.html +1644 -929
  103. package/docs-dev/javascript/utils-css/index.html +5448 -0
  104. package/docs-dev/javascript/utils-dom/index.html +1320 -685
  105. package/docs-dev/javascript/utils-file-save/index.html +1643 -928
  106. package/docs-dev/javascript/utils-floating-ui/index.html +1643 -928
  107. package/docs-dev/javascript/utils-id/index.html +1643 -928
  108. package/docs-dev/javascript/utils-pause-youtube-video/index.html +1643 -928
  109. package/docs-dev/javascript/utils-system/index.html +5751 -0
  110. package/docs-dev/sass/base/color/index.html +1451 -360
  111. package/docs-dev/sass/base/elements/index.html +1645 -930
  112. package/docs-dev/sass/base/index/index.html +1452 -361
  113. package/docs-dev/sass/base/index.html +1446 -353
  114. package/docs-dev/sass/base/keyframes/index.html +1452 -361
  115. package/docs-dev/sass/base/layout/index.html +1452 -361
  116. package/docs-dev/sass/base/normalize/index.html +1644 -929
  117. package/docs-dev/sass/base/print/index.html +1452 -361
  118. package/docs-dev/sass/base/root/index.html +1455 -364
  119. package/docs-dev/sass/base/typography/index.html +1451 -360
  120. package/docs-dev/sass/components/accordion/index.html +1538 -406
  121. package/docs-dev/sass/components/adaptive-spacing/index.html +1451 -360
  122. package/docs-dev/sass/components/badge/index.html +1468 -359
  123. package/docs-dev/sass/components/badge-stack/index.html +5991 -0
  124. package/docs-dev/sass/components/basic-hero/index.html +5985 -0
  125. package/docs-dev/sass/components/breadcrumb/index.html +6032 -0
  126. package/docs-dev/sass/components/button/index.html +1451 -360
  127. package/docs-dev/sass/components/button-group/index.html +5983 -0
  128. package/docs-dev/sass/components/button-verbose/index.html +1523 -357
  129. package/docs-dev/sass/components/callout/index.html +1572 -422
  130. package/docs-dev/sass/components/captioned-figure/index.html +1591 -385
  131. package/docs-dev/sass/components/card/index.html +1511 -363
  132. package/docs-dev/sass/components/card-grid/index.html +1451 -360
  133. package/docs-dev/sass/components/counter-list/index.html +6067 -0
  134. package/docs-dev/sass/components/css-icon/index.html +1452 -361
  135. package/docs-dev/sass/components/data-grid/index.html +1465 -367
  136. package/docs-dev/sass/components/data-table/index.html +1648 -369
  137. package/docs-dev/sass/components/fill-context/index.html +1451 -360
  138. package/docs-dev/sass/components/flipcard/index.html +1479 -357
  139. package/docs-dev/sass/components/flipcard-grid/index.html +1451 -360
  140. package/docs-dev/sass/components/form-theme/index.html +1652 -566
  141. package/docs-dev/sass/components/headline-label/index.html +6013 -0
  142. package/docs-dev/sass/components/hero/index.html +1497 -358
  143. package/docs-dev/sass/components/horizontal-rule/index.html +1451 -360
  144. package/docs-dev/sass/components/image-grid/index.html +1451 -360
  145. package/docs-dev/sass/components/index/index.html +1466 -362
  146. package/docs-dev/sass/components/index.html +1446 -353
  147. package/docs-dev/sass/components/links/index.html +1451 -360
  148. package/docs-dev/sass/components/list-inline/index.html +1451 -360
  149. package/docs-dev/sass/components/list-lines/index.html +1451 -360
  150. package/docs-dev/sass/components/list-ordered/index.html +1451 -360
  151. package/docs-dev/sass/components/list-unordered/index.html +1451 -360
  152. package/docs-dev/sass/components/menu-stack/index.html +1476 -371
  153. package/docs-dev/sass/components/modal/index.html +1486 -360
  154. package/docs-dev/sass/components/nav-strip/index.html +1459 -368
  155. package/docs-dev/sass/components/overlay-section/index.html +1459 -368
  156. package/docs-dev/sass/components/pager/index.html +1451 -360
  157. package/docs-dev/sass/components/panel/index.html +6213 -0
  158. package/docs-dev/sass/components/placeholder-block/index.html +1451 -360
  159. package/docs-dev/sass/components/popover/index.html +1497 -370
  160. package/docs-dev/sass/components/progress-bar/index.html +6192 -0
  161. package/docs-dev/sass/components/progress-circle/index.html +6084 -0
  162. package/docs-dev/sass/components/pull-quote/index.html +1463 -372
  163. package/docs-dev/sass/components/rail/index.html +6003 -0
  164. package/docs-dev/sass/components/ratio-box/index.html +1459 -368
  165. package/docs-dev/sass/components/rule/index.html +1452 -361
  166. package/docs-dev/sass/components/scroll-slider/index.html +1447 -368
  167. package/docs-dev/sass/components/skeleton/index.html +6041 -0
  168. package/docs-dev/sass/components/skip-link/index.html +1451 -360
  169. package/docs-dev/sass/components/slider/index.html +1491 -412
  170. package/docs-dev/sass/components/spoke-spinner/index.html +1453 -362
  171. package/docs-dev/sass/components/sticky-list/index.html +6203 -0
  172. package/docs-dev/sass/components/table-sticky/index.html +5707 -0
  173. package/docs-dev/sass/components/tabs/index.html +1472 -366
  174. package/docs-dev/sass/components/tag/index.html +1563 -378
  175. package/docs-dev/sass/components/tile-button/index.html +1451 -360
  176. package/docs-dev/sass/components/tile-grid/index.html +1451 -360
  177. package/docs-dev/sass/components/tile-grid-overlay/index.html +1451 -360
  178. package/docs-dev/sass/components/vignette/index.html +1457 -360
  179. package/docs-dev/sass/components/wysiwyg/index.html +1477 -368
  180. package/docs-dev/sass/core/breakpoint/index.html +1530 -387
  181. package/docs-dev/sass/core/button/index.html +1483 -390
  182. package/docs-dev/sass/core/color/index.html +1666 -443
  183. package/docs-dev/sass/core/cssvar/index.html +1451 -360
  184. package/docs-dev/sass/core/element/index.html +1790 -417
  185. package/docs-dev/sass/core/index.html +1451 -360
  186. package/docs-dev/sass/core/layout/index.html +1491 -381
  187. package/docs-dev/sass/core/path/index.html +1451 -360
  188. package/docs-dev/sass/core/selector/index.html +1451 -360
  189. package/docs-dev/sass/core/typography/index.html +1591 -442
  190. package/docs-dev/sass/core/units/index.html +1453 -356
  191. package/docs-dev/sass/core/utils/index.html +2744 -504
  192. package/docs-dev/sass/helpers/color/index.html +1643 -928
  193. package/docs-dev/sass/helpers/display/index.html +1644 -929
  194. package/docs-dev/sass/helpers/index/index.html +1643 -928
  195. package/docs-dev/sass/helpers/index.html +1446 -353
  196. package/docs-dev/sass/helpers/print/index.html +759 -298
  197. package/docs-dev/sass/helpers/typography/index.html +1643 -928
  198. package/docs-dev/sass/helpers/units/index.html +1643 -928
  199. package/docs-dev/sass/helpers/utilities/index.html +1643 -924
  200. package/docs-dev/sass/index.html +1446 -353
  201. package/js/events/index.js +17 -5
  202. package/js/index.js +1 -0
  203. package/js/settings.js +97 -0
  204. package/js/ui/breakpoints.js +19 -16
  205. package/js/ui/collapsible.js +8 -1
  206. package/js/ui/details-group.js +112 -0
  207. package/js/ui/dialog.js +103 -44
  208. package/js/ui/dialog.todo +2 -36
  209. package/js/ui/flipcard.js +37 -57
  210. package/js/ui/grid.js +15 -13
  211. package/js/ui/index.js +1 -0
  212. package/js/ui/modal-builder.js +127 -70
  213. package/js/ui/overflow-scroller.js +6 -4
  214. package/js/ui/page.js +2 -2
  215. package/js/ui/popover.js +38 -38
  216. package/js/ui/print.js +16 -25
  217. package/js/ui/programmatic-modal.js +9 -3
  218. package/js/ui/proxy-click.js +50 -36
  219. package/js/ui/resizer.js +408 -39
  220. package/js/ui/scroll-slider.js +24 -30
  221. package/js/ui/scrollpoint.js +29 -64
  222. package/js/ui/slider.js +108 -63
  223. package/js/ui/tabs.js +23 -36
  224. package/js/ui/theme-toggle.js +332 -94
  225. package/js/ui/tooltip.js +27 -32
  226. package/js/utils/class-logger.js +3 -3
  227. package/js/utils/css.js +13 -0
  228. package/js/utils/dom.js +23 -64
  229. package/js/utils/font-awesome.js +19 -0
  230. package/js/utils/index.js +2 -1
  231. package/js/utils/system.js +155 -0
  232. package/package.json +23 -8
  233. package/scss/README.md +9 -0
  234. package/scss/_breakpoint.scss +39 -5
  235. package/scss/_button.scss +7 -5
  236. package/scss/_color.scss +71 -40
  237. package/scss/_element.scss +124 -2
  238. package/scss/_layout.scss +7 -8
  239. package/scss/_typography.scss +15 -0
  240. package/scss/_units.scss +3 -2
  241. package/scss/_utils.scss +387 -16
  242. package/scss/base/_elements.scss +0 -1
  243. package/scss/base/_index.scss +1 -1
  244. package/scss/base/_keyframes.scss +15 -0
  245. package/scss/base/_layout.scss +1 -0
  246. package/scss/base/_print.scss +2 -0
  247. package/scss/base/_root.scss +2 -0
  248. package/scss/components/README.todos +1 -0
  249. package/scss/components/_accordion.scss +154 -109
  250. package/scss/components/_badge-stack.scss +84 -0
  251. package/scss/components/_badge.scss +30 -7
  252. package/scss/components/_basic-hero.scss +112 -0
  253. package/scss/components/_breadcrumb.scss +110 -0
  254. package/scss/components/_button-group.scss +90 -0
  255. package/scss/components/_button-verbose.scss +100 -18
  256. package/scss/components/_callout.scss +112 -53
  257. package/scss/components/_captioned-figure.scss +17 -0
  258. package/scss/components/_card.scss +224 -66
  259. package/scss/components/_counter-list.scss +151 -0
  260. package/scss/components/_css-icon.scss +27 -17
  261. package/scss/components/_data-grid.scss +55 -12
  262. package/scss/components/_data-table.scss +31 -0
  263. package/scss/components/_flipcard.scss +8 -3
  264. package/scss/components/_form-theme.scss +106 -95
  265. package/scss/components/_headline-label.scss +83 -0
  266. package/scss/components/_hero.scss +12 -10
  267. package/scss/components/_index.scss +73 -0
  268. package/scss/components/_menu-stack.scss +69 -32
  269. package/scss/components/_modal.scss +51 -23
  270. package/scss/components/_nav-strip.scss +2 -0
  271. package/scss/components/_overlay-section.scss +2 -5
  272. package/scss/components/_panel.scss +246 -0
  273. package/scss/components/_popover.scss +165 -64
  274. package/scss/components/_progress-bar.scss +254 -0
  275. package/scss/components/_progress-circle.scss +175 -0
  276. package/scss/components/_pull-quote.scss +13 -13
  277. package/scss/components/_rail.scss +127 -0
  278. package/scss/components/_ratio-box.scss +2 -5
  279. package/scss/components/_rule.scss +1 -0
  280. package/scss/components/_scroll-slider.scss +1 -5
  281. package/scss/components/_skeleton.scss +126 -0
  282. package/scss/components/_slider.scss +49 -72
  283. package/scss/components/_spoke-spinner.scss +2 -2
  284. package/scss/components/_sticky-list.scss +206 -0
  285. package/scss/components/_tabs.scss +22 -4
  286. package/scss/components/_tag.scss +49 -7
  287. package/scss/components/_vignette.scss +3 -5
  288. package/scss/components/_wysiwyg.scss +21 -13
  289. package/scss/helpers/_display.scss +15 -18
  290. package/scss/helpers/_print.scss +12 -7
  291. package/scss/helpers/_utilities.scss +56 -34
  292. package/types/events/index.d.ts +10 -1
  293. package/types/events/index.d.ts.map +1 -1
  294. package/types/index.d.ts +1 -0
  295. package/types/settings.d.ts +70 -0
  296. package/types/settings.d.ts.map +1 -0
  297. package/types/ui/breakpoints.d.ts +14 -14
  298. package/types/ui/breakpoints.d.ts.map +1 -1
  299. package/types/ui/collapsible.d.ts.map +1 -1
  300. package/types/ui/details-group.d.ts +38 -0
  301. package/types/ui/details-group.d.ts.map +1 -0
  302. package/types/ui/dialog.d.ts +20 -14
  303. package/types/ui/dialog.d.ts.map +1 -1
  304. package/types/ui/flipcard.d.ts +16 -10
  305. package/types/ui/flipcard.d.ts.map +1 -1
  306. package/types/ui/grid.d.ts +4 -6
  307. package/types/ui/grid.d.ts.map +1 -1
  308. package/types/ui/index.d.ts +1 -0
  309. package/types/ui/modal-builder.d.ts +116 -11
  310. package/types/ui/modal-builder.d.ts.map +1 -1
  311. package/types/ui/overflow-scroller.d.ts +2 -2
  312. package/types/ui/overflow-scroller.d.ts.map +1 -1
  313. package/types/ui/popover.d.ts +6 -7
  314. package/types/ui/popover.d.ts.map +1 -1
  315. package/types/ui/print.d.ts +0 -4
  316. package/types/ui/print.d.ts.map +1 -1
  317. package/types/ui/programmatic-modal.d.ts.map +1 -1
  318. package/types/ui/proxy-click.d.ts +19 -3
  319. package/types/ui/proxy-click.d.ts.map +1 -1
  320. package/types/ui/resizer.d.ts +116 -16
  321. package/types/ui/resizer.d.ts.map +1 -1
  322. package/types/ui/scroll-slider.d.ts +5 -7
  323. package/types/ui/scroll-slider.d.ts.map +1 -1
  324. package/types/ui/scrollpoint.d.ts +3 -8
  325. package/types/ui/scrollpoint.d.ts.map +1 -1
  326. package/types/ui/slider.d.ts +33 -14
  327. package/types/ui/slider.d.ts.map +1 -1
  328. package/types/ui/tabs.d.ts +6 -8
  329. package/types/ui/tabs.d.ts.map +1 -1
  330. package/types/ui/theme-toggle.d.ts +51 -7
  331. package/types/ui/theme-toggle.d.ts.map +1 -1
  332. package/types/ui/tooltip.d.ts +3 -5
  333. package/types/ui/tooltip.d.ts.map +1 -1
  334. package/types/utils/css.d.ts +11 -0
  335. package/types/utils/css.d.ts.map +1 -0
  336. package/types/utils/dom.d.ts +12 -32
  337. package/types/utils/dom.d.ts.map +1 -1
  338. package/types/utils/font-awesome.d.ts +5 -0
  339. package/types/utils/font-awesome.d.ts.map +1 -0
  340. package/types/utils/index.d.ts +1 -0
  341. package/types/utils/system.d.ts +113 -0
  342. package/types/utils/system.d.ts.map +1 -0
@@ -1,129 +1,367 @@
1
- // Progressive Enhancement turns select elements into accessible autocomplete fields
1
+ /**
2
+ * @module ui/theme-toggle
3
+ */
2
4
 
3
- // import { getName } from "@ulu/frontend/js/events/index.js";
5
+ import { ComponentInitializer } from "../utils/system.js";
4
6
  import { getName } from "../events/index.js";
7
+ import { resolveClasses } from "../utils/dom.js";
8
+ import { hasRequiredProps } from "@ulu/utils/object.js";
9
+ import { getElements } from "@ulu/utils/browser/dom.js";
5
10
 
6
- const attrs = {
7
- trigger: "data-site-theme-toggle",
8
- icon: "data-site-theme-toggle-icon",
9
- init: "data-site-theme-toggle-init",
10
- };
11
+ /**
12
+ * Theme Toggle Component Initializer
13
+ */
14
+ export const initializer = new ComponentInitializer({
15
+ type: "theme-toggle",
16
+ baseAttribute: "data-ulu-theme-toggle"
17
+ });
11
18
 
12
- const attrSelector = key => `[${ attrs[key] }]`;
13
- const attrSelectorInitial = key => `${ attrSelector(key) }:not([${ attrs.init }])`;
14
-
15
- // @dan change to options and remove options
16
- // add a preferred print theme option
17
- export const options = {
18
- darkTheme: "theme-dark",
19
- lightTheme: "theme-light",
20
- defaultTheme: "dark",
21
- darkIcon: "fa-solid fa-moon",
22
- lightIcon: "fa-solid fa-sun",
19
+ const attrSelectorLabel = initializer.attributeSelector("label");
20
+ const attrSelectorIcon = initializer.attributeSelector("icon");
21
+ const attrRemote = initializer.getAttribute("remote");
22
+ const attrInit = initializer.getAttribute("init");
23
+ const attrState = initializer.getAttribute("state");
24
+
25
+ // Utils for selecting things based on attributes
26
+ const queryRemotes = group => document.querySelectorAll(
27
+ `[${ attrRemote }="${ group }"]`
28
+ );
29
+ const queryRemotesInitial = group => document.querySelectorAll(
30
+ `[${ attrRemote }="${ group }"]:not([${ attrInit }])`
31
+ );
32
+ const requiredToggleProps = ["target"];
33
+ const checkToggleProps = hasRequiredProps(requiredToggleProps);
34
+ const when = (cond, fn) => cond ? fn() : null; // Consider adding as util
35
+
36
+ /**
37
+ * Default Options
38
+ * - Can be overridden using data-attributes
39
+ */
40
+ export const defaults = {
41
+ /**
42
+ * Object of each theme that should be toggle/cycled through
43
+ */
44
+ themes: {
45
+ light: {
46
+ label: "Light",
47
+ value: "light",
48
+ iconClass: "fas fa-moon",
49
+ targetClass: "theme-light",
50
+ mediaQuery: "(prefers-color-scheme: light)"
51
+ },
52
+ dark: {
53
+ label: "Dark",
54
+ iconClass: "fas fa-sun",
55
+ targetClass: "theme-dark",
56
+ mediaQuery: "(prefers-color-scheme: dark)"
57
+ }
58
+ },
59
+ /**
60
+ * Required this is the element(s) that should be changed by a specific toggle
61
+ * - The element should have data-ulu-theme-toggle-target="SOME_IDENTIFIER"
62
+ */
63
+ target: "body",
64
+ /**
65
+ * Optional group to link remote toggles (toggles that follow the main one and can toggle too)
66
+ */
67
+ group: null,
68
+ /**
69
+ * Optional callback to do something when the state changes
70
+ */
71
+ onChange(_ctx) {},
72
+ /**
73
+ * The initial state for this component
74
+ * - May be overridden by saved preference or media query if options are enabled
75
+ */
76
+ initialState: "light",
77
+ /**
78
+ * Check the OS systems user preference via 'preferenceQuery' option
79
+ */
80
+ checkMediaQuery: false,
81
+ /**
82
+ * Will store the preference in local storage so it persists between page loads
83
+ */
84
+ savePreference: false,
85
+ /**
86
+ * The key that will be used to store the preference in local storage
87
+ * - This will be used as prefix in combination with group if defined
88
+ */
89
+ storagePrefix: "ulu-theme-",
90
+ /**
91
+ * Output information to console for debugging
92
+ */
93
+ debug: false
23
94
  };
24
95
 
25
- const body = document.querySelector("[data-site-theme]");
26
- let currentTheme = body.classList.contains(options.darkTheme) ? options.darkTheme : options.lightTheme;
27
- // used to see if machine preference differs from default theme
28
- const defaultThemeInverse = options.defaultTheme === "dark" ? "light" : "dark";
96
+
97
+ // Current default objects (user can override these)
98
+ let currentDefaults = { ...defaults };
99
+
100
+ /**
101
+ * @param {Object} options Change options used as default for dialogs, can then be overridden by data attribute settings on element
102
+ */
103
+ export function setDefaults(options) {
104
+ currentDefaults = Object.assign({}, currentDefaults, options);
105
+ }
29
106
 
30
107
  /**
31
108
  * Initialize everything in document
32
109
  * - This will only initialize elements once, it is safe to call on page changes
33
110
  */
34
111
  export function init() {
35
- // switch to light theme for printing
36
- document.addEventListener(getName("beforePrint"), () => printSetup());
37
- // switch back to original theme after printing
38
- document.addEventListener(getName("afterPrint"), () => printTearDown());
39
- // document.addEventListener(getName("pageModified"), () => setup());
40
- setup();
112
+ initializer.init({
113
+ events: ["pageModified"],
114
+ withData: true,
115
+ setup({ element, data, initialize }) {
116
+ setupToggle(element, data);
117
+ initialize();
118
+ }
119
+ });
41
120
  }
42
121
 
43
- export function setup(context = document) {
44
- const body = context.querySelector("[data-site-theme]");
45
- // Initial theme on load
46
- setupTheme(body);
47
- // Add toggle event listener to buttons
48
- // @daniel add the init attribute
49
- const elements = context.querySelectorAll(attrSelectorInitial("trigger"));
50
- elements.forEach(element => {
51
- element.setAttribute(attrs.init, "");
52
- element.addEventListener("click", () => {
53
- changeTheme(body);
122
+ /**
123
+ * Sets up a single toggle
124
+ * @param {HTMLElement} toggle A toggle to be setup
125
+ */
126
+ export function setupToggle(toggle, userOptions) {
127
+ const options = Object.assign({}, defaults, userOptions);
128
+
129
+ if (!checkToggleProps(options)) {
130
+ console.error(`Missing a required option: ${ requiredToggleProps.join(", ") }`);
131
+ return;
132
+ }
133
+
134
+ const group = options.group;
135
+ const ctx = { toggle, options };
136
+ const initialKey = resolveInitial(options);
137
+
138
+ if (!initialKey) {
139
+ console.error("Unable to resolve initial key");
140
+ return;
141
+ }
142
+
143
+ setState(initialKey, ctx);
144
+
145
+ toggle.addEventListener("click", onToggleClick);
146
+
147
+ // Remotes listeners are attached initially and then we also
148
+ // update them vs toggles which would be updated by the main pageModified
149
+ // event in init
150
+ attachRemotes();
151
+ document.addEventListener(getName("pageModified"), attachRemotes);
152
+
153
+ /**
154
+ * Instance function to get the next theme in cycle
155
+ */
156
+ function toggleState(event) {
157
+ const targets = getElements(options.target);
158
+ const lastKey = targets[0].dataset.uluThemeToggleState;
159
+ const key = getNextThemeKey(lastKey, options);
160
+ if (!key) {
161
+ console.error("Issue getting next theme key");
162
+ return;
163
+ }
164
+ setState(key, { ...ctx, event });
165
+ }
166
+
167
+ /**
168
+ * Handler for click for both toggle and remote toggles
169
+ */
170
+ function onToggleClick(event) {
171
+ toggleState(event);
172
+ }
173
+
174
+ /**
175
+ * Utility to attach remote handlers
176
+ * - Used initially and when page is modified
177
+ */
178
+ function attachRemotes() {
179
+ if (!group) return;
180
+ const remotes = queryRemotesInitial(group);
181
+ remotes.forEach(remote => {
182
+ remote.addEventListener("click", onToggleClick);
183
+ initializer.initializeElement(remote);
54
184
  });
55
- });
56
- // Initial icon setup
57
- changeIcons();
185
+ }
186
+
187
+ /**
188
+ * This only cleans up remotes that are still in DOM
189
+ * - For ones that have been removed we don't store any references to them
190
+ */
191
+ function cleanupRemotes() {
192
+ if (!group) return;
193
+ const remotes = queryRemotesInitial(group);
194
+ remotes.forEach(remote => {
195
+ remote.removeEventListener("click", onToggleClick);
196
+ remote.removeAttribute(attrInit, "");
197
+ });
198
+ }
199
+
200
+ /**
201
+ * Function to cleanup listeners and remove init attributes
202
+ */
203
+ function destroy() {
204
+ toggle.removeEventListener("click", onToggleClick);
205
+ toggle.removeAttribute(attrInit, "");
206
+ cleanupRemotes();
207
+ document.removeEventListener(getName("pageModified"), attachRemotes);
208
+ }
209
+
210
+ return {
211
+ destroy,
212
+ toggle,
213
+ options,
214
+ toggleState,
215
+ setState(themeKey) {
216
+ setState(themeKey, ctx);
217
+ }
218
+ };
58
219
  }
59
220
 
221
+
222
+
60
223
  /**
61
- *
62
- * @param {Element} body Sets up initial theme on load based on user preference.
224
+ * Change the state of target/toggle
63
225
  */
64
- function setupTheme(body) {
65
- const sitePreference = localStorage.getItem("data-theme");
66
- const machinePreference = window.matchMedia && window.matchMedia(`(prefers-color-scheme: ${defaultThemeInverse})`).matches;
67
- if(sitePreference && sitePreference != currentTheme){
68
- // Check if local storage has site specific preference. And that preference is not the default.
69
- changeTheme(body);
70
- } else if (machinePreference) {
71
- // Check if user system preference differs from default theme.
72
- changeTheme(body);
73
- }
226
+ function setState(key, ctx) {
227
+ if (!key) {
228
+ console.error("Missing key");
229
+ return;
230
+ }
231
+
232
+ const { toggle, options } = ctx;
233
+ const { themes, group } = options;
234
+ const elements = {
235
+ targets: getElements(options.target),
236
+ toggles: [toggle, ...(group ? queryRemotes(group) : [])]
237
+ };
238
+
239
+ if (!elements.targets.length || !elements.toggles.length) {
240
+ console.error("Issue setting state, couldn't find needed elements", elements);
241
+ return;
242
+ }
243
+
244
+ const theme = themes[key];
245
+ const otherThemes = getOtherThemes(key, themes);
246
+ const stateCtx = {
247
+ ...ctx,
248
+ key,
249
+ elements,
250
+ theme,
251
+ otherThemes
252
+ };
253
+
254
+ if (options.debug) {
255
+ initializer.log("Set state context", stateCtx);
256
+ }
257
+
258
+ // Prepare classes to remove
259
+ const otherTargetClasses = concatThemeClasses(otherThemes, "targetClass");
260
+ const otherIconClasses = concatThemeClasses(otherThemes, "iconClass");
261
+
262
+ // Update all targets
263
+ elements.targets.forEach(element => {
264
+ element.setAttribute(attrState, key);
265
+ element.classList.remove(...otherTargetClasses);
266
+ element.classList.add(...resolveClasses(theme.targetClass));
267
+ });
268
+
269
+ // Update all toggles and inner children
270
+ elements.toggles.forEach(element => {
271
+ const label = element.querySelector(attrSelectorLabel);
272
+ const icon = element.querySelector(attrSelectorIcon);
273
+ if (label) {
274
+ label.textContent = theme.label;
275
+ }
276
+ if (icon) {
277
+ icon.classList.remove(...otherIconClasses);
278
+ icon.classList.add(...resolveClasses(theme.iconClass));
279
+ }
280
+ element.setAttribute(attrState, key);
281
+ });
282
+
283
+ // Optional callback if user want to set other things (ie. data-theme or something)
284
+ if (options.onChange) {
285
+ options.onChange(stateCtx);
286
+ }
287
+
288
+ if (options.savePreference) {
289
+ localStorage.setItem(getStorageKey(options), key);
290
+ }
74
291
  }
75
292
 
76
293
  /**
77
- *
78
- * @param {Element} body Changes the theme of the body.
294
+ * Function determines what the initial state is
295
+ * - Check OS preference, saved preference, or initialState depending on options
296
+ * @return {String} The resolved initial theme's key
79
297
  */
80
- function changeTheme(body) {
81
- let newTheme;
82
- let oldTheme;
83
- if (body.classList.contains(options.darkTheme)) {
84
- oldTheme = options.darkTheme;
85
- newTheme = options.lightTheme;
86
- } else if (body.classList.contains(options.lightTheme)) {
87
- oldTheme = options.lightTheme;
88
- newTheme = options.darkTheme;
298
+ function resolveInitial(options) {
299
+ const { savePreference, checkMediaQuery, themes, initialState } = options;
300
+ const storageKey = getStorageKey(options);
301
+ const saved = when(savePreference, () => localStorage.getItem(storageKey));
302
+ const mediaQueryPreference = when(checkMediaQuery, () => getMatchingThemeQuery(themes));
303
+ const resolved = saved || mediaQueryPreference || initialState;
304
+
305
+ if (options.debug) {
306
+ initializer.log("Preference Saved", saved);
307
+ initializer.log("Media Query Preference", mediaQueryPreference);
308
+ initializer.log("Initial State:", initialState);
309
+ }
310
+
311
+ if (!resolved) {
312
+ initializer.logError("Failed to resolve initial theme (pass 'initialState' to options)");
89
313
  }
90
- body.classList.remove(oldTheme);
91
- body.classList.add(newTheme);
92
- localStorage.setItem("data-theme", newTheme);
93
- currentTheme = newTheme;
94
- changeIcons();
314
+
315
+ return resolved;
95
316
  }
96
317
 
97
318
  /**
98
- *
99
- * @param {Element} body Used to check for theme.
100
- * @param {Element} context Used to find the icons.
319
+ * Check each theme for a matching media query
320
+ * @return {String} Matching theme key
101
321
  */
102
- function changeIcons(context = document) {
103
- const icons = context.querySelectorAll(attrSelectorInitial("icon"));
104
- icons.forEach(icon => {
105
- if (currentTheme == options.lightTheme) {
106
- icon.classList = options.darkIcon;
107
- } else {
108
- icon.classList = options.lightIcon;
322
+ function getMatchingThemeQuery(themes) {
323
+ const found = Object.entries(themes).find(([_key, theme]) => {
324
+ if (theme.mediaQuery) {
325
+ return window.matchMedia(theme.mediaQuery).matches;
109
326
  }
110
327
  });
328
+ // Return just the key
329
+ return found ? found[0] : null;
111
330
  }
112
331
 
113
- // run on beforeprint event
114
- function printSetup() {
115
- const body = document.querySelector("body");
116
- if (body.classList.contains(options.darkTheme)) {
117
- body.classList.remove(options.darkTheme);
118
- body.classList.add(options.lightTheme);
119
- }
332
+ /**
333
+ * Get the next key in the themes based on the currentKey
334
+ */
335
+ function getNextThemeKey(activeKey, options) {
336
+ const { themes } = options;
337
+ const keys = Object.keys(themes);
338
+ const index = keys.findIndex(theme => theme === activeKey);
339
+ // If not found return first, else calculate next index (wrapping)
340
+ const nextIndex = index === -1 ? 0 : (index + 1) % keys.length;
341
+ return keys[nextIndex];
120
342
  }
121
343
 
122
- // run on afterprint event
123
- function printTearDown() {
124
- const body = document.querySelector("body");
125
- if (!body.classList.contains(currentTheme)) {
126
- body.classList.remove(options.lightTheme);
127
- body.classList.add(options.darkTheme);
128
- }
344
+ /**
345
+ * Get all other theme object except the current
346
+ */
347
+ function getOtherThemes(currentKey, themes) {
348
+ const all = Object.entries(themes);
349
+ return all.filter(([key]) => key !== currentKey).map(([_key, value]) => value);
350
+ }
351
+
352
+ /**
353
+ * Concatenates multiple class properties into one array
354
+ */
355
+ function concatThemeClasses(themes, property) {
356
+ return themes.reduce((acc, theme) => {
357
+ return acc.concat(resolveClasses(theme[property]));
358
+ }, []);
359
+ }
360
+
361
+ /**
362
+ * Creates the storage key (either prefix or prefix with group name)
363
+ */
364
+ function getStorageKey(options) {
365
+ const { storagePrefix, group } = options;
366
+ return group ? `${ storagePrefix }${ group }` : storagePrefix;
129
367
  }
package/js/ui/tooltip.js CHANGED
@@ -2,46 +2,41 @@
2
2
  * @module ui/tooltip
3
3
  */
4
4
 
5
+ import { ComponentInitializer } from "../utils/system.js";
5
6
  import { getName as getEventName } from "../events/index.js";
6
7
  import { createFloatingUi } from "../utils/floating-ui.js";
7
8
  import { createElementFromHtml } from "@ulu/utils/browser/dom.js";
8
9
  import { logError } from "../utils/class-logger.js";
9
- import { getDatasetOptionalJson } from "../utils/dom.js";
10
10
  import { newId, ensureId } from "../utils/id.js";
11
11
 
12
- const attrs = {
13
- trigger: "data-ulu-tooltip",
14
- init: "data-ulu-init",
15
- body: "data-ulu-tooltip-display-body",
16
- arrow: "data-ulu-tooltip-arrow"
17
- };
18
- const attrSelector = key => `[${ attrs[key] }]`;
19
- const attrSelectorInitial = key => `${ attrSelector(key) }:not([${ attrs.init }])`;
20
12
  /**
21
- * Initialize default popover
13
+ * Tooltip Component Initializer
22
14
  */
23
- export function init() {
24
- document.addEventListener(getEventName("pageModified"), setup);
25
- setup();
26
- }
15
+ export const initializer = new ComponentInitializer({
16
+ type: "tooltip",
17
+ baseAttribute: "data-ulu-tooltip"
18
+ });
19
+
20
+ const attrBody = initializer.getAttribute("body");
21
+ const attrSelectorBody = initializer.attributeSelector("body");
22
+ const attrSelectorArrow = initializer.attributeSelector("arrow");
27
23
 
28
24
  /**
29
- * Query all popovers on current page and set them up
30
- * - Use this manually if needed
31
- * - Won't setup a popover more than once
25
+ * Initialize default popover
32
26
  */
33
- export function setup() {
34
- const triggers = document.querySelectorAll(attrSelectorInitial("trigger"));
35
- triggers.forEach(setupTrigger);
36
- }
37
-
38
- export function setupTrigger(trigger) {
39
- const passed = getDatasetOptionalJson(trigger, "uluTooltip");
40
- const options = typeof passed === "object" ? passed : {};
41
- if (typeof passed === "string") {
42
- options.content = passed;
43
- }
44
- return new Tooltip({ trigger }, options);
27
+ export function init() {
28
+ initializer.init({
29
+ events: ["pageModified"],
30
+ withData: true,
31
+ setup({ element: trigger, data, initialize }) {
32
+ const options = typeof data === "object" ? data : {};
33
+ if (typeof data === "string") {
34
+ options.content = data;
35
+ }
36
+ initialize();
37
+ (new Tooltip({ trigger }, options));
38
+ }
39
+ });
45
40
  }
46
41
 
47
42
  /**
@@ -103,7 +98,7 @@ export class Tooltip {
103
98
  template(_config) {
104
99
  return `
105
100
  <div class="popover popover--tooltip">
106
- <div class="popover__inner" ${ attrs.body }>
101
+ <div class="popover__inner" ${ attrBody }>
107
102
  </div>
108
103
  <span class="popover__arrow" data-ulu-tooltip-arrow></span>
109
104
  </div>
@@ -177,7 +172,7 @@ export class Tooltip {
177
172
  createContentElement() {
178
173
  const { options } = this;
179
174
  const content = createElementFromHtml(options.template(options));
180
- const body = content.querySelector(attrSelector("body"));
175
+ const body = content.querySelector(attrSelectorBody);
181
176
  const innerContent = this.getInnerContent();
182
177
  if (options.isHtml) {
183
178
  body.innerHTML = innerContent;
@@ -190,7 +185,7 @@ export class Tooltip {
190
185
  }
191
186
 
192
187
  this.elements.content = content;
193
- this.elements.contentArrow = content.querySelector(attrSelector("arrow"));
188
+ this.elements.contentArrow = content.querySelector(attrSelectorArrow);
194
189
  document.body.appendChild(content);
195
190
  }
196
191
  attachHandlers() {
@@ -2,10 +2,10 @@
2
2
  * @module utils/class-logger
3
3
  */
4
4
 
5
- // Goal: minimzing console conditions for nessasary production log statements
5
+ // Goal: minimizing console conditions for nessasary production log statements
6
6
 
7
7
  /**
8
- * Configuration Object
8
+ * Global Configuration Object
9
9
  */
10
10
  const config = {
11
11
  debug: false,
@@ -18,7 +18,7 @@ const hasConsole = "console" in window;
18
18
 
19
19
  // If no context output only if config (global) debug is enabled
20
20
  function allow(context) {
21
- return hasConsole && config.debug && (context?.debug || context == null);
21
+ return hasConsole && config.debug && (context?.debug || context?.options?.debug || context == null);
22
22
  }
23
23
  function getName(context) {
24
24
  return typeof context === "object" && context?.constructor?.name;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @module utils/css
3
+ */
4
+
5
+ /**
6
+ * Generates a CSS custom property name with a given prefix.
7
+ * @param {string} prefix The prefix to apply to the custom property name.
8
+ * @param {string} propertyName The base name of the custom property.
9
+ * @returns {string} The fully formed CSS custom property name (e.g., "--prefix-propertyName").
10
+ */
11
+ export function getCustomProperty(prefix, propertyName) {
12
+ return `--${prefix}-${propertyName}`;
13
+ }