@ulu/frontend 0.1.0-beta.3 → 0.1.0-beta.30

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 (242) hide show
  1. package/CHANGELOG.md +205 -1
  2. package/dist/ulu-frontend.min.css +1 -1
  3. package/dist/ulu-frontend.min.js +19 -18
  4. package/docs-dev/assets/main.js +832 -421
  5. package/docs-dev/assets/placeholder/icon-calendar.svg +1 -0
  6. package/docs-dev/assets/placeholder/icon-check.svg +1 -0
  7. package/docs-dev/assets/style.css +629 -233
  8. package/docs-dev/changelog/index.html +5660 -0
  9. package/docs-dev/changelog/updates-and-changes/index.html +5109 -0
  10. package/docs-dev/demos/accordion/index.html +758 -295
  11. package/docs-dev/demos/basic-hero/index.html +111 -0
  12. package/docs-dev/demos/button/index.html +758 -295
  13. package/docs-dev/demos/button-verbose/index.html +5118 -0
  14. package/docs-dev/demos/callout/index.html +783 -307
  15. package/docs-dev/demos/captioned-figure/index.html +758 -295
  16. package/docs-dev/demos/card/index.html +819 -719
  17. package/docs-dev/demos/card-grid/index.html +5241 -0
  18. package/docs-dev/demos/card-new/index.html +5088 -0
  19. package/docs-dev/demos/card-old/index.html +5223 -0
  20. package/docs-dev/demos/card.1/index.html +5223 -0
  21. package/docs-dev/demos/card.TRASH/index.html +5541 -0
  22. package/docs-dev/demos/css-icons/index.html +758 -295
  23. package/docs-dev/demos/data-grid/index.html +866 -483
  24. package/docs-dev/demos/data-table/index.html +783 -320
  25. package/docs-dev/demos/details-group/index.html +5114 -0
  26. package/docs-dev/demos/file-save/index.html +758 -295
  27. package/docs-dev/demos/flipcard/index.html +758 -295
  28. package/docs-dev/demos/form-theme/index.html +776 -326
  29. package/docs-dev/demos/hero/index.html +12 -4
  30. package/docs-dev/demos/image-grid/index.html +12 -4
  31. package/docs-dev/demos/index.html +758 -295
  32. package/docs-dev/demos/list-inline/index.html +5100 -0
  33. package/docs-dev/demos/list-inline.1/index.html +4727 -0
  34. package/docs-dev/demos/list-lines/index.html +5090 -0
  35. package/docs-dev/demos/menu-stack/index.html +777 -314
  36. package/docs-dev/demos/modals/index.html +758 -295
  37. package/docs-dev/demos/nav-strip/index.html +778 -351
  38. package/docs-dev/demos/overlay-section/index.html +758 -295
  39. package/docs-dev/demos/popovers/index.html +860 -299
  40. package/docs-dev/demos/print/index.html +758 -295
  41. package/docs-dev/demos/pull-quote/index.html +758 -295
  42. package/docs-dev/demos/rule/index.html +758 -295
  43. package/docs-dev/demos/scroll-slider/index.html +72 -106
  44. package/docs-dev/demos/scrollpoints/index.html +758 -295
  45. package/docs-dev/demos/slider/index.html +12 -4
  46. package/docs-dev/demos/spoke-spinner/index.html +758 -295
  47. package/docs-dev/demos/sticky-list/index.html +5103 -0
  48. package/docs-dev/demos/tabs/index.html +758 -295
  49. package/docs-dev/demos/tag/index.html +758 -295
  50. package/docs-dev/demos/theme-toggle/index.html +5159 -0
  51. package/docs-dev/demos/tile-grid-overlay/index.html +12 -4
  52. package/docs-dev/demos/tiles/index.html +758 -295
  53. package/docs-dev/demos/tooltip/index.html +758 -295
  54. package/docs-dev/guide/building-stylesheet/index.html +758 -295
  55. package/docs-dev/guide/developing-ulu-scss-module/index.html +758 -295
  56. package/docs-dev/guide/index.html +758 -295
  57. package/docs-dev/guide/updates-and-changes/index.html +5033 -0
  58. package/docs-dev/index.html +758 -295
  59. package/docs-dev/javascript/events/index.html +755 -294
  60. package/docs-dev/javascript/index.html +758 -295
  61. package/docs-dev/javascript/settings/index.html +5214 -0
  62. package/docs-dev/javascript/ui-breakpoints/index.html +755 -294
  63. package/docs-dev/javascript/ui-collapsible/index.html +755 -294
  64. package/docs-dev/javascript/ui-details-group/index.html +5214 -0
  65. package/docs-dev/javascript/ui-dialog/index.html +755 -294
  66. package/docs-dev/javascript/ui-flipcard/index.html +755 -294
  67. package/docs-dev/javascript/ui-grid/index.html +755 -294
  68. package/docs-dev/javascript/ui-modal-builder/index.html +755 -294
  69. package/docs-dev/javascript/ui-overflow-scroller/index.html +755 -294
  70. package/docs-dev/javascript/ui-overflow-scroller-pager/index.html +755 -294
  71. package/docs-dev/javascript/ui-page/index.html +755 -294
  72. package/docs-dev/javascript/ui-popover/index.html +755 -294
  73. package/docs-dev/javascript/ui-print/index.html +755 -294
  74. package/docs-dev/javascript/ui-print-details/index.html +755 -294
  75. package/docs-dev/javascript/ui-programmatic-modal/index.html +755 -294
  76. package/docs-dev/javascript/ui-proxy-click/index.html +755 -294
  77. package/docs-dev/javascript/ui-resizer/index.html +755 -294
  78. package/docs-dev/javascript/ui-scroll-slider/index.html +755 -294
  79. package/docs-dev/javascript/ui-scrollpoint/index.html +755 -294
  80. package/docs-dev/javascript/ui-slider/index.html +755 -294
  81. package/docs-dev/javascript/ui-tabs/index.html +755 -294
  82. package/docs-dev/javascript/ui-theme-toggle/index.html +5298 -0
  83. package/docs-dev/javascript/ui-tooltip/index.html +755 -294
  84. package/docs-dev/javascript/utils-class-logger/index.html +755 -294
  85. package/docs-dev/javascript/utils-dom/index.html +819 -298
  86. package/docs-dev/javascript/utils-file-save/index.html +755 -294
  87. package/docs-dev/javascript/utils-floating-ui/index.html +755 -294
  88. package/docs-dev/javascript/utils-id/index.html +755 -294
  89. package/docs-dev/javascript/utils-pause-youtube-video/index.html +755 -294
  90. package/docs-dev/sass/base/color/index.html +755 -294
  91. package/docs-dev/sass/base/elements/index.html +755 -294
  92. package/docs-dev/sass/base/index/index.html +755 -294
  93. package/docs-dev/sass/base/index.html +758 -295
  94. package/docs-dev/sass/base/keyframes/index.html +755 -294
  95. package/docs-dev/sass/base/layout/index.html +755 -294
  96. package/docs-dev/sass/base/normalize/index.html +755 -294
  97. package/docs-dev/sass/base/print/index.html +755 -294
  98. package/docs-dev/sass/base/root/index.html +755 -294
  99. package/docs-dev/sass/base/typography/index.html +755 -294
  100. package/docs-dev/sass/components/accordion/index.html +761 -300
  101. package/docs-dev/sass/components/adaptive-spacing/index.html +755 -294
  102. package/docs-dev/sass/components/badge/index.html +763 -302
  103. package/docs-dev/sass/components/basic-hero/index.html +5265 -0
  104. package/docs-dev/sass/components/button/index.html +755 -294
  105. package/docs-dev/sass/components/button-verbose/index.html +813 -303
  106. package/docs-dev/sass/components/callout/index.html +780 -355
  107. package/docs-dev/sass/components/captioned-figure/index.html +878 -302
  108. package/docs-dev/sass/components/card/index.html +817 -313
  109. package/docs-dev/sass/components/card-grid/index.html +755 -294
  110. package/docs-dev/sass/components/css-icon/index.html +772 -304
  111. package/docs-dev/sass/components/data-grid/index.html +755 -294
  112. package/docs-dev/sass/components/data-table/index.html +951 -305
  113. package/docs-dev/sass/components/fill-context/index.html +755 -294
  114. package/docs-dev/sass/components/flipcard/index.html +791 -299
  115. package/docs-dev/sass/components/flipcard-grid/index.html +755 -294
  116. package/docs-dev/sass/components/form-theme/index.html +926 -363
  117. package/docs-dev/sass/components/hero/index.html +811 -302
  118. package/docs-dev/sass/components/horizontal-rule/index.html +755 -294
  119. package/docs-dev/sass/components/image-grid/index.html +755 -294
  120. package/docs-dev/sass/components/index/index.html +768 -304
  121. package/docs-dev/sass/components/index.html +758 -295
  122. package/docs-dev/sass/components/links/index.html +755 -294
  123. package/docs-dev/sass/components/list-inline/index.html +5279 -0
  124. package/docs-dev/sass/components/list-lines/index.html +787 -330
  125. package/docs-dev/sass/components/list-ordered/index.html +757 -296
  126. package/docs-dev/sass/components/list-unordered/index.html +755 -294
  127. package/docs-dev/sass/components/menu-stack/index.html +789 -315
  128. package/docs-dev/sass/components/modal/index.html +776 -308
  129. package/docs-dev/sass/components/nav-strip/index.html +767 -306
  130. package/docs-dev/sass/components/overlay-section/index.html +763 -302
  131. package/docs-dev/sass/components/pager/index.html +755 -294
  132. package/docs-dev/sass/components/placeholder-block/index.html +755 -294
  133. package/docs-dev/sass/components/popover/index.html +812 -315
  134. package/docs-dev/sass/components/pull-quote/index.html +767 -306
  135. package/docs-dev/sass/components/ratio-box/index.html +755 -294
  136. package/docs-dev/sass/components/rule/index.html +763 -302
  137. package/docs-dev/sass/components/scroll-slider/index.html +755 -294
  138. package/docs-dev/sass/components/skip-link/index.html +763 -302
  139. package/docs-dev/sass/components/slider/index.html +762 -301
  140. package/docs-dev/sass/components/spoke-spinner/index.html +755 -294
  141. package/docs-dev/sass/components/sticky-list/index.html +5483 -0
  142. package/docs-dev/sass/components/tabs/index.html +764 -303
  143. package/docs-dev/sass/components/tag/index.html +755 -294
  144. package/docs-dev/sass/components/tile-button/index.html +755 -294
  145. package/docs-dev/sass/components/tile-grid/index.html +755 -294
  146. package/docs-dev/sass/components/tile-grid-overlay/index.html +755 -294
  147. package/docs-dev/sass/components/vignette/index.html +769 -302
  148. package/docs-dev/sass/components/wysiwyg/index.html +755 -294
  149. package/docs-dev/sass/core/breakpoint/index.html +755 -294
  150. package/docs-dev/sass/core/button/index.html +755 -294
  151. package/docs-dev/sass/core/color/index.html +793 -325
  152. package/docs-dev/sass/core/cssvar/index.html +755 -294
  153. package/docs-dev/sass/core/element/index.html +755 -294
  154. package/docs-dev/sass/core/index.html +755 -294
  155. package/docs-dev/sass/core/layout/index.html +755 -294
  156. package/docs-dev/sass/core/path/index.html +755 -294
  157. package/docs-dev/sass/core/selector/index.html +755 -294
  158. package/docs-dev/sass/core/typography/index.html +755 -294
  159. package/docs-dev/sass/core/units/index.html +755 -294
  160. package/docs-dev/sass/core/utils/index.html +1481 -382
  161. package/docs-dev/sass/helpers/color/index.html +755 -294
  162. package/docs-dev/sass/helpers/display/index.html +755 -294
  163. package/docs-dev/sass/helpers/index/index.html +755 -294
  164. package/docs-dev/sass/helpers/index.html +758 -295
  165. package/docs-dev/sass/helpers/print/index.html +755 -294
  166. package/docs-dev/sass/helpers/typography/index.html +755 -294
  167. package/docs-dev/sass/helpers/units/index.html +755 -294
  168. package/docs-dev/sass/helpers/utilities/index.html +755 -294
  169. package/docs-dev/sass/index.html +758 -295
  170. package/js/index.js +1 -0
  171. package/js/settings.js +78 -0
  172. package/js/ui/details-group.js +121 -0
  173. package/js/ui/index.js +1 -0
  174. package/js/ui/modal-builder.js +3 -2
  175. package/js/ui/overflow-scroller.js +5 -4
  176. package/js/ui/popover.js +1 -0
  177. package/js/ui/programmatic-modal.js +9 -3
  178. package/js/ui/slider.js +7 -6
  179. package/js/ui/theme-toggle.js +330 -89
  180. package/js/utils/dom.js +43 -1
  181. package/js/utils/font-awesome.js +18 -0
  182. package/js/utils/index.js +2 -1
  183. package/package.json +9 -6
  184. package/scss/_color.scss +9 -2
  185. package/scss/_layout.scss +1 -4
  186. package/scss/_utils.scss +187 -11
  187. package/scss/components/README.todos +14 -0
  188. package/scss/components/_accordion.scss +17 -18
  189. package/scss/components/_badge.scss +3 -2
  190. package/scss/components/_basic-hero.scss +112 -0
  191. package/scss/components/_button-verbose.scss +66 -12
  192. package/scss/components/_callout.scss +43 -54
  193. package/scss/components/_captioned-figure.scss +23 -5
  194. package/scss/components/_card-grid.scss +1 -1
  195. package/scss/components/_card.scss +190 -70
  196. package/scss/components/_css-icon.scss +16 -11
  197. package/scss/components/_data-table.scss +41 -4
  198. package/scss/components/_flipcard.scss +20 -14
  199. package/scss/components/_form-theme.scss +135 -123
  200. package/scss/components/_hero.scss +9 -0
  201. package/scss/components/_index.scss +18 -0
  202. package/scss/components/_list-inline.scss +80 -0
  203. package/scss/components/_list-lines.scss +44 -33
  204. package/scss/components/_list-ordered.scss +0 -1
  205. package/scss/components/_menu-stack.scss +42 -26
  206. package/scss/components/_modal.scss +23 -19
  207. package/scss/components/_nav-strip.scss +25 -16
  208. package/scss/components/_overlay-section.scss +2 -1
  209. package/scss/components/_pager.scss +6 -6
  210. package/scss/components/_placeholder-block.scss +4 -4
  211. package/scss/components/_popover.scss +174 -73
  212. package/scss/components/_pull-quote.scss +12 -12
  213. package/scss/components/_rule.scss +0 -1
  214. package/scss/components/_scroll-slider.scss +1 -1
  215. package/scss/components/_skip-link.scss +2 -1
  216. package/scss/components/_slider.scss +17 -38
  217. package/scss/components/_sticky-list.scss +206 -0
  218. package/scss/components/_tabs.scss +5 -2
  219. package/scss/components/_tag.scss +1 -1
  220. package/scss/components/_vignette.scss +1 -0
  221. package/types/index.d.ts +1 -0
  222. package/types/settings.d.ts +38 -0
  223. package/types/settings.d.ts.map +1 -0
  224. package/types/ui/details-group.d.ts +43 -0
  225. package/types/ui/details-group.d.ts.map +1 -0
  226. package/types/ui/index.d.ts +1 -0
  227. package/types/ui/modal-builder.d.ts +2 -2
  228. package/types/ui/modal-builder.d.ts.map +1 -1
  229. package/types/ui/overflow-scroller.d.ts +2 -2
  230. package/types/ui/overflow-scroller.d.ts.map +1 -1
  231. package/types/ui/popover.d.ts.map +1 -1
  232. package/types/ui/programmatic-modal.d.ts.map +1 -1
  233. package/types/ui/slider.d.ts +2 -2
  234. package/types/ui/slider.d.ts.map +1 -1
  235. package/types/ui/tabs.d.ts.map +1 -1
  236. package/types/ui/theme-toggle.d.ts +58 -7
  237. package/types/ui/theme-toggle.d.ts.map +1 -1
  238. package/types/utils/dom.d.ts +19 -1
  239. package/types/utils/dom.d.ts.map +1 -1
  240. package/types/utils/font-awesome.d.ts +5 -0
  241. package/types/utils/font-awesome.d.ts.map +1 -0
  242. package/types/utils/index.d.ts +1 -0
