@ulu/frontend 0.1.0-beta.9 → 0.1.0-beta.90

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 (316) hide show
  1. package/CHANGELOG.md +606 -0
  2. package/README.dev.md +3 -3
  3. package/README.md +14 -4
  4. package/dist/ulu-frontend.min.css +1 -1
  5. package/dist/ulu-frontend.min.js +36 -29
  6. package/docs-dev/.nojekyll +1 -0
  7. package/docs-dev/assets/main.js +8290 -635
  8. package/docs-dev/assets/placeholder/icon-calendar.svg +1 -0
  9. package/docs-dev/assets/placeholder/icon-check.svg +1 -0
  10. package/docs-dev/assets/style.css +789 -338
  11. package/docs-dev/changelog/index.html +6985 -0
  12. package/docs-dev/demos/accordion/index.html +1126 -334
  13. package/docs-dev/demos/badge/index.html +5535 -0
  14. package/docs-dev/demos/basic-hero/index.html +111 -0
  15. package/docs-dev/demos/breakpoints-manager/index.html +5546 -0
  16. package/docs-dev/demos/button/index.html +1172 -338
  17. package/docs-dev/demos/button-group/index.html +5532 -0
  18. package/docs-dev/demos/button-verbose/index.html +5538 -0
  19. package/docs-dev/demos/callout/index.html +1177 -344
  20. package/docs-dev/demos/captioned-figure/index.html +1120 -327
  21. package/docs-dev/demos/card/index.html +1186 -754
  22. package/docs-dev/demos/card-grid/index.html +5657 -0
  23. package/docs-dev/demos/counter-list/index.html +5540 -0
  24. package/docs-dev/demos/css-icons/index.html +1148 -332
  25. package/docs-dev/demos/data-grid/index.html +1120 -327
  26. package/docs-dev/demos/data-table/index.html +1298 -372
  27. package/docs-dev/demos/details-group/index.html +5567 -0
  28. package/docs-dev/demos/file-save/index.html +1120 -327
  29. package/docs-dev/demos/flipcard/index.html +1120 -327
  30. package/docs-dev/demos/form-theme/index.html +1144 -364
  31. package/docs-dev/demos/headline-label/index.html +5502 -0
  32. package/docs-dev/demos/hero/index.html +12 -4
  33. package/docs-dev/demos/image-grid/index.html +12 -4
  34. package/docs-dev/demos/index.html +1121 -328
  35. package/docs-dev/demos/list-inline/index.html +1120 -327
  36. package/docs-dev/demos/list-lines/index.html +1120 -327
  37. package/docs-dev/demos/menu-stack/index.html +1159 -351
  38. package/docs-dev/demos/modals/index.html +1259 -343
  39. package/docs-dev/demos/nav-strip/index.html +1120 -327
  40. package/docs-dev/demos/overlay-section/index.html +1205 -342
  41. package/docs-dev/demos/panel/index.html +5562 -0
  42. package/docs-dev/demos/popovers/index.html +1368 -333
  43. package/docs-dev/demos/print/index.html +1120 -327
  44. package/docs-dev/demos/pull-quote/index.html +1120 -327
  45. package/docs-dev/demos/rail/index.html +5598 -0
  46. package/docs-dev/demos/rule/index.html +1137 -332
  47. package/docs-dev/demos/scroll-slider/index.html +72 -106
  48. package/docs-dev/demos/scrollpoints/index.html +1121 -328
  49. package/docs-dev/demos/slider/index.html +12 -4
  50. package/docs-dev/demos/spoke-spinner/index.html +1120 -327
  51. package/docs-dev/demos/{list-inline.1 → sticky-list}/index.html +1159 -363
  52. package/docs-dev/demos/tabs/index.html +1156 -327
  53. package/docs-dev/demos/tag/index.html +1120 -327
  54. package/docs-dev/demos/theme-toggle/index.html +5579 -0
  55. package/docs-dev/demos/tile-grid-overlay/index.html +12 -4
  56. package/docs-dev/demos/tiles/index.html +1120 -327
  57. package/docs-dev/demos/tooltip/index.html +1120 -327
  58. package/docs-dev/demos/wysiwyg/index.html +5552 -0
  59. package/docs-dev/guide/building-stylesheet/index.html +1120 -327
  60. package/docs-dev/guide/developing-ulu-scss-module/index.html +1120 -327
  61. package/docs-dev/guide/index.html +1120 -327
  62. package/docs-dev/index.html +1120 -327
  63. package/docs-dev/javascript/events/index.html +1158 -327
  64. package/docs-dev/javascript/index.html +1120 -327
  65. package/docs-dev/javascript/settings/index.html +5705 -0
  66. package/docs-dev/javascript/ui-breakpoints/index.html +1134 -343
  67. package/docs-dev/javascript/ui-collapsible/index.html +1119 -328
  68. package/docs-dev/javascript/ui-details-group/index.html +5622 -0
  69. package/docs-dev/javascript/ui-dialog/index.html +1151 -345
  70. package/docs-dev/javascript/ui-flipcard/index.html +1180 -333
  71. package/docs-dev/javascript/ui-grid/index.html +1130 -365
  72. package/docs-dev/javascript/ui-modal-builder/index.html +1303 -372
  73. package/docs-dev/javascript/ui-overflow-scroller/index.html +1119 -328
  74. package/docs-dev/javascript/ui-overflow-scroller-pager/index.html +1119 -328
  75. package/docs-dev/javascript/ui-page/index.html +1119 -328
  76. package/docs-dev/javascript/ui-popover/index.html +1129 -342
  77. package/docs-dev/javascript/ui-print/index.html +1117 -334
  78. package/docs-dev/javascript/ui-print-details/index.html +1119 -328
  79. package/docs-dev/javascript/ui-programmatic-modal/index.html +1119 -328
  80. package/docs-dev/javascript/ui-proxy-click/index.html +1208 -332
  81. package/docs-dev/javascript/ui-resizer/index.html +1400 -344
  82. package/docs-dev/javascript/ui-scroll-slider/index.html +1159 -336
  83. package/docs-dev/javascript/ui-scrollpoint/index.html +1129 -345
  84. package/docs-dev/javascript/ui-slider/index.html +1317 -335
  85. package/docs-dev/javascript/ui-tabs/index.html +1138 -384
  86. package/docs-dev/javascript/ui-theme-toggle/index.html +5710 -0
  87. package/docs-dev/javascript/ui-tooltip/index.html +1136 -349
  88. package/docs-dev/javascript/utils-class-logger/index.html +1120 -329
  89. package/docs-dev/javascript/utils-css/index.html +5524 -0
  90. package/docs-dev/javascript/utils-dom/index.html +1153 -442
  91. package/docs-dev/javascript/utils-file-save/index.html +1119 -328
  92. package/docs-dev/javascript/utils-floating-ui/index.html +1119 -328
  93. package/docs-dev/javascript/utils-id/index.html +1119 -328
  94. package/docs-dev/javascript/utils-pause-youtube-video/index.html +1119 -328
  95. package/docs-dev/javascript/utils-system/index.html +5827 -0
  96. package/docs-dev/sass/base/color/index.html +1119 -328
  97. package/docs-dev/sass/base/elements/index.html +1127 -336
  98. package/docs-dev/sass/base/index/index.html +1119 -328
  99. package/docs-dev/sass/base/index.html +1120 -327
  100. package/docs-dev/sass/base/keyframes/index.html +1120 -329
  101. package/docs-dev/sass/base/layout/index.html +1119 -328
  102. package/docs-dev/sass/base/normalize/index.html +1119 -328
  103. package/docs-dev/sass/base/print/index.html +1119 -328
  104. package/docs-dev/sass/base/root/index.html +1119 -328
  105. package/docs-dev/sass/base/typography/index.html +1119 -328
  106. package/docs-dev/sass/components/accordion/index.html +1142 -344
  107. package/docs-dev/sass/components/adaptive-spacing/index.html +1119 -328
  108. package/docs-dev/sass/components/badge/index.html +1145 -343
  109. package/docs-dev/sass/components/basic-hero/index.html +5685 -0
  110. package/docs-dev/sass/components/button/index.html +1119 -328
  111. package/docs-dev/sass/components/button-group/index.html +5683 -0
  112. package/docs-dev/sass/components/button-verbose/index.html +1205 -339
  113. package/docs-dev/sass/components/callout/index.html +1224 -414
  114. package/docs-dev/sass/components/captioned-figure/index.html +1243 -337
  115. package/docs-dev/sass/components/card/index.html +1215 -352
  116. package/docs-dev/sass/components/card-grid/index.html +1119 -328
  117. package/docs-dev/sass/components/counter-list/index.html +5767 -0
  118. package/docs-dev/sass/components/css-icon/index.html +1140 -342
  119. package/docs-dev/sass/components/data-grid/index.html +1139 -341
  120. package/docs-dev/sass/components/data-table/index.html +1327 -346
  121. package/docs-dev/sass/components/fill-context/index.html +1119 -328
  122. package/docs-dev/sass/components/flipcard/index.html +1161 -339
  123. package/docs-dev/sass/components/flipcard-grid/index.html +1119 -328
  124. package/docs-dev/sass/components/form-theme/index.html +1334 -447
  125. package/docs-dev/sass/components/headline-label/index.html +5713 -0
  126. package/docs-dev/sass/components/hero/index.html +1179 -340
  127. package/docs-dev/sass/components/horizontal-rule/index.html +1119 -328
  128. package/docs-dev/sass/components/image-grid/index.html +1119 -328
  129. package/docs-dev/sass/components/index/index.html +1136 -338
  130. package/docs-dev/sass/components/index.html +1120 -327
  131. package/docs-dev/sass/components/links/index.html +1119 -328
  132. package/docs-dev/sass/components/list-inline/index.html +1119 -328
  133. package/docs-dev/sass/components/list-lines/index.html +1119 -328
  134. package/docs-dev/sass/components/list-ordered/index.html +1119 -328
  135. package/docs-dev/sass/components/list-unordered/index.html +1119 -328
  136. package/docs-dev/sass/components/menu-stack/index.html +1156 -352
  137. package/docs-dev/sass/components/modal/index.html +1173 -347
  138. package/docs-dev/sass/components/nav-strip/index.html +1127 -336
  139. package/docs-dev/sass/components/overlay-section/index.html +1127 -336
  140. package/docs-dev/sass/components/pager/index.html +1119 -328
  141. package/docs-dev/sass/components/panel/index.html +5913 -0
  142. package/docs-dev/sass/components/placeholder-block/index.html +1119 -328
  143. package/docs-dev/sass/components/popover/index.html +1179 -352
  144. package/docs-dev/sass/components/pull-quote/index.html +1131 -340
  145. package/docs-dev/sass/components/rail/index.html +5700 -0
  146. package/docs-dev/sass/components/ratio-box/index.html +1127 -336
  147. package/docs-dev/sass/components/rule/index.html +1120 -329
  148. package/docs-dev/sass/components/scroll-slider/index.html +1129 -350
  149. package/docs-dev/sass/components/skip-link/index.html +1119 -328
  150. package/docs-dev/sass/components/slider/index.html +1173 -394
  151. package/docs-dev/sass/components/spoke-spinner/index.html +1121 -330
  152. package/docs-dev/sass/components/sticky-list/index.html +5903 -0
  153. package/docs-dev/sass/components/table-sticky/index.html +5677 -0
  154. package/docs-dev/sass/components/tabs/index.html +1154 -348
  155. package/docs-dev/sass/components/tag/index.html +1121 -330
  156. package/docs-dev/sass/components/tile-button/index.html +1119 -328
  157. package/docs-dev/sass/components/tile-grid/index.html +1119 -328
  158. package/docs-dev/sass/components/tile-grid-overlay/index.html +1119 -328
  159. package/docs-dev/sass/components/vignette/index.html +1131 -334
  160. package/docs-dev/sass/components/wysiwyg/index.html +1159 -350
  161. package/docs-dev/sass/core/breakpoint/index.html +1212 -369
  162. package/docs-dev/sass/core/button/index.html +1151 -358
  163. package/docs-dev/sass/core/color/index.html +1291 -368
  164. package/docs-dev/sass/core/cssvar/index.html +1119 -328
  165. package/docs-dev/sass/core/element/index.html +1440 -367
  166. package/docs-dev/sass/core/index.html +1119 -328
  167. package/docs-dev/sass/core/layout/index.html +1173 -363
  168. package/docs-dev/sass/core/path/index.html +1119 -328
  169. package/docs-dev/sass/core/selector/index.html +1119 -328
  170. package/docs-dev/sass/core/typography/index.html +1119 -328
  171. package/docs-dev/sass/core/units/index.html +1127 -330
  172. package/docs-dev/sass/core/utils/index.html +2389 -449
  173. package/docs-dev/sass/helpers/color/index.html +1119 -328
  174. package/docs-dev/sass/helpers/display/index.html +1120 -329
  175. package/docs-dev/sass/helpers/index/index.html +1119 -328
  176. package/docs-dev/sass/helpers/index.html +1120 -327
  177. package/docs-dev/sass/helpers/print/index.html +759 -298
  178. package/docs-dev/sass/helpers/typography/index.html +1119 -328
  179. package/docs-dev/sass/helpers/units/index.html +1119 -328
  180. package/docs-dev/sass/helpers/utilities/index.html +1121 -330
  181. package/docs-dev/sass/index.html +1120 -327
  182. package/js/events/index.js +17 -5
  183. package/js/index.js +1 -0
  184. package/js/settings.js +97 -0
  185. package/js/ui/breakpoints.js +19 -16
  186. package/js/ui/collapsible.js +8 -1
  187. package/js/ui/details-group.js +112 -0
  188. package/js/ui/dialog.js +103 -44
  189. package/js/ui/dialog.todo +2 -36
  190. package/js/ui/flipcard.js +37 -57
  191. package/js/ui/grid.js +15 -13
  192. package/js/ui/index.js +1 -0
  193. package/js/ui/modal-builder.js +127 -70
  194. package/js/ui/overflow-scroller.js +6 -4
  195. package/js/ui/page.js +2 -2
  196. package/js/ui/popover.js +38 -38
  197. package/js/ui/print.js +16 -25
  198. package/js/ui/programmatic-modal.js +9 -3
  199. package/js/ui/proxy-click.js +50 -36
  200. package/js/ui/resizer.js +408 -39
  201. package/js/ui/scroll-slider.js +24 -30
  202. package/js/ui/scrollpoint.js +29 -64
  203. package/js/ui/slider.js +108 -63
  204. package/js/ui/tabs.js +23 -36
  205. package/js/ui/theme-toggle.js +332 -94
  206. package/js/ui/tooltip.js +27 -32
  207. package/js/utils/class-logger.js +3 -3
  208. package/js/utils/css.js +13 -0
  209. package/js/utils/dom.js +23 -64
  210. package/js/utils/font-awesome.js +19 -0
  211. package/js/utils/index.js +2 -1
  212. package/js/utils/system.js +155 -0
  213. package/package.json +23 -8
  214. package/scss/README.md +4 -0
  215. package/scss/_breakpoint.scss +39 -5
  216. package/scss/_button.scss +7 -5
  217. package/scss/_color.scss +40 -9
  218. package/scss/_element.scss +124 -2
  219. package/scss/_layout.scss +7 -8
  220. package/scss/_units.scss +3 -2
  221. package/scss/_utils.scss +387 -16
  222. package/scss/base/_elements.scss +0 -1
  223. package/scss/base/_keyframes.scss +10 -0
  224. package/scss/base/_layout.scss +1 -0
  225. package/scss/base/_print.scss +2 -0
  226. package/scss/base/_root.scss +2 -0
  227. package/scss/components/README.todos +14 -0
  228. package/scss/components/_accordion.scss +33 -19
  229. package/scss/components/_badge.scss +23 -4
  230. package/scss/components/_basic-hero.scss +112 -0
  231. package/scss/components/_button-group.scss +90 -0
  232. package/scss/components/_button-verbose.scss +100 -18
  233. package/scss/components/_callout.scss +125 -78
  234. package/scss/components/_captioned-figure.scss +17 -0
  235. package/scss/components/_card-grid.scss +1 -1
  236. package/scss/components/_card.scss +228 -66
  237. package/scss/components/_counter-list.scss +151 -0
  238. package/scss/components/_css-icon.scss +35 -21
  239. package/scss/components/_data-grid.scss +55 -12
  240. package/scss/components/_data-table.scss +39 -3
  241. package/scss/components/_flipcard.scss +8 -3
  242. package/scss/components/_form-theme.scss +119 -108
  243. package/scss/components/_headline-label.scss +83 -0
  244. package/scss/components/_hero.scss +12 -10
  245. package/scss/components/_index.scss +42 -0
  246. package/scss/components/_menu-stack.scss +42 -26
  247. package/scss/components/_modal.scss +58 -29
  248. package/scss/components/_nav-strip.scss +2 -0
  249. package/scss/components/_overlay-section.scss +2 -5
  250. package/scss/components/_panel.scss +246 -0
  251. package/scss/components/_popover.scss +165 -64
  252. package/scss/components/_pull-quote.scss +13 -13
  253. package/scss/components/_rail.scss +120 -0
  254. package/scss/components/_ratio-box.scss +2 -5
  255. package/scss/components/_rule.scss +1 -0
  256. package/scss/components/_scroll-slider.scss +1 -5
  257. package/scss/components/_slider.scss +49 -72
  258. package/scss/components/_spoke-spinner.scss +2 -2
  259. package/scss/components/_sticky-list.scss +206 -0
  260. package/scss/components/_tabs.scss +22 -4
  261. package/scss/components/_vignette.scss +3 -5
  262. package/scss/components/_wysiwyg.scss +21 -13
  263. package/scss/helpers/_display.scss +15 -18
  264. package/scss/helpers/_print.scss +12 -7
  265. package/scss/helpers/_utilities.scss +42 -32
  266. package/types/events/index.d.ts +10 -1
  267. package/types/events/index.d.ts.map +1 -1
  268. package/types/index.d.ts +1 -0
  269. package/types/settings.d.ts +70 -0
  270. package/types/settings.d.ts.map +1 -0
  271. package/types/ui/breakpoints.d.ts +14 -14
  272. package/types/ui/breakpoints.d.ts.map +1 -1
  273. package/types/ui/collapsible.d.ts.map +1 -1
  274. package/types/ui/details-group.d.ts +38 -0
  275. package/types/ui/details-group.d.ts.map +1 -0
  276. package/types/ui/dialog.d.ts +20 -14
  277. package/types/ui/dialog.d.ts.map +1 -1
  278. package/types/ui/flipcard.d.ts +16 -10
  279. package/types/ui/flipcard.d.ts.map +1 -1
  280. package/types/ui/grid.d.ts +4 -6
  281. package/types/ui/grid.d.ts.map +1 -1
  282. package/types/ui/index.d.ts +2 -1
  283. package/types/ui/modal-builder.d.ts +116 -11
  284. package/types/ui/modal-builder.d.ts.map +1 -1
  285. package/types/ui/overflow-scroller.d.ts +2 -2
  286. package/types/ui/overflow-scroller.d.ts.map +1 -1
  287. package/types/ui/popover.d.ts +6 -7
  288. package/types/ui/popover.d.ts.map +1 -1
  289. package/types/ui/print.d.ts +0 -4
  290. package/types/ui/print.d.ts.map +1 -1
  291. package/types/ui/programmatic-modal.d.ts.map +1 -1
  292. package/types/ui/proxy-click.d.ts +19 -3
  293. package/types/ui/proxy-click.d.ts.map +1 -1
  294. package/types/ui/resizer.d.ts +116 -16
  295. package/types/ui/resizer.d.ts.map +1 -1
  296. package/types/ui/scroll-slider.d.ts +5 -7
  297. package/types/ui/scroll-slider.d.ts.map +1 -1
  298. package/types/ui/scrollpoint.d.ts +3 -8
  299. package/types/ui/scrollpoint.d.ts.map +1 -1
  300. package/types/ui/slider.d.ts +33 -14
  301. package/types/ui/slider.d.ts.map +1 -1
  302. package/types/ui/tabs.d.ts +6 -8
  303. package/types/ui/tabs.d.ts.map +1 -1
  304. package/types/ui/theme-toggle.d.ts +51 -7
  305. package/types/ui/theme-toggle.d.ts.map +1 -1
  306. package/types/ui/tooltip.d.ts +3 -5
  307. package/types/ui/tooltip.d.ts.map +1 -1
  308. package/types/utils/css.d.ts +11 -0
  309. package/types/utils/css.d.ts.map +1 -0
  310. package/types/utils/dom.d.ts +12 -32
  311. package/types/utils/dom.d.ts.map +1 -1
  312. package/types/utils/font-awesome.d.ts +5 -0
  313. package/types/utils/font-awesome.d.ts.map +1 -0
  314. package/types/utils/index.d.ts +2 -1
  315. package/types/utils/system.d.ts +113 -0
  316. 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
+ }