@ulu/frontend 0.0.23 → 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 (211) hide show
  1. package/deprecated/js/drupal-programmatic-modal.js +91 -0
  2. package/{js/ui/modals.js → deprecated/js/micromodal-modals.js} +41 -67
  3. package/dist/ulu-frontend.min.css +1 -1
  4. package/dist/ulu-frontend.min.js +70 -1
  5. package/index.js +6 -1
  6. package/js/events/index.js +58 -7
  7. package/js/index.js +3 -7
  8. package/js/{helpers/css-breakpoint.js → ui/breakpoints.js} +9 -11
  9. package/js/ui/collapsible.js +195 -0
  10. package/js/ui/dialog.js +157 -0
  11. package/js/ui/dialog.todo +37 -0
  12. package/js/ui/flipcard.js +55 -11
  13. package/js/ui/grid.js +2 -47
  14. package/js/ui/index.js +21 -0
  15. package/js/ui/modal-builder.js +197 -0
  16. package/js/ui/overflow-scroller-pager.js +1 -1
  17. package/js/ui/overflow-scroller.js +8 -5
  18. package/js/ui/page.js +14 -0
  19. package/js/ui/popover.js +135 -0
  20. package/js/ui/print-details.js +44 -0
  21. package/js/ui/print.js +67 -0
  22. package/js/ui/programmatic-modal.js +79 -81
  23. package/js/ui/proxy-click.js +80 -0
  24. package/js/ui/resizer.js +3 -3
  25. package/js/ui/scroll-slider.js +56 -0
  26. package/js/ui/scrollpoint.js +300 -0
  27. package/js/ui/slider.js +72 -10
  28. package/js/ui/tabs.js +85 -58
  29. package/js/ui/theme-toggle.js +129 -0
  30. package/js/ui/tooltip.js +268 -67
  31. package/js/utils/{logger.js → class-logger.js} +6 -5
  32. package/js/utils/dom.js +122 -0
  33. package/js/utils/file-save.js +67 -0
  34. package/js/utils/floating-ui.js +83 -0
  35. package/js/utils/id.js +22 -0
  36. package/js/utils/index.js +7 -0
  37. package/js/{helpers → utils}/pause-youtube-video.js +1 -1
  38. package/package.json +32 -11
  39. package/resources/drupal/twig-macros/accordion.twig +99 -0
  40. package/resources/drupal/twig-macros/dropdown.twig +44 -0
  41. package/resources/drupal/twig-macros/flipcard.twig +69 -0
  42. package/resources/drupal/twig-macros/image.twig +30 -0
  43. package/resources/drupal/twig-macros/layout.twig +338 -0
  44. package/resources/drupal/twig-macros/slider.twig +214 -0
  45. package/resources/drupal/twig-macros/tabs.twig +84 -0
  46. package/scss/README.md +13 -1
  47. package/scss/_breakpoint.scss +69 -26
  48. package/scss/_button.scss +148 -57
  49. package/scss/_color.scss +46 -28
  50. package/scss/_cssvar.scss +103 -12
  51. package/scss/_element.scss +84 -67
  52. package/scss/_index.scss +0 -3
  53. package/scss/_layout.scss +57 -26
  54. package/scss/_path.scss +2 -2
  55. package/scss/_selector.scss +20 -11
  56. package/scss/_typography.scss +115 -82
  57. package/scss/_units.scss +14 -13
  58. package/scss/_utils.scss +280 -18
  59. package/scss/base/_color.scss +2 -1
  60. package/scss/base/_elements.scss +61 -35
  61. package/scss/base/_index.scss +60 -23
  62. package/scss/base/_keyframes.scss +115 -16
  63. package/scss/base/_layout.scss +10 -6
  64. package/scss/base/_normalize.scss +6 -122
  65. package/scss/base/_print.scss +49 -0
  66. package/scss/base/_root.scss +28 -0
  67. package/scss/base/_typography.scss +4 -1
  68. package/scss/components/_accordion.scss +217 -0
  69. package/scss/components/_adaptive-spacing.scss +148 -0
  70. package/scss/components/_badge.scss +17 -14
  71. package/scss/components/_button-verbose.scss +138 -0
  72. package/scss/components/_button.scss +9 -4
  73. package/scss/components/_callout.scss +175 -0
  74. package/scss/components/_captioned-figure.scss +173 -0
  75. package/scss/components/_card-grid.scss +75 -0
  76. package/scss/components/_card.scss +420 -0
  77. package/scss/components/_css-icon.scss +433 -0
  78. package/scss/{_grid.scss → components/_data-grid.scss} +100 -68
  79. package/scss/components/_data-table.scss +180 -0
  80. package/scss/components/_fill-context.scss +20 -22
  81. package/scss/components/_flipcard-grid.scss +66 -0
  82. package/scss/components/_flipcard.scss +304 -0
  83. package/scss/components/_form-theme.scss +633 -0
  84. package/scss/components/_hero.scss +183 -0
  85. package/scss/components/_horizontal-rule.scss +51 -0
  86. package/scss/components/_image-grid.scss +71 -0
  87. package/scss/components/_index.scss +276 -38
  88. package/scss/components/_links.scss +1 -1
  89. package/scss/components/_list-lines.scss +14 -3
  90. package/scss/components/_list-ordered.scss +3 -1
  91. package/scss/components/_list-unordered.scss +3 -1
  92. package/scss/components/_menu-stack.scss +245 -0
  93. package/scss/components/_modal.scss +495 -0
  94. package/scss/components/_nav-strip.scss +148 -0
  95. package/scss/components/_overlay-section.scss +122 -0
  96. package/scss/components/_pager.scss +168 -0
  97. package/scss/components/_placeholder-block.scss +121 -0
  98. package/scss/components/_popover.scss +263 -0
  99. package/scss/components/_pull-quote.scss +111 -0
  100. package/scss/components/_ratio-box.scss +64 -0
  101. package/scss/components/_rule.scss +12 -9
  102. package/scss/components/_scroll-slider.scss +204 -0
  103. package/scss/components/_skip-link.scss +92 -0
  104. package/scss/components/_slider.scss +241 -0
  105. package/scss/components/_spoke-spinner.scss +193 -0
  106. package/scss/components/_tabs.scss +179 -0
  107. package/scss/components/_tag.scss +142 -0
  108. package/scss/components/_tile-button.scss +131 -0
  109. package/scss/components/_tile-grid-overlay.scss +132 -0
  110. package/scss/components/_tile-grid.scss +172 -0
  111. package/scss/components/_vignette.scss +65 -0
  112. package/scss/components/_wysiwyg.scss +94 -0
  113. package/scss/helpers/_color.scss +1 -0
  114. package/scss/helpers/_display.scss +2 -1
  115. package/scss/helpers/_index.scss +45 -22
  116. package/scss/helpers/_print.scss +20 -43
  117. package/scss/helpers/_typography.scss +3 -0
  118. package/scss/helpers/_units.scss +10 -13
  119. package/scss/helpers/_utilities.scss +5 -1
  120. package/scss/stylesheets/base-styles.scss +7 -0
  121. package/scss/stylesheets/component-styles.scss +7 -0
  122. package/scss/stylesheets/helper-styles.scss +7 -0
  123. package/types/events/index.d.ts +1 -1
  124. package/types/events/index.d.ts.map +1 -1
  125. package/types/index.d.ts +2 -2
  126. package/types/{helpers/css-breakpoint.d.ts → ui/breakpoints.d.ts} +3 -3
  127. package/types/ui/breakpoints.d.ts.map +1 -0
  128. package/types/ui/collapsible.d.ts +67 -0
  129. package/types/ui/collapsible.d.ts.map +1 -0
  130. package/types/ui/dialog.d.ts +42 -0
  131. package/types/ui/dialog.d.ts.map +1 -0
  132. package/types/ui/flipcard.d.ts +8 -1
  133. package/types/ui/flipcard.d.ts.map +1 -1
  134. package/types/ui/grid.d.ts +0 -11
  135. package/types/ui/grid.d.ts.map +1 -1
  136. package/types/ui/index.d.ts +23 -0
  137. package/types/ui/index.d.ts.map +1 -0
  138. package/types/ui/modal-builder.d.ts +54 -0
  139. package/types/ui/modal-builder.d.ts.map +1 -0
  140. package/types/ui/overflow-scroller-pager.d.ts +1 -1
  141. package/types/ui/overflow-scroller-pager.d.ts.map +1 -1
  142. package/types/ui/overflow-scroller.d.ts +3 -1
  143. package/types/ui/overflow-scroller.d.ts.map +1 -1
  144. package/types/ui/page.d.ts +5 -0
  145. package/types/ui/page.d.ts.map +1 -0
  146. package/types/ui/popover.d.ts +40 -0
  147. package/types/ui/popover.d.ts.map +1 -0
  148. package/types/ui/print-details.d.ts +10 -0
  149. package/types/ui/print-details.d.ts.map +1 -0
  150. package/types/ui/print.d.ts +10 -0
  151. package/types/ui/print.d.ts.map +1 -0
  152. package/types/ui/programmatic-modal.d.ts +19 -1
  153. package/types/ui/programmatic-modal.d.ts.map +1 -1
  154. package/types/ui/proxy-click.d.ts +18 -0
  155. package/types/ui/proxy-click.d.ts.map +1 -0
  156. package/types/ui/resizer.d.ts +1 -1
  157. package/types/ui/resizer.d.ts.map +1 -1
  158. package/types/ui/scroll-slider.d.ts +13 -0
  159. package/types/ui/scroll-slider.d.ts.map +1 -0
  160. package/types/ui/scrollpoint.d.ts +133 -0
  161. package/types/ui/scrollpoint.d.ts.map +1 -0
  162. package/types/ui/slider.d.ts +14 -2
  163. package/types/ui/slider.d.ts.map +1 -1
  164. package/types/ui/tabs.d.ts +22 -0
  165. package/types/ui/tabs.d.ts.map +1 -1
  166. package/types/ui/theme-toggle.d.ts +14 -0
  167. package/types/ui/theme-toggle.d.ts.map +1 -0
  168. package/types/ui/tooltip.d.ts +92 -10
  169. package/types/ui/tooltip.d.ts.map +1 -1
  170. package/types/utils/{logger.d.ts → class-logger.d.ts} +1 -1
  171. package/types/utils/class-logger.d.ts.map +1 -0
  172. package/types/utils/dom.d.ts +48 -0
  173. package/types/utils/dom.d.ts.map +1 -0
  174. package/types/utils/file-save.d.ts +64 -0
  175. package/types/utils/file-save.d.ts.map +1 -0
  176. package/types/utils/floating-ui.d.ts +19 -0
  177. package/types/utils/floating-ui.d.ts.map +1 -0
  178. package/types/utils/id.d.ts +10 -0
  179. package/types/utils/id.d.ts.map +1 -0
  180. package/types/utils/index.d.ts +9 -0
  181. package/types/utils/index.d.ts.map +1 -0
  182. package/types/utils/pause-youtube-video.d.ts.map +1 -0
  183. package/js/helpers/file-save.js +0 -52
  184. package/js/helpers/scrollbar-width-property.js +0 -14
  185. package/project.todo +0 -22
  186. package/scss/_calculate.scss +0 -64
  187. package/scss/_utility.scss +0 -12
  188. package/types/helpers/css-breakpoint.d.ts.map +0 -1
  189. package/types/helpers/file-save.d.ts +0 -17
  190. package/types/helpers/file-save.d.ts.map +0 -1
  191. package/types/helpers/node-data-manager.d.ts +0 -45
  192. package/types/helpers/node-data-manager.d.ts.map +0 -1
  193. package/types/helpers/pause-youtube-video.d.ts.map +0 -1
  194. package/types/helpers/scrollbar-width-property.d.ts +0 -11
  195. package/types/helpers/scrollbar-width-property.d.ts.map +0 -1
  196. package/types/ui/modals.d.ts +0 -27
  197. package/types/ui/modals.d.ts.map +0 -1
  198. package/types/utils/logger.d.ts.map +0 -1
  199. package/vite.config.js +0 -36
  200. /package/{js/deprecated → deprecated/js}/doc-ready.js +0 -0
  201. /package/{js/deprecated → deprecated/js}/jquery-prototypes.js +0 -0
  202. /package/{js/deprecated → deprecated/js}/mini-collapsible-popper-positioning.js +0 -0
  203. /package/{js/deprecated → deprecated/js}/mini-collapsible.js +0 -0
  204. /package/{js/helpers → deprecated/js}/node-data-manager.js +0 -0
  205. /package/{js/deprecated → deprecated/js}/script-loader.js +0 -0
  206. /package/{js/deprecated → deprecated/js}/waypoints/README.md +0 -0
  207. /package/{js/deprecated → deprecated/js}/waypoints/anchor-menu.js +0 -0
  208. /package/{js/deprecated → deprecated/js}/waypoints/element-waypoint.js +0 -0
  209. /package/{js/deprecated → deprecated/js}/waypoints/examples/page-link-menu.md +0 -0
  210. /package/{js/deprecated → deprecated/js}/waypoints/state-in-attribute.js +0 -0
  211. /package/types/{helpers → utils}/pause-youtube-video.d.ts +0 -0