@@ -1,129 +1,370 @@
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";
4
5
  import { getName } from "../events/index.js";
6
+ import { getDatasetJson, getElements, resolveClasses } from "../utils/dom.js";
7
+ import { hasRequiredProps } from "@ulu/utils/object.js";
5
8
 
6
- const attrs = {
7
- trigger: "data-site-theme-toggle",
8
- icon: "data-site-theme-toggle-icon",
9
- init: "data-site-theme-toggle-init",
9
+ /**
10
+ * Default data attributes
11
+ */
12
+ export const attrs = {
13
+ init: "data-ulu-theme-toggle-init",
14
+ toggle: "data-ulu-theme-toggle",
15
+ toggleIcon: "data-ulu-theme-toggle-icon",
16
+ toggleLabel: "data-ulu-theme-toggle-label",
17
+ toggleRemote: "data-ulu-theme-toggle-remote",
18
+ state: "data-ulu-theme-toggle-state",
10
19
  };
11
20
 
21
+ // Utils for selecting things based on attributes
12
22
  const attrSelector = key => `[${ attrs[key] }]`;
13
23
  const attrSelectorInitial = key => `${ attrSelector(key) }:not([${ attrs.init }])`;
24
+ const queryAllInitial = key => document.querySelectorAll(attrSelectorInitial(key));
25
+ const queryRemotes = group => document.querySelectorAll(
26
+ `[${ attrs.toggleRemote }="${ group }"]`
27
+ );
28
+ const queryRemotesInitial = group => document.querySelectorAll(
29
+ `[${ attrs.toggleRemote }="${ group }"]:not([${ attrs.init }])`
30
+ );
31
+ const debugLog = (...msgs) => console.log("Theme Toggle:", ...msgs);
32
+ const requiredToggleProps = ["target"];
33
+ const checkToggleProps = hasRequiredProps(requiredToggleProps);
34
+ const when = (cond, fn) => cond ? fn() : null; // Consider adding as util
14
35
 
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",
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());
112
+ document.addEventListener(getName("pageModified"), setup);
40
113
  setup();
