@ulu/frontend 0.0.22 → 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/deprecated/js/drupal-programmatic-modal.js +91 -0
  3. package/{js/ui/modals.js → deprecated/js/micromodal-modals.js} +41 -67
  4. package/dist/ulu-frontend.min.css +1 -1
  5. package/dist/ulu-frontend.min.js +70 -1
  6. package/index.js +6 -1
  7. package/js/events/index.js +58 -7
  8. package/js/index.js +3 -7
  9. package/js/{helpers/css-breakpoint.js → ui/breakpoints.js} +9 -11
  10. package/js/ui/collapsible.js +195 -0
  11. package/js/ui/dialog.js +157 -0
  12. package/js/ui/dialog.todo +37 -0
  13. package/js/ui/flipcard.js +55 -11
  14. package/js/ui/grid.js +2 -47
  15. package/js/ui/index.js +21 -0
  16. package/js/ui/modal-builder.js +197 -0
  17. package/js/ui/overflow-scroller-pager.js +1 -1
  18. package/js/ui/overflow-scroller.js +8 -5
  19. package/js/ui/page.js +14 -0
  20. package/js/ui/popover.js +135 -0
  21. package/js/ui/print-details.js +44 -0
  22. package/js/ui/print.js +67 -0
  23. package/js/ui/programmatic-modal.js +79 -81
  24. package/js/ui/proxy-click.js +80 -0
  25. package/js/ui/resizer.js +3 -3
  26. package/js/ui/scroll-slider.js +56 -0
  27. package/js/ui/scrollpoint.js +300 -0
  28. package/js/ui/slider.js +72 -10
  29. package/js/ui/tabs.js +85 -58
  30. package/js/ui/theme-toggle.js +129 -0
  31. package/js/ui/tooltip.js +268 -67
  32. package/js/utils/{logger.js → class-logger.js} +6 -5
  33. package/js/utils/dom.js +122 -0
  34. package/js/utils/file-save.js +67 -0
  35. package/js/utils/floating-ui.js +83 -0
  36. package/js/utils/id.js +22 -0
  37. package/js/utils/index.js +7 -0
  38. package/js/{helpers → utils}/pause-youtube-video.js +1 -1
  39. package/package.json +32 -11
  40. package/resources/drupal/twig-macros/accordion.twig +99 -0
  41. package/resources/drupal/twig-macros/dropdown.twig +44 -0
  42. package/resources/drupal/twig-macros/flipcard.twig +69 -0
  43. package/resources/drupal/twig-macros/image.twig +30 -0
  44. package/resources/drupal/twig-macros/layout.twig +338 -0
  45. package/resources/drupal/twig-macros/slider.twig +214 -0
  46. package/resources/drupal/twig-macros/tabs.twig +84 -0
  47. package/scss/README.md +13 -1
  48. package/scss/_breakpoint.scss +69 -26
  49. package/scss/_button.scss +148 -57
  50. package/scss/_color.scss +46 -28
  51. package/scss/_cssvar.scss +103 -12
  52. package/scss/_element.scss +84 -67
  53. package/scss/_index.scss +0 -3
  54. package/scss/_layout.scss +71 -37
  55. package/scss/_path.scss +2 -2
  56. package/scss/_selector.scss +20 -11
  57. package/scss/_typography.scss +115 -82
  58. package/scss/_units.scss +14 -13
  59. package/scss/_utils.scss +280 -18
  60. package/scss/base/_color.scss +2 -1
  61. package/scss/base/_elements.scss +61 -35
  62. package/scss/base/_index.scss +60 -23
  63. package/scss/base/_keyframes.scss +115 -16
  64. package/scss/base/_layout.scss +10 -6
  65. package/scss/base/_normalize.scss +6 -122
  66. package/scss/base/_print.scss +49 -0
  67. package/scss/base/_root.scss +28 -0
  68. package/scss/base/_typography.scss +4 -1
  69. package/scss/components/_accordion.scss +217 -0
  70. package/scss/components/_adaptive-spacing.scss +148 -0
  71. package/scss/components/_badge.scss +17 -14
  72. package/scss/components/_button-verbose.scss +138 -0
  73. package/scss/components/_button.scss +9 -4
  74. package/scss/components/_callout.scss +175 -0
  75. package/scss/components/_captioned-figure.scss +173 -0
  76. package/scss/components/_card-grid.scss +75 -0
  77. package/scss/components/_card.scss +420 -0
  78. package/scss/components/_css-icon.scss +433 -0
  79. package/scss/{_grid.scss → components/_data-grid.scss} +100 -68
  80. package/scss/components/_data-table.scss +180 -0
  81. package/scss/components/_fill-context.scss +20 -22
  82. package/scss/components/_flipcard-grid.scss +66 -0
  83. package/scss/components/_flipcard.scss +304 -0
  84. package/scss/components/_form-theme.scss +633 -0
  85. package/scss/components/_hero.scss +183 -0
  86. package/scss/components/_horizontal-rule.scss +51 -0
  87. package/scss/components/_image-grid.scss +71 -0
  88. package/scss/components/_index.scss +276 -38
  89. package/scss/components/_links.scss +1 -1
  90. package/scss/components/_list-lines.scss +14 -3
  91. package/scss/components/_list-ordered.scss +3 -1
  92. package/scss/components/_list-unordered.scss +3 -1
  93. package/scss/components/_menu-stack.scss +245 -0
  94. package/scss/components/_modal.scss +495 -0
  95. package/scss/components/_nav-strip.scss +148 -0
  96. package/scss/components/_overlay-section.scss +122 -0
  97. package/scss/components/_pager.scss +168 -0
  98. package/scss/components/_placeholder-block.scss +121 -0
  99. package/scss/components/_popover.scss +263 -0
  100. package/scss/components/_pull-quote.scss +111 -0
  101. package/scss/components/_ratio-box.scss +64 -0
  102. package/scss/components/_rule.scss +12 -9
  103. package/scss/components/_scroll-slider.scss +204 -0
  104. package/scss/components/_skip-link.scss +92 -0
  105. package/scss/components/_slider.scss +241 -0
  106. package/scss/components/_spoke-spinner.scss +193 -0
  107. package/scss/components/_tabs.scss +179 -0
  108. package/scss/components/_tag.scss +142 -0
  109. package/scss/components/_tile-button.scss +131 -0
  110. package/scss/components/_tile-grid-overlay.scss +132 -0
  111. package/scss/components/_tile-grid.scss +172 -0
  112. package/scss/components/_vignette.scss +65 -0
  113. package/scss/components/_wysiwyg.scss +94 -0
  114. package/scss/helpers/_color.scss +1 -0
  115. package/scss/helpers/_display.scss +2 -1
  116. package/scss/helpers/_index.scss +45 -22
  117. package/scss/helpers/_print.scss +20 -43
  118. package/scss/helpers/_typography.scss +3 -0
  119. package/scss/helpers/_units.scss +10 -13
  120. package/scss/helpers/_utilities.scss +5 -1
  121. package/scss/stylesheets/base-styles.scss +7 -0
  122. package/scss/stylesheets/component-styles.scss +7 -0
  123. package/scss/stylesheets/helper-styles.scss +7 -0
  124. package/types/events/index.d.ts +1 -1
  125. package/types/events/index.d.ts.map +1 -1
  126. package/types/index.d.ts +2 -2
  127. package/types/{helpers/css-breakpoint.d.ts → ui/breakpoints.d.ts} +3 -3
  128. package/types/ui/breakpoints.d.ts.map +1 -0
  129. package/types/ui/collapsible.d.ts +67 -0
  130. package/types/ui/collapsible.d.ts.map +1 -0
  131. package/types/ui/dialog.d.ts +42 -0
  132. package/types/ui/dialog.d.ts.map +1 -0
  133. package/types/ui/flipcard.d.ts +8 -1
  134. package/types/ui/flipcard.d.ts.map +1 -1
  135. package/types/ui/grid.d.ts +0 -11
  136. package/types/ui/grid.d.ts.map +1 -1
  137. package/types/ui/index.d.ts +23 -0
  138. package/types/ui/index.d.ts.map +1 -0
  139. package/types/ui/modal-builder.d.ts +54 -0
  140. package/types/ui/modal-builder.d.ts.map +1 -0
  141. package/types/ui/overflow-scroller-pager.d.ts +1 -1
  142. package/types/ui/overflow-scroller-pager.d.ts.map +1 -1
  143. package/types/ui/overflow-scroller.d.ts +3 -1
  144. package/types/ui/overflow-scroller.d.ts.map +1 -1
  145. package/types/ui/page.d.ts +5 -0
  146. package/types/ui/page.d.ts.map +1 -0
  147. package/types/ui/popover.d.ts +40 -0
  148. package/types/ui/popover.d.ts.map +1 -0
  149. package/types/ui/print-details.d.ts +10 -0
  150. package/types/ui/print-details.d.ts.map +1 -0
  151. package/types/ui/print.d.ts +10 -0
  152. package/types/ui/print.d.ts.map +1 -0
  153. package/types/ui/programmatic-modal.d.ts +19 -1
  154. package/types/ui/programmatic-modal.d.ts.map +1 -1
  155. package/types/ui/proxy-click.d.ts +18 -0
  156. package/types/ui/proxy-click.d.ts.map +1 -0
  157. package/types/ui/resizer.d.ts +1 -1
  158. package/types/ui/resizer.d.ts.map +1 -1
  159. package/types/ui/scroll-slider.d.ts +13 -0
  160. package/types/ui/scroll-slider.d.ts.map +1 -0
  161. package/types/ui/scrollpoint.d.ts +133 -0
  162. package/types/ui/scrollpoint.d.ts.map +1 -0
  163. package/types/ui/slider.d.ts +14 -2
  164. package/types/ui/slider.d.ts.map +1 -1
  165. package/types/ui/tabs.d.ts +22 -0
  166. package/types/ui/tabs.d.ts.map +1 -1
  167. package/types/ui/theme-toggle.d.ts +14 -0
  168. package/types/ui/theme-toggle.d.ts.map +1 -0
  169. package/types/ui/tooltip.d.ts +92 -10
  170. package/types/ui/tooltip.d.ts.map +1 -1
  171. package/types/utils/{logger.d.ts → class-logger.d.ts} +1 -1
  172. package/types/utils/class-logger.d.ts.map +1 -0
  173. package/types/utils/dom.d.ts +48 -0
  174. package/types/utils/dom.d.ts.map +1 -0
  175. package/types/utils/file-save.d.ts +64 -0
  176. package/types/utils/file-save.d.ts.map +1 -0
  177. package/types/utils/floating-ui.d.ts +19 -0
  178. package/types/utils/floating-ui.d.ts.map +1 -0
  179. package/types/utils/id.d.ts +10 -0
  180. package/types/utils/id.d.ts.map +1 -0
  181. package/types/utils/index.d.ts +9 -0
  182. package/types/utils/index.d.ts.map +1 -0
  183. package/types/utils/pause-youtube-video.d.ts.map +1 -0
  184. package/js/helpers/file-save.js +0 -52
  185. package/js/helpers/scrollbar-width-property.js +0 -14
  186. package/project.todo +0 -22
  187. package/scss/_calculate.scss +0 -64
  188. package/scss/_utility.scss +0 -12
  189. package/types/helpers/css-breakpoint.d.ts.map +0 -1
  190. package/types/helpers/file-save.d.ts +0 -17
  191. package/types/helpers/file-save.d.ts.map +0 -1
  192. package/types/helpers/node-data-manager.d.ts +0 -45
  193. package/types/helpers/node-data-manager.d.ts.map +0 -1
  194. package/types/helpers/pause-youtube-video.d.ts.map +0 -1
  195. package/types/helpers/scrollbar-width-property.d.ts +0 -11
  196. package/types/helpers/scrollbar-width-property.d.ts.map +0 -1
  197. package/types/ui/modals.d.ts +0 -27
  198. package/types/ui/modals.d.ts.map +0 -1
  199. package/types/utils/logger.d.ts.map +0 -1
  200. package/vite.config.js +0 -36
  201. /package/{js/deprecated → deprecated/js}/doc-ready.js +0 -0
  202. /package/{js/deprecated → deprecated/js}/jquery-prototypes.js +0 -0
  203. /package/{js/deprecated → deprecated/js}/mini-collapsible-popper-positioning.js +0 -0
  204. /package/{js/deprecated → deprecated/js}/mini-collapsible.js +0 -0
  205. /package/{js/helpers → deprecated/js}/node-data-manager.js +0 -0
  206. /package/{js/deprecated → deprecated/js}/script-loader.js +0 -0
  207. /package/{js/deprecated → deprecated/js}/waypoints/README.md +0 -0
  208. /package/{js/deprecated → deprecated/js}/waypoints/anchor-menu.js +0 -0
  209. /package/{js/deprecated → deprecated/js}/waypoints/element-waypoint.js +0 -0
  210. /package/{js/deprecated → deprecated/js}/waypoints/examples/page-link-menu.md +0 -0
  211. /package/{js/deprecated → deprecated/js}/waypoints/state-in-attribute.js +0 -0
  212. /package/types/{helpers → utils}/pause-youtube-video.d.ts +0 -0
