@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.
- package/deprecated/js/drupal-programmatic-modal.js +91 -0
- package/{js/ui/modals.js → deprecated/js/micromodal-modals.js} +41 -67
- package/dist/ulu-frontend.min.css +1 -1
- package/dist/ulu-frontend.min.js +70 -1
- package/index.js +6 -1
- package/js/events/index.js +58 -7
- package/js/index.js +3 -7
- package/js/{helpers/css-breakpoint.js → ui/breakpoints.js} +9 -11
- package/js/ui/collapsible.js +195 -0
- package/js/ui/dialog.js +157 -0
- package/js/ui/dialog.todo +37 -0
- package/js/ui/flipcard.js +55 -11
- package/js/ui/grid.js +2 -47
- package/js/ui/index.js +21 -0
- package/js/ui/modal-builder.js +197 -0
- package/js/ui/overflow-scroller-pager.js +1 -1
- package/js/ui/overflow-scroller.js +8 -5
- package/js/ui/page.js +14 -0
- package/js/ui/popover.js +135 -0
- package/js/ui/print-details.js +44 -0
- package/js/ui/print.js +67 -0
- package/js/ui/programmatic-modal.js +79 -81
- package/js/ui/proxy-click.js +80 -0
- package/js/ui/resizer.js +3 -3
- package/js/ui/scroll-slider.js +56 -0
- package/js/ui/scrollpoint.js +300 -0
- package/js/ui/slider.js +72 -10
- package/js/ui/tabs.js +85 -58
- package/js/ui/theme-toggle.js +129 -0
- package/js/ui/tooltip.js +268 -67
- package/js/utils/{logger.js → class-logger.js} +6 -5
- package/js/utils/dom.js +122 -0
- package/js/utils/file-save.js +67 -0
- package/js/utils/floating-ui.js +83 -0
- package/js/utils/id.js +22 -0
- package/js/utils/index.js +7 -0
- package/js/{helpers → utils}/pause-youtube-video.js +1 -1
- package/package.json +32 -11
- package/resources/drupal/twig-macros/accordion.twig +99 -0
- package/resources/drupal/twig-macros/dropdown.twig +44 -0
- package/resources/drupal/twig-macros/flipcard.twig +69 -0
- package/resources/drupal/twig-macros/image.twig +30 -0
- package/resources/drupal/twig-macros/layout.twig +338 -0
- package/resources/drupal/twig-macros/slider.twig +214 -0
- package/resources/drupal/twig-macros/tabs.twig +84 -0
- package/scss/README.md +13 -1
- package/scss/_breakpoint.scss +69 -26
- package/scss/_button.scss +148 -57
- package/scss/_color.scss +46 -28
- package/scss/_cssvar.scss +103 -12
- package/scss/_element.scss +84 -67
- package/scss/_index.scss +0 -3
- package/scss/_layout.scss +57 -26
- package/scss/_path.scss +2 -2
- package/scss/_selector.scss +20 -11
- package/scss/_typography.scss +115 -82
- package/scss/_units.scss +14 -13
- package/scss/_utils.scss +280 -18
- package/scss/base/_color.scss +2 -1
- package/scss/base/_elements.scss +61 -35
- package/scss/base/_index.scss +60 -23
- package/scss/base/_keyframes.scss +115 -16
- package/scss/base/_layout.scss +10 -6
- package/scss/base/_normalize.scss +6 -122
- package/scss/base/_print.scss +49 -0
- package/scss/base/_root.scss +28 -0
- package/scss/base/_typography.scss +4 -1
- package/scss/components/_accordion.scss +217 -0
- package/scss/components/_adaptive-spacing.scss +148 -0
- package/scss/components/_badge.scss +17 -14
- package/scss/components/_button-verbose.scss +138 -0
- package/scss/components/_button.scss +9 -4
- package/scss/components/_callout.scss +175 -0
- package/scss/components/_captioned-figure.scss +173 -0
- package/scss/components/_card-grid.scss +75 -0
- package/scss/components/_card.scss +420 -0
- package/scss/components/_css-icon.scss +433 -0
- package/scss/{_grid.scss → components/_data-grid.scss} +100 -68
- package/scss/components/_data-table.scss +180 -0
- package/scss/components/_fill-context.scss +20 -22
- package/scss/components/_flipcard-grid.scss +66 -0
- package/scss/components/_flipcard.scss +304 -0
- package/scss/components/_form-theme.scss +633 -0
- package/scss/components/_hero.scss +183 -0
- package/scss/components/_horizontal-rule.scss +51 -0
- package/scss/components/_image-grid.scss +71 -0
- package/scss/components/_index.scss +276 -38
- package/scss/components/_links.scss +1 -1
- package/scss/components/_list-lines.scss +14 -3
- package/scss/components/_list-ordered.scss +3 -1
- package/scss/components/_list-unordered.scss +3 -1
- package/scss/components/_menu-stack.scss +245 -0
- package/scss/components/_modal.scss +495 -0
- package/scss/components/_nav-strip.scss +148 -0
- package/scss/components/_overlay-section.scss +122 -0
- package/scss/components/_pager.scss +168 -0
- package/scss/components/_placeholder-block.scss +121 -0
- package/scss/components/_popover.scss +263 -0
- package/scss/components/_pull-quote.scss +111 -0
- package/scss/components/_ratio-box.scss +64 -0
- package/scss/components/_rule.scss +12 -9
- package/scss/components/_scroll-slider.scss +204 -0
- package/scss/components/_skip-link.scss +92 -0
- package/scss/components/_slider.scss +241 -0
- package/scss/components/_spoke-spinner.scss +193 -0
- package/scss/components/_tabs.scss +179 -0
- package/scss/components/_tag.scss +142 -0
- package/scss/components/_tile-button.scss +131 -0
- package/scss/components/_tile-grid-overlay.scss +132 -0
- package/scss/components/_tile-grid.scss +172 -0
- package/scss/components/_vignette.scss +65 -0
- package/scss/components/_wysiwyg.scss +94 -0
- package/scss/helpers/_color.scss +1 -0
- package/scss/helpers/_display.scss +2 -1
- package/scss/helpers/_index.scss +45 -22
- package/scss/helpers/_print.scss +20 -43
- package/scss/helpers/_typography.scss +3 -0
- package/scss/helpers/_units.scss +10 -13
- package/scss/helpers/_utilities.scss +5 -1
- package/scss/stylesheets/base-styles.scss +7 -0
- package/scss/stylesheets/component-styles.scss +7 -0
- package/scss/stylesheets/helper-styles.scss +7 -0
- package/types/events/index.d.ts +1 -1
- package/types/events/index.d.ts.map +1 -1
- package/types/index.d.ts +2 -2
- package/types/{helpers/css-breakpoint.d.ts → ui/breakpoints.d.ts} +3 -3
- package/types/ui/breakpoints.d.ts.map +1 -0
- package/types/ui/collapsible.d.ts +67 -0
- package/types/ui/collapsible.d.ts.map +1 -0
- package/types/ui/dialog.d.ts +42 -0
- package/types/ui/dialog.d.ts.map +1 -0
- package/types/ui/flipcard.d.ts +8 -1
- package/types/ui/flipcard.d.ts.map +1 -1
- package/types/ui/grid.d.ts +0 -11
- package/types/ui/grid.d.ts.map +1 -1
- package/types/ui/index.d.ts +23 -0
- package/types/ui/index.d.ts.map +1 -0
- package/types/ui/modal-builder.d.ts +54 -0
- package/types/ui/modal-builder.d.ts.map +1 -0
- package/types/ui/overflow-scroller-pager.d.ts +1 -1
- package/types/ui/overflow-scroller-pager.d.ts.map +1 -1
- package/types/ui/overflow-scroller.d.ts +3 -1
- package/types/ui/overflow-scroller.d.ts.map +1 -1
- package/types/ui/page.d.ts +5 -0
- package/types/ui/page.d.ts.map +1 -0
- package/types/ui/popover.d.ts +40 -0
- package/types/ui/popover.d.ts.map +1 -0
- package/types/ui/print-details.d.ts +10 -0
- package/types/ui/print-details.d.ts.map +1 -0
- package/types/ui/print.d.ts +10 -0
- package/types/ui/print.d.ts.map +1 -0
- package/types/ui/programmatic-modal.d.ts +19 -1
- package/types/ui/programmatic-modal.d.ts.map +1 -1
- package/types/ui/proxy-click.d.ts +18 -0
- package/types/ui/proxy-click.d.ts.map +1 -0
- package/types/ui/resizer.d.ts +1 -1
- package/types/ui/resizer.d.ts.map +1 -1
- package/types/ui/scroll-slider.d.ts +13 -0
- package/types/ui/scroll-slider.d.ts.map +1 -0
- package/types/ui/scrollpoint.d.ts +133 -0
- package/types/ui/scrollpoint.d.ts.map +1 -0
- package/types/ui/slider.d.ts +14 -2
- package/types/ui/slider.d.ts.map +1 -1
- package/types/ui/tabs.d.ts +22 -0
- package/types/ui/tabs.d.ts.map +1 -1
- package/types/ui/theme-toggle.d.ts +14 -0
- package/types/ui/theme-toggle.d.ts.map +1 -0
- package/types/ui/tooltip.d.ts +92 -10
- package/types/ui/tooltip.d.ts.map +1 -1
- package/types/utils/{logger.d.ts → class-logger.d.ts} +1 -1
- package/types/utils/class-logger.d.ts.map +1 -0
- package/types/utils/dom.d.ts +48 -0
- package/types/utils/dom.d.ts.map +1 -0
- package/types/utils/file-save.d.ts +64 -0
- package/types/utils/file-save.d.ts.map +1 -0
- package/types/utils/floating-ui.d.ts +19 -0
- package/types/utils/floating-ui.d.ts.map +1 -0
- package/types/utils/id.d.ts +10 -0
- package/types/utils/id.d.ts.map +1 -0
- package/types/utils/index.d.ts +9 -0
- package/types/utils/index.d.ts.map +1 -0
- package/types/utils/pause-youtube-video.d.ts.map +1 -0
- package/js/helpers/file-save.js +0 -52
- package/js/helpers/scrollbar-width-property.js +0 -14
- package/project.todo +0 -22
- package/scss/_calculate.scss +0 -64
- package/scss/_utility.scss +0 -12
- package/types/helpers/css-breakpoint.d.ts.map +0 -1
- package/types/helpers/file-save.d.ts +0 -17
- package/types/helpers/file-save.d.ts.map +0 -1
- package/types/helpers/node-data-manager.d.ts +0 -45
- package/types/helpers/node-data-manager.d.ts.map +0 -1
- package/types/helpers/pause-youtube-video.d.ts.map +0 -1
- package/types/helpers/scrollbar-width-property.d.ts +0 -11
- package/types/helpers/scrollbar-width-property.d.ts.map +0 -1
- package/types/ui/modals.d.ts +0 -27
- package/types/ui/modals.d.ts.map +0 -1
- package/types/utils/logger.d.ts.map +0 -1
- package/vite.config.js +0 -36
- /package/{js/deprecated → deprecated/js}/doc-ready.js +0 -0
- /package/{js/deprecated → deprecated/js}/jquery-prototypes.js +0 -0
- /package/{js/deprecated → deprecated/js}/mini-collapsible-popper-positioning.js +0 -0
- /package/{js/deprecated → deprecated/js}/mini-collapsible.js +0 -0
- /package/{js/helpers → deprecated/js}/node-data-manager.js +0 -0
- /package/{js/deprecated → deprecated/js}/script-loader.js +0 -0
- /package/{js/deprecated → deprecated/js}/waypoints/README.md +0 -0
- /package/{js/deprecated → deprecated/js}/waypoints/anchor-menu.js +0 -0
- /package/{js/deprecated → deprecated/js}/waypoints/element-waypoint.js +0 -0
- /package/{js/deprecated → deprecated/js}/waypoints/examples/page-link-menu.md +0 -0
- /package/{js/deprecated → deprecated/js}/waypoints/state-in-attribute.js +0 -0
- /package/types/{helpers → utils}/pause-youtube-video.d.ts +0 -0
package/js/events/index.js
CHANGED
|
@@ -1,22 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module events
|
|
3
3
|
*/
|
|
4
|
+
|
|
4
5
|
import { debounce } from "@ulu/utils/performance.js";
|
|
6
|
+
import { isBrowser } from "@ulu/utils/browser/dom.js";
|
|
7
|
+
|
|
8
|
+
// Setup global document events
|
|
9
|
+
if (isBrowser()) {
|
|
10
|
+
initResize();
|
|
11
|
+
initPrint();
|
|
12
|
+
}
|
|
5
13
|
|
|
6
14
|
/**
|
|
7
15
|
* Event object - called on dispatch
|
|
8
16
|
*/
|
|
9
17
|
const events = {
|
|
18
|
+
/**
|
|
19
|
+
* Event is dispatched when DOM in the page has changed, triggers updates from
|
|
20
|
+
* all modules listening for the change (init instances, etc)
|
|
21
|
+
* - Is triggered by modules that were responsible for modifying the page
|
|
22
|
+
*/
|
|
10
23
|
pageModified(context) {
|
|
11
|
-
context.dispatchEvent(new
|
|
24
|
+
context.dispatchEvent(new CustomEvent(getName("pageModified"), { bubbles: true }));
|
|
12
25
|
},
|
|
26
|
+
/**
|
|
27
|
+
* Event called when page is resized
|
|
28
|
+
*/
|
|
13
29
|
pageResized(context) {
|
|
14
|
-
context.dispatchEvent(new
|
|
30
|
+
context.dispatchEvent(new CustomEvent(getName("pageResized"), { bubbles: true }));
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* Event dispatched before page print begins (teardown/restructure/hide things)
|
|
34
|
+
*/
|
|
35
|
+
beforePrint(context) {
|
|
36
|
+
context.dispatchEvent(new CustomEvent(getName("beforePrint"), { bubbles: true }));
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* Event dispatched after page print (cleanup)
|
|
40
|
+
*/
|
|
41
|
+
afterPrint(context) {
|
|
42
|
+
context.dispatchEvent(new CustomEvent(getName("afterPrint"), { bubbles: true }));
|
|
15
43
|
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Add global document events
|
|
19
|
-
window.addEventListener('resize', debounce(() => dispatch("pageResized", document), 250));
|
|
44
|
+
};
|
|
20
45
|
|
|
21
46
|
/**
|
|
22
47
|
* Triggers one of our custom events
|
|
@@ -36,10 +61,36 @@ export function dispatch(type, context) {
|
|
|
36
61
|
}
|
|
37
62
|
|
|
38
63
|
/**
|
|
39
|
-
*
|
|
64
|
+
* Namespaced event
|
|
40
65
|
* @param {String} type Type of event to get the actual event name for
|
|
41
66
|
* @returns {String}
|
|
42
67
|
*/
|
|
43
68
|
export function getName(type) {
|
|
44
69
|
return "ulu:" + type;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Setup resize handler/dispatch
|
|
74
|
+
*/
|
|
75
|
+
function initResize() {
|
|
76
|
+
window.addEventListener("resize", debounce(() => dispatch("pageResized", document), 250));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Setup print listeners
|
|
81
|
+
* - Note: Tested with matchMedia but these events are more consistent
|
|
82
|
+
* Experimented with normalizing both events but they fired
|
|
83
|
+
* strangely, using any delay won't work (ie setTimeout / RAF)
|
|
84
|
+
* chrome pauses immediately javascript after the initial event.
|
|
85
|
+
* Reverting to a straightforward method for now. If this ends up
|
|
86
|
+
* needing something more robust we can work that out on this side
|
|
87
|
+
* and it won't change how the custom events file.
|
|
88
|
+
*/
|
|
89
|
+
function initPrint() {
|
|
90
|
+
window.addEventListener("beforeprint", () => {
|
|
91
|
+
dispatch("beforePrint", document);
|
|
92
|
+
});
|
|
93
|
+
window.addEventListener("afterprint", () => {
|
|
94
|
+
dispatch("afterPrint", document);
|
|
95
|
+
});
|
|
45
96
|
}
|
package/js/index.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
// =============================================================================
|
|
2
1
|
// Main Library Import
|
|
3
|
-
// =============================================================================
|
|
4
|
-
|
|
5
|
-
// Used for:
|
|
6
2
|
// - Allow users to access commonly needed items with at one point/file
|
|
3
|
+
// - This is the entry for the pre-built version
|
|
7
4
|
// - Could allow changing of the ulu/js file structure if needed
|
|
8
5
|
// - Will not include things that aren't used in every site (those would need to
|
|
9
6
|
// imported manually
|
|
10
7
|
|
|
11
|
-
export { CssBreakpoints } from "./helpers/css-breakpoint.js";
|
|
12
8
|
export * as events from "./events/index.js";
|
|
13
|
-
export * as
|
|
14
|
-
export * as
|
|
9
|
+
export * as ui from "./ui/index.js";
|
|
10
|
+
export * as utils from "./utils/index.js";
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @module
|
|
2
|
+
* @module ui/breakpoints
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
// Pass breakpoints from CSS to stylesheet, use this to attach behaviors on breakpoints
|
|
6
6
|
import { removeArrayElement } from "@ulu/utils/array.js";
|
|
7
7
|
import { getName } from "../events/index.js";
|
|
8
|
-
import { log, logError } from "../utils/logger.js";
|
|
8
|
+
import { log, logError } from "../utils/class-logger.js";
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
// Resize Handler to update breakpoints for all instances (Called after resize finished)
|
|
12
12
|
window.addEventListener(getName("pageResized"), () => {
|
|
13
|
-
|
|
13
|
+
BreakpointManager.instances.forEach(i => i.update());
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -18,10 +18,10 @@ window.addEventListener(getName("pageResized"), () => {
|
|
|
18
18
|
* Class that provides method for retrieving and acting on breakpoints passed
|
|
19
19
|
* from CSS (using element psuedo content prop)
|
|
20
20
|
*/
|
|
21
|
-
export class
|
|
21
|
+
export class BreakpointManager {
|
|
22
22
|
static instances = [];
|
|
23
23
|
static defaults = {
|
|
24
|
-
element: document
|
|
24
|
+
element: document?.documentElement,
|
|
25
25
|
valueFromPsuedo: false,
|
|
26
26
|
customProperty: "--breakpoint",
|
|
27
27
|
psuedoSelector: ':before',
|
|
@@ -37,7 +37,7 @@ export class CssBreakpoints {
|
|
|
37
37
|
* @param {String} config.psuedoSelector Change psuedo selector used to get the breakpoint from the psuedo's content property
|
|
38
38
|
*/
|
|
39
39
|
constructor(config) {
|
|
40
|
-
Object.assign(this,
|
|
40
|
+
Object.assign(this, BreakpointManager.defaults, config);
|
|
41
41
|
this.active = null;
|
|
42
42
|
this.previous = null;
|
|
43
43
|
this.activeIndex = null;
|
|
@@ -48,7 +48,7 @@ export class CssBreakpoints {
|
|
|
48
48
|
this.order.forEach(n => this.breakpoints[n] = new Breakpoint(n, this));
|
|
49
49
|
log(this, this);
|
|
50
50
|
this.update(); // Run for the first time, then whenever browser resizes
|
|
51
|
-
|
|
51
|
+
BreakpointManager.instances.push(this);
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* Add a callback for everytime a breakpoint changes
|
|
@@ -76,7 +76,7 @@ export class CssBreakpoints {
|
|
|
76
76
|
* Get breakpoint from a custom property
|
|
77
77
|
*/
|
|
78
78
|
getBreakpointInProperty() {
|
|
79
|
-
return getComputedStyle(this.element).getPropertyValue(this.customProperty);
|
|
79
|
+
return getComputedStyle(this.element).getPropertyValue(this.customProperty).trim();
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
82
|
* Get breakpoint from element (design note: user could override prototype)
|
|
@@ -281,6 +281,4 @@ class Breakpoint {
|
|
|
281
281
|
msg.unshift(`Breakpoint (${ this.name }):`);
|
|
282
282
|
this._manager.log.apply(this._manager, msg);
|
|
283
283
|
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
export default CssBreakpoints;
|
|
284
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ui/collapsible
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { getName as getEventName } from "../events/index.js";
|
|
6
|
+
import { log, logError } from "../utils/class-logger.js";
|
|
7
|
+
import { ensureId } from "../utils/id.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Class for accessible hide/show components
|
|
11
|
+
*/
|
|
12
|
+
export class Collapsible {
|
|
13
|
+
static defaults = {
|
|
14
|
+
clickOutsideCloses: false,
|
|
15
|
+
// oneOpenPerContext: false, // This should be another module that manages instances within a context (accordions)
|
|
16
|
+
// clickWithinCloses: false, // Not sure how this was used but seems like it should be separate
|
|
17
|
+
focusoutCloses: false,
|
|
18
|
+
escapeCloses: false,
|
|
19
|
+
/**
|
|
20
|
+
* The module won't attach the handlers (you need to do it yourself)
|
|
21
|
+
*/
|
|
22
|
+
selfManaged: false,
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* This collapsible starts in open state
|
|
26
|
+
*/
|
|
27
|
+
startOpen: false,
|
|
28
|
+
/**
|
|
29
|
+
* Open/active state class
|
|
30
|
+
*/
|
|
31
|
+
openClass: "is-active",
|
|
32
|
+
/**
|
|
33
|
+
* Output debug info
|
|
34
|
+
*/
|
|
35
|
+
debug: true,
|
|
36
|
+
onChange(_ctx) {
|
|
37
|
+
// do something
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* @param {Object} elements Elements object
|
|
42
|
+
* @param {Node} elements.trigger Trigger button/element that opens/closes collapsible
|
|
43
|
+
* @param {Node} elements.content The content element that the trigger reveals
|
|
44
|
+
* @param {Object} config Configuration options (see defaults)
|
|
45
|
+
* @returns {Object} Collapsible instance
|
|
46
|
+
*/
|
|
47
|
+
constructor(elements, config) {
|
|
48
|
+
const { trigger, content } = elements;
|
|
49
|
+
if (!trigger || !content) {
|
|
50
|
+
logError(this, "missing required elements (trigger or content)");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const options = Object.assign({}, Collapsible.defaults, config);
|
|
54
|
+
this.elements = elements;
|
|
55
|
+
this.options = options;
|
|
56
|
+
this.isOpen = false;
|
|
57
|
+
this.handlers = {}; // Spot to cache event handlers
|
|
58
|
+
ensureId(trigger);
|
|
59
|
+
ensureId(content);
|
|
60
|
+
this.debugLog(this, this);
|
|
61
|
+
if (!options.selfManaged) {
|
|
62
|
+
this.attachHandlers();
|
|
63
|
+
}
|
|
64
|
+
this.setup();
|
|
65
|
+
}
|
|
66
|
+
attachHandlers() {
|
|
67
|
+
const { trigger, content } = this.elements;
|
|
68
|
+
const { focusoutCloses } = this.options;
|
|
69
|
+
this.clickHandler = event => {
|
|
70
|
+
this.onClick(event);
|
|
71
|
+
}
|
|
72
|
+
this.focusoutHandler = (event) => {
|
|
73
|
+
if (focusoutCloses) {
|
|
74
|
+
this.close(event);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
trigger.addEventListener("click", this.clickHandler);
|
|
78
|
+
content.addEventListener("focusout", this.focusoutHandler);
|
|
79
|
+
}
|
|
80
|
+
removeHandlers() {
|
|
81
|
+
const { trigger, content } = this.elements;
|
|
82
|
+
trigger.removeEventListener("click", this.clickHandler);
|
|
83
|
+
content.removeEventListener("focusout", this.focusoutHandler);
|
|
84
|
+
}
|
|
85
|
+
onClick(event) {
|
|
86
|
+
this.toggle(event);
|
|
87
|
+
}
|
|
88
|
+
destroy() {
|
|
89
|
+
this.removeHandlers();
|
|
90
|
+
this.destroyTemporaryHandlers();
|
|
91
|
+
}
|
|
92
|
+
debugLog(...msgs) {
|
|
93
|
+
if (this.options.debug) {
|
|
94
|
+
log(this, ...msgs);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
setup() {
|
|
98
|
+
const { trigger, content } = this.elements;
|
|
99
|
+
const { startOpen } = this.options;
|
|
100
|
+
trigger.setAttribute("role", "button");
|
|
101
|
+
trigger.setAttribute("aria-controls", content.id);
|
|
102
|
+
content.setAttribute("aria-labelledby", trigger.id);
|
|
103
|
+
this.setState(startOpen);
|
|
104
|
+
}
|
|
105
|
+
createEvent(name, detail) {
|
|
106
|
+
return new CustomEvent(getEventName("collapsible:" + name), { detail });
|
|
107
|
+
}
|
|
108
|
+
setState(isOpen, event) {
|
|
109
|
+
const ctx = {
|
|
110
|
+
collapsible: this,
|
|
111
|
+
isOpen,
|
|
112
|
+
event
|
|
113
|
+
};
|
|
114
|
+
this.debugLog(this, "Set state", ctx);
|
|
115
|
+
const { trigger, content } = this.elements;
|
|
116
|
+
const { openClass } = this.options;
|
|
117
|
+
const setClass = el => el.classList[isOpen ? "add" : "remove"](openClass);
|
|
118
|
+
trigger.setAttribute("aria-expanded", isOpen ? "true" : "false");
|
|
119
|
+
setClass(trigger);
|
|
120
|
+
setClass(content);
|
|
121
|
+
this.isOpen = isOpen;
|
|
122
|
+
this.options.onChange(ctx);
|
|
123
|
+
trigger.dispatchEvent(this.createEvent("change", ctx));
|
|
124
|
+
if (isOpen) {
|
|
125
|
+
this.setupTemporaryHandlers();
|
|
126
|
+
} else {
|
|
127
|
+
this.destroyTemporaryHandlers();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Setup handlers needed for closing once open
|
|
132
|
+
*/
|
|
133
|
+
setupTemporaryHandlers() {
|
|
134
|
+
const { content, trigger } = this.elements;
|
|
135
|
+
const { clickOutsideCloses, escapeCloses } = this.options;
|
|
136
|
+
const onDocumentClick = (event) => {
|
|
137
|
+
const { target } = event;
|
|
138
|
+
const inTrigger = trigger.contains(target);
|
|
139
|
+
const inContent = content.contains(target);
|
|
140
|
+
if (clickOutsideCloses && !inTrigger && !inContent) {
|
|
141
|
+
this.close(event);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const onDocumentKeydown = (event) => {
|
|
145
|
+
if (escapeCloses && event.key === "Escape") {
|
|
146
|
+
this.close(event);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
document.addEventListener("click", onDocumentClick);
|
|
150
|
+
document.addEventListener("keydown", onDocumentKeydown);
|
|
151
|
+
this.handlers.onDocumentClick = onDocumentClick;
|
|
152
|
+
this.handlers.onDocumentKeydown = onDocumentKeydown;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Destroy handlers attached for closing once open
|
|
156
|
+
*/
|
|
157
|
+
destroyTemporaryHandlers() {
|
|
158
|
+
const { onDocumentClick, onDocumentKeydown } = this.handlers;
|
|
159
|
+
if (onDocumentClick) {
|
|
160
|
+
document.removeEventListener("click", onDocumentClick);
|
|
161
|
+
}
|
|
162
|
+
if (onDocumentClick) {
|
|
163
|
+
document.removeEventListener("keydown", onDocumentKeydown);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
open(event) {
|
|
167
|
+
this.setState(true, event);
|
|
168
|
+
}
|
|
169
|
+
close(event) {
|
|
170
|
+
this.setState(false, event);
|
|
171
|
+
}
|
|
172
|
+
toggle(event) {
|
|
173
|
+
this.setState(!this.isOpen, event);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// This is removed because I think it's not useful, users should keep references
|
|
177
|
+
// Static Methods for managing instances of this class
|
|
178
|
+
// static instances = [];
|
|
179
|
+
// /**
|
|
180
|
+
// * Get collapsible instance by trigger element
|
|
181
|
+
// * @param {Node|String} trigger Trigger node or trigger ID
|
|
182
|
+
// */
|
|
183
|
+
// static getInstance(trigger) {
|
|
184
|
+
// return Collapsible.instances.find(c => typeof trigger === "string" ?
|
|
185
|
+
// c.elements.trigger.id === trigger :
|
|
186
|
+
// c.elements.trigger === trigger
|
|
187
|
+
// );
|
|
188
|
+
// }
|
|
189
|
+
// static removeInstance(instance) {
|
|
190
|
+
// const index = Collapsible.instances.findIndex(c => c === instance);
|
|
191
|
+
// if (index > -1) {
|
|
192
|
+
// Collapsible.instances.splice(index, 1);
|
|
193
|
+
// }
|
|
194
|
+
// }
|
|
195
|
+
}
|
package/js/ui/dialog.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ui/dialog
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { getName } from "../events/index.js";
|
|
6
|
+
import { getDatasetJson, wasClickOutside } from "../utils/dom.js";
|
|
7
|
+
import { pauseVideos as pauseYoutubeVideos, prepVideos as prepYoutubeVideos } from "../utils/pause-youtube-video.js";
|
|
8
|
+
/**
|
|
9
|
+
* Default data attributes
|
|
10
|
+
*/
|
|
11
|
+
export const attrs = {
|
|
12
|
+
init: "data-ulu-dialog-init",
|
|
13
|
+
dialog: "data-ulu-dialog",
|
|
14
|
+
trigger: "data-ulu-dialog-trigger",
|
|
15
|
+
close: "data-ulu-dialog-close",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Utils for selecting things based on attributes
|
|
19
|
+
const attrSelector = key => `[${ attrs[key] }]`;
|
|
20
|
+
const attrSelectorInitial = key => `${ attrSelector(key) }:not([${ attrs.init }])`;
|
|
21
|
+
const queryAllInitial = key => document.querySelectorAll(attrSelectorInitial(key));
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Dialog Defaults
|
|
25
|
+
* - Can be overridden using data-attributes
|
|
26
|
+
*/
|
|
27
|
+
export const defaults = {
|
|
28
|
+
/**
|
|
29
|
+
* Use non-modal interface for dialog
|
|
30
|
+
*/
|
|
31
|
+
nonModal: false,
|
|
32
|
+
/**
|
|
33
|
+
* Move the dialog to the document end (hoist out of content)
|
|
34
|
+
* - helpful if dialogs are within editor body, etc
|
|
35
|
+
*/
|
|
36
|
+
documentEnd: false,
|
|
37
|
+
/**
|
|
38
|
+
* Requires styling that reduces any padding/border on dialog
|
|
39
|
+
*/
|
|
40
|
+
clickOutsideCloses: true,
|
|
41
|
+
/**
|
|
42
|
+
* Whether or not to pause videos when dialog closes (currently just youtube and native)
|
|
43
|
+
*/
|
|
44
|
+
pauseVideos: true,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// Current default objects (user can override these)
|
|
49
|
+
let currentDefaults = { ...defaults };
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {Object} options Change options used as default for dialogs, can then be overriden by data attribute settings on element
|
|
53
|
+
*/
|
|
54
|
+
export function setDefaults(options) {
|
|
55
|
+
currentDefaults = Object.assign({}, currentDefaults, options);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Initialize everything in document
|
|
60
|
+
* - This will only initialize elements once, it is safe to call on page changes
|
|
61
|
+
*/
|
|
62
|
+
export function init() {
|
|
63
|
+
document.addEventListener(getName("pageModified"), setup);
|
|
64
|
+
setup();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Setup dialogs and triggers
|
|
69
|
+
*/
|
|
70
|
+
export function setup() {
|
|
71
|
+
// Then setup all dialogs (including those that were built)
|
|
72
|
+
const dialogs = queryAllInitial("dialog");
|
|
73
|
+
dialogs.forEach(setupDialog);
|
|
74
|
+
|
|
75
|
+
const triggers = queryAllInitial("trigger");
|
|
76
|
+
triggers.forEach(setupTrigger);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Setup click handlers on a trigger
|
|
81
|
+
* @param {Node} trigger
|
|
82
|
+
*/
|
|
83
|
+
export function setupTrigger(trigger) {
|
|
84
|
+
trigger.addEventListener("click", handleTrigger);
|
|
85
|
+
trigger.setAttribute(attrs.init, "");
|
|
86
|
+
|
|
87
|
+
function handleTrigger() {
|
|
88
|
+
const id = trigger.dataset.uluDialogTrigger;
|
|
89
|
+
const dialog = document.getElementById(id);
|
|
90
|
+
if (!dialog) {
|
|
91
|
+
console.error("Could not locate dialog (id)", id);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (dialog?.tagName?.toLowerCase() !== "dialog") {
|
|
95
|
+
console.error("Attempted to trigger non <dialog> element. Did you mean to use modal builder?" );
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const options = getDialogOptions(dialog);
|
|
99
|
+
dialog[options.nonModal ? "show" : "showModal"]();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Setup click handlers for a dialog
|
|
105
|
+
* @param {Node} dialog
|
|
106
|
+
*/
|
|
107
|
+
export function setupDialog(dialog) {
|
|
108
|
+
const options = getDialogOptions(dialog);
|
|
109
|
+
dialog.addEventListener("click", handleClicks);
|
|
110
|
+
dialog.setAttribute(attrs.init, "");
|
|
111
|
+
if (options.documentEnd) {
|
|
112
|
+
document.body.appendChild(dialog);
|
|
113
|
+
}
|
|
114
|
+
if (options.pauseVideos) {
|
|
115
|
+
prepVideos(dialog);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function handleClicks(event) {
|
|
119
|
+
const { target } = event;
|
|
120
|
+
const closeFromButton = target.closest("[data-ulu-dialog-close]");
|
|
121
|
+
let closeFromOutside = options.clickOutsideCloses &&
|
|
122
|
+
target === dialog &&
|
|
123
|
+
wasClickOutside(dialog, event);
|
|
124
|
+
if (closeFromOutside || closeFromButton) {
|
|
125
|
+
if (options.pauseVideos) {
|
|
126
|
+
pauseVideos(dialog);
|
|
127
|
+
}
|
|
128
|
+
dialog.close();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* For a given dialog, get it's options (from data attribute)
|
|
135
|
+
* @param {Node} dialog
|
|
136
|
+
* @returns {Object}
|
|
137
|
+
*/
|
|
138
|
+
export function getDialogOptions(dialog) {
|
|
139
|
+
const options = getDatasetJson(dialog, "uluDialog");
|
|
140
|
+
return Object.assign({}, currentDefaults, options);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Pause native and youtube videos for a given dialog
|
|
145
|
+
*/
|
|
146
|
+
function prepVideos(dialog) {
|
|
147
|
+
prepYoutubeVideos(dialog);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Prep videos to be paused for a given dialog
|
|
151
|
+
*/
|
|
152
|
+
function pauseVideos(dialog) {
|
|
153
|
+
pauseYoutubeVideos(dialog);
|
|
154
|
+
const nativeVideos = dialog.querySelectorAll("video");
|
|
155
|
+
nativeVideos.forEach(video => video.pause());
|
|
156
|
+
}
|
|
157
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Styling:
|
|
2
|
+
✔ Animations? @done
|
|
3
|
+
☐ Check styling on left right
|
|
4
|
+
☐ Setup fullscreen option / modifier
|
|
5
|
+
☐ Setup programmatic modal with new system
|
|
6
|
+
☐ Resizing
|
|
7
|
+
✔ Work out how this can work with native and click outside @done
|
|
8
|
+
✔ Figure out how to setup icon for the resizer for all sites @done
|
|
9
|
+
* Fontawesome, or should this just be CSS so we don't need any icons for it
|
|
10
|
+
* Same for close button should we just use CSS and omit any FA requirements
|
|
11
|
+
* Or make it super easy to implement the icon only in the template (like default icon classses)
|
|
12
|
+
* Then user can choose to implement their own (via class) or use the styles that come with it
|
|
13
|
+
☐ Prevent Browser Scroll
|
|
14
|
+
✔ How should height work? Should a centered modal expand to it's content's height (up to the viewport height) or always be cropped and scroll (static height)? @done
|
|
15
|
+
Todos:
|
|
16
|
+
✔ How should the relationship between a dialog and it's trigger work @done
|
|
17
|
+
* Think there should be no relationship? Just triggers are triggers just attach a handler to open DOM centric way (add errors when the dialog doesn't exist)
|
|
18
|
+
* For the dialogs they should just have handlers attached for close
|
|
19
|
+
* Init should find all one's that need to be built and build them, then it should initialize them the same way as non-built dialogs
|
|
20
|
+
✔ Support both modal and non-modal dialogs (rename)? @done
|
|
21
|
+
✘ Divide into 4 modules @cancelled
|
|
22
|
+
1. Initializer (data-attributes)
|
|
23
|
+
2. Templater (for Drupal projects and standard modals)
|
|
24
|
+
3. Open/Close Behaviors
|
|
25
|
+
4. Programmatic Modal
|
|
26
|
+
* This is tough because its opionated towards our system in Drupal (jQuery)
|
|
27
|
+
✔ How should this all be structured @done
|
|
28
|
+
* JS template dialogs?
|
|
29
|
+
* Pros
|
|
30
|
+
* Many users create the modals in content in CMS we don't want to make that difficult or get stuck with structure
|
|
31
|
+
* Cons
|
|
32
|
+
* Templating is in JS so dialogs don't make much sense on their own
|
|
33
|
+
* Could continue the div to dialog conversion (so modal content is inline unless JS running). Don't want to optimize too much for no js anymore anyways
|
|
34
|
+
* writing the dialog by hand makes sense for non defualt modal styles or users that have another structure but need the scripting part
|
|
35
|
+
* Solution
|
|
36
|
+
* Breakup module into two parts the underlying modal scripting (open close trigger) and (conversion modal templating [w. resizer] and as <div>)
|
|
37
|
+
* Then we can have both without any extra code and seperation
|
package/js/ui/flipcard.js
CHANGED
|
@@ -2,16 +2,10 @@
|
|
|
2
2
|
* @module ui/flipcard
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
// =============================================================================
|
|
6
|
-
// Flipcard
|
|
7
|
-
// =============================================================================
|
|
8
|
-
|
|
9
|
-
// Version: 1.0.1
|
|
10
|
-
|
|
11
|
-
// Changes 1.0.1 | Added allow selection
|
|
12
|
-
|
|
13
5
|
import { trimWhitespace } from "@ulu/utils/string.js";
|
|
14
|
-
import { log, logError } from "../utils/logger.js";
|
|
6
|
+
import { log, logError } from "../utils/class-logger.js";
|
|
7
|
+
import { getName } from "../events/index.js";
|
|
8
|
+
import { getDatasetOptionalJson } from "../utils/dom.js";
|
|
15
9
|
const debugMode = false; // Global dev debug
|
|
16
10
|
|
|
17
11
|
export class Flipcard {
|
|
@@ -23,7 +17,7 @@ export class Flipcard {
|
|
|
23
17
|
selectionMin: 10, // Minimum length that qualifies as a selection
|
|
24
18
|
exclude: "a, input, textarea, button" // Selectors to avoid closing a flipcard onProxyclick
|
|
25
19
|
},
|
|
26
|
-
}
|
|
20
|
+
};
|
|
27
21
|
constructor(container, front, back, config, debug = false) {
|
|
28
22
|
if (!container, !front, !back) {
|
|
29
23
|
logError(this, 'Missing an element (container, front, back)');
|
|
@@ -133,7 +127,57 @@ export class Flipcard {
|
|
|
133
127
|
`;
|
|
134
128
|
}
|
|
135
129
|
}
|
|
136
|
-
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Default data attributes
|
|
133
|
+
*/
|
|
134
|
+
export const attrs = {
|
|
135
|
+
init: "data-ulu-flipcard-init",
|
|
136
|
+
flipcard: "data-ulu-flipcard",
|
|
137
|
+
front: "data-ulu-flipcard-front",
|
|
138
|
+
back: "data-ulu-flipcard-back",
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Utils for selecting things based on attributes
|
|
142
|
+
const attrSelector = key => `[${ attrs[key] }]`;
|
|
143
|
+
const attrSelectorInitial = key => `${ attrSelector(key) }:not([${ attrs.init }])`;
|
|
144
|
+
|
|
145
|
+
// const containers = document.querySelectorAll('[data-ulu-flipcard]');
|
|
146
|
+
const instances = [];
|
|
147
|
+
|
|
148
|
+
export function init() {
|
|
149
|
+
document.addEventListener(getName("pageModified"), setup);
|
|
150
|
+
setup();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function setup() {
|
|
154
|
+
const builders = document.querySelectorAll(attrSelectorInitial("flipcard"));
|
|
155
|
+
builders.forEach(setupFlipcard);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// containers.forEach(init);
|
|
159
|
+
|
|
160
|
+
function setupFlipcard(container) {
|
|
161
|
+
container.setAttribute(attrs.init, "");
|
|
162
|
+
const options = getDatasetOptionalJson(container, "uluFlipcard");
|
|
163
|
+
const config = Object.assign({}, options);
|
|
164
|
+
const front = container.querySelector(attrSelectorInitial("front"));
|
|
165
|
+
const back = container.querySelector(attrSelectorInitial("back"));
|
|
166
|
+
instances.push(new Flipcard(container, front, back, config));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// getDatasetOptionalJson
|
|
170
|
+
function setupSlider(container) {
|
|
171
|
+
container.setAttribute(attrs.init, "");
|
|
172
|
+
const options = getDatasetOptionalJson(container, "uluFlipcard");
|
|
173
|
+
const config = Object.assign({}, options);
|
|
174
|
+
const elements = {
|
|
175
|
+
track: container.querySelector(attrSelector("track")),
|
|
176
|
+
controls: container.querySelector(attrSelector("controls"))
|
|
177
|
+
};
|
|
178
|
+
// replace with OverflowScroller when finished removing sitescrollslider
|
|
179
|
+
instances.push(new SiteScrollSlider(elements, config));
|
|
180
|
+
}
|
|
137
181
|
|
|
138
182
|
/**
|
|
139
183
|
* Preliminary Notes:
|