41
114
  }
42
115
 
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);
116
+ /**
117
+ * Query and setup all
118
+ */
119
+ export function setup() {
120
+ queryAllInitial("toggle").forEach(setupToggle);
121
+ }
122
+
123
+ /**
124
+ * Sets up a single toggle
125
+ * @param {HTMLElement} toggle A toggle to be setup
126
+ */
127
+ export function setupToggle(toggle, passedOptions) {
128
+ const elementOptions = getDatasetJson(toggle, "uluThemeToggle");
129
+ const options = Object.assign({}, defaults, passedOptions, elementOptions);
130
+
131
+ if (!checkToggleProps(options)) {
132
+ console.error(`Missing a required option: ${ requiredToggleProps.join(", ") }`);
133
+ return;
134
+ }
135
+
136
+ const group = options.group;
137
+ const ctx = { toggle, options };
138
+ const initialKey = resolveInitial(options);
139
+
140
+ if (!initialKey) {
141
+ console.error("Unable to resolve initial key");
142
+ return;
143
+ }
144
+
145
+ setState(initialKey, ctx);
146
+
147
+ toggle.addEventListener("click", onToggleClick);
148
+ toggle.setAttribute(attrs.init, "");
149
+
150
+ // Remotes listeners are attached initially and then we also
151
+ // update them vs toggles which would be updated by the main pageModified
152
+ // event in init
153
+ attachRemotes();
154
+ document.addEventListener(getName("pageModified"), attachRemotes);
155
+
156
+ /**
157
+ * Instance function to get the next theme in cycle
158
+ */
159
+ function toggleState(event) {
160
+ const targets = getElements(options.target);
161
+ const lastKey = targets[0].dataset.uluThemeToggleState;
162
+ const key = getNextThemeKey(lastKey, options);
163
+ if (!key) {
164
+ console.error("Issue getting next theme key");
165
+ return;
166
+ }
167
+ setState(key, { ...ctx, event });
168
+ }
169
+
170
+ /**
171
+ * Handler for click for both toggle and remote toggles
172
+ */
173
+ function onToggleClick(event) {
174
+ toggleState(event);
175
+ }
176
+
177
+ /**
178
+ * Utility to attach remote handlers
179
+ * - Used initially and when page is modified
180
+ */
181
+ function attachRemotes() {
182
+ if (!group) return;
183
+ const remotes = queryRemotesInitial(group);
184
+ remotes.forEach(remote => {
185
+ remote.addEventListener("click", onToggleClick);
186
+ remote.setAttribute(attrs.init, "");
54
187
  });
55
- });
56
- // Initial icon setup
57
- changeIcons();
188
+ }
189
+
190
+ /**
191
+ * This only cleans up remotes that are still in DOM
192
+ * - For ones that have been removed we don't store any references to them
193
+ */
194
+ function cleanupRemotes() {
195
+ if (!group) return;
196
+ const remotes = queryRemotesInitial(group);
197
+ remotes.forEach(remote => {
198
+ remote.removeEventListener("click", onToggleClick);
199
+ remote.removeAttribute(attrs.init, "");
200
+ });
201
+ }
202
+
203
+ /**
204
+ * Function to cleanup listeners and remove init attributes
205
+ */
206
+ function destroy() {
207
+ toggle.removeEventListener("click", onToggleClick);
208
+ toggle.removeAttribute(attrs.init, "");
209
+ cleanupRemotes();
210
+ document.removeEventListener(getName("pageModified"), attachRemotes);
211
+ }
212
+
213
+ return {
214
+ destroy,
215
+ toggle,
216
+ options,
217
+ toggleState,
218
+ setState(themeKey) {
219
+ setState(themeKey, ctx);
220
+ }
221
+ };
58
222
  }
