@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/floatable.ts
CHANGED
|
@@ -1,175 +1,350 @@
|
|
|
1
|
+
import {on} from '@oscarpalmer/toretto/event';
|
|
2
|
+
import {findAncestor} from '@oscarpalmer/toretto/find';
|
|
3
|
+
|
|
1
4
|
export class Floatable {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
frame: DOMHighResTimeStamp | undefined;
|
|
6
|
-
|
|
7
|
-
constructor(
|
|
8
|
-
public anchor: HTMLElement,
|
|
9
|
-
public content: HTMLElement,
|
|
10
|
-
) {
|
|
11
|
-
setStyles(content);
|
|
12
|
-
}
|
|
5
|
+
active = false;
|
|
6
|
+
|
|
7
|
+
disabled = false;
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
frame: DOMHighResTimeStamp | undefined;
|
|
10
|
+
|
|
11
|
+
ignoreFocus = false;
|
|
12
|
+
|
|
13
|
+
constructor(readonly options: Options) {
|
|
14
|
+
initialize(this);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
destroy(): void {
|
|
18
|
+
stop(this);
|
|
19
|
+
|
|
20
|
+
const state = getState();
|
|
21
|
+
|
|
22
|
+
state?.active.delete(this);
|
|
23
|
+
state?.mapped.delete(this.options.anchor);
|
|
24
|
+
state?.mapped.delete(this.options.content);
|
|
25
|
+
|
|
26
|
+
this.options.anchor = undefined as never;
|
|
27
|
+
this.options.content = undefined as never;
|
|
28
|
+
}
|
|
18
29
|
|
|
19
|
-
|
|
20
|
-
|
|
30
|
+
disable(): void {
|
|
31
|
+
if (this.disabled) {
|
|
32
|
+
return;
|
|
21
33
|
}
|
|
34
|
+
|
|
35
|
+
this.disabled = true;
|
|
36
|
+
|
|
37
|
+
stop(this);
|
|
38
|
+
|
|
39
|
+
getState()?.active.delete(this);
|
|
22
40
|
}
|
|
23
41
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
time?: number,
|
|
29
|
-
): void {
|
|
30
|
-
stop(floatable);
|
|
42
|
+
enable(): void {
|
|
43
|
+
if (!this.disabled) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
31
46
|
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
this.disabled = false;
|
|
48
|
+
|
|
49
|
+
initialize(this);
|
|
34
50
|
}
|
|
35
51
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
toggle(active: boolean, ignoreFocus?: boolean): void {
|
|
53
|
+
if (this.disabled || this.active === active) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const state = getState();
|
|
58
|
+
|
|
59
|
+
if (state == null) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.ignoreFocus = !active && (ignoreFocus ?? false);
|
|
64
|
+
|
|
65
|
+
stop(this);
|
|
66
|
+
|
|
67
|
+
if (active) {
|
|
68
|
+
state.active.add(this);
|
|
69
|
+
} else {
|
|
70
|
+
state.active.delete(this);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (this.options.interactive) {
|
|
74
|
+
if (active) {
|
|
75
|
+
state.order.push(this);
|
|
76
|
+
} else {
|
|
77
|
+
const index = state.order.indexOf(this);
|
|
78
|
+
|
|
79
|
+
if (index > -1) {
|
|
80
|
+
state.order.splice(index, 1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (active) {
|
|
86
|
+
activate(this);
|
|
87
|
+
} else {
|
|
88
|
+
deactivate(this);
|
|
89
|
+
}
|
|
42
90
|
}
|
|
43
91
|
}
|
|
44
92
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
93
|
+
type FloatableWindow = Window & {
|
|
94
|
+
// biome-ignore lint/style/useNamingConvention: Non-standard name to avoid conflicts
|
|
95
|
+
_oscarpalmer_oui_floatable: State;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
type Options = {
|
|
99
|
+
anchor: HTMLElement;
|
|
100
|
+
content: HTMLElement;
|
|
101
|
+
defaultPosition: Position;
|
|
102
|
+
interactive: boolean;
|
|
103
|
+
positionAttribute: string;
|
|
104
|
+
preferAbove: boolean;
|
|
105
|
+
onAfter?: (active: boolean) => void;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
type Position =
|
|
109
|
+
| 'above'
|
|
110
|
+
| 'above-end'
|
|
111
|
+
| 'above-start'
|
|
112
|
+
| 'below'
|
|
113
|
+
| 'below-end'
|
|
114
|
+
| 'below-start'
|
|
115
|
+
| 'end'
|
|
116
|
+
| 'end-bottom'
|
|
117
|
+
| 'end-top'
|
|
118
|
+
| 'horizontal'
|
|
119
|
+
| 'horizontal-bottom'
|
|
120
|
+
| 'horizontal-top'
|
|
121
|
+
| 'start'
|
|
122
|
+
| 'start-bottom'
|
|
123
|
+
| 'start-top'
|
|
124
|
+
| 'vertical'
|
|
125
|
+
| 'vertical-end'
|
|
126
|
+
| 'vertical-start';
|
|
127
|
+
|
|
128
|
+
type State = {
|
|
129
|
+
active: Set<Floatable>;
|
|
130
|
+
mapped: Map<HTMLElement, Floatable>;
|
|
131
|
+
order: Floatable[];
|
|
132
|
+
};
|
|
52
133
|
|
|
53
|
-
|
|
134
|
+
//
|
|
135
|
+
|
|
136
|
+
function activate(floatable: Floatable): void {
|
|
137
|
+
floatable.active = true;
|
|
138
|
+
|
|
139
|
+
document.body.append(floatable.options.content);
|
|
140
|
+
|
|
141
|
+
floatable.options.content.hidden = false;
|
|
142
|
+
|
|
143
|
+
updatePosition(floatable);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function closeAbove(element: HTMLElement): void {
|
|
147
|
+
const state = getState();
|
|
148
|
+
|
|
149
|
+
if (state == null) {
|
|
54
150
|
return;
|
|
55
151
|
}
|
|
56
152
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
153
|
+
const index = state.order.findIndex(
|
|
154
|
+
floatable =>
|
|
155
|
+
floatable.options.anchor === element ||
|
|
156
|
+
floatable.options.content === element,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
if (index === -1 || index === state.order.length - 1) {
|
|
160
|
+
return;
|
|
63
161
|
}
|
|
64
|
-
}
|
|
65
162
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
content: DOMRect,
|
|
69
|
-
): {left?: number; right?: number} {
|
|
70
|
-
if (content.width + 2 * margin >= window.innerWidth) {
|
|
71
|
-
return {
|
|
72
|
-
left: margin,
|
|
73
|
-
right: margin,
|
|
74
|
-
};
|
|
163
|
+
while (index < state.order.length - 1) {
|
|
164
|
+
void state.order.pop()?.toggle(false, true);
|
|
75
165
|
}
|
|
166
|
+
}
|
|
76
167
|
|
|
77
|
-
|
|
78
|
-
const
|
|
168
|
+
function closeAll(): void {
|
|
169
|
+
const state = getState();
|
|
170
|
+
|
|
171
|
+
const floatables = [...(state?.active ?? [])];
|
|
172
|
+
|
|
173
|
+
state?.active.clear();
|
|
79
174
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
left: margin,
|
|
83
|
-
};
|
|
175
|
+
for (const floatable of floatables) {
|
|
176
|
+
void floatable.toggle(false, true);
|
|
84
177
|
}
|
|
178
|
+
}
|
|
85
179
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
180
|
+
function closeNonInteractive(state: State): void {
|
|
181
|
+
for (const floatable of state.active) {
|
|
182
|
+
if (!floatable.options.interactive) {
|
|
183
|
+
void floatable.toggle(false);
|
|
184
|
+
}
|
|
90
185
|
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function deactivate(floatable: Floatable): void {
|
|
189
|
+
floatable.active = false;
|
|
190
|
+
|
|
191
|
+
floatable.options.content.hidden = true;
|
|
192
|
+
|
|
193
|
+
floatable.options.anchor.insertAdjacentElement(
|
|
194
|
+
INSERT_AFTEREND,
|
|
195
|
+
floatable.options.content,
|
|
196
|
+
);
|
|
91
197
|
|
|
92
|
-
|
|
93
|
-
left,
|
|
94
|
-
};
|
|
198
|
+
floatable.options.onAfter?.(false);
|
|
95
199
|
}
|
|
96
200
|
|
|
97
|
-
function
|
|
98
|
-
|
|
201
|
+
function getState(): State {
|
|
202
|
+
return (window as unknown as FloatableWindow)._oscarpalmer_oui_floatable;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function getX(anchor: DOMRect, content: DOMRect, position: Position): number {
|
|
206
|
+
if (position.endsWith(POSITION_SUFFIX_END)) {
|
|
207
|
+
const end = anchor.right - content.width;
|
|
99
208
|
|
|
100
|
-
|
|
101
|
-
return top;
|
|
209
|
+
return end < 0 ? 0 : end;
|
|
102
210
|
}
|
|
103
211
|
|
|
104
|
-
|
|
212
|
+
if (position.endsWith(POSITION_SUFFIX_START)) {
|
|
213
|
+
const start = anchor.left;
|
|
105
214
|
|
|
106
|
-
|
|
107
|
-
|
|
215
|
+
return start + content.width > window.innerWidth
|
|
216
|
+
? window.innerWidth - content.width
|
|
217
|
+
: start;
|
|
108
218
|
}
|
|
109
219
|
|
|
110
|
-
|
|
111
|
-
|
|
220
|
+
if (position.startsWith(POSITION_END)) {
|
|
221
|
+
return anchor.right;
|
|
222
|
+
}
|
|
112
223
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
order?: Floatable[],
|
|
117
|
-
): void {
|
|
118
|
-
floatable.active = true;
|
|
224
|
+
if (position.startsWith(POSITION_START)) {
|
|
225
|
+
return anchor.left - content.width;
|
|
226
|
+
}
|
|
119
227
|
|
|
120
|
-
|
|
228
|
+
if (position.startsWith(POSITION_HORIZONTAL)) {
|
|
229
|
+
if (anchor.right + content.width <= window.innerWidth) {
|
|
230
|
+
return anchor.right;
|
|
231
|
+
}
|
|
121
232
|
|
|
122
|
-
|
|
123
|
-
order.push(floatable);
|
|
233
|
+
return anchor.left - content.width;
|
|
124
234
|
}
|
|
125
235
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
floatable.content.hidden = false;
|
|
236
|
+
const left = anchor.left + (anchor.width - content.width) / 2;
|
|
129
237
|
|
|
130
|
-
|
|
238
|
+
return left < 0 ? 0 : left;
|
|
131
239
|
}
|
|
132
240
|
|
|
133
|
-
function
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
241
|
+
function getY(
|
|
242
|
+
anchor: DOMRect,
|
|
243
|
+
content: DOMRect,
|
|
244
|
+
position: Position,
|
|
245
|
+
preferAbove: boolean,
|
|
246
|
+
): number {
|
|
247
|
+
if (position.startsWith(POSITION_ABOVE)) {
|
|
248
|
+
return anchor.top - content.height;
|
|
249
|
+
}
|
|
139
250
|
|
|
140
|
-
|
|
251
|
+
if (position.startsWith(POSITION_BELOW)) {
|
|
252
|
+
return anchor.bottom;
|
|
253
|
+
}
|
|
141
254
|
|
|
142
|
-
|
|
255
|
+
if (position.endsWith(POSITION_BOTTOM)) {
|
|
256
|
+
const bottom = anchor.bottom - content.height;
|
|
143
257
|
|
|
144
|
-
|
|
145
|
-
order?.splice(index, 1);
|
|
258
|
+
return bottom < 0 ? 0 : bottom;
|
|
146
259
|
}
|
|
147
260
|
|
|
148
|
-
|
|
261
|
+
if (position.endsWith(POSITION_TOP)) {
|
|
262
|
+
const top = anchor.top;
|
|
149
263
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
function run(): void {
|
|
155
|
-
const anchor = floatable.anchor.getBoundingClientRect();
|
|
156
|
-
const content = floatable.content.getBoundingClientRect();
|
|
264
|
+
return top + content.height > window.innerHeight
|
|
265
|
+
? window.innerHeight - content.height
|
|
266
|
+
: top;
|
|
267
|
+
}
|
|
157
268
|
|
|
158
|
-
|
|
159
|
-
|
|
269
|
+
if (position.startsWith(POSITION_VERTICAL)) {
|
|
270
|
+
if (preferAbove && anchor.top - content.height >= 0) {
|
|
271
|
+
return anchor.top - content.height;
|
|
272
|
+
}
|
|
160
273
|
|
|
161
|
-
|
|
274
|
+
if (anchor.bottom + content.height <= document.body.clientHeight) {
|
|
275
|
+
return anchor.bottom;
|
|
276
|
+
}
|
|
162
277
|
|
|
163
|
-
|
|
278
|
+
return anchor.top - content.height;
|
|
164
279
|
}
|
|
165
280
|
|
|
166
|
-
|
|
281
|
+
return anchor.top + (anchor.height - content.height) / 2;
|
|
167
282
|
}
|
|
168
283
|
|
|
169
|
-
function
|
|
284
|
+
function initialize(floatable: Floatable): void {
|
|
285
|
+
const {anchor, content, interactive} = floatable.options;
|
|
286
|
+
|
|
287
|
+
if (interactive) {
|
|
288
|
+
anchor.setAttribute(ATTRIBUTE_ANCHOR, '');
|
|
289
|
+
content.setAttribute(ATTRIBUTE_CONTENT, '');
|
|
290
|
+
}
|
|
291
|
+
|
|
170
292
|
content.style.position = 'fixed';
|
|
171
293
|
content.style.inset = '0 auto auto 0';
|
|
172
294
|
content.style.zIndex = '10';
|
|
295
|
+
|
|
296
|
+
const state = getState();
|
|
297
|
+
|
|
298
|
+
state?.mapped.set(anchor, floatable);
|
|
299
|
+
state?.mapped.set(content, floatable);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function isDisabled(element: HTMLElement): boolean {
|
|
303
|
+
return (
|
|
304
|
+
(element as HTMLInputElement).disabled ||
|
|
305
|
+
(element as HTMLInputElement).readOnly ||
|
|
306
|
+
element.getAttribute('aria-disabled') === 'true'
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function onClick(event: PointerEvent): void {
|
|
311
|
+
const related = findAncestor(event.target as never, SELECTOR_ALL);
|
|
312
|
+
|
|
313
|
+
if (!(related instanceof HTMLElement)) {
|
|
314
|
+
closeAll();
|
|
315
|
+
} else if (related.hasAttribute(ATTRIBUTE_CONTENT)) {
|
|
316
|
+
closeAbove(related);
|
|
317
|
+
} else if (!isDisabled(related)) {
|
|
318
|
+
toggle(related);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function onKeyDown(event: KeyboardEvent): void {
|
|
323
|
+
if (event.key !== 'Escape') {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const state = getState();
|
|
328
|
+
|
|
329
|
+
if (state == null || state.active.size === 0) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
closeNonInteractive(state);
|
|
334
|
+
|
|
335
|
+
const related = findAncestor(event.target as never, SELECTOR_CONTENT);
|
|
336
|
+
|
|
337
|
+
if (!(related instanceof HTMLElement)) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
closeAbove(related);
|
|
342
|
+
|
|
343
|
+
const floatable = state.mapped.get(related);
|
|
344
|
+
|
|
345
|
+
if (floatable != null) {
|
|
346
|
+
void floatable.toggle(false);
|
|
347
|
+
}
|
|
173
348
|
}
|
|
174
349
|
|
|
175
350
|
function stop(floatable: Floatable): void {
|
|
@@ -177,20 +352,179 @@ function stop(floatable: Floatable): void {
|
|
|
177
352
|
cancelAnimationFrame(floatable.frame);
|
|
178
353
|
}
|
|
179
354
|
|
|
180
|
-
|
|
181
|
-
|
|
355
|
+
floatable.frame = undefined;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function toggle(anchor: HTMLElement): void {
|
|
359
|
+
const floatable = getState()?.mapped.get(anchor);
|
|
360
|
+
|
|
361
|
+
if (floatable == null) {
|
|
362
|
+
closeAll();
|
|
363
|
+
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const active = Boolean(floatable.active);
|
|
368
|
+
const content = findAncestor(floatable.options.anchor, SELECTOR_CONTENT);
|
|
369
|
+
|
|
370
|
+
if (content == null) {
|
|
371
|
+
closeAll();
|
|
372
|
+
} else {
|
|
373
|
+
closeAbove(content as HTMLElement);
|
|
182
374
|
}
|
|
183
375
|
|
|
184
|
-
|
|
185
|
-
|
|
376
|
+
void floatable.toggle(!active);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function updatePosition(floatable: Floatable): void {
|
|
380
|
+
let position: Position =
|
|
381
|
+
(floatable.options.anchor
|
|
382
|
+
.getAttribute(floatable.options.positionAttribute)
|
|
383
|
+
?.trim() as Position) ?? floatable.options.defaultPosition;
|
|
384
|
+
|
|
385
|
+
if (!POSITIONS.has(position as Position)) {
|
|
386
|
+
position = floatable.options.defaultPosition;
|
|
186
387
|
}
|
|
187
388
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
389
|
+
let previousAnchor: DOMRect;
|
|
390
|
+
let previousContent: DOMRect;
|
|
391
|
+
|
|
392
|
+
let calculated = false;
|
|
393
|
+
let runOnAfter = false;
|
|
394
|
+
|
|
395
|
+
function run(): void {
|
|
396
|
+
const anchor = floatable.options.anchor.getBoundingClientRect();
|
|
397
|
+
const content = floatable.options.content.getBoundingClientRect();
|
|
398
|
+
|
|
399
|
+
if (calculated) {
|
|
400
|
+
if (previousAnchor != null && previousContent != null) {
|
|
401
|
+
if (
|
|
402
|
+
PROPERTIES.every(
|
|
403
|
+
property => anchor[property] === previousAnchor[property],
|
|
404
|
+
) &&
|
|
405
|
+
PROPERTIES.every(
|
|
406
|
+
property => content[property] === previousContent[property],
|
|
407
|
+
)
|
|
408
|
+
) {
|
|
409
|
+
floatable.frame = requestAnimationFrame(run);
|
|
410
|
+
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
calculated = true;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
update(anchor, content);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function update(anchor: DOMRect, content: DOMRect): void {
|
|
422
|
+
previousAnchor = anchor;
|
|
423
|
+
previousContent = content;
|
|
424
|
+
|
|
425
|
+
const left = getX(anchor, content, position);
|
|
426
|
+
const top = getY(anchor, content, position, floatable.options.preferAbove);
|
|
427
|
+
|
|
428
|
+
if (top + content.height > window.innerHeight) {
|
|
429
|
+
floatable.options.content.style.height = `${window.innerHeight - top}px`;
|
|
430
|
+
} else {
|
|
431
|
+
floatable.options.content.style.height = '';
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (left + content.width > window.innerWidth) {
|
|
435
|
+
floatable.options.content.style.width = `${window.innerWidth - left}px`;
|
|
436
|
+
} else {
|
|
437
|
+
floatable.options.content.style.width = '';
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// floatable.options.content.style.inset = `${top}px auto auto ${left}px`;
|
|
441
|
+
floatable.options.content.style.transform = `translate3d(${left}px, ${top}px, 0)`;
|
|
442
|
+
|
|
443
|
+
floatable.frame = requestAnimationFrame(run);
|
|
444
|
+
|
|
445
|
+
if (!runOnAfter) {
|
|
446
|
+
runOnAfter = true;
|
|
447
|
+
|
|
448
|
+
floatable.options.onAfter?.(true);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
floatable.frame = requestAnimationFrame(run);
|
|
191
453
|
}
|
|
192
454
|
|
|
193
455
|
//
|
|
194
456
|
|
|
195
|
-
const
|
|
196
|
-
|
|
457
|
+
const ATTRIBUTE_ANCHOR = 'oui-floatable-anchor';
|
|
458
|
+
|
|
459
|
+
const ATTRIBUTE_CONTENT = 'oui-floatable-content';
|
|
460
|
+
|
|
461
|
+
const INSERT_AFTEREND = 'afterend';
|
|
462
|
+
|
|
463
|
+
const POSITION_ABOVE = 'above';
|
|
464
|
+
|
|
465
|
+
const POSITION_BELOW = 'below';
|
|
466
|
+
|
|
467
|
+
const POSITION_BOTTOM = 'bottom';
|
|
468
|
+
|
|
469
|
+
const POSITION_END = 'end';
|
|
470
|
+
|
|
471
|
+
const POSITION_HORIZONTAL = 'horizontal';
|
|
472
|
+
|
|
473
|
+
const POSITION_START = 'start';
|
|
474
|
+
|
|
475
|
+
const POSITION_SUFFIX_END = '-end';
|
|
476
|
+
|
|
477
|
+
const POSITION_SUFFIX_START = '-start';
|
|
478
|
+
|
|
479
|
+
const POSITION_TOP = 'top';
|
|
480
|
+
|
|
481
|
+
const POSITION_VERTICAL = 'vertical';
|
|
482
|
+
|
|
483
|
+
const POSITIONS: Set<Position> = new Set([
|
|
484
|
+
'above',
|
|
485
|
+
'above-end',
|
|
486
|
+
'above-start',
|
|
487
|
+
'below',
|
|
488
|
+
'below-end',
|
|
489
|
+
'below-start',
|
|
490
|
+
'end',
|
|
491
|
+
'end-bottom',
|
|
492
|
+
'end-top',
|
|
493
|
+
'horizontal',
|
|
494
|
+
'horizontal-bottom',
|
|
495
|
+
'horizontal-top',
|
|
496
|
+
'start',
|
|
497
|
+
'start-bottom',
|
|
498
|
+
'start-top',
|
|
499
|
+
'vertical',
|
|
500
|
+
'vertical-end',
|
|
501
|
+
'vertical-start',
|
|
502
|
+
]);
|
|
503
|
+
|
|
504
|
+
const PROPERTIES: (keyof DOMRect)[] = ['bottom', 'left', 'right', 'top'];
|
|
505
|
+
|
|
506
|
+
const SELECTOR_ALL = `[${ATTRIBUTE_ANCHOR}], [${ATTRIBUTE_CONTENT}]`;
|
|
507
|
+
|
|
508
|
+
const SELECTOR_CONTENT = `[${ATTRIBUTE_CONTENT}]`;
|
|
509
|
+
|
|
510
|
+
//
|
|
511
|
+
|
|
512
|
+
if ((window as unknown as FloatableWindow)._oscarpalmer_oui_floatable == null) {
|
|
513
|
+
Object.defineProperty(window, '_oscarpalmer_oui_floatable', {
|
|
514
|
+
enumerable: false,
|
|
515
|
+
value: {
|
|
516
|
+
active: new Set(),
|
|
517
|
+
mapped: new Map(),
|
|
518
|
+
order: [],
|
|
519
|
+
},
|
|
520
|
+
writable: false,
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
on(document, 'keydown', event => {
|
|
524
|
+
onKeyDown(event);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
on(document, 'click', event => {
|
|
528
|
+
onClick(event);
|
|
529
|
+
});
|
|
530
|
+
}
|