@ulu/frontend 0.0.23 → 0.1.0-beta.1
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 +20 -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 +33 -13
- 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 +156 -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/ui/tooltip.js
CHANGED
|
@@ -1,85 +1,286 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module ui/tooltip
|
|
3
3
|
*/
|
|
4
|
-
// =============================================================================
|
|
5
|
-
// Tooltip
|
|
6
|
-
// =============================================================================
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
import { getName as getEventName } from "../events/index.js";
|
|
6
|
+
import { createFloatingUi } from "../utils/floating-ui.js";
|
|
7
|
+
import { createElementFromHtml } from "@ulu/utils/browser/dom.js";
|
|
8
|
+
import { logError } from "../utils/class-logger.js";
|
|
9
|
+
import { getDatasetOptionalJson } from "../utils/dom.js";
|
|
10
|
+
import { newId, ensureId } from "../utils/id.js";
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
const attrs = {
|
|
13
|
+
trigger: "data-ulu-tooltip",
|
|
14
|
+
init: "data-ulu-init",
|
|
15
|
+
body: "data-ulu-tooltip-display-body",
|
|
16
|
+
arrow: "data-ulu-tooltip-arrow"
|
|
17
|
+
};
|
|
18
|
+
const attrSelector = key => `[${ attrs[key] }]`;
|
|
19
|
+
const attrSelectorInitial = key => `${ attrSelector(key) }:not([${ attrs.init }])`;
|
|
20
|
+
/**
|
|
21
|
+
* Initialize default popover
|
|
22
|
+
*/
|
|
23
|
+
export function init() {
|
|
24
|
+
document.addEventListener(getEventName("pageModified"), setup);
|
|
25
|
+
setup();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Query all popovers on current page and set them up
|
|
30
|
+
* - Use this manually if needed
|
|
31
|
+
* - Won't setup a popover more than once
|
|
32
|
+
*/
|
|
33
|
+
export function setup() {
|
|
34
|
+
const triggers = document.querySelectorAll(attrSelectorInitial("trigger"));
|
|
35
|
+
triggers.forEach(setupTrigger);
|
|
36
|
+
}
|
|
12
37
|
|
|
13
|
-
|
|
14
|
-
|
|
38
|
+
export function setupTrigger(trigger) {
|
|
39
|
+
const passed = getDatasetOptionalJson(trigger, "uluTooltip");
|
|
40
|
+
const options = typeof passed === "object" ? passed : {};
|
|
41
|
+
if (typeof passed === "string") {
|
|
42
|
+
options.content = passed;
|
|
43
|
+
}
|
|
44
|
+
return new Tooltip({ trigger }, options);
|
|
45
|
+
}
|
|
15
46
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Tooltip
|
|
49
|
+
* - Provides basic tooltip functionality
|
|
50
|
+
* - Uses floating UI for positioning
|
|
51
|
+
*/
|
|
52
|
+
export class Tooltip {
|
|
53
|
+
/**
|
|
54
|
+
* Defaults options
|
|
55
|
+
*/
|
|
56
|
+
static defaults = {
|
|
57
|
+
/**
|
|
58
|
+
* Should the tooltip and content be linked accessibly
|
|
59
|
+
* - Note tooltips can only apply to interactive elements! (ie button, input, role="...", etc)
|
|
60
|
+
* @type {Boolean}
|
|
61
|
+
*/
|
|
62
|
+
accessible: true,
|
|
63
|
+
/**
|
|
64
|
+
* String/markup to insert into tooltip display
|
|
65
|
+
* @type {String}
|
|
66
|
+
*/
|
|
67
|
+
content: null,
|
|
68
|
+
openClass: "is-active",
|
|
69
|
+
contentClass: "",
|
|
70
|
+
isHtml: false,
|
|
71
|
+
/**
|
|
72
|
+
* Pull content from pre-existing content on page
|
|
73
|
+
* @type {String|Node}
|
|
74
|
+
*/
|
|
75
|
+
fromElement: null,
|
|
76
|
+
/**
|
|
77
|
+
* If used on a link that is an anchor link it will display the content of the anchor like fromElement
|
|
78
|
+
*/
|
|
79
|
+
fromAnchor: false,
|
|
80
|
+
/**
|
|
81
|
+
* Move the content to the bottom of the document
|
|
82
|
+
* @type {Boolean}
|
|
83
|
+
*/
|
|
84
|
+
endOfDocument: true,
|
|
85
|
+
/**
|
|
86
|
+
* Events to show tooltip on
|
|
87
|
+
* @type {Array.<String>}
|
|
88
|
+
*/
|
|
89
|
+
showEvents: ["pointerenter", "focus"],
|
|
90
|
+
/**
|
|
91
|
+
* Events to hide tooltip on
|
|
92
|
+
* @type {Array.<String>}
|
|
93
|
+
*/
|
|
94
|
+
hideEvents: ["pointerleave", "blur"],
|
|
95
|
+
/**
|
|
96
|
+
* Delay when using the directive
|
|
97
|
+
* @type {Number}
|
|
98
|
+
*/
|
|
99
|
+
delay: 500,
|
|
100
|
+
/**
|
|
101
|
+
* Template for the content display
|
|
102
|
+
*/
|
|
103
|
+
template(_config) {
|
|
104
|
+
return `
|
|
105
|
+
<div class="popover popover--tooltip">
|
|
106
|
+
<div class="popover__inner" ${ attrs.body }>
|
|
107
|
+
</div>
|
|
108
|
+
<span class="popover__arrow" data-ulu-tooltip-arrow></span>
|
|
109
|
+
</div>
|
|
110
|
+
`;
|
|
24
111
|
},
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Callback when tooltip is shown or hidden
|
|
114
|
+
* @type {Function}
|
|
115
|
+
*/
|
|
116
|
+
onChange(_ctx) {
|
|
117
|
+
// do something
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
static defaultFloatingOptions = {
|
|
121
|
+
// strategy: "fixed"
|
|
122
|
+
};
|
|
123
|
+
constructor(elements, userOptions, floatingOptions) {
|
|
124
|
+
const { trigger } = elements;
|
|
125
|
+
if (!trigger) {
|
|
126
|
+
logError(this, "missing required trigger");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this.options = Object.assign({}, Tooltip.defaults, userOptions);
|
|
130
|
+
this.floatingOptions = Object.assign({}, Tooltip.defaultFloatingOptions, floatingOptions);
|
|
131
|
+
this.elements = { ...elements };
|
|
132
|
+
this.handlers = {};
|
|
133
|
+
this.isOpen = false;
|
|
134
|
+
ensureId(trigger);
|
|
135
|
+
this.setup();
|
|
136
|
+
}
|
|
137
|
+
setup() {
|
|
138
|
+
this.createContentElement();
|
|
139
|
+
this.attachHandlers();
|
|
140
|
+
this.setupAccessibility();
|
|
141
|
+
}
|
|
142
|
+
setupAccessibility() {
|
|
143
|
+
const { trigger, content } = this.elements;
|
|
144
|
+
const { accessible } = this.options;
|
|
145
|
+
if (!accessible) return;
|
|
146
|
+
trigger.setAttribute("aria-describedby", content.id);
|
|
147
|
+
}
|
|
148
|
+
destroy() {
|
|
149
|
+
this.destroyHandlers();
|
|
150
|
+
this.destroyDisplay();
|
|
151
|
+
}
|
|
152
|
+
getInnerContent() {
|
|
153
|
+
const { fromElement, content, isHtml, fromAnchor } = this.options;
|
|
154
|
+
if (content) {
|
|
155
|
+
return content;
|
|
156
|
+
} else if (fromElement || fromAnchor) {
|
|
157
|
+
const element = fromAnchor ? this.getAnchorElement() : document.querySelector(fromElement);
|
|
158
|
+
if (element) {
|
|
159
|
+
return isHtml ? element.innerHTML : element.innerText;
|
|
160
|
+
} else {
|
|
161
|
+
return "";
|
|
30
162
|
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
name: 'offset',
|
|
35
|
-
options: {
|
|
36
|
-
offset: [ 0, 10 ],
|
|
37
|
-
},
|
|
163
|
+
} else {
|
|
164
|
+
logError(this, "Could not resolve inner content");
|
|
38
165
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
classes: []
|
|
48
|
-
}
|
|
49
|
-
constructor(context, markup, config) {
|
|
50
|
-
if (!context) {
|
|
51
|
-
logError(this, 'Missing context element');
|
|
166
|
+
}
|
|
167
|
+
getAnchorElement() {
|
|
168
|
+
const { trigger } = this.elements;
|
|
169
|
+
const { href } = trigger;
|
|
170
|
+
const id = href ? href.split("#")[1] : null;
|
|
171
|
+
const element = id ? document.getElementById(id) : null;
|
|
172
|
+
if (!element) {
|
|
173
|
+
console.error("Unable to get 'fromAnchor' element", trigger);
|
|
52
174
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
175
|
+
return element;
|
|
176
|
+
}
|
|
177
|
+
createContentElement() {
|
|
178
|
+
const { options } = this;
|
|
179
|
+
const content = createElementFromHtml(options.template(options));
|
|
180
|
+
const body = content.querySelector(attrSelector("body"));
|
|
181
|
+
const innerContent = this.getInnerContent();
|
|
182
|
+
if (options.isHtml) {
|
|
183
|
+
body.innerHTML = innerContent;
|
|
184
|
+
} else {
|
|
185
|
+
body.textContent = innerContent;
|
|
186
|
+
}
|
|
187
|
+
content.id = newId();
|
|
188
|
+
if (options.contentClass) {
|
|
189
|
+
content.classList.add(options.contentClass);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this.elements.content = content;
|
|
193
|
+
this.elements.contentArrow = content.querySelector(attrSelector("arrow"));
|
|
194
|
+
document.body.appendChild(content);
|
|
195
|
+
}
|
|
196
|
+
attachHandlers() {
|
|
197
|
+
const { trigger } = this.elements;
|
|
198
|
+
const { showEvents, hideEvents, delay } = this.options;
|
|
199
|
+
let tid = null;
|
|
200
|
+
|
|
201
|
+
const onShow = (event) => {
|
|
202
|
+
if (tid) return;
|
|
203
|
+
tid = setTimeout(() => {
|
|
204
|
+
this.show(event);
|
|
205
|
+
clearTimeout(tid);
|
|
206
|
+
}, delay);
|
|
207
|
+
};
|
|
69
208
|
|
|
70
|
-
|
|
71
|
-
|
|
209
|
+
const onHide = (event) => {
|
|
210
|
+
if (tid) {
|
|
211
|
+
clearTimeout(tid);
|
|
212
|
+
tid = null;
|
|
213
|
+
}
|
|
214
|
+
this.hide(event);
|
|
215
|
+
};
|
|
216
|
+
const onDocumentKeydown = (event) => {
|
|
217
|
+
if (event.key === "Escape") {
|
|
218
|
+
this.hide(event);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
showEvents.forEach(name => {
|
|
222
|
+
trigger.addEventListener(name, onShow);
|
|
223
|
+
});
|
|
224
|
+
hideEvents.forEach(name => {
|
|
225
|
+
trigger.addEventListener(name, onHide);
|
|
226
|
+
});
|
|
227
|
+
document.addEventListener("keydown", onDocumentKeydown);
|
|
228
|
+
this.handlers = { onShow, onHide, onDocumentKeydown };
|
|
229
|
+
}
|
|
230
|
+
destroyHandlers() {
|
|
231
|
+
const { trigger } = this;
|
|
232
|
+
const { onShow, onHide, onDocumentKeydown } = this.handlers;
|
|
233
|
+
const { showEvents, hideEvents } = this.options;
|
|
234
|
+
if (onShow) {
|
|
235
|
+
showEvents.forEach(name => {
|
|
236
|
+
trigger.removeEventListener(name, onShow);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
if (onHide) {
|
|
240
|
+
hideEvents.forEach(name => {
|
|
241
|
+
trigger.removeEventListener(name, onHide);
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
if (onDocumentKeydown) {
|
|
245
|
+
document.removeEventListener("keydown", onDocumentKeydown);
|
|
72
246
|
}
|
|
73
|
-
this.inPage = true;
|
|
74
|
-
element.appendChild(arrow);
|
|
75
|
-
return document.body.appendChild(element);
|
|
76
247
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
248
|
+
setState(isOpen, event) {
|
|
249
|
+
const ctx = {
|
|
250
|
+
instance: this,
|
|
251
|
+
isOpen,
|
|
252
|
+
event
|
|
253
|
+
};
|
|
254
|
+
const { trigger, content } = this.elements;
|
|
255
|
+
const { openClass } = this.options;
|
|
256
|
+
const setClass = el => el.classList[isOpen ? "add" : "remove"](openClass);
|
|
257
|
+
// trigger.setAttribute("aria-expanded", isOpen ? "true" : "false");
|
|
258
|
+
setClass(trigger);
|
|
259
|
+
setClass(content);
|
|
260
|
+
this.isOpen = isOpen;
|
|
261
|
+
this.options.onChange(ctx);
|
|
262
|
+
trigger.dispatchEvent(this.createEvent("change", ctx));
|
|
263
|
+
this.destroyFloatingInstance();
|
|
264
|
+
if (isOpen) {
|
|
265
|
+
this.createFloatingInstance();
|
|
80
266
|
}
|
|
81
|
-
|
|
82
|
-
|
|
267
|
+
}
|
|
268
|
+
createEvent(name, detail) {
|
|
269
|
+
return new CustomEvent(getEventName("tooltip:" + name), { detail });
|
|
270
|
+
}
|
|
271
|
+
createFloatingInstance() {
|
|
272
|
+
this.floatingCleanup = createFloatingUi(this.elements, this.floatingOptions);
|
|
273
|
+
}
|
|
274
|
+
destroyFloatingInstance() {
|
|
275
|
+
if (this.floatingCleanup) {
|
|
276
|
+
this.floatingCleanup();
|
|
277
|
+
this.floatingCleanup = null;
|
|
83
278
|
}
|
|
84
279
|
}
|
|
280
|
+
show(event) {
|
|
281
|
+
this.setState(true, event);
|
|
282
|
+
}
|
|
283
|
+
hide(event) {
|
|
284
|
+
this.setState(false, event);
|
|
285
|
+
}
|
|
85
286
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @module utils/logger
|
|
2
|
+
* @module utils/class-logger
|
|
3
3
|
*/
|
|
4
|
+
|
|
4
5
|
// Goal: minimzing console conditions for nessasary production log statements
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -18,17 +19,17 @@ const hasConsole = "console" in window;
|
|
|
18
19
|
// If no context output only if config (global) debug is enabled
|
|
19
20
|
function allow(context) {
|
|
20
21
|
return hasConsole && config.debug && (context?.debug || context == null);
|
|
21
|
-
}
|
|
22
|
+
}
|
|
22
23
|
function getName(context) {
|
|
23
24
|
return typeof context === "object" && context?.constructor?.name;
|
|
24
|
-
}
|
|
25
|
+
}
|
|
25
26
|
function output(method, context, messages) {
|
|
26
27
|
const label = getName(context) || "Logger";
|
|
27
28
|
console[method](label, ...messages);
|
|
28
29
|
if (config.outputContext) {
|
|
29
|
-
console.log(
|
|
30
|
+
console.log("Context:\n", context);
|
|
30
31
|
}
|
|
31
|
-
}
|
|
32
|
+
}
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* Changes to make to configuration
|
package/js/utils/dom.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module utils/dom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export const regexJsonString = /^[{\[][\s\S]*[}\]]$/;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get an elements JSON dataset value
|
|
10
|
+
* @param {Node} element
|
|
11
|
+
* @param {String} key key in dataset object for element
|
|
12
|
+
* @returns {Object} Empty object or JSON object from dataset
|
|
13
|
+
*/
|
|
14
|
+
export function getDatasetJson(element, key) {
|
|
15
|
+
const passed = element.dataset[key];
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(passed);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error(`Error getting JSON from dataset (${ key }) -- "${ passed }"\n`, element, error);
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get an elements JSON dataset value that could potentially just be a single string
|
|
26
|
+
* - If JSON it will return the object else it will return the value directly
|
|
27
|
+
* @param {Node} element
|
|
28
|
+
* @param {String} key key in dataset object for element
|
|
29
|
+
* @returns {Object|String} JSON object or current dataset value (string or empty string if no value)
|
|
30
|
+
*/
|
|
31
|
+
export function getDatasetOptionalJson(element, key) {
|
|
32
|
+
const passed = element.dataset[key];
|
|
33
|
+
if (passed && regexJsonString.test(passed.trim())) {
|
|
34
|
+
return getDatasetJson(element, key);
|
|
35
|
+
} else {
|
|
36
|
+
return passed;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Check if a pointer event x/y was outside an elements bounding box
|
|
42
|
+
*/
|
|
43
|
+
export function wasClickOutside(element, event) {
|
|
44
|
+
const rect = element.getBoundingClientRect();
|
|
45
|
+
return (event.clientY < rect.top || // above
|
|
46
|
+
event.clientY > rect.top + rect.height || // below
|
|
47
|
+
event.clientX < rect.left || // left side
|
|
48
|
+
event.clientX > rect.left + rect.width); // right side
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Sets up the positional classes that would come from the equal
|
|
54
|
+
* height module. Needs to be rerun by user when layout changes
|
|
55
|
+
* or new instances are added to the screen
|
|
56
|
+
* - Used for gutter crops
|
|
57
|
+
* - Used for rule placement
|
|
58
|
+
* - **Devs** Remember that default classes should match sass defaults
|
|
59
|
+
* @param {Node} parent The grid parent <data-grid="">
|
|
60
|
+
* @param {Object} classes Override the default equal heights classes
|
|
61
|
+
*/
|
|
62
|
+
export function setPositionClasses(parent, classes = {
|
|
63
|
+
columnFirst: 'position-column-first',
|
|
64
|
+
columnLast: 'position-column-last',
|
|
65
|
+
rowFirst: 'position-row-first',
|
|
66
|
+
rowLast: 'position-row-last'
|
|
67
|
+
}) {
|
|
68
|
+
const children = [...parent.children];
|
|
69
|
+
const rows = [];
|
|
70
|
+
let lastY;
|
|
71
|
+
// Check element against last
|
|
72
|
+
// If they don't match it's a new row create a new array
|
|
73
|
+
// Then push into the last array in the rows array
|
|
74
|
+
children.forEach((child) => {
|
|
75
|
+
const y = child.getBoundingClientRect().y;
|
|
76
|
+
if (lastY !== y) rows.push([]);
|
|
77
|
+
rows[rows.length - 1].push(child);
|
|
78
|
+
lastY = y;
|
|
79
|
+
child.classList.remove(...Object.values(classes)); // Remove previously set classes
|
|
80
|
+
});
|
|
81
|
+
// Apply Classes
|
|
82
|
+
rows.forEach((row, index) => {
|
|
83
|
+
if (index === 0)
|
|
84
|
+
row.forEach(child => child.classList.add(classes.rowFirst));
|
|
85
|
+
if (index == rows.length - 1)
|
|
86
|
+
row.forEach(child => child.classList.add(classes.rowLast));
|
|
87
|
+
|
|
88
|
+
row.forEach((child, childIndex) => {
|
|
89
|
+
if (childIndex === 0)
|
|
90
|
+
child.classList.add(classes.columnFirst);
|
|
91
|
+
if (childIndex == row.length - 1)
|
|
92
|
+
child.classList.add(classes.columnLast);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Resolve a target to Element
|
|
99
|
+
* @param {String|Node} target The selector or node/element
|
|
100
|
+
* @param {Object} context [document] The context to query possible selectors from
|
|
101
|
+
*/
|
|
102
|
+
export function getElement(target, context = document) {
|
|
103
|
+
if (typeof target === "string") {
|
|
104
|
+
return context.querySelector(target);
|
|
105
|
+
} else if (target instanceof Element) {
|
|
106
|
+
return target;
|
|
107
|
+
} else {
|
|
108
|
+
console.warn("Unable to getElement()", target);
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Sets a CSS custom property equal to the scrollbar width
|
|
115
|
+
* @param {Node} element The element that is the child of a scrollabel container
|
|
116
|
+
* @param {Node} container The container that can be scrolled
|
|
117
|
+
* @param {Stirng} propName Custom property to set
|
|
118
|
+
*/
|
|
119
|
+
export function addScrollbarProperty(element = document.body, container = window, propName = "--ulu-scrollbar-width") {
|
|
120
|
+
const scrollbarWidth = container.innerWidth - element.clientWidth;
|
|
121
|
+
element.style.setProperty(propName, `${ scrollbarWidth }px`);
|
|
122
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module utils/file-save
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options
|
|
7
|
+
* @typedef {Object} FileSaveOptions
|
|
8
|
+
* @property {String} filename Filename for blob when creating a link (ie createLink) [default "filesave-file.txt"]
|
|
9
|
+
* @property {String} type Filename for blob when creating a link (ie createLink) [default "text/plain;charset=utf-8"]
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Simple script that is useful for testing
|
|
15
|
+
* - Make a file
|
|
16
|
+
* - Create a URL to it
|
|
17
|
+
* - Gives utility function to create a link to the file (for front end)
|
|
18
|
+
* - Gives method to destroy the file when no longer needed
|
|
19
|
+
* - User can redefine the program by passing options object matching props.
|
|
20
|
+
* Limited to new browsers that support Blob(), also user preferences or type of browser may limit access to Blob functionality
|
|
21
|
+
*/
|
|
22
|
+
export class FileSave {
|
|
23
|
+
static defaults = {
|
|
24
|
+
filename: "filesave-file.txt",
|
|
25
|
+
type: "text/plain;charset=utf-8"
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @param {*} data Data to put in blob file
|
|
29
|
+
* @param {FileSaveOptions} options Options for file, see defaults (ie. type, filename)
|
|
30
|
+
*/
|
|
31
|
+
constructor(data, options) {
|
|
32
|
+
this.options = Object.assign({}, FileSave.defaults, options);
|
|
33
|
+
this.data = data;
|
|
34
|
+
this.blob = new Blob([data], { type: this.options.type });
|
|
35
|
+
this.url = URL.createObjectURL(this.blob);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Remove the blob url
|
|
39
|
+
*/
|
|
40
|
+
destroy() {
|
|
41
|
+
return URL.revokeObjectURL(this.url);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get the blob url
|
|
45
|
+
*/
|
|
46
|
+
getUrl() {
|
|
47
|
+
return this.url;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create link element with blob as href
|
|
51
|
+
* @param {String} text The text to put in the link
|
|
52
|
+
*/
|
|
53
|
+
createLink(text) {
|
|
54
|
+
const link = document.createElement('a');
|
|
55
|
+
const textNode = document.createTextNode(text);
|
|
56
|
+
link.setAttribute('download', this.options.filename);
|
|
57
|
+
link.setAttribute('href', this.url);
|
|
58
|
+
link.appendChild(textNode);
|
|
59
|
+
return link;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check for Compatibility (optional, implement on user side)
|
|
63
|
+
*/
|
|
64
|
+
static isBrowserSupported() {
|
|
65
|
+
return "FileReader" in window;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module utils/floating-ui
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
computePosition,
|
|
7
|
+
autoUpdate,
|
|
8
|
+
offset,
|
|
9
|
+
inline,
|
|
10
|
+
flip,
|
|
11
|
+
shift,
|
|
12
|
+
arrow,
|
|
13
|
+
} from "@floating-ui/dom";
|
|
14
|
+
|
|
15
|
+
export const defaults = {
|
|
16
|
+
strategy: "absolute",
|
|
17
|
+
placement: "bottom",
|
|
18
|
+
inline: false,
|
|
19
|
+
offset: {
|
|
20
|
+
mainAxis: 16
|
|
21
|
+
},
|
|
22
|
+
shift: true,
|
|
23
|
+
flip: true,
|
|
24
|
+
arrow: true, // Options for arrow (not element)
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} elements Elements (trigger, content, and optionally contentArrow)
|
|
30
|
+
* @param {*} options Configuration options for floatingUi
|
|
31
|
+
* @returns {Function} floating cleanup function call when no longer needed
|
|
32
|
+
*/
|
|
33
|
+
export function createFloatingUi(elements, config) {
|
|
34
|
+
const options = Object.assign({}, defaults, config);
|
|
35
|
+
const { placement, strategy } = options;
|
|
36
|
+
const { trigger, content, contentArrow } = elements;
|
|
37
|
+
|
|
38
|
+
return autoUpdate(trigger, content, () => {
|
|
39
|
+
computePosition(trigger, content, {
|
|
40
|
+
placement,
|
|
41
|
+
strategy,
|
|
42
|
+
middleware: [
|
|
43
|
+
...addPlugin(inline, options.inline),
|
|
44
|
+
...addPlugin(offset, options.offset),
|
|
45
|
+
...addPlugin(flip, options.flip),
|
|
46
|
+
...addPlugin(shift, options.shift),
|
|
47
|
+
...addPlugin(arrow, contentArrow && options.arrow, { element: contentArrow }),
|
|
48
|
+
]
|
|
49
|
+
}).then(data => {
|
|
50
|
+
const { x, y, middlewareData, placement } = data;
|
|
51
|
+
const arrowPos = middlewareData.arrow;
|
|
52
|
+
|
|
53
|
+
// Update computed styles for the content (popover container)
|
|
54
|
+
Object.assign(content.style, {
|
|
55
|
+
left: `${ x }px`,
|
|
56
|
+
top: `${ y }px`
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Update placement attribute (used by arrow for theming)
|
|
60
|
+
content.setAttribute("data-placement", placement);
|
|
61
|
+
|
|
62
|
+
// If arrow was enabled, add it's computed styles
|
|
63
|
+
if (arrowPos) {
|
|
64
|
+
Object.assign(contentArrow.style, {
|
|
65
|
+
// position: "absolute",
|
|
66
|
+
left: arrowPos?.x != null ? `${ arrowPos.x }px` : "",
|
|
67
|
+
top: arrowPos?.y != null ? `${ arrowPos.y }px` : "",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function addPlugin(plugin, option, overrides = {}) {
|
|
75
|
+
if (!option) {
|
|
76
|
+
return [];
|
|
77
|
+
// If object add it as options, else just enable without options
|
|
78
|
+
} else if (typeof option === "object") {
|
|
79
|
+
return [plugin({ ...option, ...overrides })];
|
|
80
|
+
} else {
|
|
81
|
+
return [plugin(overrides)];
|
|
82
|
+
}
|
|
83
|
+
}
|
package/js/utils/id.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module utils/id
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
let idCount = 0;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create new uid
|
|
9
|
+
*/
|
|
10
|
+
export function newId() {
|
|
11
|
+
return `ulu-uid-${ ++idCount }`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Sets an ID if element doesn't have one vie newUid
|
|
16
|
+
* @param {Node} element Element to make sure has an id
|
|
17
|
+
*/
|
|
18
|
+
export function ensureId(element) {
|
|
19
|
+
if (!element.id) {
|
|
20
|
+
element.id = newId();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * as classLogger from "./class-logger.js";
|
|
2
|
+
export * as dom from "./dom.js";
|
|
3
|
+
export * as floatingUi from "./floating-ui.js";
|
|
4
|
+
export * as id from "./id.js";
|
|
5
|
+
export * as index from "./index.js";
|
|
6
|
+
export * as pauseYoutubeVideo from "./pause-youtube-video.js";
|
|
7
|
+
export * as fileSave from "./file-save.js";
|