59
223
 
224
+
225
+
60
226
  /**
61
- *
62
- * @param {Element} body Sets up initial theme on load based on user preference.
227
+ * Change the state of target/toggle
63
228
  */
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
- }
229
+ function setState(key, ctx) {
230
+ if (!key) {
231
+ console.error("Missing key");
232
+ return;
233
+ }
234
+
235
+ const { toggle, options } = ctx;
236
+ const { themes, group } = options;
237
+ const elements = {
238
+ targets: getElements(options.target),
239
+ toggles: [toggle, ...(group ? queryRemotes(group) : [])]
240
+ };
241
+
242
+ if (!elements.targets.length || !elements.toggles.length) {
243
+ console.error("Issue setting state, couldn't find needed elements", elements);
244
+ return;
245
+ }
246
+
247
+ const theme = themes[key];
248
+ const otherThemes = getOtherThemes(key, themes);
249
+ const stateCtx = {
250
+ ...ctx,
251
+ key,
252
+ elements,
253
+ theme,
254
+ otherThemes
255
+ };
256
+
257
+ if (options.debug) {
258
+ debugLog("set state context", stateCtx);
259
+ }
260
+
261
+ // Prepare classes to remove
262
+ const otherTargetClasses = concatThemeClasses(otherThemes, "targetClass");
263
+ const otherIconClasses = concatThemeClasses(otherThemes, "iconClass");
264
+
265
+ // Update all targets
266
+ elements.targets.forEach(element => {
267
+ element.setAttribute(attrs.state, key);
268
+ element.classList.remove(...otherTargetClasses);
269
+ element.classList.add(...resolveClasses(theme.targetClass));
270
+ });
271
+
272
+ // Update all toggles and inner children
273
+ elements.toggles.forEach(element => {
274
+ const label = element.querySelector(attrSelector("toggleLabel"));
275
+ const icon = element.querySelector(attrSelector("toggleIcon"));
276
+ if (label) {
277
+ label.textContent = theme.label;
278
+ }
279
+ if (icon) {
280
+ icon.classList.remove(...otherIconClasses);
281
+ icon.classList.add(...resolveClasses(theme.iconClass));
282
+ }
283
+ element.setAttribute(attrs.state, key);
284
+ });
285
+
286
+ // Optional callback if user want to set other things (ie. data-theme or something)
287
+ if (options.onChange) {
288
+ options.onChange(stateCtx);
289
+ }
290
+
291
+ if (options.savePreference) {
292
+ localStorage.setItem(getStorageKey(options), key);
293
+ }
74
294
  }
