@oscarpalmer/oui 0.24.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/css/{tooltip.css → floatable.css} +59 -24
- package/dist/css/reset.css +17 -0
- package/dist/js/dialog.d.mts +38 -0
- package/dist/js/dialog.mjs +92 -0
- package/dist/js/embedded-CHK82Uh5.d.mts +76 -0
- package/dist/js/embedded-ai6cqURc.d.mts +68 -0
- package/dist/js/floatable/embedded.d.mts +2 -0
- package/dist/js/floatable/embedded.mjs +157 -0
- package/dist/js/floatable/standalone.d.mts +2 -0
- package/dist/js/floatable/standalone.mjs +2 -0
- package/dist/js/focus-trap/embedded.d.mts +2 -26
- package/dist/js/focus-trap/embedded.mjs +158 -80
- 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 +6 -3
- package/dist/js/index.mjs +9 -5
- package/dist/js/internal/draggable.d.mts +2 -2
- package/dist/js/internal/draggable.mjs +3 -3
- 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 +38 -1
- package/dist/js/popover.mjs +83 -44
- package/dist/js/sortable.d.mts +2 -2
- package/dist/js/sortable.mjs +5 -5
- package/dist/js/splitter.mjs +49 -48
- package/dist/js/toolbar.d.mts +1 -0
- package/dist/js/toolbar.mjs +107 -0
- package/dist/js/tooltip.d.mts +27 -1
- package/dist/js/tooltip.mjs +85 -49
- package/package.json +14 -2
- package/src/css/{tooltip.scss → floatable.scss} +47 -5
- package/src/css/mixins.scss +0 -2
- package/src/css/reset.scss +17 -0
- package/src/js/dialog.ts +188 -0
- package/src/js/floatable/embedded.ts +323 -0
- package/src/js/floatable/standalone.ts +1 -0
- package/src/js/focus-trap/embedded.ts +219 -93
- package/src/js/focus-trap/standalone.ts +4 -2
- package/src/js/index.ts +6 -4
- package/src/js/internal/draggable.ts +20 -20
- package/src/js/movable.ts +17 -17
- package/src/js/popover.ts +143 -61
- 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 +138 -54
- package/dist/css/popover.css +0 -30
- package/dist/js/floatable.d.mts +0 -59
- package/dist/js/floatable.mjs +0 -130
- package/src/css/popover.scss +0 -10
- package/src/js/floatable.ts +0 -237
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);
|