package/js/ui/grid.js CHANGED
@@ -2,6 +2,7 @@
2
2
  * @module ui/grid
3
3
  */
4
4
 
5
+ import { setPositionClasses } from "../utils/dom.js";
5
6
  import { getName } from "../events/index.js";
6
7
 
7
8
  /**
@@ -22,50 +23,4 @@ export function init(selector = "[data-grid]", classes) {
22
23
  */
23
24
  export function setup(selector, classes) {
24
25
  document.querySelectorAll(selector).forEach(element => setPositionClasses(element, classes || undefined));
25
- }
26
-
27
- /**
28
- * Sets up the positonal classes that would come from the equal
29
- * height module. Needs to be rerun by user when layout changes
30
- * or new instances are added to the screen
31
- * - Used for gutter crops
32
- * - Used for rule placement
33
- * - **Devs** Remember that default classes should match sass defaults
34
- * @param {Node} parent The grid parent <data-grid="">
35
- * @param {Object} classes Override the default equal heights classes
36
- */
37
- export function setPositionClasses(parent, classes = {
38
- columnFirst: 'position-column-first',
39
- columnLast: 'position-column-last',
40
- rowFirst: 'position-row-first',
41
- rowLast: 'position-row-last'
42
- }) {
43
- const children = [...parent.children];
44
- const rows = [];
45
- let lastY;
46
- // Check element against last
47
- // If they don't match it's a new row create a new array
48
- // Then push into the last array in the rows array
49
- children.forEach((child) => {
50
- const y = child.getBoundingClientRect().y;
51
- if (lastY !== y) rows.push([]);
52
- rows[rows.length - 1].push(child);
53
- lastY = y;
54
- child.classList.remove(...Object.values(classes)); // Remove previously set classes
55
- });
56
- // Apply Classes
57
- rows.forEach((row, index) => {
58
- if (index === 0)
59
- row.forEach(child => child.classList.add(classes.rowFirst));
60
- if (index == rows.length - 1)
61
- row.forEach(child => child.classList.add(classes.rowLast));
62
-
63
- row.forEach((child, childIndex) => {
64
- if (childIndex === 0)
65
- child.classList.add(classes.columnFirst);
66
- if (childIndex == row.length - 1)
67
- child.classList.add(classes.columnLast);
68
- });
69
- });
70
-
71
- }
26
+ }
package/js/ui/index.js ADDED
@@ -0,0 +1,21 @@
1
+ export * as page from "./page.js";
2
+ export * as breakpoints from "./breakpoints.js";
3
+ export * as collapsible from "./collapsible.js";
4
+ export * as modalBuilder from "./modal-builder.js";
5
+ export * as dialog from "./dialog.js";
6
+ export * as flipcard from "./flipcard.js";
7
+ export * as grid from "./grid.js";
8
+ export * as index from "./index.js";
9
+ export * as overflowScrollerPager from "./overflow-scroller-pager.js";
10
+ export * as overflowScroller from "./overflow-scroller.js";
11
+ export * as popover from "./popover.js";
12
+ export * as tooltip from "./tooltip.js";
13
+ export * as resizer from "./resizer.js";
14
+ export * as slider from "./slider.js";
15
+ export * as tabs from "./tabs.js";
16
+ export * as proxyClick from "./proxy-click.js";
17
+ export * as scrollpoint from "./scrollpoint.js";
18
+ export * as print from "./print.js";
19
+ export * as printDetails from "./print-details.js";
20
+ export * as scrollSlider from "./scroll-slider.js";
21
+ export * as themeToggle from "./theme-toggle.js";
@@ -0,0 +1,197 @@
1
+ /**
2
+ * @module ui/modal-builder
3
+ */
4
+
5
+ // Note this needs to be run before dialogs are initialized!
6
+
7
+ import { getName } from "../events/index.js";
8
+ import { createElementFromHtml } from "@ulu/utils/browser/dom.js";
9
+ import { Resizer } from "./resizer.js";
10
+ import { getDatasetJson } from "../utils/dom.js";
11
+ import { defaults as dialogDefaults, attrs as dialogAttrs } from "./dialog.js";
12
+
13
+ const attrs = {
14
+ builder: "data-ulu-modal-builder",
15
+ body: "data-ulu-modal-builder-body",
16
+ resizer: "data-ulu-modal-builder-resizer"
17
+ };
18
+
19
+ const attrSelector = key => `[${ attrs[key] }]`;
20
+
21
+ /**
22
+ * Default builder options (extends dialog defaults, watch name collisions)
23
+ * - Decided to extend defaults so the interface in HTML is singular
24
+ * - This is sometimes easier to template (merging and serializing options
25
+ * in twig for example)
26
+ */
27
+ export const defaults = {
28
+ title: null,
29
+ titleIcon: null,
30
+ nonModal: false,
31
+ documentEnd: true,
32
+ allowResize: false,
33
+ position: "center",
34
+ bodyFills: false,
35
+ noBackdrop: false,
36
+ size: "default",
37
+ print: false,
38
+ noMinHeight: false,
39
+ class: "",
40
+ classCloseIcon: "css-icon css-icon--close",
41
+ classResizerIcon: "css-icon css-icon--drag",
42
+ debug: false,
43
+ templateCloseIcon(config) {
44
+ return `<span class="modal__close-icon ${ config.classCloseIcon }" aria-hidden="true"></span>`;
45
+ },
46
+ templateResizerIcon(config) {
47
+ return `<span class="modal__resizer-icon ${ config.classResizerIcon }" aria-hidden="true"></span>`;
48
+ },
49
+ /**
50
+ * Default modal template
51
+ * @param {String} id ID for new modal
52
+ * @param {Object} config Resolved options
53
+ * @returns {String} Markup for modal
54
+ */
55
+ template(id, config) {
56
+ const classes = [
57
+ "modal",
58
+ `modal--${ config.position }`,
59
+ `modal--${ config.size }`,
60
+ `modal--${ config.allowResize ? "resize" : "no-resize" }`,
61
+ ...(!config.title ? ["modal--no-header"] : []),
62
+ ...(config.bodyFills ? ["modal--body-fills"] : []),
63
+ ...(config.noBackdrop ? ["modal--no-backdrop"] : []),
64
+ ...(config.noMinHeight ? ["modal--no-min-height"] : [] ),
65
+ ...(config.class ? [config.class] : []),
66
+ ];
67
+ return `
68
+ <dialog id="${ id }" class="${ classes.join(" ") }">
69
+ ${ config.title ? `
70
+ <header class="modal__header">
71
+ <h2 class="modal__title">
72
+ ${ config.titleIcon ?
73
+ `<span class="modal__title-icon ${ config.titleIcon }" aria-hidden="true"></span>` : ""
74
+ }
75
+ <span class="modal__title-text">${ config.title }</span>
76
+ </h2>
77
+ <button class="modal__close" aria-label="Close modal" ${ dialogAttrs.close } autofocus>
78
+ ${ config.templateCloseIcon(config) }
79
+ </button>
80
+ </header>
81
+ ` : "" }
82
+ <div class="modal__body" ${ attrs.body }></div>
83
+ ${ config.hasResizer ?
84
+ `<div class="modal__resizer" ${ attrs.resizer }>
85
+ ${ config.templateResizerIcon(config) }
86
+ </div>` : ""
87
+ }
88
+ </div>
89
+ `;
90
+ }
91
+ };
92
+
93
+ // Current default objects (user can override these)
94
+ let currentDefaults = { ...defaults };
95
+
96
+ /**
97
+ * @param {Object} options Change options used as default for dialogs, can then be overridden by data attribute settings on element
98
+ */
99
+ export function setDefaults(options) {
100
+ currentDefaults = Object.assign({}, currentDefaults, options);
101
+ }
102
+
103
+ /**
104
+ * Initialize everything in document
105
+ * - This will only initialize elements once, it is safe to call on page changes
106
+ */
107
+ export function init() {
108
+ document.addEventListener(getName("pageModified"), setup);
109
+ setup();
110
+ }
111
+
112
+ /**
113
+ * Query and setup all builder
114
+ */
115
+ export function setup() {
116
+ const builders = document.querySelectorAll(attrSelector("builder"));
117
+ builders.forEach(setupBuilder);
118
+ }
119
+
120
+ /**
121
+ * Build a dialog for the given content
122
+ * @param {Node} element
123
+ */
124
+ export function setupBuilder(element) {
125
+ const options = getDatasetJson(element, "uluModalBuilder");
126
+ element.removeAttribute(attrs.builder);
127
+ buildModal(element, options);
128
+ }
129
+
130
+ /**
131
+ *
132
+ * @param {Node} content Content element of the dialog (what is inserted into the body)
133
+ * @param {Object} options Options for built dialog (see defaults)
134
+ */
135
+ export function buildModal(content, options) {
136
+
137
+ const config = Object.assign({}, currentDefaults, options);
138
+
139
+ if (config.position !== "center" && config.allowResize) {
140
+ config.hasResizer = true;
141
+ }
142
+ if (config.debug) {
143
+ console.log(config, content);
144
+ }
145
+ if (!content.id) {
146
+ throw new Error("Missing ID on modal");
147
+ }
148
+
149
+ const markup = config.template(content.id, config);
150
+ const modal = createElementFromHtml(markup.trim());
151
+ const selectChild = key => modal.querySelector(attrSelector(key));
152
+ const body = selectChild("body");
153
+ const resizer = selectChild("resizer");
154
+ const dialogOptions = separateDialogOptions(config);
155
+
156
+ // Replace content with new dialog, and then insert the content into the new dialogs body
157
+ content.removeAttribute("id");
158
+ content.removeAttribute("hidden");
159
+ content.removeAttribute(attrs.builder);
160
+ content.parentNode.replaceChild(modal, content);
161
+ body.appendChild(content);
162
+
163
+ // Add dialog options for other scripts
164
+ modal.setAttribute(dialogAttrs.dialog, JSON.stringify(dialogOptions));
165
+
166
+ if (config.hasResizer) {
167
+ new Resizer(modal, resizer, {
168
+ fromLeft: config.position === "right"
169
+ });
170
+ }
171
+
172
+ if (config.print) {
173
+ let printClone;
174
+ document.addEventListener(getName("beforePrint"), () => {
175
+ printClone = content.cloneNode(true);
176
+ modal.after(printClone);
177
+ });
178
+ document.addEventListener(getName("afterPrint"), () => {
179
+ printClone.remove();
180
+ });
181
+ }
182
+ return { modal };
183
+ }
184
+
185
+ /**
186
+ * Returns JSON string to embed in data-ulu-dialog for dialog handling
187
+ * @param {Object} config Config object to pull dialog specific settings from
188
+ * @returns {Object}
189
+ */
190
+ function separateDialogOptions(config) {
191
+ return Object.keys(dialogDefaults).reduce((acc, key) => {
192
+ if (key in config) {
193
+ acc[key] = config[key];
194
+ }
195
+ return acc;
196
+ }, {});
197
+ }
@@ -14,7 +14,7 @@
14
14
  * @return {Function} A function to be used in overflow scrollers "amount" configuration property
