@oscarpalmer/oui 0.15.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/button.css +0 -3
- package/dist/css/popover.css +3 -0
- package/dist/css/splitter.css +78 -0
- package/dist/js/floatable.cjs +77 -35
- package/dist/js/floatable.js +77 -35
- package/dist/js/popover.cjs +8 -2
- package/dist/js/popover.js +8 -2
- package/dist/js/splitter.cjs +272 -0
- package/dist/js/splitter.js +272 -0
- package/dist/js/tooltip.cjs +7 -1
- package/dist/js/tooltip.js +7 -1
- package/package.json +22 -2
- package/src/css/button.scss +0 -3
- package/src/css/popover.scss +4 -0
- package/src/css/splitter.scss +87 -0
- package/src/js/floatable.ts +124 -37
- package/src/js/popover.ts +9 -2
- package/src/js/splitter.ts +407 -0
- package/src/js/tooltip.ts +8 -1
- package/types/floatable.d.cts +5 -1
- package/types/floatable.d.ts +6 -1
- package/types/splitter.d.cts +10 -0
- package/types/splitter.d.ts +11 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
@use 'mixins.scss' as mixins;
|
|
2
|
+
|
|
3
|
+
oui-splitter {
|
|
4
|
+
:where(&) {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-flow: row nowrap;
|
|
7
|
+
flex: 1;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
:where(& > :first-child) {
|
|
11
|
+
flex: var(--oui-splitter-size, .5);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
:where(& > :last-child) {
|
|
15
|
+
flex: calc(1 - var(--oui-splitter-size, .5));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
:where([oui-splitter-separator]) {
|
|
20
|
+
width: .25rem;
|
|
21
|
+
background-color: rgba(0, 0, 0, 0.1);
|
|
22
|
+
border-radius: .25rem;
|
|
23
|
+
position: relative;
|
|
24
|
+
transition: background .125s ease-in-out;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
:where([oui-splitter-separator]:has([oui-splitter-handle]:focus)),
|
|
28
|
+
:where([oui-splitter-separator]:has([oui-splitter-handle]:hover)),
|
|
29
|
+
:where([oui-splitter-separator][oui-splitter-active]) {
|
|
30
|
+
background-color: color-mix(in oklab, var(--oui-blue-6), transparent 82.5%);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:where([oui-splitter-handle]) {
|
|
34
|
+
width: 2rem;
|
|
35
|
+
height: 4rem;
|
|
36
|
+
padding: .875rem;
|
|
37
|
+
position: absolute;
|
|
38
|
+
inset: 50% auto auto 50%;
|
|
39
|
+
transform: translate3d(-50%, -50%, 0);
|
|
40
|
+
cursor: ew-resize;
|
|
41
|
+
|
|
42
|
+
&:focus {
|
|
43
|
+
outline: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&::after {
|
|
47
|
+
content: '';
|
|
48
|
+
width: 100%;
|
|
49
|
+
height: 100%;
|
|
50
|
+
display: block;
|
|
51
|
+
background-color: rgba(0, 0, 0, 0.25);
|
|
52
|
+
outline: .25rem solid transparent;
|
|
53
|
+
outline-offset: .25rem;
|
|
54
|
+
border-radius: .25rem;
|
|
55
|
+
transition: background .125s ease-in-out, outline .125s ease-in-out;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
:where([oui-splitter-handle]:focus),
|
|
60
|
+
:where([oui-splitter-handle]:hover),
|
|
61
|
+
:where([oui-splitter-handle][oui-splitter-active]) {
|
|
62
|
+
&::after {
|
|
63
|
+
background-color: color-mix(in oklab, var(--oui-blue-6), transparent 50%);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
:where([oui-splitter-handle]:focus-visible)::after {
|
|
68
|
+
outline-color: color-mix(in oklab, var(--oui-blue-6), transparent 50%);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
oui-splitter[type='horizontal' i],
|
|
72
|
+
oui-splitter[oui-splitter-horizontal] {
|
|
73
|
+
:where(&) {
|
|
74
|
+
flex-flow: column nowrap;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
:where(& [oui-splitter-separator]) {
|
|
78
|
+
width: 100%;
|
|
79
|
+
height: .25rem;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
:where(& [oui-splitter-handle]) {
|
|
83
|
+
width: 4rem;
|
|
84
|
+
height: 2rem;
|
|
85
|
+
cursor: ns-resize;
|
|
86
|
+
}
|
|
87
|
+
}
|
package/src/js/floatable.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
export class Floatable {
|
|
2
2
|
activate: number | undefined;
|
|
3
|
+
|
|
3
4
|
active = false;
|
|
5
|
+
|
|
4
6
|
deactivate: number | undefined;
|
|
7
|
+
|
|
5
8
|
frame: DOMHighResTimeStamp | undefined;
|
|
6
9
|
|
|
7
10
|
constructor(
|
|
8
11
|
public anchor: HTMLElement,
|
|
9
12
|
public content: HTMLElement,
|
|
13
|
+
readonly attribute: string,
|
|
14
|
+
readonly position: Position,
|
|
15
|
+
readonly preferAbove: boolean,
|
|
10
16
|
) {
|
|
11
17
|
setStyles(content);
|
|
12
18
|
}
|
|
@@ -21,6 +27,28 @@ export class Floatable {
|
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
29
|
|
|
30
|
+
type Position =
|
|
31
|
+
| 'above'
|
|
32
|
+
| 'above-end'
|
|
33
|
+
| 'above-start'
|
|
34
|
+
| 'below'
|
|
35
|
+
| 'below-end'
|
|
36
|
+
| 'below-start'
|
|
37
|
+
| 'end'
|
|
38
|
+
| 'end-bottom'
|
|
39
|
+
| 'end-top'
|
|
40
|
+
| 'horizontal'
|
|
41
|
+
| 'horizontal-bottom'
|
|
42
|
+
| 'horizontal-top'
|
|
43
|
+
| 'start'
|
|
44
|
+
| 'start-bottom'
|
|
45
|
+
| 'start-top'
|
|
46
|
+
| 'vertical'
|
|
47
|
+
| 'vertical-end'
|
|
48
|
+
| 'vertical-start';
|
|
49
|
+
|
|
50
|
+
//
|
|
51
|
+
|
|
24
52
|
export function activate(
|
|
25
53
|
floatable: Floatable,
|
|
26
54
|
active: Set<Floatable>,
|
|
@@ -63,48 +91,78 @@ export function deactivate(
|
|
|
63
91
|
}
|
|
64
92
|
}
|
|
65
93
|
|
|
66
|
-
function getX(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
94
|
+
function getX(anchor: DOMRect, content: DOMRect, position: Position): number {
|
|
95
|
+
if (position.endsWith('-end')) {
|
|
96
|
+
const end = anchor.right - content.width;
|
|
97
|
+
|
|
98
|
+
return end - margin < 0 ? margin : end;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (position.endsWith('-start')) {
|
|
102
|
+
const start = anchor.left;
|
|
103
|
+
|
|
104
|
+
return start + content.width + margin > window.innerWidth
|
|
105
|
+
? window.innerWidth - content.width - margin
|
|
106
|
+
: start;
|
|
75
107
|
}
|
|
76
108
|
|
|
77
|
-
|
|
78
|
-
|
|
109
|
+
if (position.startsWith('end')) {
|
|
110
|
+
return anchor.right + margin;
|
|
111
|
+
}
|
|
79
112
|
|
|
80
|
-
if (
|
|
81
|
-
return
|
|
82
|
-
left: margin,
|
|
83
|
-
};
|
|
113
|
+
if (position.startsWith('start')) {
|
|
114
|
+
return anchor.left - content.width - margin;
|
|
84
115
|
}
|
|
85
116
|
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
right
|
|
89
|
-
}
|
|
117
|
+
if (position.startsWith('horizontal')) {
|
|
118
|
+
if (anchor.right + content.width + margin <= window.innerWidth) {
|
|
119
|
+
return anchor.right + margin;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return anchor.left - content.width - margin;
|
|
90
123
|
}
|
|
91
124
|
|
|
92
|
-
return
|
|
93
|
-
left,
|
|
94
|
-
};
|
|
125
|
+
return anchor.left + (anchor.width - content.width) / 2;
|
|
95
126
|
}
|
|
96
127
|
|
|
97
|
-
function getY(
|
|
98
|
-
|
|
128
|
+
function getY(
|
|
129
|
+
anchor: DOMRect,
|
|
130
|
+
content: DOMRect,
|
|
131
|
+
position: Position,
|
|
132
|
+
preferAbove: boolean,
|
|
133
|
+
): number {
|
|
134
|
+
if (position.startsWith('above')) {
|
|
135
|
+
return anchor.top - content.height - margin;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (position.startsWith('below')) {
|
|
139
|
+
return anchor.bottom + margin;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (position.endsWith('bottom')) {
|
|
143
|
+
const bottom = anchor.bottom - content.height;
|
|
144
|
+
|
|
145
|
+
return bottom + margin < 0 ? margin : bottom;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (position.endsWith('top')) {
|
|
149
|
+
const top = anchor.top;
|
|
99
150
|
|
|
100
|
-
|
|
101
|
-
|
|
151
|
+
return top + content.height + margin > window.innerHeight
|
|
152
|
+
? window.innerHeight - content.height - margin
|
|
153
|
+
: top;
|
|
102
154
|
}
|
|
103
155
|
|
|
104
|
-
|
|
156
|
+
if (position.startsWith('vertical')) {
|
|
157
|
+
if (preferAbove && anchor.top - content.height - margin >= 0) {
|
|
158
|
+
return anchor.top - content.height - margin;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (anchor.bottom + content.height + margin <= document.body.clientHeight) {
|
|
162
|
+
return anchor.bottom + margin;
|
|
163
|
+
}
|
|
105
164
|
|
|
106
|
-
|
|
107
|
-
return top;
|
|
165
|
+
return anchor.top - content.height - margin;
|
|
108
166
|
}
|
|
109
167
|
|
|
110
168
|
return anchor.top + (anchor.height - content.height) / 2;
|
|
@@ -151,17 +209,25 @@ function onDeactivate(
|
|
|
151
209
|
}
|
|
152
210
|
|
|
153
211
|
function update(floatable: Floatable): void {
|
|
212
|
+
let position: Position =
|
|
213
|
+
(floatable.anchor.getAttribute(floatable.attribute)?.trim() as Position) ??
|
|
214
|
+
floatable.position;
|
|
215
|
+
|
|
216
|
+
if (!positions.has(position as Position)) {
|
|
217
|
+
position = floatable.position;
|
|
218
|
+
}
|
|
219
|
+
|
|
154
220
|
function run(): void {
|
|
155
|
-
|
|
156
|
-
|
|
221
|
+
const anchor = floatable.anchor.getBoundingClientRect();
|
|
222
|
+
const content = floatable.content.getBoundingClientRect();
|
|
157
223
|
|
|
158
|
-
|
|
159
|
-
|
|
224
|
+
const left = getX(anchor, content, position);
|
|
225
|
+
const top = getY(anchor, content, position, floatable.preferAbove);
|
|
160
226
|
|
|
161
|
-
|
|
227
|
+
floatable.content.style.inset = `${top}px auto auto ${left}px`;
|
|
162
228
|
|
|
163
|
-
|
|
164
|
-
|
|
229
|
+
floatable.frame = requestAnimationFrame(run);
|
|
230
|
+
}
|
|
165
231
|
|
|
166
232
|
floatable.frame = requestAnimationFrame(run);
|
|
167
233
|
}
|
|
@@ -193,4 +259,25 @@ function stop(floatable: Floatable): void {
|
|
|
193
259
|
//
|
|
194
260
|
|
|
195
261
|
const margin =
|
|
196
|
-
Number.parseInt(getComputedStyle(document.documentElement).fontSize, 10) /
|
|
262
|
+
Number.parseInt(getComputedStyle(document.documentElement).fontSize, 10) / 2;
|
|
263
|
+
|
|
264
|
+
const positions = new Set<Position>([
|
|
265
|
+
'above',
|
|
266
|
+
'above-end',
|
|
267
|
+
'above-start',
|
|
268
|
+
'below',
|
|
269
|
+
'below-end',
|
|
270
|
+
'below-start',
|
|
271
|
+
'end',
|
|
272
|
+
'end-bottom',
|
|
273
|
+
'end-top',
|
|
274
|
+
'horizontal',
|
|
275
|
+
'horizontal-bottom',
|
|
276
|
+
'horizontal-top',
|
|
277
|
+
'start',
|
|
278
|
+
'start-bottom',
|
|
279
|
+
'start-top',
|
|
280
|
+
'vertical',
|
|
281
|
+
'vertical-end',
|
|
282
|
+
'vertical-start',
|
|
283
|
+
]);
|
package/src/js/popover.ts
CHANGED
|
@@ -50,7 +50,14 @@ export class OuiPopoverElement extends HTMLElement {
|
|
|
50
50
|
toggle.setAttribute('aria-controls', content.id);
|
|
51
51
|
toggle.setAttribute('aria-haspopup', 'dialog');
|
|
52
52
|
|
|
53
|
-
this.#floatable = new Floatable(
|
|
53
|
+
this.#floatable = new Floatable(
|
|
54
|
+
toggle,
|
|
55
|
+
content,
|
|
56
|
+
'position',
|
|
57
|
+
'below-start',
|
|
58
|
+
false,
|
|
59
|
+
);
|
|
60
|
+
|
|
54
61
|
this.#focusTrap = createFocusTrap(content);
|
|
55
62
|
|
|
56
63
|
mapped.set(toggle, this.#floatable);
|
|
@@ -132,7 +139,7 @@ function closeAbove(element: HTMLElement): void {
|
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
141
|
|
|
135
|
-
order[index].content
|
|
142
|
+
focus(order[index].content);
|
|
136
143
|
}
|
|
137
144
|
|
|
138
145
|
function focus(content: HTMLElement): void {
|