@oscarpalmer/oui 0.21.0 → 0.22.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/popover.css +9 -9
- package/dist/css/tooltip.css +20 -20
- package/dist/js/index.d.mts +46 -3
- package/dist/js/index.mjs +2536 -6
- package/dist/js/internal/draggable.d.mts +1 -1
- package/dist/js/internal/draggable.mjs +3 -0
- package/dist/js/internal/floatable.d.mts +2 -2
- package/dist/js/internal/floatable.mjs +38 -135
- package/dist/js/movable.d.mts +1 -1
- package/dist/js/popover.mjs +1 -1
- package/dist/js/sortable.d.mts +1 -1
- package/dist/js/tooltip.d.mts +1 -1
- package/dist/js/tooltip.mjs +95 -97
- package/package.json +13 -5
- package/src/css/mixins.scss +8 -12
- package/src/css/popover.scss +1 -1
- package/src/css/tooltip.scss +22 -22
- package/src/js/internal/floatable.ts +53 -270
- package/src/js/popover.ts +1 -1
- package/src/js/tooltip.ts +129 -131
- /package/dist/js/{movable-BEo2LbUI.d.mts → movable-BKFgoaOl.d.mts} +0 -0
package/src/js/tooltip.ts
CHANGED
|
@@ -4,53 +4,57 @@ import {findAncestor} from '@oscarpalmer/toretto/find';
|
|
|
4
4
|
import {attributable} from './internal/attributable';
|
|
5
5
|
import {Floatable} from './internal/floatable';
|
|
6
6
|
|
|
7
|
-
type Attributes = {
|
|
8
|
-
ariaDescribedby: string | null;
|
|
9
|
-
ariaLabelledBy: string | null;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
type Content = {
|
|
13
|
-
attributes: Attributes;
|
|
14
|
-
element: HTMLElement;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
7
|
class Tooltip {
|
|
18
|
-
|
|
8
|
+
elements: HTMLElement[];
|
|
19
9
|
|
|
20
10
|
floatable: Floatable;
|
|
21
11
|
|
|
22
|
-
|
|
12
|
+
constructor(anchor: HTMLElement, elements: HTMLElement[]) {
|
|
13
|
+
const content = getContent();
|
|
23
14
|
|
|
24
|
-
|
|
25
|
-
this.attributes = content.attributes;
|
|
15
|
+
this.elements = elements;
|
|
26
16
|
|
|
27
17
|
this.floatable = new Floatable({
|
|
28
18
|
anchor,
|
|
29
|
-
content
|
|
30
|
-
defaultPosition: '
|
|
19
|
+
content,
|
|
20
|
+
defaultPosition: 'above',
|
|
31
21
|
interactive: false,
|
|
32
|
-
preferAbove: true,
|
|
33
22
|
positionAttribute: ATTRIBUTE_POSITION,
|
|
34
|
-
|
|
23
|
+
reusable: true,
|
|
24
|
+
onAfter: (active: boolean): void => {
|
|
25
|
+
if (active) {
|
|
26
|
+
content.innerHTML = '';
|
|
27
|
+
|
|
28
|
+
content.append(...this.elements);
|
|
29
|
+
}
|
|
35
30
|
|
|
36
|
-
|
|
31
|
+
if (active || TOOLTIPS_ACTIVE.size > 0) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
content.removeAttribute(ATTRIBUTE_CONTENT_ACTIVE);
|
|
36
|
+
|
|
37
|
+
contentTimer = setTimeout(() => {
|
|
38
|
+
content.innerHTML = '';
|
|
39
|
+
|
|
40
|
+
content.remove();
|
|
41
|
+
}, delayClose);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
destroy(): void {
|
|
42
|
-
const {
|
|
47
|
+
const {floatable} = this;
|
|
43
48
|
|
|
44
49
|
TOOLTIPS_ACTIVE.delete(this);
|
|
50
|
+
TOOLTIPS_PENDING.delete(this);
|
|
51
|
+
|
|
45
52
|
TOOLTIPS_ALL.delete(floatable.options.anchor);
|
|
46
53
|
TOOLTIPS_ALL.delete(floatable.options.content);
|
|
47
54
|
|
|
48
|
-
reset(floatable.options.anchor, attributes);
|
|
49
|
-
|
|
50
|
-
floatable.options.content.remove();
|
|
51
55
|
floatable.destroy();
|
|
52
56
|
|
|
53
|
-
this.
|
|
57
|
+
this.elements = [];
|
|
54
58
|
this.floatable = undefined as never;
|
|
55
59
|
}
|
|
56
60
|
}
|
|
@@ -60,79 +64,54 @@ function addTooltip(anchor: HTMLElement): void {
|
|
|
60
64
|
return;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
const
|
|
67
|
+
const elements = getElements(anchor);
|
|
64
68
|
|
|
65
|
-
if (
|
|
66
|
-
TOOLTIPS_ALL.set(anchor, new Tooltip(anchor,
|
|
69
|
+
if (elements != null) {
|
|
70
|
+
TOOLTIPS_ALL.set(anchor, new Tooltip(anchor, elements));
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
function closeTooltips(
|
|
71
|
-
const
|
|
74
|
+
function closeTooltips(current?: Tooltip): void {
|
|
75
|
+
const pending = [...TOOLTIPS_PENDING];
|
|
72
76
|
|
|
73
|
-
for (const
|
|
74
|
-
if (
|
|
77
|
+
for (const item of pending) {
|
|
78
|
+
if (item === current) {
|
|
75
79
|
continue;
|
|
76
80
|
}
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
clearTimeout(active.timer);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
active.floatable?.options.content.removeAttribute('oui-tooltip-content-active');
|
|
83
|
-
|
|
84
|
-
requestAnimationFrame(() => {
|
|
85
|
-
active.timer = setTimeout(() => {
|
|
86
|
-
TOOLTIPS_ACTIVE.delete(active);
|
|
87
|
-
|
|
88
|
-
active.floatable?.toggle(false);
|
|
89
|
-
}, delay) as never;
|
|
90
|
-
});
|
|
82
|
+
TOOLTIPS_PENDING.delete(item);
|
|
91
83
|
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function getContent(anchor: HTMLElement): Content | undefined {
|
|
95
|
-
const attributes = {
|
|
96
|
-
ariaDescribedby: anchor.getAttribute('aria-describedby'),
|
|
97
|
-
ariaLabelledBy: anchor.getAttribute('aria-labelledby'),
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const wrapper = getWrapper(anchor);
|
|
101
84
|
|
|
102
|
-
const
|
|
85
|
+
const active = [...TOOLTIPS_ACTIVE];
|
|
103
86
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
for (const element of labelledElements) {
|
|
108
|
-
wrapper.append(element);
|
|
87
|
+
for (const item of active) {
|
|
88
|
+
if (item === current) {
|
|
89
|
+
continue;
|
|
109
90
|
}
|
|
110
91
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
};
|
|
92
|
+
TOOLTIPS_ACTIVE.delete(item);
|
|
93
|
+
|
|
94
|
+
item.floatable.toggle(false);
|
|
115
95
|
}
|
|
96
|
+
}
|
|
116
97
|
|
|
117
|
-
|
|
98
|
+
function getElements(anchor: HTMLElement): HTMLElement[] | undefined {
|
|
99
|
+
let elements = findElements(anchor.getAttribute(ARIA_LABELLEDBY));
|
|
118
100
|
|
|
119
|
-
if (
|
|
120
|
-
|
|
101
|
+
if (elements.length > 0) {
|
|
102
|
+
return elements.map(element => element.cloneNode(true)) as HTMLElement[];
|
|
103
|
+
}
|
|
121
104
|
|
|
122
|
-
|
|
123
|
-
wrapper.append(element);
|
|
124
|
-
}
|
|
105
|
+
elements = findElements(anchor.getAttribute(ARIA_DESCRIBEDBY));
|
|
125
106
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
element: wrapper,
|
|
129
|
-
};
|
|
107
|
+
if (elements.length > 0) {
|
|
108
|
+
return elements.map(element => element.cloneNode(true)) as HTMLElement[];
|
|
130
109
|
}
|
|
131
110
|
|
|
132
|
-
let content = anchor.getAttribute(
|
|
111
|
+
let content = anchor.getAttribute(ARIA_LABEL);
|
|
133
112
|
|
|
134
113
|
if (isNullableOrWhitespace(content)) {
|
|
135
|
-
content = anchor.getAttribute(
|
|
114
|
+
content = anchor.getAttribute(ARIA_DESCRIPTION);
|
|
136
115
|
}
|
|
137
116
|
|
|
138
117
|
if (isNullableOrWhitespace(content)) {
|
|
@@ -143,15 +122,10 @@ function getContent(anchor: HTMLElement): Content | undefined {
|
|
|
143
122
|
|
|
144
123
|
paragraph.textContent = content;
|
|
145
124
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
attributes,
|
|
150
|
-
element: wrapper,
|
|
151
|
-
};
|
|
125
|
+
return [paragraph];
|
|
152
126
|
}
|
|
153
127
|
|
|
154
|
-
function
|
|
128
|
+
function findElements(attribute: string | null): HTMLElement[] {
|
|
155
129
|
const ids = attribute?.split(EXPRESSION_WHITESPACE) ?? [];
|
|
156
130
|
const elements: HTMLElement[] = [];
|
|
157
131
|
|
|
@@ -174,24 +148,31 @@ function getElements(attribute: string | null): HTMLElement[] {
|
|
|
174
148
|
return elements;
|
|
175
149
|
}
|
|
176
150
|
|
|
177
|
-
function
|
|
178
|
-
|
|
151
|
+
function getContent(): HTMLElement {
|
|
152
|
+
if (contentElement != null) {
|
|
153
|
+
return contentElement;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
contentElement = document.createElement('div');
|
|
179
157
|
|
|
180
|
-
|
|
158
|
+
contentElement.setAttribute(ATTRIBUTE_CONTENT, '');
|
|
181
159
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
wrapper.id = `${SELECTOR}-${++index}`;
|
|
185
|
-
wrapper.role = 'tooltip';
|
|
186
|
-
wrapper.tabIndex = -1;
|
|
160
|
+
contentElement.role = 'tooltip';
|
|
161
|
+
contentElement.tabIndex = -1;
|
|
187
162
|
|
|
188
|
-
return
|
|
163
|
+
return contentElement;
|
|
189
164
|
}
|
|
190
165
|
|
|
191
166
|
function onActivate(event: Event): void {
|
|
192
167
|
onToggle(event, true);
|
|
193
168
|
}
|
|
194
169
|
|
|
170
|
+
function onChange(): void {
|
|
171
|
+
if (TOOLTIPS_ACTIVE.size === 0) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
195
176
|
function onClick(event: Event): void {
|
|
196
177
|
if (findAncestor(event, SELECTOR_CONTENT) != null) {
|
|
197
178
|
event.stopPropagation();
|
|
@@ -207,46 +188,54 @@ function onToggle(event: Event, activate: boolean): void {
|
|
|
207
188
|
cancelAnimationFrame(toggle);
|
|
208
189
|
}
|
|
209
190
|
|
|
191
|
+
if (findAncestor(event, SELECTOR_CONTENT) != null) {
|
|
192
|
+
if (contentTimer != null) {
|
|
193
|
+
clearTimeout(contentTimer);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
210
199
|
toggle = requestAnimationFrame(() => {
|
|
211
200
|
toggle = undefined;
|
|
212
201
|
|
|
213
|
-
const element = findAncestor(event,
|
|
202
|
+
const element = findAncestor(event, SELECTOR) as HTMLElement;
|
|
214
203
|
|
|
215
204
|
const tooltip = TOOLTIPS_ALL.get(element);
|
|
216
205
|
|
|
217
|
-
if (TOOLTIPS_ACTIVE.size > 0) {
|
|
206
|
+
if (TOOLTIPS_ACTIVE.size > 0 || TOOLTIPS_PENDING.size > 0) {
|
|
218
207
|
closeTooltips(tooltip);
|
|
219
208
|
}
|
|
220
209
|
|
|
221
|
-
if (
|
|
222
|
-
tooltip == null ||
|
|
223
|
-
tooltip.floatable.options.content.hasAttribute('oui-tooltip-content-active') === activate
|
|
224
|
-
) {
|
|
210
|
+
if (tooltip == null || tooltip.floatable.active === activate) {
|
|
225
211
|
return;
|
|
226
212
|
}
|
|
227
213
|
|
|
228
214
|
if (activate) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
215
|
+
clearTimeout(contentTimer);
|
|
216
|
+
|
|
217
|
+
TOOLTIPS_PENDING.add(tooltip);
|
|
218
|
+
|
|
219
|
+
contentTimer = setTimeout(() => {
|
|
220
|
+
TOOLTIPS_ACTIVE.add(tooltip);
|
|
221
|
+
TOOLTIPS_PENDING.delete(tooltip);
|
|
222
|
+
|
|
223
|
+
tooltip.floatable.options.content.style.positionAnchor =
|
|
224
|
+
tooltip.floatable.options.anchor.style.anchorName;
|
|
232
225
|
|
|
233
|
-
|
|
234
|
-
tooltip.floatable.toggle(true);
|
|
226
|
+
tooltip.floatable.toggle(true);
|
|
235
227
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
tooltip.floatable.options.content.setAttribute(ATTRIBUTE_CONTENT_ACTIVE, '');
|
|
230
|
+
});
|
|
231
|
+
}, delayOpen);
|
|
239
232
|
|
|
240
233
|
return;
|
|
241
234
|
}
|
|
242
235
|
|
|
243
|
-
|
|
236
|
+
TOOLTIPS_ACTIVE.delete(tooltip);
|
|
244
237
|
|
|
245
|
-
tooltip.
|
|
246
|
-
TOOLTIPS_ACTIVE.delete(tooltip);
|
|
247
|
-
|
|
248
|
-
tooltip.floatable.toggle(false);
|
|
249
|
-
}, delay) as never;
|
|
238
|
+
tooltip.floatable.toggle(false);
|
|
250
239
|
});
|
|
251
240
|
}
|
|
252
241
|
|
|
@@ -256,31 +245,32 @@ function removeTooltip(element: HTMLElement): void {
|
|
|
256
245
|
}
|
|
257
246
|
}
|
|
258
247
|
|
|
259
|
-
function
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
} else {
|
|
263
|
-
anchor.setAttribute('aria-describedby', attributes.ariaDescribedby);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function setTooltipDelay(value: number): void {
|
|
268
|
-
delay = typeof value === 'number' && value >= 0 ? value : delay;
|
|
248
|
+
function setTooltipDelay(open?: number, close?: number): void {
|
|
249
|
+
delayClose = typeof close === 'number' && close >= 0 ? close : delayClose;
|
|
250
|
+
delayOpen = typeof open === 'number' && open >= 0 ? open : delayOpen;
|
|
269
251
|
}
|
|
270
252
|
|
|
271
253
|
//
|
|
272
254
|
|
|
273
|
-
const
|
|
255
|
+
const ARIA_DESCRIBEDBY = 'aria-describedby';
|
|
274
256
|
|
|
275
|
-
const
|
|
257
|
+
const ARIA_DESCRIPTION = 'aria-description';
|
|
276
258
|
|
|
277
|
-
const
|
|
259
|
+
const ARIA_LABEL = 'aria-label';
|
|
278
260
|
|
|
279
|
-
const
|
|
261
|
+
const ARIA_LABELLEDBY = 'aria-labelledby';
|
|
280
262
|
|
|
281
|
-
const
|
|
263
|
+
const ATTRIBUTE = 'oui-tooltip';
|
|
264
|
+
|
|
265
|
+
const ATTRIBUTE_CONTENT = `${ATTRIBUTE}-content`;
|
|
266
|
+
|
|
267
|
+
const ATTRIBUTE_CONTENT_ACTIVE = `${ATTRIBUTE_CONTENT}-active`;
|
|
282
268
|
|
|
283
|
-
const
|
|
269
|
+
const ATTRIBUTE_POSITION = `${ATTRIBUTE}-position`;
|
|
270
|
+
|
|
271
|
+
const SELECTOR = `[${ATTRIBUTE}]`;
|
|
272
|
+
|
|
273
|
+
const SELECTOR_CONTENT = `[${ATTRIBUTE_CONTENT}]`;
|
|
284
274
|
|
|
285
275
|
const EXPRESSION_WHITESPACE = /\s+/;
|
|
286
276
|
|
|
@@ -288,20 +278,28 @@ const TOOLTIPS_ACTIVE: Set<Tooltip> = new Set();
|
|
|
288
278
|
|
|
289
279
|
const TOOLTIPS_ALL: WeakMap<HTMLElement, Tooltip> = new WeakMap();
|
|
290
280
|
|
|
291
|
-
|
|
281
|
+
const TOOLTIPS_PENDING: Set<Tooltip> = new Set();
|
|
282
|
+
|
|
283
|
+
let contentElement: HTMLElement | undefined;
|
|
284
|
+
|
|
285
|
+
let contentTimer: number | undefined;
|
|
286
|
+
|
|
287
|
+
let delayClose = 250;
|
|
292
288
|
|
|
293
|
-
let
|
|
289
|
+
let delayOpen = 500;
|
|
294
290
|
|
|
295
291
|
let toggle: DOMHighResTimeStamp | undefined;
|
|
296
292
|
|
|
297
293
|
//
|
|
298
294
|
|
|
299
|
-
attributable(
|
|
295
|
+
attributable(ATTRIBUTE, addTooltip, removeTooltip);
|
|
300
296
|
|
|
301
297
|
on(document, 'click', onClick, {capture: true});
|
|
302
298
|
on(document, 'focusin', onActivate);
|
|
303
299
|
on(document, 'focusout', onDeactivate);
|
|
304
300
|
on(document, 'mousemove', onActivate);
|
|
301
|
+
on(window, 'resize', onChange);
|
|
302
|
+
on(document, 'scroll', onChange);
|
|
305
303
|
on(document, 'touchstart', onActivate);
|
|
306
304
|
|
|
307
305
|
//
|
|
File without changes
|