@oscarpalmer/oui 0.16.0 → 0.18.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/badge.css +3 -3
- package/dist/css/content.css +9 -11
- package/dist/css/form.css +36 -22
- package/dist/css/message.css +4 -0
- package/dist/css/popover.css +3 -0
- package/dist/css/reset.css +176 -56
- package/dist/css/splitter.css +3 -3
- package/dist/css/tooltip.css +8 -0
- package/dist/js/attributable.full.js +48 -0
- package/dist/js/attributable.js +30 -45
- package/dist/js/floatable.full.js +413 -0
- package/dist/js/floatable.js +259 -132
- package/dist/js/focus-trap/embedded.full.js +351 -0
- package/dist/js/focus-trap/embedded.js +131 -131
- package/dist/js/focus-trap/standalone.full.js +402 -0
- package/dist/js/focus-trap/standalone.js +4 -4
- package/dist/js/index.js +4 -5
- package/dist/js/oui.full.js +1596 -0
- package/dist/js/popover.full.js +822 -0
- package/dist/js/popover.js +83 -156
- package/dist/js/splitter.full.js +605 -0
- package/dist/js/splitter.js +279 -200
- package/dist/js/tooltip.full.js +679 -0
- package/dist/js/tooltip.js +159 -144
- package/package.json +31 -59
- package/src/css/content.scss +1 -2
- package/src/css/form.scss +12 -4
- package/src/css/message.scss +3 -0
- package/src/css/mixins.scss +7 -0
- package/src/css/popover.scss +4 -0
- package/src/css/reset.scss +180 -54
- package/src/css/splitter.scss +3 -3
- package/src/css/tooltip.scss +8 -0
- package/src/js/floatable.ts +458 -124
- package/src/js/focus-trap/embedded.ts +149 -64
- package/src/js/focus-trap/standalone.ts +4 -4
- package/src/js/index.ts +1 -0
- package/src/js/popover.ts +51 -114
- package/src/js/splitter.ts +346 -109
- package/src/js/tooltip.ts +157 -63
- package/types/floatable.d.ts +19 -7
- package/types/focus-trap/embedded.d.ts +18 -5
- package/types/index.d.ts +1 -0
- package/types/popover.d.ts +2 -0
- package/types/splitter.d.ts +13 -1
- package/types/tooltip.d.ts +2 -1
- package/dist/js/attributable.cjs +0 -51
- package/dist/js/floatable.cjs +0 -134
- package/dist/js/focus-trap/embedded.cjs +0 -150
- package/dist/js/focus-trap/standalone.cjs +0 -8
- package/dist/js/index.cjs +0 -6
- package/dist/js/popover.cjs +0 -160
- package/dist/js/splitter.cjs +0 -211
- package/dist/js/tooltip.cjs +0 -160
- package/types/attributable.d.cts +0 -5
- package/types/floatable.d.cts +0 -16
- package/types/focus-trap/embedded.d.cts +0 -15
- package/types/focus-trap/standalone.d.cts +0 -3
- package/types/index.d.cts +0 -14
- package/types/popover.d.cts +0 -14
- package/types/splitter.d.cts +0 -10
- package/types/tooltip.d.cts +0 -3
package/src/js/tooltip.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
|
|
2
2
|
import {on} from '@oscarpalmer/toretto/event';
|
|
3
|
+
import {findAncestor} from '@oscarpalmer/toretto/find';
|
|
3
4
|
import {attributable} from './attributable';
|
|
4
|
-
import {
|
|
5
|
+
import {Floatable} from './floatable';
|
|
5
6
|
|
|
6
7
|
type Attributes = {
|
|
7
8
|
ariaDescribedby: string | null;
|
|
@@ -14,40 +15,80 @@ type Content = {
|
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
class Tooltip {
|
|
17
|
-
abortController = new AbortController();
|
|
18
18
|
attributes: Attributes;
|
|
19
|
+
|
|
19
20
|
floatable: Floatable;
|
|
20
21
|
|
|
22
|
+
timer: number | undefined;
|
|
23
|
+
|
|
21
24
|
constructor(anchor: HTMLElement, content: Content) {
|
|
22
25
|
this.attributes = content.attributes;
|
|
23
|
-
this.floatable = new Floatable(anchor, content.element);
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
this.floatable = new Floatable({
|
|
28
|
+
anchor,
|
|
29
|
+
content: content.element,
|
|
30
|
+
defaultPosition: 'vertical',
|
|
31
|
+
interactive: false,
|
|
32
|
+
preferAbove: true,
|
|
33
|
+
positionAttribute: ATTRIBUTE_POSITION,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
anchor.insertAdjacentElement('afterend', content.element);
|
|
37
|
+
|
|
38
|
+
TOOLTIPS_ALL.set(content.element, this);
|
|
26
39
|
}
|
|
27
40
|
|
|
28
41
|
destroy(): void {
|
|
29
|
-
|
|
30
|
-
reset(this.floatable.anchor, this.attributes);
|
|
42
|
+
const {attributes, floatable} = this;
|
|
31
43
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
TOOLTIPS_ACTIVE.delete(this);
|
|
45
|
+
TOOLTIPS_ALL.delete(floatable.options.anchor);
|
|
46
|
+
TOOLTIPS_ALL.delete(floatable.options.content);
|
|
47
|
+
|
|
48
|
+
reset(floatable.options.anchor, attributes);
|
|
49
|
+
|
|
50
|
+
floatable.options.content.remove();
|
|
51
|
+
floatable.destroy();
|
|
35
52
|
|
|
36
|
-
this.abortController = undefined as never;
|
|
37
53
|
this.attributes = undefined as never;
|
|
38
54
|
this.floatable = undefined as never;
|
|
39
55
|
}
|
|
40
56
|
}
|
|
41
57
|
|
|
42
58
|
function addTooltip(anchor: HTMLElement): void {
|
|
43
|
-
if (
|
|
59
|
+
if (TOOLTIPS_ALL.has(anchor)) {
|
|
44
60
|
return;
|
|
45
61
|
}
|
|
46
62
|
|
|
47
63
|
const content = getContent(anchor);
|
|
48
64
|
|
|
49
65
|
if (content != null) {
|
|
50
|
-
|
|
66
|
+
TOOLTIPS_ALL.set(anchor, new Tooltip(anchor, content));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function closeTooltips(next: Tooltip | undefined): void {
|
|
71
|
+
const tooltips = [...TOOLTIPS_ACTIVE];
|
|
72
|
+
|
|
73
|
+
for (const active of tooltips) {
|
|
74
|
+
if (active === next) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (active.timer != null) {
|
|
79
|
+
clearTimeout(active.timer);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
active.floatable.options.content.removeAttribute(
|
|
83
|
+
'oui-tooltip-content-active',
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
requestAnimationFrame(() => {
|
|
87
|
+
active.timer = setTimeout(() => {
|
|
88
|
+
TOOLTIPS_ACTIVE.delete(active);
|
|
89
|
+
active.floatable.toggle(false);
|
|
90
|
+
}, delay) as never;
|
|
91
|
+
});
|
|
51
92
|
}
|
|
52
93
|
}
|
|
53
94
|
|
|
@@ -112,7 +153,7 @@ function getContent(anchor: HTMLElement): Content | undefined {
|
|
|
112
153
|
}
|
|
113
154
|
|
|
114
155
|
function getElements(attribute: string | null): HTMLElement[] {
|
|
115
|
-
const ids = attribute?.split(
|
|
156
|
+
const ids = attribute?.split(EXPRESSION_WHITESPACE) ?? [];
|
|
116
157
|
const elements: HTMLElement[] = [];
|
|
117
158
|
|
|
118
159
|
for (const id of ids) {
|
|
@@ -137,57 +178,92 @@ function getElements(attribute: string | null): HTMLElement[] {
|
|
|
137
178
|
function getWrapper(anchor: HTMLElement): HTMLElement {
|
|
138
179
|
const wrapper = document.createElement('div');
|
|
139
180
|
|
|
140
|
-
wrapper.setAttribute(
|
|
181
|
+
wrapper.setAttribute(ATTRIBUTE_CONTENT, '');
|
|
141
182
|
|
|
142
|
-
wrapper.className = anchor.getAttribute(
|
|
183
|
+
wrapper.className = anchor.getAttribute(ATTRIBUTE_CLASS) ?? '';
|
|
143
184
|
wrapper.hidden = true;
|
|
144
|
-
wrapper.id = `${
|
|
185
|
+
wrapper.id = `${SELECTOR}-${++index}`;
|
|
145
186
|
wrapper.role = 'tooltip';
|
|
146
187
|
wrapper.tabIndex = -1;
|
|
147
188
|
|
|
148
189
|
return wrapper;
|
|
149
190
|
}
|
|
150
191
|
|
|
151
|
-
function
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
192
|
+
function onActivate(event: Event): void {
|
|
193
|
+
onToggle(event, true);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function onClick(event: Event): void {
|
|
197
|
+
if (findAncestor(event.target as HTMLElement, SELECTOR_CONTENT) != null) {
|
|
198
|
+
event.stopPropagation();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function onDeactivate(event: Event): void {
|
|
203
|
+
onToggle(event, false);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function onToggle(event: Event, activate: boolean): void {
|
|
207
|
+
if (toggle != null) {
|
|
208
|
+
cancelAnimationFrame(toggle);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
toggle = requestAnimationFrame(() => {
|
|
212
|
+
toggle = undefined;
|
|
213
|
+
|
|
214
|
+
const element = findAncestor(
|
|
215
|
+
event.target as HTMLElement,
|
|
216
|
+
SELECTOR_FULL,
|
|
217
|
+
) as HTMLElement;
|
|
218
|
+
|
|
219
|
+
const tooltip = TOOLTIPS_ALL.get(element);
|
|
220
|
+
|
|
221
|
+
if (TOOLTIPS_ACTIVE.size > 0) {
|
|
222
|
+
closeTooltips(tooltip);
|
|
171
223
|
}
|
|
172
224
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
{
|
|
181
|
-
signal: abortController.signal,
|
|
182
|
-
},
|
|
183
|
-
);
|
|
225
|
+
if (
|
|
226
|
+
tooltip == null ||
|
|
227
|
+
tooltip.floatable.options.content.hasAttribute(
|
|
228
|
+
'oui-tooltip-content-active',
|
|
229
|
+
) === activate
|
|
230
|
+
) {
|
|
231
|
+
return;
|
|
184
232
|
}
|
|
185
|
-
|
|
233
|
+
|
|
234
|
+
if (activate) {
|
|
235
|
+
if (tooltip.timer != null) {
|
|
236
|
+
clearTimeout(tooltip.timer);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
TOOLTIPS_ACTIVE.add(tooltip);
|
|
240
|
+
tooltip.floatable.toggle(true);
|
|
241
|
+
|
|
242
|
+
requestAnimationFrame(() => {
|
|
243
|
+
tooltip.floatable.options.content.setAttribute(
|
|
244
|
+
'oui-tooltip-content-active',
|
|
245
|
+
'',
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
tooltip.floatable.options.content.removeAttribute(
|
|
253
|
+
'oui-tooltip-content-active',
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
tooltip.timer ??= setTimeout(() => {
|
|
257
|
+
TOOLTIPS_ACTIVE.delete(tooltip);
|
|
258
|
+
tooltip.floatable.toggle(false);
|
|
259
|
+
}, delay) as never;
|
|
260
|
+
});
|
|
186
261
|
}
|
|
187
262
|
|
|
188
263
|
function removeTooltip(element: HTMLElement): void {
|
|
189
|
-
|
|
190
|
-
|
|
264
|
+
if (!element.hasAttribute(ATTRIBUTE_CONTENT)) {
|
|
265
|
+
TOOLTIPS_ALL.get(element)?.destroy();
|
|
266
|
+
}
|
|
191
267
|
}
|
|
192
268
|
|
|
193
269
|
function reset(anchor: HTMLElement, attributes: Attributes): void {
|
|
@@ -198,28 +274,46 @@ function reset(anchor: HTMLElement, attributes: Attributes): void {
|
|
|
198
274
|
}
|
|
199
275
|
}
|
|
200
276
|
|
|
277
|
+
function setDelay(value: number): void {
|
|
278
|
+
delay = typeof value === 'number' && value >= 0 ? value : delay;
|
|
279
|
+
}
|
|
280
|
+
|
|
201
281
|
//
|
|
202
282
|
|
|
203
|
-
const
|
|
283
|
+
const SELECTOR = 'oui-tooltip';
|
|
284
|
+
|
|
285
|
+
const ATTRIBUTE_CLASS = `${SELECTOR}-class`;
|
|
286
|
+
|
|
287
|
+
const ATTRIBUTE_CONTENT = `${SELECTOR}-content`;
|
|
288
|
+
|
|
289
|
+
const ATTRIBUTE_POSITION = `${SELECTOR}-position`;
|
|
290
|
+
|
|
291
|
+
const SELECTOR_CONTENT = `[${ATTRIBUTE_CONTENT}]`;
|
|
292
|
+
|
|
293
|
+
const SELECTOR_FULL = `[${SELECTOR}], [${ATTRIBUTE_CONTENT}]`;
|
|
204
294
|
|
|
205
|
-
const
|
|
295
|
+
const EXPRESSION_WHITESPACE = /\s+/;
|
|
206
296
|
|
|
207
|
-
const
|
|
297
|
+
const TOOLTIPS_ACTIVE: Set<Tooltip> = new Set();
|
|
208
298
|
|
|
209
|
-
const
|
|
299
|
+
const TOOLTIPS_ALL: WeakMap<HTMLElement, Tooltip> = new WeakMap();
|
|
210
300
|
|
|
211
|
-
|
|
301
|
+
let delay = 250;
|
|
212
302
|
|
|
213
303
|
let index = 0;
|
|
214
304
|
|
|
305
|
+
let toggle: DOMHighResTimeStamp | undefined;
|
|
306
|
+
|
|
215
307
|
//
|
|
216
308
|
|
|
217
|
-
attributable(
|
|
309
|
+
attributable(SELECTOR, addTooltip, removeTooltip);
|
|
218
310
|
|
|
219
|
-
on(document, '
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
311
|
+
on(document, 'click', onClick, {capture: true});
|
|
312
|
+
on(document, 'focusin', onActivate);
|
|
313
|
+
on(document, 'focusout', onDeactivate);
|
|
314
|
+
on(document, 'mousemove', onActivate);
|
|
315
|
+
on(document, 'touchstart', onActivate);
|
|
316
|
+
|
|
317
|
+
//
|
|
318
|
+
|
|
319
|
+
export {setDelay};
|
package/types/floatable.d.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
export declare class Floatable {
|
|
2
|
-
|
|
3
|
-
content: HTMLElement;
|
|
4
|
-
activate: number | undefined;
|
|
2
|
+
readonly options: Options;
|
|
5
3
|
active: boolean;
|
|
6
|
-
|
|
4
|
+
disabled: boolean;
|
|
7
5
|
frame: DOMHighResTimeStamp | undefined;
|
|
8
|
-
|
|
6
|
+
ignoreFocus: boolean;
|
|
7
|
+
constructor(options: Options);
|
|
9
8
|
destroy(): void;
|
|
9
|
+
disable(): void;
|
|
10
|
+
enable(): void;
|
|
11
|
+
toggle(active: boolean, ignoreFocus?: boolean): void;
|
|
10
12
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
type Options = {
|
|
14
|
+
anchor: HTMLElement;
|
|
15
|
+
content: HTMLElement;
|
|
16
|
+
defaultPosition: Position;
|
|
17
|
+
interactive: boolean;
|
|
18
|
+
positionAttribute: string;
|
|
19
|
+
preferAbove: boolean;
|
|
20
|
+
onAfter?: (active: boolean) => void;
|
|
21
|
+
};
|
|
22
|
+
type Position = 'above' | 'above-end' | 'above-start' | 'below' | 'below-end' | 'below-start' | 'end' | 'end-bottom' | 'end-top' | 'horizontal' | 'horizontal-bottom' | 'horizontal-top' | 'start' | 'start-bottom' | 'start-top' | 'vertical' | 'vertical-end' | 'vertical-start';
|
|
23
|
+
export declare function deactivate(floatable: Floatable): void;
|
|
24
|
+
export {};
|
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
export declare class FocusTrap {
|
|
2
|
+
disabled: boolean;
|
|
3
|
+
connected: boolean;
|
|
2
4
|
element: HTMLElement;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
embedded: boolean;
|
|
6
|
+
get contain(): boolean;
|
|
7
|
+
get noescape(): boolean;
|
|
8
|
+
constructor(element: HTMLElement, options?: Options);
|
|
9
|
+
connect(): void;
|
|
5
10
|
destroy(): void;
|
|
6
11
|
disable(): void;
|
|
12
|
+
disconnect(): void;
|
|
7
13
|
enable(): void;
|
|
14
|
+
focus(last: boolean): void;
|
|
8
15
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
type Options = {
|
|
17
|
+
noescape: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare function createFocusTrap(element: HTMLElement, options?: Options): FocusTrap;
|
|
20
|
+
export declare const FOCUS_TRAP_SELECTOR = "oui-focus-trap";
|
|
21
|
+
export declare const FOCUS_TRAP_CONTAIN = "oui-focus-trap-contain";
|
|
22
|
+
export declare const FOCUS_TRAP_NOESCAPE = "oui-focus-trap-noescape";
|
|
23
|
+
export declare const FOCUSTRAPS_ALL: WeakMap<HTMLElement, FocusTrap>;
|
|
24
|
+
export {};
|
package/types/index.d.ts
CHANGED
package/types/popover.d.ts
CHANGED
package/types/splitter.d.ts
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
declare global {
|
|
2
2
|
interface HTMLElementTagNameMap {
|
|
3
|
-
|
|
3
|
+
[SELECTOR]: OuiSplitterElement;
|
|
4
4
|
}
|
|
5
5
|
}
|
|
6
6
|
export declare class OuiSplitterElement extends HTMLElement {
|
|
7
7
|
#private;
|
|
8
|
+
static readonly observedAttributes: string[];
|
|
9
|
+
get max(): number;
|
|
10
|
+
set max(value: number);
|
|
11
|
+
get min(): number;
|
|
12
|
+
set min(value: number);
|
|
13
|
+
get size(): number;
|
|
14
|
+
set size(value: number);
|
|
15
|
+
get type(): string;
|
|
16
|
+
set type(value: string);
|
|
8
17
|
constructor();
|
|
9
18
|
connectedCallback(): void;
|
|
10
19
|
disconnectedCallback(): void;
|
|
20
|
+
attributeChangedCallback(name: string): void;
|
|
11
21
|
}
|
|
22
|
+
declare const SELECTOR = "oui-splitter";
|
|
23
|
+
export {};
|
package/types/tooltip.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
declare function setDelay(value: number): void;
|
|
2
|
+
export { setDelay };
|
package/dist/js/attributable.cjs
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
function attributable(attribute, add, remove) {
|
|
4
|
-
const observer = new MutationObserver((entries) => {
|
|
5
|
-
const { length } = entries;
|
|
6
|
-
for (let index = 0; index < length; index += 1) {
|
|
7
|
-
const entry = entries[index];
|
|
8
|
-
if (entry.type === "attributes" && entry.target instanceof HTMLElement) {
|
|
9
|
-
if (entry.target.hasAttribute(attribute)) {
|
|
10
|
-
handleAdded(attribute, [entry.target], add);
|
|
11
|
-
} else {
|
|
12
|
-
handleRemoved([entry.target], remove);
|
|
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(
|
|
32
|
-
(node) => node instanceof HTMLElement
|
|
33
|
-
);
|
|
34
|
-
const { length } = elements;
|
|
35
|
-
for (let index = 0; index < length; index += 1) {
|
|
36
|
-
const element = elements[index];
|
|
37
|
-
if (element.hasAttribute(attribute)) {
|
|
38
|
-
add(element);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
function handleRemoved(nodes, remove) {
|
|
43
|
-
const elements = [...nodes].filter(
|
|
44
|
-
(node) => node instanceof HTMLElement
|
|
45
|
-
);
|
|
46
|
-
const { length } = elements;
|
|
47
|
-
for (let index = 0; index < length; index += 1) {
|
|
48
|
-
remove(elements[index]);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
exports.attributable = attributable;
|
package/dist/js/floatable.cjs
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
class Floatable {
|
|
4
|
-
constructor(anchor, content) {
|
|
5
|
-
this.anchor = anchor;
|
|
6
|
-
this.content = content;
|
|
7
|
-
setStyles(content);
|
|
8
|
-
}
|
|
9
|
-
activate;
|
|
10
|
-
active = false;
|
|
11
|
-
deactivate;
|
|
12
|
-
frame;
|
|
13
|
-
destroy() {
|
|
14
|
-
this.activate = void 0;
|
|
15
|
-
this.deactivate = void 0;
|
|
16
|
-
this.frame = void 0;
|
|
17
|
-
this.anchor = void 0;
|
|
18
|
-
this.content = void 0;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function activate(floatable, active, order, time) {
|
|
22
|
-
stop(floatable);
|
|
23
|
-
if (floatable.active) {
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
if (time == null || time === 0) {
|
|
27
|
-
onActivate(floatable, active, order);
|
|
28
|
-
} else {
|
|
29
|
-
floatable.activate = +setTimeout(() => {
|
|
30
|
-
onActivate(floatable, active, order);
|
|
31
|
-
}, time);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function deactivate(floatable, active, order, time) {
|
|
35
|
-
stop(floatable);
|
|
36
|
-
if (!floatable.active) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
if (time == null || time === 0) {
|
|
40
|
-
onDeactivate(floatable, active, order);
|
|
41
|
-
} else {
|
|
42
|
-
floatable.deactivate = +setTimeout(() => {
|
|
43
|
-
onDeactivate(floatable, active, order);
|
|
44
|
-
}, time);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function getX(anchor, content) {
|
|
48
|
-
if (content.width + 2 * margin >= window.innerWidth) {
|
|
49
|
-
return {
|
|
50
|
-
left: margin,
|
|
51
|
-
right: margin
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
const left = anchor.left + (anchor.width - content.width) / 2;
|
|
55
|
-
const right = window.innerWidth - margin;
|
|
56
|
-
if (left < margin) {
|
|
57
|
-
return {
|
|
58
|
-
left: margin
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
if (left + content.width > right) {
|
|
62
|
-
return {
|
|
63
|
-
right: margin
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
return {
|
|
67
|
-
left
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
function getY(anchor, content) {
|
|
71
|
-
let top = anchor.top - content.height - margin;
|
|
72
|
-
if (top - margin >= 0) {
|
|
73
|
-
return top;
|
|
74
|
-
}
|
|
75
|
-
top = anchor.bottom + margin;
|
|
76
|
-
if (top + content.height + margin <= window.innerHeight) {
|
|
77
|
-
return top;
|
|
78
|
-
}
|
|
79
|
-
return anchor.top + (anchor.height - content.height) / 2;
|
|
80
|
-
}
|
|
81
|
-
function onActivate(floatable, active, order) {
|
|
82
|
-
floatable.active = true;
|
|
83
|
-
active.add(floatable);
|
|
84
|
-
if (order != null) {
|
|
85
|
-
order.push(floatable);
|
|
86
|
-
}
|
|
87
|
-
document.body.append(floatable.content);
|
|
88
|
-
floatable.content.hidden = false;
|
|
89
|
-
update(floatable);
|
|
90
|
-
}
|
|
91
|
-
function onDeactivate(floatable, active, order) {
|
|
92
|
-
floatable.active = false;
|
|
93
|
-
active.delete(floatable);
|
|
94
|
-
const index = order?.indexOf(floatable) ?? -1;
|
|
95
|
-
if (index > -1) {
|
|
96
|
-
order?.splice(index, 1);
|
|
97
|
-
}
|
|
98
|
-
floatable.content.hidden = true;
|
|
99
|
-
floatable.anchor.insertAdjacentElement("afterend", floatable.content);
|
|
100
|
-
}
|
|
101
|
-
function update(floatable) {
|
|
102
|
-
function run() {
|
|
103
|
-
const anchor = floatable.anchor.getBoundingClientRect();
|
|
104
|
-
const content = floatable.content.getBoundingClientRect();
|
|
105
|
-
const { left, right } = getX(anchor, content);
|
|
106
|
-
const top = getY(anchor, content);
|
|
107
|
-
floatable.content.style.inset = `${top}px ${right == null ? "auto" : `${right}px`} auto ${left == null ? "auto" : `${left}px`}`;
|
|
108
|
-
floatable.frame = requestAnimationFrame(run);
|
|
109
|
-
}
|
|
110
|
-
floatable.frame = requestAnimationFrame(run);
|
|
111
|
-
}
|
|
112
|
-
function setStyles(content) {
|
|
113
|
-
content.style.position = "fixed";
|
|
114
|
-
content.style.inset = "0 auto auto 0";
|
|
115
|
-
content.style.zIndex = "10";
|
|
116
|
-
}
|
|
117
|
-
function stop(floatable) {
|
|
118
|
-
if (floatable.frame != null) {
|
|
119
|
-
cancelAnimationFrame(floatable.frame);
|
|
120
|
-
}
|
|
121
|
-
if (floatable.activate != null) {
|
|
122
|
-
clearTimeout(floatable.activate);
|
|
123
|
-
}
|
|
124
|
-
if (floatable.deactivate != null) {
|
|
125
|
-
clearTimeout(floatable.deactivate);
|
|
126
|
-
}
|
|
127
|
-
floatable.activate = void 0;
|
|
128
|
-
floatable.deactivate = void 0;
|
|
129
|
-
floatable.frame = void 0;
|
|
130
|
-
}
|
|
131
|
-
const margin = Number.parseInt(getComputedStyle(document.documentElement).fontSize, 10) / 2;
|
|
132
|
-
exports.Floatable = Floatable;
|
|
133
|
-
exports.activate = activate;
|
|
134
|
-
exports.deactivate = deactivate;
|