75
295
 
76
296
  /**
77
- *
78
- * @param {Element} body Changes the theme of the body.
297
+ * Function determines what the initial state is
298
+ * - Check OS preference, saved preference, or initialState depending on options
299
+ * @return {String} The resolved initial theme's key
79
300
  */
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;
301
+ function resolveInitial(options) {
302
+ const { savePreference, checkMediaQuery, themes, initialState } = options;
303
+ const storageKey = getStorageKey(options);
304
+ const saved = when(savePreference, () => localStorage.getItem(storageKey));
305
+ const mediaQueryPreference = when(checkMediaQuery, () => getMatchingThemeQuery(themes));
306
+ const resolved = saved || mediaQueryPreference || initialState;
307
+
308
+ if (options.debug) {
309
+ debugLog("Preference Saved:", saved);
310
+ debugLog("Media Query Preference:", mediaQueryPreference);
311
+ debugLog("Initial State:", initialState);
89
312
  }
90
- body.classList.remove(oldTheme);
91
- body.classList.add(newTheme);
92
- localStorage.setItem("data-theme", newTheme);
93
- currentTheme = newTheme;
94
- changeIcons();
313
+
314
+ if (!resolved) {
315
+ console.error("Failed to resolve initial theme (pass 'initialState' to options)");
316
+ }
317
+
318
+ return resolved;
95
319
  }
