@oscarpalmer/oui 0.18.0 → 0.20.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/dist/css/a11y.css +0 -1
- package/dist/css/badge.css +10 -10
- package/dist/css/button.css +9 -9
- package/dist/css/color.css +99 -99
- package/dist/css/content.css +1 -1
- package/dist/css/flex.css +39 -39
- package/dist/css/form.css +29 -57
- package/dist/css/message.css +6 -6
- package/dist/css/popover.css +22 -0
- package/dist/css/reset.css +11 -10
- package/dist/css/table.css +5 -5
- package/dist/css/tooltip.css +61 -1
- package/dist/js/attributable.d.mts +4 -0
- package/dist/js/{attributable.js → attributable.mjs} +8 -1
- package/dist/js/floatable.d.mts +26 -0
- package/dist/js/{floatable.js → floatable.mjs} +73 -50
- package/dist/js/focus-trap/embedded.d.mts +26 -0
- package/dist/js/focus-trap/{embedded.js → embedded.mjs} +10 -8
- package/dist/js/focus-trap/standalone.d.mts +1 -0
- package/dist/js/focus-trap/{standalone.js → standalone.mjs} +5 -2
- package/dist/js/index.d.mts +4 -0
- package/dist/js/index.mjs +5 -0
- package/dist/js/popover.d.mts +20 -0
- package/dist/js/{popover.js → popover.mjs} +11 -9
- package/dist/js/splitter.d.mts +25 -0
- package/dist/js/{splitter.js → splitter.mjs} +60 -58
- package/dist/js/tooltip.d.mts +4 -0
- package/dist/js/{tooltip.js → tooltip.mjs} +21 -19
- package/package.json +65 -70
- package/src/css/a11y.scss +1 -1
- package/src/css/badge.scss +23 -19
- package/src/css/button.scss +9 -9
- package/src/css/color.scss +99 -99
- package/src/css/content.scss +6 -8
- package/src/css/flex.scss +21 -20
- package/src/css/form.scss +37 -60
- package/src/css/message.scss +8 -12
- package/src/css/mixins.scss +41 -13
- package/src/css/popover.scss +7 -2
- package/src/css/reset.scss +18 -18
- package/src/css/splitter.scss +15 -13
- package/src/css/table.scss +12 -12
- package/src/css/tooltip.scss +79 -4
- package/src/js/attributable.ts +14 -11
- package/src/js/floatable.ts +134 -74
- package/src/js/focus-trap/embedded.ts +7 -31
- package/src/js/popover.ts +3 -3
- package/src/js/splitter.ts +10 -43
- package/src/js/tooltip.ts +8 -20
- package/dist/css/value.css +0 -1
- package/dist/js/attributable.full.js +0 -48
- package/dist/js/floatable.full.js +0 -413
- package/dist/js/focus-trap/embedded.full.js +0 -351
- package/dist/js/focus-trap/standalone.full.js +0 -402
- package/dist/js/index.js +0 -5
- package/dist/js/oui.full.js +0 -1596
- package/dist/js/popover.full.js +0 -822
- package/dist/js/splitter.full.js +0 -605
- package/dist/js/tooltip.full.js +0 -679
- package/types/attributable.d.ts +0 -1
- package/types/floatable.d.ts +0 -24
- package/types/focus-trap/embedded.d.ts +0 -24
- package/types/focus-trap/standalone.d.ts +0 -1
- package/types/index.d.ts +0 -4
- package/types/popover.d.ts +0 -17
- package/types/splitter.d.ts +0 -23
- package/types/tooltip.d.ts +0 -2
package/src/js/splitter.ts
CHANGED
|
@@ -7,7 +7,6 @@ import {type StyleToggler, toggleStyles} from '@oscarpalmer/toretto/style';
|
|
|
7
7
|
import supportsTouch from '@oscarpalmer/toretto/touch';
|
|
8
8
|
|
|
9
9
|
declare global {
|
|
10
|
-
// biome-ignore lint/nursery/useConsistentTypeDefinitions: Extending builtins to allow custom element type help
|
|
11
10
|
interface HTMLElementTagNameMap {
|
|
12
11
|
[SELECTOR]: OuiSplitterElement;
|
|
13
12
|
}
|
|
@@ -57,9 +56,7 @@ export class OuiSplitterElement extends HTMLElement {
|
|
|
57
56
|
constructor() {
|
|
58
57
|
super();
|
|
59
58
|
|
|
60
|
-
const panels = [...this.children].filter(
|
|
61
|
-
child => child instanceof HTMLElement,
|
|
62
|
-
);
|
|
59
|
+
const panels = [...this.children].filter(child => child instanceof HTMLElement);
|
|
63
60
|
|
|
64
61
|
if (panels.length !== 2) {
|
|
65
62
|
throw new Error(`${SELECTOR} must have exactly two panels.`);
|
|
@@ -242,9 +239,7 @@ function getTypes(splitter: Splitter, width?: number, height?: number): Types {
|
|
|
242
239
|
if (type === 'auto') {
|
|
243
240
|
return {
|
|
244
241
|
auto: true,
|
|
245
|
-
horizontal:
|
|
246
|
-
(height ?? splitter.rectangle.height) >
|
|
247
|
-
(width ?? splitter.rectangle.width),
|
|
242
|
+
horizontal: (height ?? splitter.rectangle.height) > (width ?? splitter.rectangle.width),
|
|
248
243
|
};
|
|
249
244
|
}
|
|
250
245
|
|
|
@@ -254,11 +249,7 @@ function getTypes(splitter: Splitter, width?: number, height?: number): Types {
|
|
|
254
249
|
};
|
|
255
250
|
}
|
|
256
251
|
|
|
257
|
-
function getValue(
|
|
258
|
-
element: HTMLElement,
|
|
259
|
-
name: string,
|
|
260
|
-
defaultValue: number,
|
|
261
|
-
): number {
|
|
252
|
+
function getValue(element: HTMLElement, name: string, defaultValue: number): number {
|
|
262
253
|
const value = element.getAttribute(name);
|
|
263
254
|
|
|
264
255
|
if (isNullableOrWhitespace(value)) {
|
|
@@ -288,11 +279,7 @@ function getValues(element: OuiSplitterElement): Values {
|
|
|
288
279
|
},
|
|
289
280
|
};
|
|
290
281
|
|
|
291
|
-
const value = getContained(
|
|
292
|
-
values.min,
|
|
293
|
-
getValue(element, 'size', values.now.current),
|
|
294
|
-
values.max,
|
|
295
|
-
);
|
|
282
|
+
const value = getContained(values.min, getValue(element, 'size', values.now.current), values.max);
|
|
296
283
|
|
|
297
284
|
values.now.current = value;
|
|
298
285
|
values.now.previous = value;
|
|
@@ -336,10 +323,7 @@ function onKeydown(event: KeyboardEvent): void {
|
|
|
336
323
|
|
|
337
324
|
function onNavigate(event: KeyboardEvent): void {
|
|
338
325
|
const splitter = MAPPED_ELEMENTS.get(
|
|
339
|
-
findAncestor(
|
|
340
|
-
event.target as never,
|
|
341
|
-
'[oui-splitter-separator]',
|
|
342
|
-
) as HTMLSpanElement,
|
|
326
|
+
findAncestor(event, '[oui-splitter-separator]') as HTMLSpanElement,
|
|
343
327
|
);
|
|
344
328
|
|
|
345
329
|
if (splitter == null) {
|
|
@@ -365,11 +349,7 @@ function onNavigate(event: KeyboardEvent): void {
|
|
|
365
349
|
|
|
366
350
|
setSize(
|
|
367
351
|
splitter,
|
|
368
|
-
getContained(
|
|
369
|
-
values.min,
|
|
370
|
-
values.now.current + offset * NAVIGATION_MODIFIER,
|
|
371
|
-
values.max,
|
|
372
|
-
),
|
|
352
|
+
getContained(values.min, values.now.current + offset * NAVIGATION_MODIFIER, values.max),
|
|
373
353
|
true,
|
|
374
354
|
);
|
|
375
355
|
}
|
|
@@ -387,11 +367,7 @@ function onObservation(entries: ResizeObserverEntry[]): void {
|
|
|
387
367
|
return;
|
|
388
368
|
}
|
|
389
369
|
|
|
390
|
-
splitter.types = getTypes(
|
|
391
|
-
splitter,
|
|
392
|
-
entry.contentRect.width,
|
|
393
|
-
entry.contentRect.height,
|
|
394
|
-
);
|
|
370
|
+
splitter.types = getTypes(splitter, entry.contentRect.width, entry.contentRect.height);
|
|
395
371
|
|
|
396
372
|
if (splitter.types.horizontal) {
|
|
397
373
|
splitter.element.setAttribute(ATTRIBUTE_HORIZONTAL, '');
|
|
@@ -414,9 +390,7 @@ function onPointerdown(event: Event): void {
|
|
|
414
390
|
return;
|
|
415
391
|
}
|
|
416
392
|
|
|
417
|
-
splitter = MAPPED_ELEMENTS.get(
|
|
418
|
-
findAncestor(event.target as never, SELECTOR_HANDLE) as HTMLSpanElement,
|
|
419
|
-
);
|
|
393
|
+
splitter = MAPPED_ELEMENTS.get(findAncestor(event, SELECTOR_HANDLE) as HTMLSpanElement);
|
|
420
394
|
|
|
421
395
|
if (splitter == null) {
|
|
422
396
|
return;
|
|
@@ -468,10 +442,7 @@ function setAriaValue(splitter: Splitter): void {
|
|
|
468
442
|
splitter.separator.setAttribute('aria-valuemax', String(splitter.values.max));
|
|
469
443
|
splitter.separator.setAttribute('aria-valuemin', String(splitter.values.min));
|
|
470
444
|
|
|
471
|
-
splitter.separator.setAttribute(
|
|
472
|
-
'aria-valuenow',
|
|
473
|
-
String(splitter.values.now.current),
|
|
474
|
-
);
|
|
445
|
+
splitter.separator.setAttribute('aria-valuenow', String(splitter.values.now.current));
|
|
475
446
|
}
|
|
476
447
|
|
|
477
448
|
function setSize(splitter: Splitter, size: number, previous?: boolean): void {
|
|
@@ -506,11 +477,7 @@ const KEYS_NEGATIVE: Set<string> = new Set(['ArrowLeft', 'ArrowUp']);
|
|
|
506
477
|
|
|
507
478
|
const KEYS_VERTICAL: Set<string> = new Set(['ArrowLeft', 'ArrowRight']);
|
|
508
479
|
|
|
509
|
-
const KEYS_ALL: Set<string> = new Set([
|
|
510
|
-
...KEYS_ABSOLUTE,
|
|
511
|
-
...KEYS_HORIZONTAL,
|
|
512
|
-
...KEYS_VERTICAL,
|
|
513
|
-
]);
|
|
480
|
+
const KEYS_ALL: Set<string> = new Set([...KEYS_ABSOLUTE, ...KEYS_HORIZONTAL, ...KEYS_VERTICAL]);
|
|
514
481
|
|
|
515
482
|
const MAPPED_ELEMENTS: WeakMap<HTMLElement, Splitter> = new WeakMap();
|
|
516
483
|
|
package/src/js/tooltip.ts
CHANGED
|
@@ -79,14 +79,12 @@ function closeTooltips(next: Tooltip | undefined): void {
|
|
|
79
79
|
clearTimeout(active.timer);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
active.floatable
|
|
83
|
-
'oui-tooltip-content-active',
|
|
84
|
-
);
|
|
82
|
+
active.floatable?.options.content.removeAttribute('oui-tooltip-content-active');
|
|
85
83
|
|
|
86
84
|
requestAnimationFrame(() => {
|
|
87
85
|
active.timer = setTimeout(() => {
|
|
88
86
|
TOOLTIPS_ACTIVE.delete(active);
|
|
89
|
-
active.floatable
|
|
87
|
+
active.floatable?.toggle(false);
|
|
90
88
|
}, delay) as never;
|
|
91
89
|
});
|
|
92
90
|
}
|
|
@@ -194,7 +192,7 @@ function onActivate(event: Event): void {
|
|
|
194
192
|
}
|
|
195
193
|
|
|
196
194
|
function onClick(event: Event): void {
|
|
197
|
-
if (findAncestor(event
|
|
195
|
+
if (findAncestor(event, SELECTOR_CONTENT) != null) {
|
|
198
196
|
event.stopPropagation();
|
|
199
197
|
}
|
|
200
198
|
}
|
|
@@ -211,10 +209,7 @@ function onToggle(event: Event, activate: boolean): void {
|
|
|
211
209
|
toggle = requestAnimationFrame(() => {
|
|
212
210
|
toggle = undefined;
|
|
213
211
|
|
|
214
|
-
const element = findAncestor(
|
|
215
|
-
event.target as HTMLElement,
|
|
216
|
-
SELECTOR_FULL,
|
|
217
|
-
) as HTMLElement;
|
|
212
|
+
const element = findAncestor(event, SELECTOR_FULL) as HTMLElement;
|
|
218
213
|
|
|
219
214
|
const tooltip = TOOLTIPS_ALL.get(element);
|
|
220
215
|
|
|
@@ -224,9 +219,7 @@ function onToggle(event: Event, activate: boolean): void {
|
|
|
224
219
|
|
|
225
220
|
if (
|
|
226
221
|
tooltip == null ||
|
|
227
|
-
tooltip.floatable.options.content.hasAttribute(
|
|
228
|
-
'oui-tooltip-content-active',
|
|
229
|
-
) === activate
|
|
222
|
+
tooltip.floatable.options.content.hasAttribute('oui-tooltip-content-active') === activate
|
|
230
223
|
) {
|
|
231
224
|
return;
|
|
232
225
|
}
|
|
@@ -240,18 +233,13 @@ function onToggle(event: Event, activate: boolean): void {
|
|
|
240
233
|
tooltip.floatable.toggle(true);
|
|
241
234
|
|
|
242
235
|
requestAnimationFrame(() => {
|
|
243
|
-
tooltip.floatable.options.content.setAttribute(
|
|
244
|
-
'oui-tooltip-content-active',
|
|
245
|
-
'',
|
|
246
|
-
);
|
|
236
|
+
tooltip.floatable.options.content.setAttribute('oui-tooltip-content-active', '');
|
|
247
237
|
});
|
|
248
238
|
|
|
249
239
|
return;
|
|
250
240
|
}
|
|
251
241
|
|
|
252
|
-
tooltip.floatable.options.content.removeAttribute(
|
|
253
|
-
'oui-tooltip-content-active',
|
|
254
|
-
);
|
|
242
|
+
tooltip.floatable.options.content.removeAttribute('oui-tooltip-content-active');
|
|
255
243
|
|
|
256
244
|
tooltip.timer ??= setTimeout(() => {
|
|
257
245
|
TOOLTIPS_ACTIVE.delete(tooltip);
|
|
@@ -298,7 +286,7 @@ const TOOLTIPS_ACTIVE: Set<Tooltip> = new Set();
|
|
|
298
286
|
|
|
299
287
|
const TOOLTIPS_ALL: WeakMap<HTMLElement, Tooltip> = new WeakMap();
|
|
300
288
|
|
|
301
|
-
let delay =
|
|
289
|
+
let delay = 500;
|
|
302
290
|
|
|
303
291
|
let index = 0;
|
|
304
292
|
|
package/dist/css/value.css
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
function attributable(attribute, add, remove) {
|
|
2
|
-
const observer = new MutationObserver(entries => {
|
|
3
|
-
const { length } = entries;
|
|
4
|
-
for (let index = 0; index < length; index += 1) {
|
|
5
|
-
const entry = entries[index];
|
|
6
|
-
if (entry.type === 'attributes' && entry.target instanceof HTMLElement) {
|
|
7
|
-
if (entry.target.hasAttribute(attribute)) {
|
|
8
|
-
handleAdded(attribute, [entry.target], add);
|
|
9
|
-
}
|
|
10
|
-
else {
|
|
11
|
-
handleRemoved([entry.target], remove);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
else if (entry.type === 'childList') {
|
|
15
|
-
handleAdded(attribute, entry.addedNodes, add);
|
|
16
|
-
handleRemoved(entry.removedNodes, remove);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
observer.observe(document, {
|
|
21
|
-
attributeFilter: [attribute],
|
|
22
|
-
attributes: true,
|
|
23
|
-
childList: true,
|
|
24
|
-
subtree: true,
|
|
25
|
-
});
|
|
26
|
-
requestAnimationFrame(() => {
|
|
27
|
-
handleAdded(attribute, document.querySelectorAll(`[${attribute}]`), add);
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
function handleAdded(attribute, nodes, add) {
|
|
31
|
-
const elements = [...nodes].filter(node => node instanceof HTMLElement);
|
|
32
|
-
const { length } = elements;
|
|
33
|
-
for (let index = 0; index < length; index += 1) {
|
|
34
|
-
const element = elements[index];
|
|
35
|
-
if (element.hasAttribute(attribute)) {
|
|
36
|
-
add(element);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
function handleRemoved(nodes, remove) {
|
|
41
|
-
const elements = [...nodes].filter(node => node instanceof HTMLElement);
|
|
42
|
-
const { length } = elements;
|
|
43
|
-
for (let index = 0; index < length; index += 1) {
|
|
44
|
-
remove(elements[index]);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export { attributable };
|
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
function isEventTarget(value) {
|
|
2
|
-
return typeof value === "object" && value != null && typeof value.addEventListener === "function" && typeof value.removeEventListener === "function" && typeof value.dispatchEvent === "function";
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
function getBoolean(value, defaultValue) {
|
|
6
|
-
return defaultValue ?? false;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function calculate() {
|
|
10
|
-
return new Promise((resolve) => {
|
|
11
|
-
const values = [];
|
|
12
|
-
let last;
|
|
13
|
-
function step(now) {
|
|
14
|
-
if (last != null) values.push(now - last);
|
|
15
|
-
last = now;
|
|
16
|
-
if (values.length >= CALCULATION_TOTAL) {
|
|
17
|
-
const median = values.sort().slice(2, -2).reduce((first, second) => first + second, 0) / (values.length - CALCULATION_TRIM);
|
|
18
|
-
resolve(median);
|
|
19
|
-
} else requestAnimationFrame(step);
|
|
20
|
-
}
|
|
21
|
-
requestAnimationFrame(step);
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
function noop() {}
|
|
25
|
-
var CALCULATION_TOTAL = 10;
|
|
26
|
-
var CALCULATION_TRIM = 4;
|
|
27
|
-
calculate().then((value) => {
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
function createEventOptions(options) {
|
|
31
|
-
return {
|
|
32
|
-
capture: getBoolean(),
|
|
33
|
-
once: getBoolean(),
|
|
34
|
-
passive: getBoolean(options?.passive, true),
|
|
35
|
-
signal: options?.signal
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
function on(target, type, listener, options) {
|
|
39
|
-
if (!isEventTarget(target) || typeof type !== "string" || typeof listener !== "function") return noop;
|
|
40
|
-
const extended = createEventOptions(options);
|
|
41
|
-
target.addEventListener(type, listener, extended);
|
|
42
|
-
return () => {
|
|
43
|
-
target.removeEventListener(type, listener, extended);
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function findAncestor(origin, selector) {
|
|
48
|
-
if (!(origin instanceof Element) || selector == null) return null;
|
|
49
|
-
if (typeof selector === "string") {
|
|
50
|
-
if (origin.matches?.(selector)) return origin;
|
|
51
|
-
return origin.closest(selector);
|
|
52
|
-
}
|
|
53
|
-
if (typeof selector !== "function") return null;
|
|
54
|
-
if (selector(origin)) return origin;
|
|
55
|
-
let parent = origin.parentElement;
|
|
56
|
-
while (parent != null && !selector(parent)) {
|
|
57
|
-
if (parent === document.body) return null;
|
|
58
|
-
parent = parent.parentElement;
|
|
59
|
-
}
|
|
60
|
-
return parent;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
class Floatable {
|
|
64
|
-
options;
|
|
65
|
-
active = false;
|
|
66
|
-
disabled = false;
|
|
67
|
-
frame;
|
|
68
|
-
ignoreFocus = false;
|
|
69
|
-
constructor(options) {
|
|
70
|
-
this.options = options;
|
|
71
|
-
initialize(this);
|
|
72
|
-
}
|
|
73
|
-
destroy() {
|
|
74
|
-
stop(this);
|
|
75
|
-
const state = getState();
|
|
76
|
-
state?.active.delete(this);
|
|
77
|
-
state?.mapped.delete(this.options.anchor);
|
|
78
|
-
state?.mapped.delete(this.options.content);
|
|
79
|
-
this.options.anchor = undefined;
|
|
80
|
-
this.options.content = undefined;
|
|
81
|
-
}
|
|
82
|
-
disable() {
|
|
83
|
-
if (this.disabled) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
this.disabled = true;
|
|
87
|
-
stop(this);
|
|
88
|
-
getState()?.active.delete(this);
|
|
89
|
-
}
|
|
90
|
-
enable() {
|
|
91
|
-
if (!this.disabled) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
this.disabled = false;
|
|
95
|
-
initialize(this);
|
|
96
|
-
}
|
|
97
|
-
toggle(active, ignoreFocus) {
|
|
98
|
-
if (this.disabled || this.active === active) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
const state = getState();
|
|
102
|
-
if (state == null) {
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
this.ignoreFocus = !active && (ignoreFocus ?? false);
|
|
106
|
-
stop(this);
|
|
107
|
-
if (active) {
|
|
108
|
-
state.active.add(this);
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
state.active.delete(this);
|
|
112
|
-
}
|
|
113
|
-
if (this.options.interactive) {
|
|
114
|
-
if (active) {
|
|
115
|
-
state.order.push(this);
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
const index = state.order.indexOf(this);
|
|
119
|
-
if (index > -1) {
|
|
120
|
-
state.order.splice(index, 1);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
if (active) {
|
|
125
|
-
activate(this);
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
deactivate(this);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
//
|
|
133
|
-
function activate(floatable) {
|
|
134
|
-
floatable.active = true;
|
|
135
|
-
document.body.append(floatable.options.content);
|
|
136
|
-
floatable.options.content.hidden = false;
|
|
137
|
-
updatePosition(floatable);
|
|
138
|
-
}
|
|
139
|
-
function closeAbove(element) {
|
|
140
|
-
const state = getState();
|
|
141
|
-
if (state == null) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
const index = state.order.findIndex(floatable => floatable.options.anchor === element ||
|
|
145
|
-
floatable.options.content === element);
|
|
146
|
-
if (index === -1 || index === state.order.length - 1) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
while (index < state.order.length - 1) {
|
|
150
|
-
void state.order.pop()?.toggle(false, true);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
function closeAll() {
|
|
154
|
-
const state = getState();
|
|
155
|
-
const floatables = [...(state?.active ?? [])];
|
|
156
|
-
state?.active.clear();
|
|
157
|
-
for (const floatable of floatables) {
|
|
158
|
-
void floatable.toggle(false, true);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
function closeNonInteractive(state) {
|
|
162
|
-
for (const floatable of state.active) {
|
|
163
|
-
if (!floatable.options.interactive) {
|
|
164
|
-
void floatable.toggle(false);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
function deactivate(floatable) {
|
|
169
|
-
floatable.active = false;
|
|
170
|
-
floatable.options.content.hidden = true;
|
|
171
|
-
floatable.options.anchor.insertAdjacentElement(INSERT_AFTEREND, floatable.options.content);
|
|
172
|
-
floatable.options.onAfter?.(false);
|
|
173
|
-
}
|
|
174
|
-
function getState() {
|
|
175
|
-
return window._oscarpalmer_oui_floatable;
|
|
176
|
-
}
|
|
177
|
-
function getX(anchor, content, position) {
|
|
178
|
-
if (position.endsWith(POSITION_SUFFIX_END)) {
|
|
179
|
-
const end = anchor.right - content.width;
|
|
180
|
-
return end < 0 ? 0 : end;
|
|
181
|
-
}
|
|
182
|
-
if (position.endsWith(POSITION_SUFFIX_START)) {
|
|
183
|
-
const start = anchor.left;
|
|
184
|
-
return start + content.width > window.innerWidth
|
|
185
|
-
? window.innerWidth - content.width
|
|
186
|
-
: start;
|
|
187
|
-
}
|
|
188
|
-
if (position.startsWith(POSITION_END)) {
|
|
189
|
-
return anchor.right;
|
|
190
|
-
}
|
|
191
|
-
if (position.startsWith(POSITION_START)) {
|
|
192
|
-
return anchor.left - content.width;
|
|
193
|
-
}
|
|
194
|
-
if (position.startsWith(POSITION_HORIZONTAL)) {
|
|
195
|
-
if (anchor.right + content.width <= window.innerWidth) {
|
|
196
|
-
return anchor.right;
|
|
197
|
-
}
|
|
198
|
-
return anchor.left - content.width;
|
|
199
|
-
}
|
|
200
|
-
const left = anchor.left + (anchor.width - content.width) / 2;
|
|
201
|
-
return left < 0 ? 0 : left;
|
|
202
|
-
}
|
|
203
|
-
function getY(anchor, content, position, preferAbove) {
|
|
204
|
-
if (position.startsWith(POSITION_ABOVE)) {
|
|
205
|
-
return anchor.top - content.height;
|
|
206
|
-
}
|
|
207
|
-
if (position.startsWith(POSITION_BELOW)) {
|
|
208
|
-
return anchor.bottom;
|
|
209
|
-
}
|
|
210
|
-
if (position.endsWith(POSITION_BOTTOM)) {
|
|
211
|
-
const bottom = anchor.bottom - content.height;
|
|
212
|
-
return bottom < 0 ? 0 : bottom;
|
|
213
|
-
}
|
|
214
|
-
if (position.endsWith(POSITION_TOP)) {
|
|
215
|
-
const top = anchor.top;
|
|
216
|
-
return top + content.height > window.innerHeight
|
|
217
|
-
? window.innerHeight - content.height
|
|
218
|
-
: top;
|
|
219
|
-
}
|
|
220
|
-
if (position.startsWith(POSITION_VERTICAL)) {
|
|
221
|
-
if (preferAbove && anchor.top - content.height >= 0) {
|
|
222
|
-
return anchor.top - content.height;
|
|
223
|
-
}
|
|
224
|
-
if (anchor.bottom + content.height <= document.body.clientHeight) {
|
|
225
|
-
return anchor.bottom;
|
|
226
|
-
}
|
|
227
|
-
return anchor.top - content.height;
|
|
228
|
-
}
|
|
229
|
-
return anchor.top + (anchor.height - content.height) / 2;
|
|
230
|
-
}
|
|
231
|
-
function initialize(floatable) {
|
|
232
|
-
const { anchor, content, interactive } = floatable.options;
|
|
233
|
-
if (interactive) {
|
|
234
|
-
anchor.setAttribute(ATTRIBUTE_ANCHOR, '');
|
|
235
|
-
content.setAttribute(ATTRIBUTE_CONTENT, '');
|
|
236
|
-
}
|
|
237
|
-
content.style.position = 'fixed';
|
|
238
|
-
content.style.inset = '0 auto auto 0';
|
|
239
|
-
content.style.zIndex = '10';
|
|
240
|
-
const state = getState();
|
|
241
|
-
state?.mapped.set(anchor, floatable);
|
|
242
|
-
state?.mapped.set(content, floatable);
|
|
243
|
-
}
|
|
244
|
-
function isDisabled(element) {
|
|
245
|
-
return (element.disabled ||
|
|
246
|
-
element.readOnly ||
|
|
247
|
-
element.getAttribute('aria-disabled') === 'true');
|
|
248
|
-
}
|
|
249
|
-
function onClick(event) {
|
|
250
|
-
const related = findAncestor(event.target, SELECTOR_ALL);
|
|
251
|
-
if (!(related instanceof HTMLElement)) {
|
|
252
|
-
closeAll();
|
|
253
|
-
}
|
|
254
|
-
else if (related.hasAttribute(ATTRIBUTE_CONTENT)) {
|
|
255
|
-
closeAbove(related);
|
|
256
|
-
}
|
|
257
|
-
else if (!isDisabled(related)) {
|
|
258
|
-
toggle(related);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
function onKeyDown(event) {
|
|
262
|
-
if (event.key !== 'Escape') {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
const state = getState();
|
|
266
|
-
if (state == null || state.active.size === 0) {
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
closeNonInteractive(state);
|
|
270
|
-
const related = findAncestor(event.target, SELECTOR_CONTENT);
|
|
271
|
-
if (!(related instanceof HTMLElement)) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
closeAbove(related);
|
|
275
|
-
const floatable = state.mapped.get(related);
|
|
276
|
-
if (floatable != null) {
|
|
277
|
-
void floatable.toggle(false);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
function stop(floatable) {
|
|
281
|
-
if (floatable.frame != null) {
|
|
282
|
-
cancelAnimationFrame(floatable.frame);
|
|
283
|
-
}
|
|
284
|
-
floatable.frame = undefined;
|
|
285
|
-
}
|
|
286
|
-
function toggle(anchor) {
|
|
287
|
-
const floatable = getState()?.mapped.get(anchor);
|
|
288
|
-
if (floatable == null) {
|
|
289
|
-
closeAll();
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
const active = Boolean(floatable.active);
|
|
293
|
-
const content = findAncestor(floatable.options.anchor, SELECTOR_CONTENT);
|
|
294
|
-
if (content == null) {
|
|
295
|
-
closeAll();
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
closeAbove(content);
|
|
299
|
-
}
|
|
300
|
-
void floatable.toggle(!active);
|
|
301
|
-
}
|
|
302
|
-
function updatePosition(floatable) {
|
|
303
|
-
let position = floatable.options.anchor
|
|
304
|
-
.getAttribute(floatable.options.positionAttribute)
|
|
305
|
-
?.trim() ?? floatable.options.defaultPosition;
|
|
306
|
-
if (!POSITIONS.has(position)) {
|
|
307
|
-
position = floatable.options.defaultPosition;
|
|
308
|
-
}
|
|
309
|
-
let previousAnchor;
|
|
310
|
-
let previousContent;
|
|
311
|
-
let calculated = false;
|
|
312
|
-
let runOnAfter = false;
|
|
313
|
-
function run() {
|
|
314
|
-
const anchor = floatable.options.anchor.getBoundingClientRect();
|
|
315
|
-
const content = floatable.options.content.getBoundingClientRect();
|
|
316
|
-
if (calculated) {
|
|
317
|
-
if (previousAnchor != null && previousContent != null) {
|
|
318
|
-
if (PROPERTIES.every(property => anchor[property] === previousAnchor[property]) &&
|
|
319
|
-
PROPERTIES.every(property => content[property] === previousContent[property])) {
|
|
320
|
-
floatable.frame = requestAnimationFrame(run);
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
else {
|
|
326
|
-
calculated = true;
|
|
327
|
-
}
|
|
328
|
-
update(anchor, content);
|
|
329
|
-
}
|
|
330
|
-
function update(anchor, content) {
|
|
331
|
-
previousAnchor = anchor;
|
|
332
|
-
previousContent = content;
|
|
333
|
-
const left = getX(anchor, content, position);
|
|
334
|
-
const top = getY(anchor, content, position, floatable.options.preferAbove);
|
|
335
|
-
if (top + content.height > window.innerHeight) {
|
|
336
|
-
floatable.options.content.style.height = `${window.innerHeight - top}px`;
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
floatable.options.content.style.height = '';
|
|
340
|
-
}
|
|
341
|
-
if (left + content.width > window.innerWidth) {
|
|
342
|
-
floatable.options.content.style.width = `${window.innerWidth - left}px`;
|
|
343
|
-
}
|
|
344
|
-
else {
|
|
345
|
-
floatable.options.content.style.width = '';
|
|
346
|
-
}
|
|
347
|
-
// floatable.options.content.style.inset = `${top}px auto auto ${left}px`;
|
|
348
|
-
floatable.options.content.style.transform = `translate3d(${left}px, ${top}px, 0)`;
|
|
349
|
-
floatable.frame = requestAnimationFrame(run);
|
|
350
|
-
if (!runOnAfter) {
|
|
351
|
-
runOnAfter = true;
|
|
352
|
-
floatable.options.onAfter?.(true);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
floatable.frame = requestAnimationFrame(run);
|
|
356
|
-
}
|
|
357
|
-
//
|
|
358
|
-
const ATTRIBUTE_ANCHOR = 'oui-floatable-anchor';
|
|
359
|
-
const ATTRIBUTE_CONTENT = 'oui-floatable-content';
|
|
360
|
-
const INSERT_AFTEREND = 'afterend';
|
|
361
|
-
const POSITION_ABOVE = 'above';
|
|
362
|
-
const POSITION_BELOW = 'below';
|
|
363
|
-
const POSITION_BOTTOM = 'bottom';
|
|
364
|
-
const POSITION_END = 'end';
|
|
365
|
-
const POSITION_HORIZONTAL = 'horizontal';
|
|
366
|
-
const POSITION_START = 'start';
|
|
367
|
-
const POSITION_SUFFIX_END = '-end';
|
|
368
|
-
const POSITION_SUFFIX_START = '-start';
|
|
369
|
-
const POSITION_TOP = 'top';
|
|
370
|
-
const POSITION_VERTICAL = 'vertical';
|
|
371
|
-
const POSITIONS = new Set([
|
|
372
|
-
'above',
|
|
373
|
-
'above-end',
|
|
374
|
-
'above-start',
|
|
375
|
-
'below',
|
|
376
|
-
'below-end',
|
|
377
|
-
'below-start',
|
|
378
|
-
'end',
|
|
379
|
-
'end-bottom',
|
|
380
|
-
'end-top',
|
|
381
|
-
'horizontal',
|
|
382
|
-
'horizontal-bottom',
|
|
383
|
-
'horizontal-top',
|
|
384
|
-
'start',
|
|
385
|
-
'start-bottom',
|
|
386
|
-
'start-top',
|
|
387
|
-
'vertical',
|
|
388
|
-
'vertical-end',
|
|
389
|
-
'vertical-start',
|
|
390
|
-
]);
|
|
391
|
-
const PROPERTIES = ['bottom', 'left', 'right', 'top'];
|
|
392
|
-
const SELECTOR_ALL = `[${ATTRIBUTE_ANCHOR}], [${ATTRIBUTE_CONTENT}]`;
|
|
393
|
-
const SELECTOR_CONTENT = `[${ATTRIBUTE_CONTENT}]`;
|
|
394
|
-
//
|
|
395
|
-
if (window._oscarpalmer_oui_floatable == null) {
|
|
396
|
-
Object.defineProperty(window, '_oscarpalmer_oui_floatable', {
|
|
397
|
-
enumerable: false,
|
|
398
|
-
value: {
|
|
399
|
-
active: new Set(),
|
|
400
|
-
mapped: new Map(),
|
|
401
|
-
order: [],
|
|
402
|
-
},
|
|
403
|
-
writable: false,
|
|
404
|
-
});
|
|
405
|
-
on(document, 'keydown', event => {
|
|
406
|
-
onKeyDown(event);
|
|
407
|
-
});
|
|
408
|
-
on(document, 'click', event => {
|
|
409
|
-
onClick(event);
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
export { Floatable, deactivate };
|