@oscarpalmer/oui 0.36.0 → 0.37.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 +18 -15
- package/dist/css/button.css +35 -31
- package/dist/css/color.css +20 -20
- package/dist/css/combobox.css +133 -34
- package/dist/css/content.css +29 -10
- package/dist/css/floatable.css +10 -10
- package/dist/css/form.css +393 -129
- package/dist/css/listbox.css +41 -17
- package/dist/css/message.css +13 -10
- package/dist/css/reset.css +29 -88
- package/dist/css/splitter.css +2 -2
- package/dist/css/tabs.css +103 -0
- package/dist/js/focus-trap/focus-trap.embedded.mjs +6 -10
- package/dist/js/index.d.mts +2 -1
- package/dist/js/index.mjs +2 -1
- package/dist/js/internal/draggable.mjs +16 -22
- package/dist/js/internal/listbox/listbox.form.mjs +1 -1
- package/dist/js/internal/listbox/listbox.state.mjs +1 -1
- package/dist/js/internal/navigable.d.mts +11 -5
- package/dist/js/internal/navigable.mjs +44 -18
- package/dist/js/movable/movable.embedded.mjs +3 -6
- package/dist/js/resizable/resizable.embedded.mjs +16 -26
- package/dist/js/splitter.mjs +15 -12
- package/dist/js/tabs.d.mts +25 -0
- package/dist/js/tabs.mjs +240 -0
- package/dist/js/tooltip.mjs +13 -12
- package/package.json +1 -1
- package/src/css/badge.scss +6 -18
- package/src/css/button.scss +6 -45
- package/src/css/color.scss +20 -20
- package/src/css/combobox.scss +20 -51
- package/src/css/content.scss +30 -3
- package/src/css/floatable.scss +17 -17
- package/src/css/form.scss +84 -72
- package/src/css/listbox.scss +2 -31
- package/src/css/message.scss +13 -28
- package/src/css/mixins.scss +253 -28
- package/src/css/reset.scss +37 -86
- package/src/css/splitter.scss +2 -2
- package/src/css/tabs.scss +85 -0
- package/src/js/focus-trap/focus-trap.embedded.ts +11 -19
- package/src/js/index.ts +1 -0
- package/src/js/internal/draggable.ts +29 -33
- package/src/js/internal/listbox/listbox.form.ts +1 -1
- package/src/js/internal/listbox/listbox.state.ts +1 -1
- package/src/js/internal/navigable.ts +80 -23
- package/src/js/movable/movable.embedded.ts +11 -13
- package/src/js/resizable/resizable.embedded.ts +25 -39
- package/src/js/splitter.ts +21 -18
- package/src/js/tabs.ts +447 -0
- package/src/js/tooltip.ts +20 -17
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {clamp} from '@oscarpalmer/atoms/number';
|
|
2
|
-
import {getAria, setRole} from '@oscarpalmer/toretto/aria';
|
|
3
|
-
import {
|
|
2
|
+
import {getAria, setAria, setRole} from '@oscarpalmer/toretto/aria';
|
|
3
|
+
import {getAttribute} from '@oscarpalmer/toretto/attribute';
|
|
4
|
+
import {on} from '@oscarpalmer/toretto/event';
|
|
4
5
|
import {findAncestor} from '@oscarpalmer/toretto/find';
|
|
5
6
|
import {isHTMLOrSVGElement} from '@oscarpalmer/toretto/is';
|
|
6
7
|
import type {AriaRole, RemovableEventListener} from '@oscarpalmer/toretto/models';
|
|
@@ -25,6 +26,16 @@ export class OuiNavigable {
|
|
|
25
26
|
this.options = options;
|
|
26
27
|
|
|
27
28
|
this.listeners = [
|
|
29
|
+
on(
|
|
30
|
+
element,
|
|
31
|
+
'click',
|
|
32
|
+
event => {
|
|
33
|
+
onClick(this, event);
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
passive: false,
|
|
37
|
+
},
|
|
38
|
+
),
|
|
28
39
|
on(element, 'focusin', event => {
|
|
29
40
|
onFocusin(this, event);
|
|
30
41
|
}),
|
|
@@ -40,7 +51,9 @@ export class OuiNavigable {
|
|
|
40
51
|
),
|
|
41
52
|
];
|
|
42
53
|
|
|
43
|
-
this.orientation =
|
|
54
|
+
this.orientation = options?.orientation ?? getNavigableOrientation(element, options.vertical);
|
|
55
|
+
|
|
56
|
+
setAria(element, 'orientation', this.orientation);
|
|
44
57
|
|
|
45
58
|
if (options.role != null) {
|
|
46
59
|
setRole(element, options.role);
|
|
@@ -57,6 +70,14 @@ export class OuiNavigable {
|
|
|
57
70
|
this.active.element = undefined;
|
|
58
71
|
this.element = undefined as never;
|
|
59
72
|
}
|
|
73
|
+
|
|
74
|
+
navigate(index: number, focus?: boolean): void {
|
|
75
|
+
const items = getItems(this.element, this.options.selector);
|
|
76
|
+
|
|
77
|
+
if (index >= 0 && index < items.length) {
|
|
78
|
+
onNavigate(this, index, items, focus ?? false);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
60
81
|
}
|
|
61
82
|
|
|
62
83
|
type OuiNavigableActive = {
|
|
@@ -64,17 +85,25 @@ type OuiNavigableActive = {
|
|
|
64
85
|
index: number;
|
|
65
86
|
};
|
|
66
87
|
|
|
88
|
+
type OuiNavigableCallback<ReturnType> = (
|
|
89
|
+
event: KeyboardEvent,
|
|
90
|
+
navigable: OuiNavigable,
|
|
91
|
+
) => ReturnType;
|
|
92
|
+
|
|
67
93
|
export type OuiNavigableOptions = {
|
|
94
|
+
orientation?: OuiNavigableOrientation;
|
|
68
95
|
role?: AriaRole;
|
|
69
96
|
selector: string;
|
|
70
97
|
vertical: boolean;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
98
|
+
onActivate?: (navigable: OuiNavigable) => void;
|
|
99
|
+
onDeactivate?: OuiNavigableCallback<void>;
|
|
100
|
+
onNavigation?: (navigable: OuiNavigable) => void;
|
|
101
|
+
onPreventNavigation?: OuiNavigableCallback<boolean>;
|
|
102
|
+
onShouldActivate?: OuiNavigableCallback<boolean>;
|
|
103
|
+
onShouldDeactivate?: OuiNavigableCallback<boolean | undefined>;
|
|
75
104
|
};
|
|
76
105
|
|
|
77
|
-
type OuiNavigableOrientation = 'horizontal' | 'vertical';
|
|
106
|
+
export type OuiNavigableOrientation = 'horizontal' | 'vertical';
|
|
78
107
|
|
|
79
108
|
// #endregion
|
|
80
109
|
|
|
@@ -88,15 +117,17 @@ export function getNavigable(element: HTMLElement, options: OuiNavigableOptions)
|
|
|
88
117
|
return new OuiNavigable(element, options);
|
|
89
118
|
}
|
|
90
119
|
|
|
91
|
-
function
|
|
120
|
+
export function getNavigableOrientation(
|
|
92
121
|
element: HTMLElement,
|
|
93
|
-
|
|
122
|
+
vertical: boolean,
|
|
123
|
+
aria?: boolean,
|
|
94
124
|
): OuiNavigableOrientation {
|
|
95
|
-
const attribute =
|
|
125
|
+
const attribute =
|
|
126
|
+
(aria ?? true) ? getAria(element, 'orientation') : getAttribute(element, 'orientation');
|
|
96
127
|
|
|
97
128
|
return ORIENTATIONS.has(attribute as OuiNavigableOrientation)
|
|
98
129
|
? (attribute as OuiNavigableOrientation)
|
|
99
|
-
:
|
|
130
|
+
: vertical
|
|
100
131
|
? ORIENTATION_VERTICAL
|
|
101
132
|
: ORIENTATION_HORIZONTAL;
|
|
102
133
|
}
|
|
@@ -105,6 +136,39 @@ function isValidItem(element: Element): element is HTMLElement {
|
|
|
105
136
|
return 'disabled' in element ? element.disabled !== true : true;
|
|
106
137
|
}
|
|
107
138
|
|
|
139
|
+
function onClick(navigable: OuiNavigable, event: Event): void {
|
|
140
|
+
const item = findAncestor(event, navigable.options.selector) as HTMLElement;
|
|
141
|
+
const items = getItems(navigable.element, navigable.options.selector);
|
|
142
|
+
|
|
143
|
+
const index = items.indexOf(item);
|
|
144
|
+
|
|
145
|
+
if (index === -1) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (index !== navigable.active.index) {
|
|
150
|
+
onNavigate(navigable, index, items, false);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
navigable.options.onActivate?.(navigable);
|
|
154
|
+
|
|
155
|
+
item.focus();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function onNavigate(
|
|
159
|
+
navigable: OuiNavigable,
|
|
160
|
+
index: number,
|
|
161
|
+
items: HTMLElement[],
|
|
162
|
+
focus: boolean,
|
|
163
|
+
): void {
|
|
164
|
+
navigable.active.element = items[index];
|
|
165
|
+
navigable.active.index = index;
|
|
166
|
+
|
|
167
|
+
updateNavigableFocus(navigable, focus, items);
|
|
168
|
+
|
|
169
|
+
navigable.options.onNavigation?.(navigable);
|
|
170
|
+
}
|
|
171
|
+
|
|
108
172
|
function onFocusin(navigable: OuiNavigable, event: FocusEvent): void {
|
|
109
173
|
const item = findAncestor(event, navigable.options.selector);
|
|
110
174
|
|
|
@@ -155,6 +219,8 @@ function onKeydown(navigable: OuiNavigable, event: KeyboardEvent): void {
|
|
|
155
219
|
}
|
|
156
220
|
|
|
157
221
|
if (mustActivate || shouldActivate) {
|
|
222
|
+
navigable.options.onActivate?.(navigable);
|
|
223
|
+
|
|
158
224
|
return;
|
|
159
225
|
}
|
|
160
226
|
|
|
@@ -188,14 +254,9 @@ function onKeydown(navigable: OuiNavigable, event: KeyboardEvent): void {
|
|
|
188
254
|
}
|
|
189
255
|
}
|
|
190
256
|
|
|
191
|
-
if (index
|
|
192
|
-
|
|
257
|
+
if (index != null && index !== navigable.active.index) {
|
|
258
|
+
onNavigate(navigable, index, items, true);
|
|
193
259
|
}
|
|
194
|
-
|
|
195
|
-
navigable.active.element = items[index];
|
|
196
|
-
navigable.active.index = index;
|
|
197
|
-
|
|
198
|
-
updateNavigableFocus(navigable, true, items);
|
|
199
260
|
}
|
|
200
261
|
|
|
201
262
|
export function updateNavigableFocus(
|
|
@@ -219,10 +280,6 @@ export function updateNavigableFocus(
|
|
|
219
280
|
if (focus) {
|
|
220
281
|
navigable.active.element?.focus();
|
|
221
282
|
}
|
|
222
|
-
|
|
223
|
-
dispatch(navigable.active.element, 'navigable:change', {
|
|
224
|
-
detail: {...navigable.active},
|
|
225
|
-
});
|
|
226
283
|
}
|
|
227
284
|
|
|
228
285
|
// #endregion
|
|
@@ -199,20 +199,18 @@ function onEnd(
|
|
|
199
199
|
setOffset(state, container);
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
202
|
+
if (
|
|
203
|
+
dispatch(state.element, 'oui:movable:end', {
|
|
204
|
+
detail: {
|
|
205
|
+
from: {...globals.original},
|
|
206
|
+
to: getEventPosition(event),
|
|
207
|
+
},
|
|
208
|
+
}).defaultPrevented
|
|
209
|
+
) {
|
|
210
|
+
onCancel(globals, item);
|
|
211
|
+
}
|
|
213
212
|
|
|
214
|
-
|
|
215
|
-
});
|
|
213
|
+
reset();
|
|
216
214
|
}
|
|
217
215
|
|
|
218
216
|
function onMove(
|
|
@@ -101,7 +101,7 @@ function cancelResize(item?: OuiResizableItem): void {
|
|
|
101
101
|
|
|
102
102
|
resetResizable();
|
|
103
103
|
|
|
104
|
-
dispatch(item.state.element, 'resizable:cancel');
|
|
104
|
+
dispatch(item.state.element, 'oui:resizable:cancel');
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
/**
|
|
@@ -289,19 +289,15 @@ function onPointerdown(event: MouseEvent | TouchEvent): void {
|
|
|
289
289
|
event.preventDefault();
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (dispatched.defaultPrevented) {
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
292
|
+
if (dispatch(element!, 'oui:resizable:begin').defaultPrevented) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
298
295
|
|
|
299
|
-
|
|
300
|
-
|
|
296
|
+
current = stored;
|
|
297
|
+
start = position;
|
|
301
298
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
});
|
|
299
|
+
current.state.styles.set();
|
|
300
|
+
styleToggler.set();
|
|
305
301
|
}
|
|
306
302
|
|
|
307
303
|
function onPointermove(event: MouseEvent | TouchEvent): void {
|
|
@@ -320,41 +316,31 @@ function onPointermove(event: MouseEvent | TouchEvent): void {
|
|
|
320
316
|
|
|
321
317
|
getAndSetDimensions(current.state.element, current.state.options, {height, width});
|
|
322
318
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}
|
|
334
|
-
});
|
|
319
|
+
if (
|
|
320
|
+
dispatch(current.state.element, 'oui:resizable:resize', {
|
|
321
|
+
detail: {
|
|
322
|
+
height,
|
|
323
|
+
width,
|
|
324
|
+
},
|
|
325
|
+
}).defaultPrevented
|
|
326
|
+
) {
|
|
327
|
+
cancelResize(current);
|
|
328
|
+
}
|
|
335
329
|
}
|
|
336
330
|
|
|
337
331
|
function onPointerup(): void {
|
|
338
|
-
if (current == null) {
|
|
332
|
+
if (current == null || dispatch(current.state.element, 'oui:resizable:end').defaultPrevented) {
|
|
333
|
+
cancelResize(current);
|
|
334
|
+
|
|
339
335
|
return;
|
|
340
336
|
}
|
|
341
337
|
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
setTimeout(() => {
|
|
345
|
-
if (current == null || dispatched.defaultPrevented) {
|
|
346
|
-
cancelResize(current);
|
|
338
|
+
const {height, width} = current.state.element.getBoundingClientRect();
|
|
347
339
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const {height, width} = current.state.element.getBoundingClientRect();
|
|
340
|
+
current.state.height = height;
|
|
341
|
+
current.state.width = width;
|
|
352
342
|
|
|
353
|
-
|
|
354
|
-
current.state.width = width;
|
|
355
|
-
|
|
356
|
-
resetResizable();
|
|
357
|
-
});
|
|
343
|
+
resetResizable();
|
|
358
344
|
}
|
|
359
345
|
|
|
360
346
|
function removeResizable(element: HTMLElement): void {
|
package/src/js/splitter.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {round} from '@oscarpalmer/atoms/math';
|
|
|
3
3
|
import {clamp} from '@oscarpalmer/atoms/number';
|
|
4
4
|
import {setAria} from '@oscarpalmer/toretto/aria';
|
|
5
5
|
import {getAttribute, setAttribute} from '@oscarpalmer/toretto/attribute';
|
|
6
|
+
import {createElement} from '@oscarpalmer/toretto/create';
|
|
6
7
|
import {getEventPosition, on} from '@oscarpalmer/toretto/event';
|
|
7
8
|
import {findAncestor} from '@oscarpalmer/toretto/find';
|
|
8
9
|
import {setStyle, toggleStyles} from '@oscarpalmer/toretto/style';
|
|
@@ -238,28 +239,30 @@ type OuiSplitterValuesNow = {
|
|
|
238
239
|
// #region Functions
|
|
239
240
|
|
|
240
241
|
function createHandle(): HTMLSpanElement {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
242
|
+
return createElement('span', {
|
|
243
|
+
aria: {
|
|
244
|
+
hidden: true,
|
|
245
|
+
},
|
|
246
|
+
attribute: {
|
|
247
|
+
[ATTRIBUTE_HANDLE]: '',
|
|
248
|
+
},
|
|
249
|
+
});
|
|
247
250
|
}
|
|
248
251
|
|
|
249
252
|
function createSeparator(splitter: OuiSplitter): HTMLDivElement {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
253
|
+
return createElement('div', {
|
|
254
|
+
aria: {
|
|
255
|
+
controls: splitter.element.id,
|
|
256
|
+
label: 'Resize panels',
|
|
257
|
+
},
|
|
258
|
+
attribute: {
|
|
259
|
+
[ATTRIBUTE_SEPARATOR]: '',
|
|
260
|
+
},
|
|
261
|
+
property: {
|
|
262
|
+
role: 'separator',
|
|
263
|
+
tabIndex: 0,
|
|
264
|
+
},
|
|
258
265
|
});
|
|
259
|
-
|
|
260
|
-
setAttribute(separator, ATTRIBUTE_SEPARATOR, '');
|
|
261
|
-
|
|
262
|
-
return separator;
|
|
263
266
|
}
|
|
264
267
|
|
|
265
268
|
function getContained(min: number, value: number, max: number): number {
|