96
320
 
97
321
  /**
98
- *
99
- * @param {Element} body Used to check for theme.
100
- * @param {Element} context Used to find the icons.
322
+ * Check each theme for a matching media query
323
+ * @return {String} Matching theme key
101
324
  */
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;
325
+ function getMatchingThemeQuery(themes) {
326
+ const found = Object.entries(themes).find(([_key, theme]) => {
327
+ if (theme.mediaQuery) {
328
+ return window.matchMedia(theme.mediaQuery).matches;
109
329
  }
110
330
  });
331
+ // Return just the key
332
+ return found ? found[0] : null;
111
333
  }
112
334
 
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
- }
335
+ /**
336
+ * Get the next key in the themes based on the currentKey
337
+ */
338
+ function getNextThemeKey(activeKey, options) {
339
+ const { themes } = options;
340
+ const keys = Object.keys(themes);
341
+ const index = keys.findIndex(theme => theme === activeKey);
342
+ // If not found return first, else calculate next index (wrapping)
343
+ const nextIndex = index === -1 ? 0 : (index + 1) % keys.length;
344
+ return keys[nextIndex];
120
345
  }
121
346
 
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
- }
347
+ /**
348
+ * Get all other theme object except the current
349
+ */
350
+ function getOtherThemes(currentKey, themes) {
351
+ const all = Object.entries(themes);
352
+ return all.filter(([key]) => key !== currentKey).map(([_key, value]) => value);
353
+ }
354
+
355
+ /**
356
+ * Concatenates multiple class properties into one array
357
+ */
358
+ function concatThemeClasses(themes, property) {
359
+ return themes.reduce((acc, theme) => {
360
+ return acc.concat(resolveClasses(theme[property]));
361
+ }, []);
362
+ }
363
+
364
+ /**
365
+ * Creates the storage key (either prefix or prefix with group name)
366
+ */
367
+ function getStorageKey(options) {
368
+ const { storagePrefix, group } = options;
369
+ return group ? `${ storagePrefix }${ group }` : storagePrefix;
129
370
  }