package/js/ui/slider.js CHANGED
@@ -28,10 +28,14 @@
28
28
  // * Will Change use
29
29
 
30
30
  import maintain from 'ally.js/maintain/_maintain';
31
- import { log, logError, logWarning } from "../utils/logger.js";
32
31
  import { hasRequiredProps } from '@ulu/utils/object.js';
33
32
  import { trimWhitespace } from "@ulu/utils/string.js";
34
33
  import { debounce } from "@ulu/utils/performance.js";
34
+ import { log, logError, logWarning } from "../utils/class-logger.js";
35
+ import { getDatasetOptionalJson } from "../utils/dom.js";
36
+ import { createPager } from "./overflow-scroller-pager.js";
37
+ import { getName } from "../events/index.js";
38
+
35
39
  const debugMode = false; // Global dev debug
36
40
  const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
37
41
  const eventOnce = { once: true };
@@ -50,6 +54,61 @@ const requiredElements = [
50
54
  "track",
51
55
  "slides"
52
56
  ];
57
+
58
+ /**
59
+ * Default data attributes
60
+ */
61
+ export const attrs = {
62
+ init: "data-ulu-slider-init",
63
+ slider: "data-ulu-slider",
64
+ track: "data-ulu-slider-track",
65
+ trackContainer: "data-ulu-slider-track-container",
66
+ controls: "data-ulu-slider-control-context"
67
+ };
68
+
69
+ // Utils for selecting things based on attributes
70
+ const attrSelector = key => `[${ attrs[key] }]`;
71
+ const attrSelectorInitial = key => `${ attrSelector(key) }:not([${ attrs.init }])`;
72
+
73
+ const defaults = {
74
+ amount: createPager()
75
+ };
76
+
77
+ const instances = [];
78
+
79
+ export function init() {
80
+ document.addEventListener(getName("pageModified"), setup);
81
+ setup();
82
+ }
83
+
84
+ export function setup() {
85
+ const builders = document.querySelectorAll(attrSelectorInitial("slider"));
86
+ builders.forEach(setupSlider);
87
+ }
88
+
89
+ export function setupSlider(container) {
90
+ container.setAttribute(attrs.init, "");
91
+ const options = getDatasetOptionalJson(container, "uluScrollSlider");
92
+ const config = Object.assign({}, defaults, options);
93
+ const elements = {
94
+ container,
95
+ track: container.querySelector("[data-ulu-slider-track]"),
96
+ trackContainer: container.querySelector("[data-ulu-slider-track-container]"),
97
+ controlContext: container.querySelector("[data-ulu-slider-control-context]"),
98
+ slides: container.querySelectorAll("[data-ulu-slider-slide]")
99
+ };
100
+ // Add in any global settings
101
+ // Object.assign(config, {
102
+ // callbacks: {}
103
+ // });
104
+ // /
105
+ // This was added because there was an issue on the new windows, need to test this
106
+ // config.transitionFade = true;
107
+ if (elements.slides.length) {
108
+ instances.push(new Slider(elements, config, false));
109
+ }
110
+ }
111
+
53
112
  export class Slider {
54
113
  static instances = [];
55
114
  static defaults = {
@@ -61,7 +120,9 @@ export class Slider {
61
120
  transitionDuration: 700,
62
121
  transitionDurationExit: 400,
63
122
  transitionTimingFunction: "ease-in-out",
64
-
123
+ buttonClasses: ["Slider__control-icon", "button", "button--icon"],
124
+ iconClassesPrevious: ["css-icon", "css-icon--angle-left"],
125
+ iconClassesNext: ["css-icon", "css-icon--angle-right"]
65
126
  // transition: true
66
127
  }
67
128
  // constructor(container, title, trackContainer, track, slides, config, debug = false) {
@@ -136,7 +197,7 @@ export class Slider {
136
197
  * @param {number} duration Duration to wait for complete
137
198
  * @param {Function} beginTransition Css changes to begin/start transtion
138
199
  */
139
- ensureTranstionEnds(element, duration, beginTransition) {
200
+ ensureTransitionEnds(element, duration, beginTransition) {
140
201
  return new Promise(resolve => {
141
202
  const tid = {};
142
203
  // If the transition has started remove the fallback for start
@@ -179,7 +240,7 @@ export class Slider {
179
240
  const set = () => track.style.transform = `translateX(-${ x }px)`;
180
241
  // tell brwoser we're about to animate
181
242
  track.style.willChange = "transform";
182
- return this.ensureTranstionEnds(track, duration, set).then(() => {
243
+ return this.ensureTransitionEnds(track, duration, set).then(() => {
183
244
  // Remove to avoid any issues with optimization
184
245
  track.style.willChange = "auto";
185
246
  });
@@ -205,7 +266,7 @@ export class Slider {
205
266
  const { options } = this;
206
267
  const { element } = slide;
207
268
  const duration = visible ? options.transitionDuration : options.transitionDurationExit;
208
- return this.ensureTranstionEnds(element, duration, () => {
269
+ return this.ensureTransitionEnds(element, duration, () => {
209
270
  element.style.opacity = visible ? "1" : "0";
210
271
  });
211
272
  }
@@ -393,6 +454,7 @@ export class Slider {
393
454
  const button = document.createElement("button");
394
455
  button.classList.add(this.getClass("control-button"));
395
456
  button.classList.add(this.getClass(`control-button--${ action }`));
457
+ button.classList.add(...this.options.buttonClasses);
396
458
  button.setAttribute("data-slider-control", action);
397
459
  button.setAttribute("type", "button");
398
460
  button.innerHTML = this.getControlContent(action);
@@ -452,20 +514,20 @@ export class Slider {
452
514
  button.addEventListener("click", this.goto.bind(this, index));
453
515
  return button;
454
516
  }
517
+ // change to css-icon
455
518
  getControlContent(action) {
519
+ const classes = this.options[action === "next" ? "iconClassesNext" : "iconClassesPrevious"];
456
520
  return `
457
521
  <span class="hidden-visually">${ action }</span>
458
- <span aria-hidden="true">${ action === 'next' ? '→' : '←' }</span>
522
+ <span class="${ classes.join(' ') }" aria-hidden="true"></span>
459
523
  `;
460
524
  }
461
525
  getNavContent(number) {
462
- return `<span class="hidden-visually">Item</span> <span>${ number }</span>`;
526
+ return `<span class="hidden-visually">Item ${ number }</span>`;
463
527
  }
464
528
  emit(name, args) {
465
529
  if (this.options.events[name]) {
466
530
  this.options.events[name].apply(this, args);
467
531
  }
468
532
  }
469
- }
470
-
471
- export default Slider;
533
+ }
package/js/ui/tabs.js CHANGED
@@ -2,91 +2,124 @@
2
2
  * @module ui/tabs
3
3
  */
4
4
 
5
- // * - Attribute to enable behavior 'data-site-tablist'
6
- // * - Options can be passed via JSON in data attribute
7
- // * - openByUrlHash | Optionally add "openByUrlHash" to have the
8
- // * script open a tab and focus it on page load (and set it in history
9
- // * as they navigate)
10
-
11
-
12
5
  import AriaTablist from "aria-tablist";
13
6
 
14
- const errorHeader = "Site Tablist [data-site-tablist] error:";
7
+ const initAttr = "data-ulu-tablist-init";
8
+ const errorHeader = "[data-ulu-tablist] error:";
15
9
 
16
10
  /**
17
11
  * Array of current tab instances (exported if you need to interact with them)
18
12
  * @type {Array}
19
13
  */
20
- const instances = [];
21
-
22
- // Init all instances currently in page
23
- window.addEventListener("load", () => {
24
- initWithin(document);
25
- // Run this on page load, optionally exported for use when page is running
26
- instances.forEach(openByCurrentHash);
27
- });
28
-
29
- // Initialize when page updates/changes
30
- document.addEventListener("pageModified", e => initWithin(e.target));
14
+ export const instances = [];
31
15
 
32
- function initWithin(context) {
33
- if (!context) return;
34
- const tablists = context.querySelectorAll("[data-site-tablist]");
35
- tablists.forEach(init);
16
+ /**
17
+ * Init all instances currently in document
18
+ * @param {Object} options Options to serve as defaults
19
+ */
20
+ export function init(options = {}) {
21
+ const initial = () => {
22
+ initWithin(document, options);
23
+ // Run this on page load, optionally exported for use when page is running
24
+ instances.forEach(openByCurrentHash);
25
+ };
26
+
27
+ if (document.readyState === "complete") {
28
+ initial();
29
+ } else {
30
+ window.addEventListener("load", initial);
31
+ }
32
+ // Initialize when page updates/changes
33
+ document.addEventListener("pageModified", e => initWithin(e.target, options));
36
34
  }
37
- function openByCurrentHash({ options, ariaTablist }) {
38
- if (options.openByUrlHash) {
39
- const { hash } = window.location;
40
- if (hash && hash.length > 1) {
41
- const possibleId = hash.substring(1);
42
- ariaTablist.tabs.forEach(tab => {
43
- if (possibleId === tab.id) {
44
- ariaTablist.open(tab);
45
- }
46
- });
47
- }
35
+
36
+ /**
37
+ * Init all tabs within a certain context
38
+ * @param {Node} context Element to init within
39
+ * @param {Object} options Options to serve as defaults
40
+ */
41
+ export function initWithin(context, options = {}) {
42
+ if (!context) {
43
+ console.warn("Missing context to initWithin, skipping init of tabs");
44
+ return;
48
45
  }
46
+ const tablists = context.querySelectorAll(`[data-ulu-tablist]:not([${ initAttr }])`);
47
+ tablists.forEach(element => setup(element, options));
49
48
  }
50
- function init(element) {
51
- let options = {};
52
- const config = {};
49
+
50
+ /**
51
+ *
52
+ * @param {Node} element Tablist Element
53
+ * @param {Node} options Options to set as defaults (can be overridden by element dataset options)
54
+ * @return {Object} Instance object
55
+ */
56
+ export function setup(element, options = {}) {
57
+ let elementOptions = {};
53
58
 
54
- if (element.dataset.siteTablist) {
59
+ if (element.dataset.uluTablist) {
55
60
  try {
56
- options = JSON.parse(element.dataset.siteTablist);
61
+ elementOptions = JSON.parse(element.dataset.uluTablist);
57
62
  } catch(e) {
58
63
  console.error(errorHeader, "(JSON Parse for options)", element);
59
64
  }
60
65
  }
61
- if (options.vertical) {
66
+
67
+ const config = Object.assign({}, options, elementOptions);
68
+
69
+ if (config.vertical) {
62
70
  config.allArrows = true;
63
71
  }
64
72
 
65
73
  // Need to render the markup before checking height
66
74
  // - used to wait until images had loaded
67
- ready();
75
+ const instance = { element, options };
76
+ instance.ariaTablist = AriaTablist(element, {
77
+ onOpen(...args) {
78
+ args.unshift(instance);
79
+ handleOpen.apply(null, args);
80
+ },
81
+ ...config
82
+ });
83
+ instances.push(instance);
68
84
 
69
- if (options.equalHeights) {
85
+ if (config.equalHeights) {
70
86
  setHeights(element);
71
87
  }
88
+
89
+ element.setAttribute(initAttr, "");
72
90
 
73
- function ready() {
74
- const instance = { element, options };
75
- instance.ariaTablist = AriaTablist(element, {
76
- onOpen(...args) {
77
- args.unshift(instance);
78
- handleOpen.apply(null, args);
79
- },
80
- ...config
81
- });
82
- instances.push(instance);
91
+ return instance;
92
+ }
93
+
94
+ /**
95
+ * Opens the a tabpanel if it matches current hash (used in initial init)
96
+ */
97
+ function openByCurrentHash({ options, ariaTablist }) {
98
+ if (options.openByUrlHash) {
99
+ const { hash } = window.location;
100
+ if (hash && hash.length > 1) {
101
+ const possibleId = hash.substring(1);
102
+ ariaTablist.tabs.forEach(tab => {
103
+ if (possibleId === tab.id) {
104
+ ariaTablist.open(tab);
105
+ }
106
+ });
107
+ }
83
108
  }
84
109
  }
110
+
111
+ /**
112
+ * Responsible for setting hash on open if option is set
113
+ */
85
114
  function handleOpen({ options }, panel, tab) {
86
115
  if (options.openByUrlHash && window.history) {
87
116
  window.history.replaceState(null, "", `#${ tab.id }`);
88
117
  }
89
118
  }
119
+
120
+ /**
121
+ * Responsible for creating equal height tab panels
122
+ */
90
123
  function setHeights(element) {
91
124
  const tabs = [ ...element.children];
92
125
  const panels = tabs.map(n => document.querySelector(`[aria-labelledby="${ n.id }"]`));
@@ -105,9 +138,3 @@ function setHeights(element) {
105
138
  panels.forEach(panel => panel.style.minHeight = `${ max }px`);
106
139
  });
107
140
  }
108
-
109
- export { instances };
110
-
111
-
112
-
113
-
@@ -0,0 +1,129 @@
1
+ // Progressive Enhancement turns select elements into accessible autocomplete fields
2
+
3
+ // import { getName } from "@ulu/frontend/js/events/index.js";
4
+ import { getName } from "../events/index.js";
5
+
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
+ 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",
23
+ };
24
+
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";
29
+
30
+ /**
31
+ * Initialize everything in document
32
+ * - This will only initialize elements once, it is safe to call on page changes
33
+ */
34
+ 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();
41
+ }
42
+
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);
54
+ });
55
+ });
56
+ // Initial icon setup
57
+ changeIcons();
58
+ }
59
+
60
+ /**
61
+ *
62
+ * @param {Element} body Sets up initial theme on load based on user preference.
63
+ */
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
+ }
74
+ }
75
+
76
+ /**
77
+ *
78
+ * @param {Element} body Changes the theme of the body.
79
+ */
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;
89
+ }
90
+ body.classList.remove(oldTheme);
91
+ body.classList.add(newTheme);
92
+ localStorage.setItem("data-theme", newTheme);
93
+ currentTheme = newTheme;
94
+ changeIcons();
95
+ }
96
+
97
+ /**
98
+ *
99
+ * @param {Element} body Used to check for theme.
100
+ * @param {Element} context Used to find the icons.
101
+ */
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;
109
+ }
110
+ });
111
+ }
112
+
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
+ }
120
+ }
121
+
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
+ }
129
+ }