15
15
  */
16
16
 
17
- export default function create() {
17
+ export function createPager() {
18
18
  return function pager(instance, dir) {
19
19
  const isNext = dir === "next";
20
20
  const { track } = instance.elements;
@@ -11,7 +11,7 @@
11
11
  *
12
12
  */
13
13
  import { hasRequiredProps } from '@ulu/utils/object.js';
14
- import { logError } from "../utils/logger.js";
14
+ import { logError } from "../utils/class-logger.js";
15
15
  const requiredElements = [
16
16
  "track",
17
17
  "controls"
@@ -25,6 +25,9 @@ export class OverflowScroller {
25
25
  offsetStart: 100,
26
26
  offsetEnd: 100,
27
27
  amount: "auto",
28
+ buttonClasses: ["button", "button--icon"],
29
+ iconClassesPrevious: ["css-icon", "css-icon--angle-left"],
30
+ iconClassesNext: ["css-icon", "css-icon--angle-right"]
28
31
  }
29
32
  constructor(elements, config) {
30
33
  this.options = Object.assign({}, OverflowScroller.defaults, config);
@@ -78,14 +81,16 @@ export class OverflowScroller {
78
81
  const button = document.createElement("button");
79
82
  button.classList.add(this.getClass("control-button"));
80
83
  button.classList.add(this.getClass(`control-button--${ action }`));
84
+ button.classList.add(...this.options.buttonClasses);
81
85
  button.setAttribute("type", "button");
82
86
  button.innerHTML = this.getControlContent(action);
83
87
  return button;
84
88
  }
85
89
  getControlContent(action) {
90
+ const classes = this.options[action === "next" ? "iconClassesNext" : "iconClassesPrevious"];
86
91
  return `
87
92
  <span class="hidden-visually">${ action }</span>
88
- <span aria-hidden="true">${ action === 'next' ? '→' : '←' }</span>
93
+ <span class="${ classes.join(' ') }" aria-hidden="true"></span>
89
94
  `;
90
95
  }
91
96
  onScroll(event) {
@@ -158,6 +163,4 @@ export class OverflowScroller {
158
163
  const { namespace } = this.options;
159
164
  return `${ namespace }__${ child }`;
160
165
  }
161
- }
162
-
163
- export default OverflowScroller;
166
+ }
package/js/ui/page.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * General/Document Related
3
+ * - Add custom properties for scrollbar
4
+ * @module ui/page
5
+ */
6
+
7
+ import { addScrollbarProperty } from "../utils/dom";
8
+
9
+ /**
10
+ * Initialize page module
11
+ */
12
+ export function init() {
13
+ addScrollbarProperty();
14
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * @module ui/popover
3
+ */
4
+
5
+ import { getName } from "../events/index.js";
6
+ import { createFloatingUi } from "../utils/floating-ui.js";
7
+ import { Collapsible } from "./collapsible.js";
8
+
9
+ /**
10
+ * Array of current instances
11
+ */
12
+ export const instances = new WeakMap;
13
+
14
+ const logError = (...msgs) => console.error("@ulu (popovers):", ...msgs);
15
+
16
+ const attrs = {
17
+ trigger: "data-ulu-popover-trigger",
18
+ content: "data-ulu-popover-content",
19
+ arrow: "data-ulu-popover-arrow",
20
+ anchor: "data-ulu-popover-trigger-anchor",
21
+ };
22
+ const attrSelector = key => `[${ attrs[key] }]`;
23
+
24
+ // This modules collapsible defaults
25
+ const collapsibleDefaults = {
26
+ clickOutsideCloses: true,
27
+ escapeCloses: true
28
+ };
29
+
30
+ /**
31
+ * Initialize default popover
32
+ */
33
+ export function init() {
34
+ document.addEventListener(getName("pageModified"), setup);
35
+ setup();
36
+ }
37
+
38
+ /**
39
+ * Query all popovers on current page and set them up
40
+ * - Use this manually if needed
41
+ * - Won't setup a popover more than once
42
+ */
43
+ export function setup() {
44
+ const triggers = document.querySelectorAll(attrSelector("trigger"));
45
+ // Only triggers we don't have instances for
46
+ const resolved = Array.from(triggers)
47
+ .filter(trigger => !instances.has(trigger))
48
+ .map(resolve)
49
+ .filter(v => v);
50
+
51
+ resolved.forEach(({ elements, options, floatingOptions }) => {
52
+ instances.set(elements.trigger, new Popover(elements, options, floatingOptions));
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Find the popover's elements
58
+ */
59
+ export function resolve(trigger) {
60
+ const raw = trigger.dataset.uluPopoverTrigger;
61
+ const options = raw?.length ? JSON.parse(raw) : {};
62
+ const content = getContentByTrigger(trigger);
63
+ const elements = {
64
+ trigger,
65
+ content,
66
+ anchor: trigger.querySelector(attrSelector("anchor")) || trigger,
67
+ contentArrow: content.querySelector(attrSelector("arrow"))
68
+ };
69
+ const floatingOptions = options.floating || {};
70
+ delete options.floating;
71
+ if (content) {
72
+ return { elements, options, floatingOptions };
73
+ } else {
74
+ logError("Unable to make popover for", trigger);
75
+ return false;
76
+ }
77
+ }
78
+
79
+ // - grab from aria-controls (optional)
80
+ // - or from direct sibling
81
+ // - lastly check the parent container for any children that have the attribute
82
+ export function getContentByTrigger(trigger) {
83
+ let content;
84
+ const ariaControls = trigger.getAttribute("aria-controls");
85
+
86
+ if (ariaControls) {
87
+ content = document.getElementById(ariaControls);
88
+ } else if (trigger?.nextElementSibling?.hasAttribute(attrs.content)) {
89
+ content = trigger.nextElementSibling;
90
+ // @todo - Consider removing this (non standard, users like this should be using aria-controls)
91
+ } else {
92
+ const children = Array.from(trigger.parentNode.children);
93
+ const triggerIndex = children.findIndex(c => c === trigger);
94
+ const childrenAfter = children.slice(triggerIndex);
95
+ content = childrenAfter.find(child => child.matches(attrSelector("content")));
96
+ }
97
+ if (!content) {
98
+ logError("Unable to resolve 'content' element for popover", trigger);
99
+ }
100
+ return content;
101
+ }
102
+
103
+
104
+ /**
105
+ * Class that extends Collapsible adding floating-ui for popover behavior
106
+ */
107
+ export class Popover extends Collapsible {
108
+ constructor(elements, config, floatingOptions) {
109
+ const options = Object.assign({}, collapsibleDefaults, config);
110
+ super(elements, options);
111
+ this.floatingOptions = floatingOptions || {};
112
+ }
113
+ setState(isOpen, event) {
114
+ super.setState(isOpen, event);
115
+ this.destroyFloatingInstance();
116
+ if (isOpen) {
117
+ this.createFloatingInstance();
118
+ }
119
+ }
120
+ destroy() {
121
+ super.destroy();
122
+ this.destroyFloatingInstance();
123
+ }
124
+ createFloatingInstance() {
125
+ const { content, anchor, contentArrow } = this.elements;
126
+ const floatingElements = { trigger: anchor, contentArrow, content };
127
+ this.floatingCleanup = createFloatingUi(floatingElements, this.floatingOptions);
128
+ }
129
+ destroyFloatingInstance() {
130
+ if (this.floatingCleanup) {
131
+ this.floatingCleanup();
132
+ this.floatingCleanup = null;
133
+ }
134
+ }
135
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @module ui/print-details
3
+ */
4
+
5
+ import { getName } from "../events/index.js";
6
+
7
+ /**
8
+ * Default data attributes
9
+ */
10
+ export const attrs = {
11
+ opened: "data-ulu-print-details-opened",
12
+ };
13
+
14
+ const attrSelector = key => `[${ attrs[key] }]`;
15
+
16
+ const defaults = {
17
+ selector: "details:not([open])"
18
+ };
19
+
20
+ /**
21
+ * Initialize details print
22
+ * - will open details before print
23
+ * - will return to previous state after
24
+ */
25
+ export function init(options) {
26
+ const config = Object.assign({}, defaults, options);
27
+
28
+ // Add flag and open each details that's closed
29
+ document.addEventListener(getName("beforePrint"), () => {
30
+ document.querySelectorAll(config.selector).forEach(details => {
31
+ if (!details.open) {
32
+ details.setAttribute(attrs.opened, true);
33
+ details.open = true;
34
+ }
35
+ });
36
+ });
37
+ // When print ends find all flagged and close
38
+ document.addEventListener(getName("afterPrint"), () => {
39
+ document.querySelectorAll(attrSelector("opened")).forEach(details => {
40
+ details.removeAttribute(attrs.opened);
41
+ details.open = false;
42
+ });
43
+ });
44
+ }
package/js/ui/print.js ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @module ui/print
3
+ */
4
+
5
+ import { getName } from "../events/index.js";
6
+ import { getDatasetOptionalJson, getElement } from "../utils/dom.js";
7
+ import { printElement } from "@ulu/utils/browser/print.js";
8
+
9
+ export const attrs = {
10
+ trigger: "data-ulu-print",
11
+ init: "data-ulu-print-init",
12
+ };
13
+
14
+ const attrSelector = key => `[${ attrs[key] }]`;
15
+ const attrSelectorInitial = key => `${ attrSelector(key) }:not([${ attrs.init }])`;
16
+ const queryAllInitial = key => document.querySelectorAll(attrSelectorInitial(key));
17
+
18
+ /**
19
+ * Default options
20
+ */
21
+ const defaults = {
22
+ /**
23
+ * Print element/selector
24
+ */
25
+ element: null,
26
+ };
27
+
28
+ /**
29
+ * Initialize everything in document
30
+ * - This will only initialize elements once, it is safe to call on page changes
31
+ */
32
+ export function init() {
33
+ document.addEventListener(getName("pageModified"), setup);
34
+ setup();
35
+ }
36
+
37
+ /**
38
+ * Setup all triggers currently on the page
39
+ */
40
+ function setup() {
41
+ const triggers = queryAllInitial("trigger");
42
+ triggers.forEach(trigger => {
43
+ const options = getDatasetOptionalJson(trigger, "uluPrint");
44
+ setupTrigger(trigger, options);
45
+ });
46
+ }
47
+
48
+ /**
49
+ * Setup a single trigger (can be used manually without attr if needed)
50
+ */
51
+ function setupTrigger(trigger, options) {
52
+ const config = Object.assign({}, defaults, options);
53
+ trigger.addEventListener("click", (event) => {
54
+ // Option to print a specific element
55
+ if (config.element) {
56
+ const element = getElement(config.element);
57
+ if (element) {
58
+ printElement(element);
59
+ } else {
60
+ console.error("Unable to find element to print", trigger, config);
61
+ }
62
+ // Default behavior print window
63
+ } else {
64
+ window.print();
65
+ }
66
+ });
67
+ }