@oscarpalmer/oui 0.16.0 → 0.17.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 +3 -0
- package/dist/css/splitter.css +3 -3
- package/dist/js/floatable.cjs +76 -34
- package/dist/js/floatable.js +76 -34
- package/dist/js/popover.cjs +7 -1
- package/dist/js/popover.js +7 -1
- package/dist/js/splitter.cjs +80 -19
- package/dist/js/splitter.js +80 -19
- package/dist/js/tooltip.cjs +7 -1
- package/dist/js/tooltip.js +7 -1
- package/package.json +2 -2
- package/src/css/popover.scss +4 -0
- package/src/css/splitter.scss +3 -3
- package/src/js/floatable.ts +123 -36
- package/src/js/popover.ts +8 -1
- package/src/js/splitter.ts +116 -26
- package/src/js/tooltip.ts +8 -1
- package/types/floatable.d.cts +5 -1
- package/types/floatable.d.ts +6 -1
package/src/js/splitter.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {isNullableOrWhitespace} from '@oscarpalmer/atoms/is';
|
|
2
|
+
import {round} from '@oscarpalmer/atoms/math';
|
|
1
3
|
import {clamp} from '@oscarpalmer/atoms/number';
|
|
2
4
|
import {getPosition, on} from '@oscarpalmer/toretto/event';
|
|
3
5
|
import {findAncestor} from '@oscarpalmer/toretto/find';
|
|
@@ -8,11 +10,6 @@ declare global {
|
|
|
8
10
|
}
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
type Flex = {
|
|
12
|
-
previous: number;
|
|
13
|
-
value: number;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
13
|
export class OuiSplitterElement extends HTMLElement {
|
|
17
14
|
#splitter: Splitter;
|
|
18
15
|
|
|
@@ -43,12 +40,9 @@ export class OuiSplitterElement extends HTMLElement {
|
|
|
43
40
|
}
|
|
44
41
|
}
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
flex: Flex = {
|
|
48
|
-
previous: 0.5,
|
|
49
|
-
value: 0.5,
|
|
50
|
-
};
|
|
43
|
+
let index = 0;
|
|
51
44
|
|
|
45
|
+
class Splitter {
|
|
52
46
|
handle: HTMLSpanElement;
|
|
53
47
|
|
|
54
48
|
rectangle: DOMRect;
|
|
@@ -57,13 +51,21 @@ class Splitter {
|
|
|
57
51
|
|
|
58
52
|
types: Types;
|
|
59
53
|
|
|
54
|
+
values: Values;
|
|
55
|
+
|
|
60
56
|
constructor(
|
|
61
57
|
public element: OuiSplitterElement,
|
|
62
58
|
public panels: HTMLElement[],
|
|
63
59
|
) {
|
|
60
|
+
if (isNullableOrWhitespace(element.id)) {
|
|
61
|
+
element.id = `oui_splitter_${++index}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.values = getValues(element);
|
|
65
|
+
|
|
64
66
|
this.handle = createHandle();
|
|
65
67
|
this.rectangle = element.getBoundingClientRect();
|
|
66
|
-
this.separator = createSeparator();
|
|
68
|
+
this.separator = createSeparator(this);
|
|
67
69
|
|
|
68
70
|
this.separator.append(this.handle);
|
|
69
71
|
|
|
@@ -72,13 +74,16 @@ class Splitter {
|
|
|
72
74
|
mapped.set(element, this);
|
|
73
75
|
mapped.set(this.handle, this);
|
|
74
76
|
|
|
75
|
-
setSize(this, this.
|
|
77
|
+
setSize(this, this.values.now.current);
|
|
76
78
|
|
|
77
79
|
this.types = getTypes(this);
|
|
78
80
|
|
|
79
81
|
if (this.types.horizontal) {
|
|
80
82
|
this.element.setAttribute('oui-splitter-horizontal', '');
|
|
81
83
|
}
|
|
84
|
+
|
|
85
|
+
setAriaOrientation(this);
|
|
86
|
+
setAriaValue(this);
|
|
82
87
|
}
|
|
83
88
|
}
|
|
84
89
|
|
|
@@ -87,35 +92,53 @@ type Types = {
|
|
|
87
92
|
horizontal: boolean;
|
|
88
93
|
};
|
|
89
94
|
|
|
95
|
+
type Values = {
|
|
96
|
+
max: number;
|
|
97
|
+
min: number;
|
|
98
|
+
now: ValuesNow;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
type ValuesNow = {
|
|
102
|
+
current: number;
|
|
103
|
+
previous: number;
|
|
104
|
+
};
|
|
105
|
+
|
|
90
106
|
//
|
|
91
107
|
|
|
92
108
|
function createHandle(): HTMLSpanElement {
|
|
93
109
|
const handle = document.createElement('span');
|
|
94
110
|
|
|
95
|
-
handle.
|
|
96
|
-
|
|
97
|
-
handle.setAttribute('aria-label', 'Resize panels');
|
|
111
|
+
handle.setAttribute('aria-hidden', 'true');
|
|
98
112
|
handle.setAttribute('oui-splitter-handle', '');
|
|
99
113
|
|
|
100
114
|
return handle;
|
|
101
115
|
}
|
|
102
116
|
|
|
103
|
-
function createSeparator(): HTMLDivElement {
|
|
117
|
+
function createSeparator(splitter: Splitter): HTMLDivElement {
|
|
104
118
|
const separator = document.createElement('div');
|
|
105
119
|
|
|
120
|
+
separator.role = 'separator';
|
|
121
|
+
separator.tabIndex = 0;
|
|
122
|
+
|
|
123
|
+
separator.setAttribute('aria-controls', splitter.element.id);
|
|
124
|
+
separator.setAttribute('aria-label', 'Resize panels');
|
|
106
125
|
separator.setAttribute('oui-splitter-separator', '');
|
|
107
126
|
|
|
108
127
|
return separator;
|
|
109
128
|
}
|
|
110
129
|
|
|
111
|
-
function
|
|
130
|
+
function getContained(min: number, value: number, max: number): number {
|
|
131
|
+
return round(clamp(value, min, max), 6);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function getSize(splitter: Splitter, value: number): number {
|
|
112
135
|
const {height, left, top, width} = splitter.rectangle;
|
|
113
136
|
|
|
114
137
|
const offset = value - (splitter.types.horizontal ? top : left);
|
|
115
138
|
|
|
116
139
|
const fraction = offset / (splitter.types.horizontal ? height : width);
|
|
117
140
|
|
|
118
|
-
return
|
|
141
|
+
return getContained(splitter.values.min, fraction, splitter.values.max);
|
|
119
142
|
}
|
|
120
143
|
|
|
121
144
|
function getTypes(splitter: Splitter, width?: number, height?: number): Types {
|
|
@@ -143,15 +166,57 @@ function getTypes(splitter: Splitter, width?: number, height?: number): Types {
|
|
|
143
166
|
};
|
|
144
167
|
}
|
|
145
168
|
|
|
169
|
+
function getValue(
|
|
170
|
+
element: HTMLElement,
|
|
171
|
+
name: string,
|
|
172
|
+
defaultValue: number,
|
|
173
|
+
): number {
|
|
174
|
+
const value = element.getAttribute(name);
|
|
175
|
+
|
|
176
|
+
if (isNullableOrWhitespace(value)) {
|
|
177
|
+
return defaultValue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const parsed = Number.parseFloat(value);
|
|
181
|
+
|
|
182
|
+
if (Number.isNaN(parsed)) {
|
|
183
|
+
return defaultValue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return parsed < 0 ? defaultValue : parsed > 1 ? parsed / 100 : parsed;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function getValues(element: OuiSplitterElement): Values {
|
|
190
|
+
const values: Values = {
|
|
191
|
+
max: getValue(element, 'max', 0.9),
|
|
192
|
+
min: getValue(element, 'min', 0.1),
|
|
193
|
+
now: {
|
|
194
|
+
current: 0.5,
|
|
195
|
+
previous: 0.5,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const value = getContained(
|
|
200
|
+
values.min,
|
|
201
|
+
getValue(element, 'size', values.now.current),
|
|
202
|
+
values.max,
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
values.now.current = value;
|
|
206
|
+
values.now.previous = value;
|
|
207
|
+
|
|
208
|
+
return values;
|
|
209
|
+
}
|
|
210
|
+
|
|
146
211
|
function onEnd(reset: boolean): void {
|
|
147
212
|
if (splitter != null) {
|
|
148
213
|
splitter.handle.removeAttribute('oui-splitter-active');
|
|
149
214
|
splitter.separator.removeAttribute('oui-splitter-active');
|
|
150
215
|
|
|
151
216
|
if (reset) {
|
|
152
|
-
setSize(splitter, splitter.
|
|
217
|
+
setSize(splitter, splitter.values.now.previous);
|
|
153
218
|
} else {
|
|
154
|
-
splitter.
|
|
219
|
+
splitter.values.now.previous = splitter.values.now.current;
|
|
155
220
|
}
|
|
156
221
|
}
|
|
157
222
|
|
|
@@ -199,12 +264,12 @@ function onMousemove(event: MouseEvent): void {
|
|
|
199
264
|
const position = getPosition(event);
|
|
200
265
|
|
|
201
266
|
if (position != null) {
|
|
202
|
-
splitter.
|
|
267
|
+
splitter.values.now.current = getSize(
|
|
203
268
|
splitter,
|
|
204
269
|
splitter.types.horizontal ? position.y : position.x,
|
|
205
270
|
);
|
|
206
271
|
|
|
207
|
-
setSize(splitter, splitter.
|
|
272
|
+
setSize(splitter, splitter.values.now.current);
|
|
208
273
|
}
|
|
209
274
|
}
|
|
210
275
|
|
|
@@ -227,7 +292,11 @@ function onNavigate(event: KeyboardEvent): void {
|
|
|
227
292
|
}
|
|
228
293
|
|
|
229
294
|
if (absoluteKeys.has(event.key)) {
|
|
230
|
-
setSize(
|
|
295
|
+
setSize(
|
|
296
|
+
splitter,
|
|
297
|
+
event.key === 'End' ? splitter.values.max : splitter.values.min,
|
|
298
|
+
true,
|
|
299
|
+
);
|
|
231
300
|
|
|
232
301
|
return;
|
|
233
302
|
}
|
|
@@ -241,7 +310,7 @@ function onNavigate(event: KeyboardEvent): void {
|
|
|
241
310
|
|
|
242
311
|
const modifier = negativeKeys.has(event.key) ? -1 : 1;
|
|
243
312
|
|
|
244
|
-
setSize(splitter, splitter.
|
|
313
|
+
setSize(splitter, splitter.values.now.current + modifier * 0.05, true);
|
|
245
314
|
}
|
|
246
315
|
|
|
247
316
|
function onObservation(entries: ResizeObserverEntry[]): void {
|
|
@@ -268,20 +337,41 @@ function onObservation(entries: ResizeObserverEntry[]): void {
|
|
|
268
337
|
} else {
|
|
269
338
|
splitter.element.removeAttribute('oui-splitter-horizontal');
|
|
270
339
|
}
|
|
340
|
+
|
|
341
|
+
setAriaOrientation(splitter);
|
|
271
342
|
}
|
|
272
343
|
|
|
273
344
|
frame = undefined;
|
|
274
345
|
});
|
|
275
346
|
}
|
|
276
347
|
|
|
348
|
+
function setAriaOrientation(splitter: Splitter): void {
|
|
349
|
+
splitter.separator.setAttribute(
|
|
350
|
+
'aria-orientation',
|
|
351
|
+
splitter.types.horizontal ? 'horizontal' : 'vertical',
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function setAriaValue(splitter: Splitter): void {
|
|
356
|
+
splitter.separator.setAttribute('aria-valuemax', String(splitter.values.max));
|
|
357
|
+
splitter.separator.setAttribute('aria-valuemin', String(splitter.values.min));
|
|
358
|
+
|
|
359
|
+
splitter.separator.setAttribute(
|
|
360
|
+
'aria-valuenow',
|
|
361
|
+
String(splitter.values.now.current),
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
277
365
|
function setSize(splitter: Splitter, size: number, previous?: boolean): void {
|
|
278
|
-
splitter.
|
|
366
|
+
splitter.values.now.current = size;
|
|
279
367
|
|
|
280
368
|
if (previous) {
|
|
281
|
-
splitter.
|
|
369
|
+
splitter.values.now.previous = size;
|
|
282
370
|
}
|
|
283
371
|
|
|
284
372
|
splitter.element.style.setProperty('--oui-splitter-size', `${size}`);
|
|
373
|
+
|
|
374
|
+
setAriaValue(splitter);
|
|
285
375
|
}
|
|
286
376
|
|
|
287
377
|
//
|
package/src/js/tooltip.ts
CHANGED
|
@@ -20,7 +20,14 @@ class Tooltip {
|
|
|
20
20
|
|
|
21
21
|
constructor(anchor: HTMLElement, content: Content) {
|
|
22
22
|
this.attributes = content.attributes;
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
this.floatable = new Floatable(
|
|
25
|
+
anchor,
|
|
26
|
+
content.element,
|
|
27
|
+
`${selector}-position`,
|
|
28
|
+
'vertical',
|
|
29
|
+
true,
|
|
30
|
+
);
|
|
24
31
|
|
|
25
32
|
initialize(this);
|
|
26
33
|
}
|
package/types/floatable.d.cts
CHANGED
|
@@ -3,13 +3,17 @@
|
|
|
3
3
|
export declare class Floatable {
|
|
4
4
|
anchor: HTMLElement;
|
|
5
5
|
content: HTMLElement;
|
|
6
|
+
readonly attribute: string;
|
|
7
|
+
readonly position: Position;
|
|
8
|
+
readonly preferAbove: boolean;
|
|
6
9
|
activate: number | undefined;
|
|
7
10
|
active: boolean;
|
|
8
11
|
deactivate: number | undefined;
|
|
9
12
|
frame: DOMHighResTimeStamp | undefined;
|
|
10
|
-
constructor(anchor: HTMLElement, content: HTMLElement);
|
|
13
|
+
constructor(anchor: HTMLElement, content: HTMLElement, attribute: string, position: Position, preferAbove: boolean);
|
|
11
14
|
destroy(): void;
|
|
12
15
|
}
|
|
16
|
+
export type Position = "above" | "above-end" | "above-start" | "below" | "below-end" | "below-start" | "end" | "end-bottom" | "end-top" | "horizontal" | "horizontal-bottom" | "horizontal-top" | "start" | "start-bottom" | "start-top" | "vertical" | "vertical-end" | "vertical-start";
|
|
13
17
|
export declare function activate(floatable: Floatable, active: Set<Floatable>, order?: Floatable[], time?: number): void;
|
|
14
18
|
export declare function deactivate(floatable: Floatable, active: Set<Floatable>, order?: Floatable[], time?: number): void;
|
|
15
19
|
|
package/types/floatable.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
export declare class Floatable {
|
|
2
2
|
anchor: HTMLElement;
|
|
3
3
|
content: HTMLElement;
|
|
4
|
+
readonly attribute: string;
|
|
5
|
+
readonly position: Position;
|
|
6
|
+
readonly preferAbove: boolean;
|
|
4
7
|
activate: number | undefined;
|
|
5
8
|
active: boolean;
|
|
6
9
|
deactivate: number | undefined;
|
|
7
10
|
frame: DOMHighResTimeStamp | undefined;
|
|
8
|
-
constructor(anchor: HTMLElement, content: HTMLElement);
|
|
11
|
+
constructor(anchor: HTMLElement, content: HTMLElement, attribute: string, position: Position, preferAbove: boolean);
|
|
9
12
|
destroy(): void;
|
|
10
13
|
}
|
|
14
|
+
type Position = 'above' | 'above-end' | 'above-start' | 'below' | 'below-end' | 'below-start' | 'end' | 'end-bottom' | 'end-top' | 'horizontal' | 'horizontal-bottom' | 'horizontal-top' | 'start' | 'start-bottom' | 'start-top' | 'vertical' | 'vertical-end' | 'vertical-start';
|
|
11
15
|
export declare function activate(floatable: Floatable, active: Set<Floatable>, order?: Floatable[], time?: number): void;
|
|
12
16
|
export declare function deactivate(floatable: Floatable, active: Set<Floatable>, order?: Floatable[], time?: number): void;
|
|
17
|
+
export {};
|