package/js/utils/dom.js CHANGED
@@ -98,6 +98,7 @@ export function setPositionClasses(parent, classes = {
98
98
  * Resolve a target to Element
99
99
  * @param {String|Node} target The selector or node/element
100
100
  * @param {Object} context [document] The context to query possible selectors from
101
+ * @return {HTMLElement} The element or null if not found
101
102
  */
102
103
  export function getElement(target, context = document) {
103
104
  if (typeof target === "string") {
@@ -105,11 +106,52 @@ export function getElement(target, context = document) {
105
106
  } else if (target instanceof Element) {
106
107
  return target;
107
108
  } else {
108
- console.warn("Unable to getElement()", target);
109
+ console.warn("getElement: Invalid target type (expected String/Node)", target);
109
110
  return null;
110
111
  }
111
112
  }
112
113
 
114
+ /**
115
+ * Resolve a target to Elements
116
+ * @param {String|Node} target The selector or node/element
117
+ * @param {Object} context [document] The context to query possible selectors from
118
+ * @return {Array} The elements or null if not found
119
+ */
120
+ export function getElements(target, context = document) {
121
+ if (typeof target === "string") {
122
+ return [...context.querySelectorAll(target)];
123
+ } else if (target instanceof Element) {
124
+ return [target];
125
+ } else if (Array.isArray(target) || target instanceof NodeList) {
126
+ return [...target];
127
+ } else {
128
+ console.warn("getElement: Invalid target type (expected String/Node/Array/Node List)", target);
129
+ return null;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Resolves a class input (string or array) into a consistent array of class names.
135
+ * @param {string|string[]} input - The class input, which can be a string, an array of strings, or any other value.
136
+ * @returns {string[]} An array of class names. Returns an empty array for invalid or falsy input.
137
+ * @example
138
+ * resolveClassArray("fas fa-check my-class"); // Returns ["fas", "fa-check", "my-class"]
139
+ * resolveClassArray(["another-class", "yet-another-class"]); // Returns ["another-class", "yet-another-class"]
140
+ * resolveClassArray("single-class"); // Returns ["single-class"]
141
+ */
142
+ export function resolveClasses(classes) {
143
+ if (typeof classes === "string") {
144
+ return classes.split(" ").filter(c => c !== ""); // Split and remove empty strings
145
+ } else if (Array.isArray(classes)) {
146
+ return classes;
147
+ } else if (!classes) {
148
+ return [];
149
+ } else {
150
+ console.warn("resolveClassArray: Invalid class input type.", classes);
151
+ return [];
152
+ }
153
+ }
154
+
113
155
  /**
114
156
  * Sets a CSS custom property equal to the scrollbar width
115
157
  * @param {Node} element The element that is the child of a scrollabel container
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @module settings
3
+ * @description Utility module for setting up Font Awesome
4
+ */
5
+
6
+ import { updateSettings } from "../settings.js";
7
+
8
+ /**
9
+ * Sets icon settings to Font Awesome icons
10
+ */
11
+ export function configureIcons() {
12
+ updateSettings({
13
+ iconClassClose: "fas fa-xmark",
14
+ iconClassDragX: "fas fa-solid fa-grip-lines-vertical",
15
+ iconClassPrevious: "fas fa-solid fa-chevron-left",
16
+ iconClassNext: "fas fa-solid fa-chevron-right"
17
+ });
18
+ }
package/js/utils/index.js CHANGED
@@ -4,4 +4,5 @@ export * as floatingUi from "./floating-ui.js";
4
4
  export * as id from "./id.js";
5
5
  export * as index from "./index.js";
6
6
  export * as pauseYoutubeVideo from "./pause-youtube-video.js";
7
- export * as fileSave from "./file-save.js";
7
+ export * as fileSave from "./file-save.js";
8
+ export * as fontAwesome from "./font-awesome.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulu/frontend",
3
- "version": "0.1.0-beta.3",
3
+ "version": "0.1.0-beta.30",
4
4
  "description": "Modular Sass Theming Library",
5
5
  "browser": "js/index.js",
6
6
  "main": "index.js",
@@ -30,7 +30,9 @@
30
30
  "docs:build:prod": "IS_PRODUCTION=true npx @11ty/eleventy --config=docs.eleventy.js",
31
31
  "docs:assets": "vite --config docs.vite.config.js --force",
32
32
  "docs:assets:build": "vite build --config docs.vite.config.js",
33
- "docs:assets:build:prod": "IS_PRODUCTION=true vite build --config docs.vite.config.js"
33
+ "docs:assets:build:prod": "IS_PRODUCTION=true vite build --config docs.vite.config.js",
34
+ "deploy": "npm run build && npm run types && npm run docs:build:prod && npm run docs:assets:build:prod",
35
+ "deploy:docs": "npm run docs:build:prod && npm run docs:assets:build:prod"
34
36
  },
35
37
  "repository": {
36
38
  "type": "git",
@@ -66,6 +68,7 @@
66
68
  "@ulu/vitepress-auto-menus": "^0.0.3",
67
69
  "@ulu/vitepress-sassdoc": "^0.0.9",
68
70
  "algoliasearch": "^4.23.3",
71
+ "autoprefixer": "^10.4.16",
69
72
  "chokidar": "^3.6.0",
70
73
  "eleventy-plugin-nesting-toc": "^1.3.0",
71
74
  "fs-extra": "^11.2.0",
@@ -78,14 +81,14 @@
78
81
  "sass-embedded": "^1.81.0",
79
82
  "sharp": "^0.33.4",
80
83
  "svgo": "^3.3.2",
84
+ "twig": "^1.17.1",
81
85
  "typescript": "^5.3.3",
82
- "autoprefixer": "^10.4.16",
83
86
  "vite": "^5.4.11"
84
87
  },
85
88
  "dependencies": {
86
- "@ulu/utils": "^0.0.17",
89
+ "@floating-ui/dom": "^1.6.5",
90
+ "@ulu/utils": "^0.0.20",
87
91
  "ally.js": "^1.4.1",
88
- "aria-tablist": "^1.2.2",
89
- "@floating-ui/dom": "^1.6.5"
92
+ "aria-tablist": "^1.2.2"
90
93
  }
91
94
  }
package/scss/_color.scss CHANGED
@@ -23,10 +23,17 @@ $palette: (
23
23
  "type-tertiary" : rgb(157, 157, 157),
24
24
  "headline" : inherit,
25
25
  "background" : white,
26
+ "background-gray" : #F7F8F7,
26
27
  "focus" : blue,
27
- "error" : red,
28
- "warning" : orange,
29
28
  "accent" : orange,
29
+ "info" : #00bde3,
30
+ "success" : #00a91c,
31
+ "warning" : #ffbe2e,
32
+ "danger" : #d54309,
33
+ "info-background" : #e7f6f8,
34
+ "success-background" : #ecf3ec,
35
+ "warning-background" : #faf3d1,
36
+ "danger-background" : #f4e3db,
30
37
  "selected" : green,
31
38
  "box-shadow" : rgba(0, 0, 0, 0.349),
32
39
  "box-shadow-hover" : rgba(0, 0, 0, 0.5),
package/scss/_layout.scss CHANGED
@@ -215,10 +215,7 @@ $containers: (
215
215
  $breakpoints: map.get($container, "breakpoints");
216
216
 
217
217
  $width: map.get($container, "width");
218
- // @debug $specific-breakpoint;
219
- // @if $specific-breakpoint {
220
- // @debug $container;
221
- // }
218
+
222
219
  @if ($width == null) {
223
220
  $width: 100%;
224
221
  }