@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.
- package/CHANGELOG.md +4 -0
- 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 +71 -37
- 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
|
@@ -1,94 +1,92 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module ui/programmatic-modal
|
|
3
3
|
*/
|
|
4
|
-
// =============================================================================
|
|
5
|
-
// Grabs Breakpoint from CSS
|
|
6
|
-
// =============================================================================
|
|
7
4
|
|
|
8
|
-
// Version: 1.0.3
|
|
9
|
-
// Changes:
|
|
10
|
-
// 1.0.2 | Updates to work with the updated modal script which has
|
|
11
|
-
// to attach it's own trigger handlers
|
|
12
|
-
// Description: Drupal programmatic modal insertion script (interface = jquery prototype)
|
|
13
|
-
// Changes: 1.0.2 - Added ability to pass class to container
|
|
14
5
|
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
6
|
+
import { getName, dispatch } from "../events/index.js";
|
|
7
|
+
import { newId } from "../utils/id.js";
|
|
8
|
+
import { buildModal } from "./modal-builder.js";
|
|
17
9
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
10
|
+
export class ProgrammaticModalManager {
|
|
11
|
+
static defaults = {
|
|
12
|
+
triggerSelector: "[data-ulu-programmatic-modal-trigger]",
|
|
13
|
+
triggerInitAttr: "data-ulu-programmatic-modal-init"
|
|
14
|
+
};
|
|
15
|
+
constructor(passedOptions) {
|
|
16
|
+
const options = Object.assign({}, ProgrammaticModalManager.defaults, passedOptions);
|
|
17
|
+
this.options = options;
|
|
18
|
+
this.triggers = null;
|
|
19
|
+
this.cachedTrigger = null;
|
|
20
|
+
this.triggerListener;
|
|
21
|
+
this.onTriggerClick = (event) => {
|
|
22
|
+
const trigger = event.target.closest(options.triggerSelector);
|
|
23
|
+
if (trigger) this.cachedTrigger = trigger;
|
|
24
|
+
};
|
|
25
|
+
this.onPageModified = () => {
|
|
26
|
+
this.setupTriggers();
|
|
27
|
+
};
|
|
28
|
+
document.addEventListener(getName("pageModified"), this.onPageModified);
|
|
29
|
+
this.setupTriggers();
|
|
30
|
+
}
|
|
31
|
+
setupTriggers() {
|
|
32
|
+
const { triggerSelector, triggerInitAttr } = this.options;
|
|
33
|
+
const triggers = document.querySelectorAll(`${ triggerSelector }:not([${ triggerInitAttr }])`);
|
|
34
|
+
triggers.forEach(trigger => {
|
|
35
|
+
trigger.addEventListener("click", this.onTriggerClick);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
destroy() {
|
|
39
|
+
const { triggerSelector } = this.options;
|
|
40
|
+
const triggers = document.querySelectorAll(triggerSelector);
|
|
41
|
+
triggers.forEach(trigger => {
|
|
42
|
+
trigger.removeEventListener("click", this.onTriggerClick);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
createAndOpen(config, afterCreate) {
|
|
46
|
+
const { selector, noClickTrigger, removeOnClose } = config;
|
|
47
|
+
const content = document.querySelector(selector);
|
|
48
|
+
if (!content.id) {
|
|
49
|
+
content.id = newId();
|
|
50
|
+
}
|
|
32
51
|
|
|
33
|
-
|
|
34
|
-
|
|
52
|
+
let trigger;
|
|
53
|
+
if (!noClickTrigger) {
|
|
54
|
+
trigger = this.cachedTrigger;
|
|
55
|
+
// Remove cached trigger (since it no longer applies)
|
|
56
|
+
this.cachedTrigger = null;
|
|
57
|
+
}
|
|
35
58
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
newContainer();
|
|
49
|
-
// Intialize and open the new modal
|
|
50
|
-
setupModal(modal, config.settings);
|
|
51
|
-
show(id, {
|
|
52
|
-
onShow(modal) {
|
|
53
|
-
dispatch('pageModified', modal);
|
|
54
|
-
},
|
|
55
|
-
onClose(element) {
|
|
56
|
-
if (config.removeOnClose) {
|
|
57
|
-
element.parentNode.removeChild(element);
|
|
59
|
+
if (!content) {
|
|
60
|
+
console.error("No element found from config.selector. ", config);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const { modal } = buildModal(content, config.modal);
|
|
64
|
+
const ctx = { trigger, modal, config };
|
|
65
|
+
if (afterCreate) {
|
|
66
|
+
afterCreate(ctx);
|
|
67
|
+
}
|
|
68
|
+
const onModalClose = () => {
|
|
69
|
+
if (removeOnClose) {
|
|
70
|
+
modal.remove();
|
|
58
71
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
cachedTrigger.focus();
|
|
72
|
+
if (trigger) {
|
|
73
|
+
trigger.focus();
|
|
62
74
|
}
|
|
75
|
+
};
|
|
76
|
+
// Add close event (to refocus the element that triggered)
|
|
77
|
+
modal.addEventListener("close", onModalClose, { once: true });
|
|
78
|
+
|
|
79
|
+
// Setup trigger to show this modal again and not do it's old behavior (if ajax link)
|
|
80
|
+
if (!removeOnClose && trigger) {
|
|
81
|
+
trigger.addEventListener("click", (event) => {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
modal.showModal();
|
|
84
|
+
});
|
|
63
85
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
attachTriggers();
|
|
86
|
+
dispatch("pageModified", modal);
|
|
87
|
+
// Open the new modal for the first time
|
|
88
|
+
modal.showModal();
|
|
89
|
+
return ctx;
|
|
69
90
|
}
|
|
70
91
|
}
|
|
71
|
-
|
|
72
|
-
* Sets and returns the modal's id
|
|
73
|
-
*/
|
|
74
|
-
function setModalId(element, id) {
|
|
75
|
-
element.id = id || `programmatic-modal--id-${ ++count }`;
|
|
76
|
-
return element.id;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Once we remove the placeholder containers id (above)
|
|
80
|
-
* we create another programmatic placeholder container
|
|
81
|
-
* for the next programmitic container
|
|
82
|
-
*/
|
|
83
|
-
function newContainer() {
|
|
84
|
-
const container = document.createElement('div');
|
|
85
|
-
container.id = 'programmatic-modal';
|
|
86
|
-
document.body.append(container);
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Document click handler, will cache the trigger that caused the modal to open
|
|
90
|
-
*/
|
|
91
|
-
function cacheTrigger(event) {
|
|
92
|
-
const trigger = event.target.closest(selectorTrigger);
|
|
93
|
-
if (trigger) cachedTrigger = trigger;
|
|
94
|
-
}
|
|
92
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ui/proxy-click
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// Used for cards and things that look like they should be clickable even
|
|
7
|
+
// though the link in their content is the only clickable element. This way the
|
|
8
|
+
// entire cards content doesn't need to be in a link (which isn't accessible)
|
|
9
|
+
// - The script allows only for clicks with a duration of 250ms to avoid
|
|
10
|
+
// conflict with a user selecting text.
|
|
11
|
+
// - Works with either links or buttons because it just uses the elements .click()
|
|
12
|
+
// - Uses data-attributes for selection
|
|
13
|
+
import { getName } from "../events/index.js";
|
|
14
|
+
import { getDatasetOptionalJson } from "../utils/dom.js";
|
|
15
|
+
|
|
16
|
+
const attrs = {
|
|
17
|
+
trigger: "data-ulu-proxy-click",
|
|
18
|
+
init: "data-ulu-proxy-click-init",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const attrSelector = key => `[${ attrs[key] }]`;
|
|
22
|
+
const attrSelectorInitial = key => `${ attrSelector(key) }:not([${ attrs.init }])`;
|
|
23
|
+
|
|
24
|
+
export const defaults = {
|
|
25
|
+
selector: "[data-ulu-proxy-click-source]",
|
|
26
|
+
selectorPreventBase: "input, select, textarea, button, a, [tabindex='-1']",
|
|
27
|
+
selectorPrevent: "",
|
|
28
|
+
mousedownDurationPrevent: 250,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Current default objects (user can override these)
|
|
32
|
+
let currentDefaults = { ...defaults };
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {Object} options Change options used as default for dialogs, can then be overriden by data attribute settings on element
|
|
36
|
+
*/
|
|
37
|
+
export function setDefaults(options) {
|
|
38
|
+
currentDefaults = Object.assign({}, currentDefaults, options);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Initialize everything in document
|
|
42
|
+
* - This will only initialize elements once, it is safe to call on page changes
|
|
43
|
+
*/
|
|
44
|
+
export function init() {
|
|
45
|
+
document.addEventListener(getName("pageModified"), () => setup());
|
|
46
|
+
setup();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function setup(context = document) {
|
|
50
|
+
const proxies = context.querySelectorAll(attrSelectorInitial("trigger"));
|
|
51
|
+
proxies.forEach(proxy => {
|
|
52
|
+
const elOptions = getDatasetOptionalJson(proxy, "siteProxyClick");
|
|
53
|
+
const options = Object.assign({}, currentDefaults, elOptions);
|
|
54
|
+
const child = proxy.querySelector(options.selector);
|
|
55
|
+
if (child) {
|
|
56
|
+
attachHandlers(proxy, child, options);
|
|
57
|
+
proxy.setAttribute(attrs.init, "");
|
|
58
|
+
} else {
|
|
59
|
+
console.error("Unable to locate proxy click source", options.selector);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export function attachHandlers(proxy, child, options) {
|
|
64
|
+
const { selectorPreventBase: spb, selectorPrevent: sp } = options;
|
|
65
|
+
const selectorPrevent = `${ spb }${ sp ? `, ${ sp }` : "" }`;
|
|
66
|
+
let start, shouldProxy;
|
|
67
|
+
proxy.addEventListener("mousedown", ({ target, timeStamp }) => {
|
|
68
|
+
shouldProxy = false;
|
|
69
|
+
if (!target.matches(selectorPrevent)) {
|
|
70
|
+
shouldProxy = true;
|
|
71
|
+
start = timeStamp;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
proxy.addEventListener("mouseup", ({ timeStamp }) => {
|
|
75
|
+
if (shouldProxy && timeStamp - start < options.mousedownDurationPrevent) {
|
|
76
|
+
child.click();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
proxy.style.cursor = "pointer";
|
|
80
|
+
}
|
package/js/ui/resizer.js
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
// Reference: - http://jsfiddle.net/3jMQD/614/
|
|
13
13
|
|
|
14
|
-
import { logError } from "../utils/logger.js";
|
|
14
|
+
import { logError } from "../utils/class-logger.js";
|
|
15
15
|
|
|
16
|
-
export
|
|
16
|
+
export class Resizer {
|
|
17
17
|
static defaults = {
|
|
18
18
|
debug: false,
|
|
19
19
|
overrideMaxWidth: false,
|
|
@@ -32,7 +32,7 @@ export default class ElementResizer {
|
|
|
32
32
|
if (!control || !container) {
|
|
33
33
|
logError(this, "Missing required elements 'control' or 'container'");
|
|
34
34
|
}
|
|
35
|
-
this.options = Object.assign({},
|
|
35
|
+
this.options = Object.assign({}, Resizer.defaults, options);
|
|
36
36
|
this.container = container;
|
|
37
37
|
this.control = control;
|
|
38
38
|
this.handlerMousedown = this.onMousedown.bind(this);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ui/scroll-slider
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { OverflowScroller } from "./overflow-scroller.js";
|
|
6
|
+
import { createPager } from "./overflow-scroller-pager.js";
|
|
7
|
+
import { getName } from "../events/index.js";
|
|
8
|
+
import { getDatasetOptionalJson } from "../utils/dom.js";
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Default data attributes
|
|
13
|
+
*/
|
|
14
|
+
export const attrs = {
|
|
15
|
+
init: "data-ulu-scroll-slider-init",
|
|
16
|
+
slider: "data-ulu-scroll-slider",
|
|
17
|
+
track: "data-ulu-scroll-slider-track",
|
|
18
|
+
controls: "data-ulu-scroll-slider-control-context"
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Utils for selecting things based on attributes
|
|
22
|
+
const attrSelector = key => `[${ attrs[key] }]`;
|
|
23
|
+
const attrSelectorInitial = key => `${ attrSelector(key) }:not([${ attrs.init }])`;
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
const instances = [];
|
|
27
|
+
|
|
28
|
+
const defaults = {
|
|
29
|
+
amount: createPager()
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Initialize everything in document
|
|
34
|
+
* - This will only initialize elements once, it is safe to call on page changes
|
|
35
|
+
*/
|
|
36
|
+
export function init() {
|
|
37
|
+
document.addEventListener(getName("pageModified"), setup);
|
|
38
|
+
setup();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function setup() {
|
|
42
|
+
const builders = document.querySelectorAll(attrSelectorInitial("slider"));
|
|
43
|
+
builders.forEach(setupSlider);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// getDatasetOptionalJson
|
|
47
|
+
function setupSlider(container) {
|
|
48
|
+
container.setAttribute(attrs.init, "");
|
|
49
|
+
const options = getDatasetOptionalJson(container, "uluScrollSlider");
|
|
50
|
+
const config = Object.assign({}, defaults, options);
|
|
51
|
+
const elements = {
|
|
52
|
+
track: container.querySelector(attrSelector("track")),
|
|
53
|
+
controls: container.querySelector(attrSelector("controls"))
|
|
54
|
+
};
|
|
55
|
+
instances.push(new OverflowScroller(elements, config));
|
|
56
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module ui/scrollpoint
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Module that uses intersection observer to add scrollpoint like behavior.
|
|
6
|
+
/**
|
|
7
|
+
* TODO:
|
|
8
|
+
* - Included a group option or attribute (on container)
|
|
9
|
+
// for things like anchor menus (one active in group at a time).
|
|
10
|
+
*
|
|
11
|
+
* How to link elements of group
|
|
12
|
+
* <div group={ groupName: test }>
|
|
13
|
+
* <div point={ groupName: test, mirror: ["#menu-link-1"] }>
|
|
14
|
+
* or
|
|
15
|
+
* <div group={ groupName: test, children: [".selector"] }>
|
|
16
|
+
* <div class=".selector">
|
|
17
|
+
*/
|
|
18
|
+
import { getName } from "../events/index.js";
|
|
19
|
+
import { getDatasetOptionalJson, getElement } from "../utils/dom.js";
|
|
20
|
+
import { logError } from "../utils/class-logger.js";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Default data attributes
|
|
24
|
+
*/
|
|
25
|
+
export const attrs = {
|
|
26
|
+
init: "data-ulu-scrollpoint-init",
|
|
27
|
+
/**
|
|
28
|
+
* Individual scrollpoint
|
|
29
|
+
*/
|
|
30
|
+
point: "data-ulu-scrollpoint",
|
|
31
|
+
group: "data-ulu-scrollpoint-group",
|
|
32
|
+
groupAnchors: "data-ulu-scrollpoint-anchors"
|
|
33
|
+
// Goes on container for all items
|
|
34
|
+
// group: "data-ulu-scrollpoint-group"
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Utils for selecting things based on attributes
|
|
38
|
+
const attrSelector = key => `[${ attrs[key] }]`;
|
|
39
|
+
const attrSelectorInitial = key => `${ attrSelector(key) }:not([${ attrs.init }])`;
|
|
40
|
+
const queryAllInitial = key => document.querySelectorAll(attrSelectorInitial(key));
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Initialize everything in document
|
|
44
|
+
* - This will only initialize elements once, it is safe to call on page changes
|
|
45
|
+
*/
|
|
46
|
+
export function init() {
|
|
47
|
+
document.addEventListener(getName("pageModified"), setup);
|
|
48
|
+
setup();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Setup all points and groups
|
|
53
|
+
*/
|
|
54
|
+
export function setup() {
|
|
55
|
+
const elements = queryAllInitial("point");
|
|
56
|
+
// const points = Array.from(elements).map(resolve);
|
|
57
|
+
// const groups = points
|
|
58
|
+
// .filter(({ config }) => config.groupName)
|
|
59
|
+
// .reduce((acc, point) => {
|
|
60
|
+
// const { groupName } = point.config;
|
|
61
|
+
// if (acc.has(groupName)) {
|
|
62
|
+
// acc.get(groupName).push(point);
|
|
63
|
+
// } else {
|
|
64
|
+
// acc.set(groupName, [point]);
|
|
65
|
+
// }
|
|
66
|
+
// }, new Map());
|
|
67
|
+
// const singles = points.filter(({ config }) => !config.groupName);
|
|
68
|
+
// groups.forEach(setupGroup);
|
|
69
|
+
elements.forEach(element => {
|
|
70
|
+
const elOptions = getDatasetOptionalJson(element, "uluScrollpoint");
|
|
71
|
+
const config = Object.assign({}, elOptions);
|
|
72
|
+
element.setAttribute(attrs.init, "");
|
|
73
|
+
new Scrollpoint(element, config);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Single scrollpoint
|
|
81
|
+
* - Note "forward" and "reverse" refer to scroll directions
|
|
82
|
+
* - forward = vertical below / horizontal right
|
|
83
|
+
* - reverse = vertical above / horizontal left
|
|
84
|
+
* @todo Convert margin to offset
|
|
85
|
+
* @todo This only goes one direction
|
|
86
|
+
*/
|
|
87
|
+
export class Scrollpoint {
|
|
88
|
+
static defaults = {
|
|
89
|
+
/**
|
|
90
|
+
* Default observer root element
|
|
91
|
+
*/
|
|
92
|
+
root: null,
|
|
93
|
+
/**
|
|
94
|
+
* Use a selector to select the observer root element
|
|
95
|
+
*/
|
|
96
|
+
rootSelector: null,
|
|
97
|
+
/**
|
|
98
|
+
* Log debug info to console
|
|
99
|
+
*/
|
|
100
|
+
debug: false,
|
|
101
|
+
/**
|
|
102
|
+
* Change scroll orientation to horizontal
|
|
103
|
+
*/
|
|
104
|
+
horizontal: false,
|
|
105
|
+
/**
|
|
106
|
+
* Margin for observer top or left (depending on orientation)
|
|
107
|
+
*/
|
|
108
|
+
marginStart: "-25%",
|
|
109
|
+
/**
|
|
110
|
+
* Margin for observer bottom or right (depending on orientation)
|
|
111
|
+
*/
|
|
112
|
+
marginEnd: "-55%",
|
|
113
|
+
/**
|
|
114
|
+
* Threshold for observer
|
|
115
|
+
*/
|
|
116
|
+
threshold: [0,1],
|
|
117
|
+
/**
|
|
118
|
+
* The point can exited (else persists)
|
|
119
|
+
*/
|
|
120
|
+
exit: true,
|
|
121
|
+
/**
|
|
122
|
+
* The point can exit from the end
|
|
123
|
+
*/
|
|
124
|
+
exitForward: true,
|
|
125
|
+
/**
|
|
126
|
+
* The point can exit from the start
|
|
127
|
+
*/
|
|
128
|
+
exitReverse: true,
|
|
129
|
+
/**
|
|
130
|
+
* Set state classes
|
|
131
|
+
*/
|
|
132
|
+
setClasses: false,
|
|
133
|
+
/**
|
|
134
|
+
* Prefix for classes
|
|
135
|
+
*/
|
|
136
|
+
classPrefix: "scrollpoint",
|
|
137
|
+
/**
|
|
138
|
+
* Set attribute for state (less verbose same info as classes)
|
|
139
|
+
*/
|
|
140
|
+
setAttribute: true,
|
|
141
|
+
/**
|
|
142
|
+
* Attribute name to set state info in
|
|
143
|
+
*/
|
|
144
|
+
attributeName: "data-scrollpoint-state",
|
|
145
|
+
/**
|
|
146
|
+
* Group multiple points, one active at a time (scroll menus)
|
|
147
|
+
*/
|
|
148
|
+
// groupName: null,
|
|
149
|
+
/**
|
|
150
|
+
* Elements that should also get active state info (classes or attributes)
|
|
151
|
+
*/
|
|
152
|
+
syncElements: [],
|
|
153
|
+
/**
|
|
154
|
+
* Callback called when state changes
|
|
155
|
+
*/
|
|
156
|
+
onChange(_ctx) {
|
|
157
|
+
// do something
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Setup a new scrollpoint
|
|
162
|
+
* @param {Node} element The element to create the scrollpoint for
|
|
163
|
+
* @param {Object} config Options to configure the scrollpoint see Scrollpoint.defaults for more information on settings
|
|
164
|
+
*/
|
|
165
|
+
constructor(element, config) {
|
|
166
|
+
const options = Object.assign({}, Scrollpoint.defaults, config);
|
|
167
|
+
if (!element) {
|
|
168
|
+
logError(this, "Missing required element");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (options.rootSelector) {
|
|
172
|
+
options.root = document.querySelector(options.rootSelector);
|
|
173
|
+
delete options.rootSelector;
|
|
174
|
+
}
|
|
175
|
+
this.options = options;
|
|
176
|
+
this.observer = null;
|
|
177
|
+
this.lastPosition = null;
|
|
178
|
+
this.isActive = false;
|
|
179
|
+
this.element = element;
|
|
180
|
+
this.syncedElements = [
|
|
181
|
+
element,
|
|
182
|
+
...options.syncElements.map(target => getElement(target))
|
|
183
|
+
];
|
|
184
|
+
this.classes = {
|
|
185
|
+
enter: this.getClassname("enter"),
|
|
186
|
+
enterForward: this.getClassname("enter--from-forward"),
|
|
187
|
+
enterReverse: this.getClassname("enter--from-reverse"),
|
|
188
|
+
exit: this.getClassname("exit"),
|
|
189
|
+
exitForward: this.getClassname("exit--from-forward"),
|
|
190
|
+
exitReverse: this.getClassname("exit--from-reverse"),
|
|
191
|
+
};
|
|
192
|
+
this.setupObserver();
|
|
193
|
+
if (options.debug) {
|
|
194
|
+
console.log("Scrollpoint", this);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
getClassname(suffix) {
|
|
198
|
+
return this.options.classPrefix + "-" + suffix;
|
|
199
|
+
}
|
|
200
|
+
getObserverOptions() {
|
|
201
|
+
const { root, marginStart, marginEnd, threshold, horizontal } = this.options;
|
|
202
|
+
const rootMargin = horizontal
|
|
203
|
+
? `0px ${ marginStart } 0px ${ marginEnd }`
|
|
204
|
+
: `${ marginStart } 0px ${ marginEnd } 0px`;
|
|
205
|
+
return { root, rootMargin, threshold };
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* IntersectionObserver Callback
|
|
209
|
+
* - Should set the state
|
|
210
|
+
*/
|
|
211
|
+
onObserve(entries) {
|
|
212
|
+
const y = this.getScrollY();
|
|
213
|
+
const { lastPosition, isActive, options } = this;
|
|
214
|
+
const isForward = lastPosition === null ? null : lastPosition < y;
|
|
215
|
+
entries.forEach(entry => {
|
|
216
|
+
const { isIntersecting } = entry;
|
|
217
|
+
// Entering for first time
|
|
218
|
+
if (isIntersecting && !isActive) {
|
|
219
|
+
this.setState(true, isForward);
|
|
220
|
+
// Exiting
|
|
221
|
+
} else if (!isIntersecting && isActive && options.exit) {
|
|
222
|
+
// Call if allowed in either direction
|
|
223
|
+
if (isForward && options.exitForward || !isForward && options.exitReverse) {
|
|
224
|
+
this.setState(false, isForward);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
this.lastPosition = y;
|
|
229
|
+
}
|
|
230
|
+
setupObserver() {
|
|
231
|
+
const handler = entries => {
|
|
232
|
+
this.onObserve(entries);
|
|
233
|
+
};
|
|
234
|
+
const config = this.getObserverOptions();
|
|
235
|
+
if (this.options.debug) {
|
|
236
|
+
console.log("Scrollpoint (IntersectionObserver)", config);
|
|
237
|
+
}
|
|
238
|
+
this.observer = new IntersectionObserver(handler, config);
|
|
239
|
+
this.observer.observe(this.element);
|
|
240
|
+
}
|
|
241
|
+
getScrollY() {
|
|
242
|
+
const { root } = this.options;
|
|
243
|
+
return root === null || root === document ? window.scrollY : root.scrollTop;
|
|
244
|
+
}
|
|
245
|
+
setState(isActive, isForward) {
|
|
246
|
+
const { element } = this;
|
|
247
|
+
const ctx = { isActive, isForward, element, instance: this };
|
|
248
|
+
const { setClasses, setAttribute, onChange } = this.options;
|
|
249
|
+
if (setClasses) {
|
|
250
|
+
this.updateClasses(isActive, isForward);
|
|
251
|
+
}
|
|
252
|
+
if (setAttribute) {
|
|
253
|
+
this.updateStateAttribute(isActive, isForward);
|
|
254
|
+
}
|
|
255
|
+
if (onChange) {
|
|
256
|
+
onChange(ctx);
|
|
257
|
+
}
|
|
258
|
+
this.isActive = isActive;
|
|
259
|
+
}
|
|
260
|
+
getAllClasses() {
|
|
261
|
+
return Object.values(this.classes);
|
|
262
|
+
}
|
|
263
|
+
updateClasses(isActive, isForward) {
|
|
264
|
+
const { classes } = this;
|
|
265
|
+
const all = this.getAllClasses();
|
|
266
|
+
const classesEnter = [
|
|
267
|
+
classes.enter,
|
|
268
|
+
isForward ? classes.enterForward : classes.enterReverse
|
|
269
|
+
];
|
|
270
|
+
const classesExit = [
|
|
271
|
+
classes.exit,
|
|
272
|
+
isForward ? classes.exitForward : classes.exitReverse
|
|
273
|
+
];
|
|
274
|
+
this.syncedElements.forEach(element => {
|
|
275
|
+
element.classList.remove(...all);
|
|
276
|
+
if (isActive) {
|
|
277
|
+
element.classList.add(...classesEnter);
|
|
278
|
+
} else {
|
|
279
|
+
element.classList.add(...classesExit);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
updateStateAttribute(isActive, isForward) {
|
|
284
|
+
const activeTerm = isActive ? "enter" : "exit";
|
|
285
|
+
const side = isForward ? "forward" : "reverse";
|
|
286
|
+
this.syncedElements.forEach(element => {
|
|
287
|
+
element.setAttribute(this.options.attributeName, `${ activeTerm }-${ side }`);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
destroy() {
|
|
291
|
+
this.observer.disconnect();
|
|
292
|
+
this.observer = null;
|
|
293
|
+
if (this.options.setClasses) {
|
|
294
|
+
this.element.classList.remove(...this.getAllClasses());
|
|
295
|
+
}
|
|
296
|
+
if (this.options.setAttribute) {
|
|
297
|
+
this.element.removeAttribute(this.options.attributeName)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|