@oscarpalmer/oui 0.25.0 → 0.26.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/js/embedded-CHK82Uh5.d.mts +76 -0
- package/dist/js/{embedded-84gxpReS.d.mts → embedded-ai6cqURc.d.mts} +21 -17
- package/dist/js/floatable/embedded.d.mts +2 -2
- package/dist/js/floatable/embedded.mjs +27 -16
- package/dist/js/floatable/standalone.d.mts +2 -2
- package/dist/js/floatable/standalone.mjs +2 -2
- package/dist/js/focus-trap/embedded.d.mts +2 -2
- package/dist/js/focus-trap/embedded.mjs +157 -79
- package/dist/js/focus-trap/standalone.d.mts +2 -1
- package/dist/js/focus-trap/standalone.mjs +3 -3
- package/dist/js/index.d.mts +3 -2
- package/dist/js/index.mjs +4 -2
- package/dist/js/internal/draggable.d.mts +2 -2
- package/dist/js/internal/draggable.mjs +2 -2
- package/dist/js/{movable-BKFgoaOl.d.mts → movable-dNqBctx3.d.mts} +16 -16
- package/dist/js/movable.d.mts +2 -2
- package/dist/js/movable.mjs +4 -4
- package/dist/js/popover.d.mts +3 -3
- package/dist/js/popover.mjs +9 -9
- package/dist/js/sortable.d.mts +2 -2
- package/dist/js/sortable.mjs +4 -4
- package/dist/js/splitter.mjs +48 -47
- package/dist/js/toolbar.d.mts +1 -0
- package/dist/js/toolbar.mjs +107 -0
- package/dist/js/tooltip.d.mts +1 -1
- package/dist/js/tooltip.mjs +7 -7
- package/package.json +13 -1
- package/src/js/floatable/embedded.ts +65 -44
- package/src/js/floatable/standalone.ts +1 -1
- package/src/js/focus-trap/embedded.ts +219 -93
- package/src/js/focus-trap/standalone.ts +4 -2
- package/src/js/index.ts +1 -0
- package/src/js/internal/draggable.ts +20 -20
- package/src/js/movable.ts +17 -17
- package/src/js/popover.ts +18 -18
- package/src/js/sortable.ts +26 -26
- package/src/js/splitter.ts +98 -96
- package/src/js/toolbar.ts +214 -0
- package/src/js/tooltip.ts +14 -10
- package/dist/js/embedded-BpwzMSEF.d.mts +0 -26
package/src/js/splitter.ts
CHANGED
|
@@ -20,6 +20,83 @@ const ATTRIBUTE_SIZE = 'size';
|
|
|
20
20
|
|
|
21
21
|
const ATTRIBUTE_TYPE = 'type';
|
|
22
22
|
|
|
23
|
+
class OuiSplitter {
|
|
24
|
+
element: OuiSplitterElement;
|
|
25
|
+
|
|
26
|
+
handle: HTMLSpanElement;
|
|
27
|
+
|
|
28
|
+
panels: HTMLElement[];
|
|
29
|
+
|
|
30
|
+
rectangle: DOMRect;
|
|
31
|
+
|
|
32
|
+
separator: HTMLDivElement;
|
|
33
|
+
|
|
34
|
+
types!: OuiSplitterTypes;
|
|
35
|
+
|
|
36
|
+
values: OuiSplitterValues;
|
|
37
|
+
|
|
38
|
+
constructor(element: OuiSplitterElement, panels: HTMLElement[]) {
|
|
39
|
+
this.element = element;
|
|
40
|
+
this.panels = panels;
|
|
41
|
+
|
|
42
|
+
if (isNullableOrWhitespace(element.id)) {
|
|
43
|
+
element.id = `oui_splitter_${++index}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.values = getValues(element);
|
|
47
|
+
|
|
48
|
+
this.handle = createHandle();
|
|
49
|
+
this.rectangle = element.getBoundingClientRect();
|
|
50
|
+
this.separator = createSeparator(this);
|
|
51
|
+
|
|
52
|
+
this.separator.append(this.handle);
|
|
53
|
+
|
|
54
|
+
panels[0].insertAdjacentElement('afterend', this.separator);
|
|
55
|
+
|
|
56
|
+
this.updateOrientation();
|
|
57
|
+
this.updateValues();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
updateConnections(add: 0 | 1): void {
|
|
61
|
+
const method = METHODS_CONNECT[add];
|
|
62
|
+
|
|
63
|
+
MAPPED_ELEMENTS[method](this.element, this);
|
|
64
|
+
MAPPED_ELEMENTS[method](this.handle, this);
|
|
65
|
+
MAPPED_ELEMENTS[method](this.separator, this);
|
|
66
|
+
|
|
67
|
+
if (this.types.auto) {
|
|
68
|
+
RESIZE_OBSERVER[METHODS_OBSERVE[add]](this.element);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
updateOrientation(): void {
|
|
73
|
+
this.types = getTypes(this);
|
|
74
|
+
|
|
75
|
+
if (this.types.auto) {
|
|
76
|
+
RESIZE_OBSERVER.observe(this.element);
|
|
77
|
+
} else {
|
|
78
|
+
RESIZE_OBSERVER.unobserve(this.element);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (this.types.horizontal) {
|
|
82
|
+
this.element.setAttribute(ATTRIBUTE_HORIZONTAL, '');
|
|
83
|
+
} else {
|
|
84
|
+
this.element.removeAttribute(ATTRIBUTE_HORIZONTAL);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
setAriaOrientation(this);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
updateValues(): void {
|
|
91
|
+
this.values = getValues(this.element);
|
|
92
|
+
|
|
93
|
+
requestAnimationFrame(() => {
|
|
94
|
+
setSize(this, this.values.now.current);
|
|
95
|
+
setAriaValue(this);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
23
100
|
export class OuiSplitterElement extends HTMLElement {
|
|
24
101
|
static readonly observedAttributes = [
|
|
25
102
|
ATTRIBUTE_MAX,
|
|
@@ -28,7 +105,7 @@ export class OuiSplitterElement extends HTMLElement {
|
|
|
28
105
|
ATTRIBUTE_TYPE,
|
|
29
106
|
];
|
|
30
107
|
|
|
31
|
-
readonly #splitter:
|
|
108
|
+
readonly #splitter: OuiSplitter;
|
|
32
109
|
|
|
33
110
|
// Getters and setters
|
|
34
111
|
|
|
@@ -72,10 +149,10 @@ export class OuiSplitterElement extends HTMLElement {
|
|
|
72
149
|
const panels = [...this.children].filter(child => child instanceof HTMLElement);
|
|
73
150
|
|
|
74
151
|
if (panels.length !== 2) {
|
|
75
|
-
throw new Error(
|
|
152
|
+
throw new Error(MESSAGE);
|
|
76
153
|
}
|
|
77
154
|
|
|
78
|
-
this.#splitter = new
|
|
155
|
+
this.#splitter = new OuiSplitter(this, panels);
|
|
79
156
|
}
|
|
80
157
|
|
|
81
158
|
// Methods
|
|
@@ -106,97 +183,18 @@ export class OuiSplitterElement extends HTMLElement {
|
|
|
106
183
|
}
|
|
107
184
|
}
|
|
108
185
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
class Splitter {
|
|
112
|
-
element: OuiSplitterElement;
|
|
113
|
-
|
|
114
|
-
handle: HTMLSpanElement;
|
|
115
|
-
|
|
116
|
-
panels: HTMLElement[];
|
|
117
|
-
|
|
118
|
-
rectangle: DOMRect;
|
|
119
|
-
|
|
120
|
-
separator: HTMLDivElement;
|
|
121
|
-
|
|
122
|
-
types!: Types;
|
|
123
|
-
|
|
124
|
-
values: Values;
|
|
125
|
-
|
|
126
|
-
constructor(element: OuiSplitterElement, panels: HTMLElement[]) {
|
|
127
|
-
this.element = element;
|
|
128
|
-
this.panels = panels;
|
|
129
|
-
|
|
130
|
-
if (isNullableOrWhitespace(element.id)) {
|
|
131
|
-
element.id = `oui_splitter_${++index}`;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
this.values = getValues(element);
|
|
135
|
-
|
|
136
|
-
this.handle = createHandle();
|
|
137
|
-
this.rectangle = element.getBoundingClientRect();
|
|
138
|
-
this.separator = createSeparator(this);
|
|
139
|
-
|
|
140
|
-
this.separator.append(this.handle);
|
|
141
|
-
|
|
142
|
-
panels[0].insertAdjacentElement('afterend', this.separator);
|
|
143
|
-
|
|
144
|
-
this.updateOrientation();
|
|
145
|
-
this.updateValues();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
updateConnections(add: 0 | 1): void {
|
|
149
|
-
const method = METHODS_CONNECT[add];
|
|
150
|
-
|
|
151
|
-
MAPPED_ELEMENTS[method](this.element, this);
|
|
152
|
-
MAPPED_ELEMENTS[method](this.handle, this);
|
|
153
|
-
MAPPED_ELEMENTS[method](this.separator, this);
|
|
154
|
-
|
|
155
|
-
if (this.types.auto) {
|
|
156
|
-
RESIZE_OBSERVER[METHODS_OBSERVE[add]](this.element);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
updateOrientation(): void {
|
|
161
|
-
this.types = getTypes(this);
|
|
162
|
-
|
|
163
|
-
if (this.types.auto) {
|
|
164
|
-
RESIZE_OBSERVER.observe(this.element);
|
|
165
|
-
} else {
|
|
166
|
-
RESIZE_OBSERVER.unobserve(this.element);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (this.types.horizontal) {
|
|
170
|
-
this.element.setAttribute(ATTRIBUTE_HORIZONTAL, '');
|
|
171
|
-
} else {
|
|
172
|
-
this.element.removeAttribute(ATTRIBUTE_HORIZONTAL);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
setAriaOrientation(this);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
updateValues(): void {
|
|
179
|
-
this.values = getValues(this.element);
|
|
180
|
-
|
|
181
|
-
requestAnimationFrame(() => {
|
|
182
|
-
setSize(this, this.values.now.current);
|
|
183
|
-
setAriaValue(this);
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
type Types = {
|
|
186
|
+
type OuiSplitterTypes = {
|
|
189
187
|
auto: boolean;
|
|
190
188
|
horizontal: boolean;
|
|
191
189
|
};
|
|
192
190
|
|
|
193
|
-
type
|
|
191
|
+
type OuiSplitterValues = {
|
|
194
192
|
max: number;
|
|
195
193
|
min: number;
|
|
196
|
-
now:
|
|
194
|
+
now: OuiSplitterValuesNow;
|
|
197
195
|
};
|
|
198
196
|
|
|
199
|
-
type
|
|
197
|
+
type OuiSplitterValuesNow = {
|
|
200
198
|
current: number;
|
|
201
199
|
previous: number;
|
|
202
200
|
};
|
|
@@ -212,7 +210,7 @@ function createHandle(): HTMLSpanElement {
|
|
|
212
210
|
return handle;
|
|
213
211
|
}
|
|
214
212
|
|
|
215
|
-
function createSeparator(splitter:
|
|
213
|
+
function createSeparator(splitter: OuiSplitter): HTMLDivElement {
|
|
216
214
|
const separator = document.createElement('div');
|
|
217
215
|
|
|
218
216
|
separator.role = 'separator';
|
|
@@ -229,7 +227,7 @@ function getContained(min: number, value: number, max: number): number {
|
|
|
229
227
|
return round(clamp(value, min, max), VALUE_DECIMALS);
|
|
230
228
|
}
|
|
231
229
|
|
|
232
|
-
function getSize(splitter:
|
|
230
|
+
function getSize(splitter: OuiSplitter, value: number): number {
|
|
233
231
|
const {height, left, top, width} = splitter.rectangle;
|
|
234
232
|
|
|
235
233
|
const offset = value - (splitter.types.horizontal ? top : left);
|
|
@@ -239,7 +237,7 @@ function getSize(splitter: Splitter, value: number): number {
|
|
|
239
237
|
return getContained(splitter.values.min, fraction, splitter.values.max);
|
|
240
238
|
}
|
|
241
239
|
|
|
242
|
-
function getTypes(splitter:
|
|
240
|
+
function getTypes(splitter: OuiSplitter, width?: number, height?: number): OuiSplitterTypes {
|
|
243
241
|
const type = splitter.element.getAttribute('type')?.trim() ?? '';
|
|
244
242
|
|
|
245
243
|
if (type === 'horizontal') {
|
|
@@ -282,8 +280,8 @@ function getValue(element: HTMLElement, name: string, defaultValue: number): num
|
|
|
282
280
|
return parsed > 1 ? parsed / NAVIGATION_MAXIMUM_TOTAL : parsed;
|
|
283
281
|
}
|
|
284
282
|
|
|
285
|
-
function getValues(element: OuiSplitterElement):
|
|
286
|
-
const values:
|
|
283
|
+
function getValues(element: OuiSplitterElement): OuiSplitterValues {
|
|
284
|
+
const values: OuiSplitterValues = {
|
|
287
285
|
max: getValue(element, ATTRIBUTE_MAX, NAVIGATION_MAXIMUM_PERCENTAGE),
|
|
288
286
|
min: getValue(element, ATTRIBUTE_MIN, NAVIGATION_MINIMUM_PERCENTAGE),
|
|
289
287
|
now: {
|
|
@@ -445,21 +443,21 @@ function onPointerup(): void {
|
|
|
445
443
|
}
|
|
446
444
|
}
|
|
447
445
|
|
|
448
|
-
function setAriaOrientation(splitter:
|
|
446
|
+
function setAriaOrientation(splitter: OuiSplitter): void {
|
|
449
447
|
splitter.separator.setAttribute(
|
|
450
448
|
'aria-orientation',
|
|
451
449
|
splitter.types.horizontal ? 'horizontal' : 'vertical',
|
|
452
450
|
);
|
|
453
451
|
}
|
|
454
452
|
|
|
455
|
-
function setAriaValue(splitter:
|
|
453
|
+
function setAriaValue(splitter: OuiSplitter): void {
|
|
456
454
|
splitter.separator.setAttribute('aria-valuemax', String(splitter.values.max));
|
|
457
455
|
splitter.separator.setAttribute('aria-valuemin', String(splitter.values.min));
|
|
458
456
|
|
|
459
457
|
splitter.separator.setAttribute('aria-valuenow', String(splitter.values.now.current));
|
|
460
458
|
}
|
|
461
459
|
|
|
462
|
-
function setSize(splitter:
|
|
460
|
+
function setSize(splitter: OuiSplitter, size: number, previous?: boolean): void {
|
|
463
461
|
splitter.values.now.current = size;
|
|
464
462
|
|
|
465
463
|
if (previous) {
|
|
@@ -493,7 +491,9 @@ const KEYS_VERTICAL: Set<string> = new Set(['ArrowLeft', 'ArrowRight']);
|
|
|
493
491
|
|
|
494
492
|
const KEYS_ALL: Set<string> = new Set([...KEYS_ABSOLUTE, ...KEYS_HORIZONTAL, ...KEYS_VERTICAL]);
|
|
495
493
|
|
|
496
|
-
const MAPPED_ELEMENTS: WeakMap<HTMLElement,
|
|
494
|
+
const MAPPED_ELEMENTS: WeakMap<HTMLElement, OuiSplitter> = new WeakMap();
|
|
495
|
+
|
|
496
|
+
const MESSAGE = `<${SELECTOR}> must have exactly two panels.`;
|
|
497
497
|
|
|
498
498
|
const METHODS_CONNECT = ['delete', 'set'] as const;
|
|
499
499
|
|
|
@@ -519,9 +519,11 @@ const STYLING_TOGGLER: StyleToggler = toggleStyles(document.body, {
|
|
|
519
519
|
|
|
520
520
|
const VALUE_DECIMALS = 6;
|
|
521
521
|
|
|
522
|
+
let index = 0;
|
|
523
|
+
|
|
522
524
|
let frame: DOMHighResTimeStamp | undefined;
|
|
523
525
|
|
|
524
|
-
let splitter:
|
|
526
|
+
let splitter: OuiSplitter | undefined;
|
|
525
527
|
|
|
526
528
|
//
|
|
527
529
|
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import {clamp} from '@oscarpalmer/atoms/number';
|
|
2
|
+
import {on} from '@oscarpalmer/toretto/event';
|
|
3
|
+
import {findAncestor} from '@oscarpalmer/toretto/find';
|
|
4
|
+
import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
|
|
5
|
+
import type {RemovableEventListener} from '@oscarpalmer/toretto/models';
|
|
6
|
+
import {attributable} from './internal/attributable';
|
|
7
|
+
|
|
8
|
+
class OuiToolbar {
|
|
9
|
+
readonly #state: OuiToolbarState;
|
|
10
|
+
|
|
11
|
+
constructor(state: OuiToolbarState) {
|
|
12
|
+
this.#state = state;
|
|
13
|
+
|
|
14
|
+
updateFocus(this.#state, false);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
destroy(): void {
|
|
18
|
+
for (const listener of this.#state.listeners) {
|
|
19
|
+
listener();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.#state.active = undefined as never;
|
|
23
|
+
this.#state.element = undefined as never;
|
|
24
|
+
this.#state.listeners = undefined as never;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type OuiToolbarOrientation = 'horizontal' | 'vertical';
|
|
29
|
+
|
|
30
|
+
type OuiToolbarState = {
|
|
31
|
+
active: OuiToolbarStateActive;
|
|
32
|
+
element: HTMLElement;
|
|
33
|
+
listeners: RemovableEventListener[];
|
|
34
|
+
orientation: OuiToolbarOrientation;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type OuiToolbarStateActive = {
|
|
38
|
+
element?: HTMLElement;
|
|
39
|
+
index: number;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function addToolbar(element: HTMLElement): void {
|
|
43
|
+
if (!instances.has(element)) {
|
|
44
|
+
instances.set(element, new OuiToolbar(getState(element)));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getItems(element: HTMLElement): HTMLElement[] {
|
|
49
|
+
return [...element.querySelectorAll<HTMLElement>(SELECTOR)].filter(isValidItem);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getOrientation(element: HTMLElement): OuiToolbarOrientation {
|
|
53
|
+
const attribute = element.getAttribute(ARIA_ORIENTATION);
|
|
54
|
+
|
|
55
|
+
return ORIENTATIONS.has(attribute as OuiToolbarOrientation)
|
|
56
|
+
? (attribute as OuiToolbarOrientation)
|
|
57
|
+
: ORIENTATION_HORIZONTAL;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getState(element: HTMLElement): OuiToolbarState {
|
|
61
|
+
const state: OuiToolbarState = {
|
|
62
|
+
element,
|
|
63
|
+
active: {
|
|
64
|
+
index: 0,
|
|
65
|
+
},
|
|
66
|
+
listeners: [
|
|
67
|
+
on(element, EVENT_FOCUSIN, event => {
|
|
68
|
+
onFocusin(state, event);
|
|
69
|
+
}),
|
|
70
|
+
on(
|
|
71
|
+
element,
|
|
72
|
+
EVENT_KEYDOWN,
|
|
73
|
+
event => {
|
|
74
|
+
onKeydown(state, event);
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
passive: false,
|
|
78
|
+
},
|
|
79
|
+
),
|
|
80
|
+
],
|
|
81
|
+
orientation: getOrientation(element),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return state;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isValidItem(element: HTMLElement): boolean {
|
|
88
|
+
return PROPERTY_DISABLED in element ? element.disabled !== true : true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function onKeydown(state: OuiToolbarState, event: KeyboardEvent): void {
|
|
92
|
+
if (!KEYS_ALL.has(event.key)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const items = getItems(state.element);
|
|
97
|
+
|
|
98
|
+
if (items.length === 0) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let index: number | undefined;
|
|
103
|
+
|
|
104
|
+
if (KEYS_ABSOLUTE.has(event.key)) {
|
|
105
|
+
index = event.key === KEY_HOME ? 0 : items.length - 1;
|
|
106
|
+
} else if (
|
|
107
|
+
state.orientation === ORIENTATION_HORIZONTAL
|
|
108
|
+
? KEYS_HORIZONTAL.has(event.key)
|
|
109
|
+
: KEYS_VERTICAL.has(event.key)
|
|
110
|
+
) {
|
|
111
|
+
if (state.active.element instanceof HTMLSelectElement) {
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
index = clamp(
|
|
116
|
+
state.active.index + (KEYS_NEXT.has(event.key) ? 1 : -1),
|
|
117
|
+
0,
|
|
118
|
+
items.length - 1,
|
|
119
|
+
true,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (index == null || index === state.active.index) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
state.active.element = items[index];
|
|
128
|
+
state.active.index = index;
|
|
129
|
+
|
|
130
|
+
updateFocus(state, true, items);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function onFocusin(state: OuiToolbarState, event: FocusEvent): void {
|
|
134
|
+
const item = findAncestor(event, SELECTOR);
|
|
135
|
+
|
|
136
|
+
if (!isHTMLOrSVGElement(item) || !isValidItem(item)) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const items = getItems(state.element);
|
|
141
|
+
const index = items.indexOf(item);
|
|
142
|
+
|
|
143
|
+
if (index === -1) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
state.active.element = item;
|
|
148
|
+
state.active.index = index;
|
|
149
|
+
|
|
150
|
+
updateFocus(state, false, items);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function removeToolbar(element: HTMLElement): void {
|
|
154
|
+
instances.get(element)?.destroy();
|
|
155
|
+
|
|
156
|
+
instances.delete(element);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function updateFocus(state: OuiToolbarState, focus: boolean, items?: HTMLElement[]): void {
|
|
160
|
+
const updated = items ?? getItems(state.element);
|
|
161
|
+
const {length} = updated;
|
|
162
|
+
|
|
163
|
+
if (length === 0) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (let index = 0; index < length; index += 1) {
|
|
168
|
+
updated[index].tabIndex = index === state.active.index ? 0 : -1;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
state.active.element ??= updated[state.active.index];
|
|
172
|
+
|
|
173
|
+
if (focus) {
|
|
174
|
+
state.active.element?.focus();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const ARIA_ORIENTATION = 'aria-orientation';
|
|
179
|
+
|
|
180
|
+
const ATTRIBUTE = 'oui-toolbar';
|
|
181
|
+
|
|
182
|
+
const EVENT_KEYDOWN = 'keydown';
|
|
183
|
+
|
|
184
|
+
const EVENT_FOCUSIN = 'focusin';
|
|
185
|
+
|
|
186
|
+
const KEY_ARROW_DOWN = 'ArrowDown';
|
|
187
|
+
|
|
188
|
+
const KEY_ARROW_RIGHT = 'ArrowRight';
|
|
189
|
+
|
|
190
|
+
const KEY_END = 'End';
|
|
191
|
+
|
|
192
|
+
const KEY_HOME = 'Home';
|
|
193
|
+
|
|
194
|
+
const KEYS_ABSOLUTE = new Set([KEY_END, KEY_HOME]);
|
|
195
|
+
|
|
196
|
+
const KEYS_HORIZONTAL = new Set([KEY_ARROW_RIGHT, 'ArrowLeft']);
|
|
197
|
+
|
|
198
|
+
const KEYS_VERTICAL = new Set([KEY_ARROW_DOWN, 'ArrowUp']);
|
|
199
|
+
|
|
200
|
+
const KEYS_ALL = new Set([...KEYS_ABSOLUTE, ...KEYS_HORIZONTAL, ...KEYS_VERTICAL]);
|
|
201
|
+
|
|
202
|
+
const KEYS_NEXT = new Set([KEY_ARROW_DOWN, KEY_ARROW_RIGHT]);
|
|
203
|
+
|
|
204
|
+
const ORIENTATION_HORIZONTAL: OuiToolbarOrientation = 'horizontal';
|
|
205
|
+
|
|
206
|
+
const ORIENTATIONS = new Set<OuiToolbarOrientation>([ORIENTATION_HORIZONTAL, 'vertical']);
|
|
207
|
+
|
|
208
|
+
const PROPERTY_DISABLED = 'disabled';
|
|
209
|
+
|
|
210
|
+
const SELECTOR = `[${ATTRIBUTE}-item]`;
|
|
211
|
+
|
|
212
|
+
const instances = new WeakMap<HTMLElement, OuiToolbar>();
|
|
213
|
+
|
|
214
|
+
attributable(ATTRIBUTE, addToolbar, removeToolbar);
|
package/src/js/tooltip.ts
CHANGED
|
@@ -3,7 +3,11 @@ import {createElement} from '@oscarpalmer/toretto/create';
|
|
|
3
3
|
import {on} from '@oscarpalmer/toretto/event';
|
|
4
4
|
import {findAncestor} from '@oscarpalmer/toretto/find';
|
|
5
5
|
import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
createEmbeddedFloatable,
|
|
8
|
+
type OuiFloatable,
|
|
9
|
+
type OuiFloatableOptions,
|
|
10
|
+
} from './floatable/embedded';
|
|
7
11
|
import {attributable} from './internal/attributable';
|
|
8
12
|
|
|
9
13
|
// #region Types
|
|
@@ -97,6 +101,12 @@ function activate(event: Event, focus: boolean): void {
|
|
|
97
101
|
);
|
|
98
102
|
}
|
|
99
103
|
|
|
104
|
+
function addTooltip(element: HTMLElement): void {
|
|
105
|
+
if (!states.has(element)) {
|
|
106
|
+
createTooltip(element);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
100
110
|
function closeTooltip(state?: OuiTooltipState): void {
|
|
101
111
|
if (state == null) {
|
|
102
112
|
return;
|
|
@@ -211,7 +221,7 @@ function getState(anchor: HTMLElement, content: HTMLElement): OuiTooltipState {
|
|
|
211
221
|
const state: OuiTooltipState = {
|
|
212
222
|
anchor,
|
|
213
223
|
content,
|
|
214
|
-
floatable:
|
|
224
|
+
floatable: createEmbeddedFloatable(anchor, content, options),
|
|
215
225
|
tooltip: null as never,
|
|
216
226
|
};
|
|
217
227
|
|
|
@@ -224,12 +234,6 @@ function getTooltip(element: HTMLElement): OuiTooltip | undefined {
|
|
|
224
234
|
return states.get(element)?.tooltip;
|
|
225
235
|
}
|
|
226
236
|
|
|
227
|
-
function onAdd(element: HTMLElement): void {
|
|
228
|
-
if (!states.has(element)) {
|
|
229
|
-
createTooltip(element);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
237
|
function onFocusin(event: Event): void {
|
|
234
238
|
activate(event, true);
|
|
235
239
|
}
|
|
@@ -263,7 +267,7 @@ function onPointermove(event: Event): void {
|
|
|
263
267
|
activate(event, false);
|
|
264
268
|
}
|
|
265
269
|
|
|
266
|
-
function
|
|
270
|
+
function removeTooltip(element: HTMLElement): void {
|
|
267
271
|
const state = states.get(element);
|
|
268
272
|
|
|
269
273
|
if (state == null) {
|
|
@@ -338,7 +342,7 @@ on(document, 'focusin', onFocusin);
|
|
|
338
342
|
on(document, 'focusout', onFocusout);
|
|
339
343
|
on(document, 'pointermove', onPointermove);
|
|
340
344
|
|
|
341
|
-
attributable(ATTRIBUTE,
|
|
345
|
+
attributable(ATTRIBUTE, addTooltip, removeTooltip);
|
|
342
346
|
|
|
343
347
|
// #endregion
|
|
344
348
|
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
//#region src/js/focus-trap/embedded.d.ts
|
|
2
|
-
declare class FocusTrap {
|
|
3
|
-
disabled: boolean;
|
|
4
|
-
connected: boolean;
|
|
5
|
-
element: HTMLElement;
|
|
6
|
-
embedded: boolean;
|
|
7
|
-
get contain(): boolean;
|
|
8
|
-
get noescape(): boolean;
|
|
9
|
-
constructor(element: HTMLElement, options?: Options);
|
|
10
|
-
connect(): void;
|
|
11
|
-
destroy(): void;
|
|
12
|
-
disable(): void;
|
|
13
|
-
disconnect(): void;
|
|
14
|
-
enable(): void;
|
|
15
|
-
focus(last: boolean): void;
|
|
16
|
-
}
|
|
17
|
-
type Options = {
|
|
18
|
-
noescape: boolean;
|
|
19
|
-
};
|
|
20
|
-
declare function createFocusTrap(element: HTMLElement, options?: Options): FocusTrap;
|
|
21
|
-
declare function removeFocusTrap(element: HTMLElement): void;
|
|
22
|
-
declare const FOCUS_TRAP_SELECTOR = "oui-focus-trap";
|
|
23
|
-
declare const FOCUS_TRAP_CONTAIN = "oui-focus-trap-contain";
|
|
24
|
-
declare const FOCUS_TRAP_NOESCAPE = "oui-focus-trap-noescape";
|
|
25
|
-
//#endregion
|
|
26
|
-
export { createFocusTrap as a, FocusTrap as i, FOCUS_TRAP_NOESCAPE as n, removeFocusTrap as o, FOCUS_TRAP_SELECTOR as r, FOCUS_TRAP_CONTAIN as t };
|