@oscarpalmer/oui 0.14.0 → 0.15.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/README.md +2 -0
- package/dist/css/popover.css +9 -0
- package/dist/css/tooltip.css +0 -3
- package/dist/js/floatable.cjs +21 -8
- package/dist/js/floatable.js +21 -8
- package/dist/js/focus-trap/embedded.cjs +150 -0
- package/dist/js/focus-trap/embedded.js +150 -0
- package/dist/js/focus-trap/standalone.cjs +8 -0
- package/dist/js/focus-trap/standalone.js +7 -0
- package/dist/js/index.cjs +5 -1
- package/dist/js/index.js +6 -1
- package/dist/js/popover.cjs +160 -0
- package/dist/js/popover.js +160 -0
- package/dist/js/tooltip.cjs +2 -3
- package/dist/js/tooltip.js +2 -3
- package/package.json +5 -5
- package/src/css/popover.scss +9 -0
- package/src/css/tooltip.scss +0 -3
- package/src/js/floatable.ts +52 -24
- package/src/js/focus-trap/embedded.ts +215 -0
- package/src/js/focus-trap/standalone.ts +9 -0
- package/src/js/index.ts +3 -1
- package/src/js/popover.ts +221 -0
- package/src/js/tooltip.ts +2 -4
- package/types/floatable.d.cts +2 -2
- package/types/floatable.d.ts +2 -2
- package/types/focus-trap/embedded.d.cts +15 -0
- package/types/focus-trap/embedded.d.ts +11 -0
- package/types/index.d.cts +11 -0
- package/types/index.d.ts +3 -1
- package/types/popover.d.cts +14 -0
- package/types/popover.d.ts +15 -0
- package/dist/js/focus-trap.cjs +0 -104
- package/dist/js/focus-trap.js +0 -103
- package/src/js/focus-trap.ts +0 -133
- /package/types/{focus-trap.d.cts → focus-trap/standalone.d.cts} +0 -0
- /package/types/{focus-trap.d.ts → focus-trap/standalone.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Oui
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@oscarpalmer/oui)
|
|
4
|
+
|
|
3
5
|
_Oui_ _(Oscar UI)_ is a collection of styles, components, and related thingamjigs for designing websites and applications, which I often use to build prototypes and proof-of-concepts for myself and at work.
|
|
4
6
|
|
|
5
7
|
## License
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
|
|
2
|
+
:where([oui-popover-content]) {
|
|
3
|
+
box-shadow: inset 0 1px 0 0 transparent, 0 0 0 1px rgba(0, 0, 0, 0.05), 0 1px 2px -0.5px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.05), 0 4px 8px -2px rgba(0, 0, 0, 0.05), 0 8px 16px -4px rgba(0, 0, 0, 0.05), 0 16px 32px -8px rgba(0, 0, 0, 0.05), 0 32px 64px -16px rgba(0, 0, 0, 0.05), 0 64px 128px -32px rgba(0, 0, 0, 0.05);
|
|
4
|
+
padding: 1rem;
|
|
5
|
+
background-color: var(--oui-absolute);
|
|
6
|
+
border-radius: 0.5rem;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
|
package/dist/css/tooltip.css
CHANGED
package/dist/js/floatable.cjs
CHANGED
|
@@ -4,6 +4,7 @@ class Floatable {
|
|
|
4
4
|
constructor(anchor, content) {
|
|
5
5
|
this.anchor = anchor;
|
|
6
6
|
this.content = content;
|
|
7
|
+
setStyles(content);
|
|
7
8
|
}
|
|
8
9
|
activate;
|
|
9
10
|
active = false;
|
|
@@ -17,29 +18,29 @@ class Floatable {
|
|
|
17
18
|
this.content = void 0;
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
|
-
function activate(floatable, active, time) {
|
|
21
|
+
function activate(floatable, active, order, time) {
|
|
21
22
|
stop(floatable);
|
|
22
23
|
if (floatable.active) {
|
|
23
24
|
return;
|
|
24
25
|
}
|
|
25
26
|
if (time == null || time === 0) {
|
|
26
|
-
onActivate(floatable, active);
|
|
27
|
+
onActivate(floatable, active, order);
|
|
27
28
|
} else {
|
|
28
29
|
floatable.activate = +setTimeout(() => {
|
|
29
|
-
onActivate(floatable, active);
|
|
30
|
+
onActivate(floatable, active, order);
|
|
30
31
|
}, time);
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
|
-
function deactivate(floatable, active, time) {
|
|
34
|
+
function deactivate(floatable, active, order, time) {
|
|
34
35
|
stop(floatable);
|
|
35
36
|
if (!floatable.active) {
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
39
|
if (time == null || time === 0) {
|
|
39
|
-
onDeactivate(floatable, active);
|
|
40
|
+
onDeactivate(floatable, active, order);
|
|
40
41
|
} else {
|
|
41
42
|
floatable.deactivate = +setTimeout(() => {
|
|
42
|
-
onDeactivate(floatable, active);
|
|
43
|
+
onDeactivate(floatable, active, order);
|
|
43
44
|
}, time);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
@@ -77,16 +78,23 @@ function getY(anchor, content) {
|
|
|
77
78
|
}
|
|
78
79
|
return anchor.top + (anchor.height - content.height) / 2;
|
|
79
80
|
}
|
|
80
|
-
function onActivate(floatable, active) {
|
|
81
|
+
function onActivate(floatable, active, order) {
|
|
81
82
|
floatable.active = true;
|
|
82
83
|
active.add(floatable);
|
|
84
|
+
if (order != null) {
|
|
85
|
+
order.push(floatable);
|
|
86
|
+
}
|
|
83
87
|
document.body.append(floatable.content);
|
|
84
88
|
floatable.content.hidden = false;
|
|
85
89
|
update(floatable);
|
|
86
90
|
}
|
|
87
|
-
function onDeactivate(floatable, active) {
|
|
91
|
+
function onDeactivate(floatable, active, order) {
|
|
88
92
|
floatable.active = false;
|
|
89
93
|
active.delete(floatable);
|
|
94
|
+
const index = order?.indexOf(floatable) ?? -1;
|
|
95
|
+
if (index > -1) {
|
|
96
|
+
order?.splice(index, 1);
|
|
97
|
+
}
|
|
90
98
|
floatable.content.hidden = true;
|
|
91
99
|
floatable.anchor.insertAdjacentElement("afterend", floatable.content);
|
|
92
100
|
}
|
|
@@ -101,6 +109,11 @@ function update(floatable) {
|
|
|
101
109
|
}
|
|
102
110
|
floatable.frame = requestAnimationFrame(run);
|
|
103
111
|
}
|
|
112
|
+
function setStyles(content) {
|
|
113
|
+
content.style.position = "fixed";
|
|
114
|
+
content.style.inset = "0 auto auto 0";
|
|
115
|
+
content.style.zIndex = "10";
|
|
116
|
+
}
|
|
104
117
|
function stop(floatable) {
|
|
105
118
|
if (floatable.frame != null) {
|
|
106
119
|
cancelAnimationFrame(floatable.frame);
|
package/dist/js/floatable.js
CHANGED
|
@@ -2,6 +2,7 @@ class Floatable {
|
|
|
2
2
|
constructor(anchor, content) {
|
|
3
3
|
this.anchor = anchor;
|
|
4
4
|
this.content = content;
|
|
5
|
+
setStyles(content);
|
|
5
6
|
}
|
|
6
7
|
activate;
|
|
7
8
|
active = false;
|
|
@@ -15,29 +16,29 @@ class Floatable {
|
|
|
15
16
|
this.content = void 0;
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
|
-
function activate(floatable, active, time) {
|
|
19
|
+
function activate(floatable, active, order, time) {
|
|
19
20
|
stop(floatable);
|
|
20
21
|
if (floatable.active) {
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
24
|
if (time == null || time === 0) {
|
|
24
|
-
onActivate(floatable, active);
|
|
25
|
+
onActivate(floatable, active, order);
|
|
25
26
|
} else {
|
|
26
27
|
floatable.activate = +setTimeout(() => {
|
|
27
|
-
onActivate(floatable, active);
|
|
28
|
+
onActivate(floatable, active, order);
|
|
28
29
|
}, time);
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
|
-
function deactivate(floatable, active, time) {
|
|
32
|
+
function deactivate(floatable, active, order, time) {
|
|
32
33
|
stop(floatable);
|
|
33
34
|
if (!floatable.active) {
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
37
|
if (time == null || time === 0) {
|
|
37
|
-
onDeactivate(floatable, active);
|
|
38
|
+
onDeactivate(floatable, active, order);
|
|
38
39
|
} else {
|
|
39
40
|
floatable.deactivate = +setTimeout(() => {
|
|
40
|
-
onDeactivate(floatable, active);
|
|
41
|
+
onDeactivate(floatable, active, order);
|
|
41
42
|
}, time);
|
|
42
43
|
}
|
|
43
44
|
}
|
|
@@ -75,16 +76,23 @@ function getY(anchor, content) {
|
|
|
75
76
|
}
|
|
76
77
|
return anchor.top + (anchor.height - content.height) / 2;
|
|
77
78
|
}
|
|
78
|
-
function onActivate(floatable, active) {
|
|
79
|
+
function onActivate(floatable, active, order) {
|
|
79
80
|
floatable.active = true;
|
|
80
81
|
active.add(floatable);
|
|
82
|
+
if (order != null) {
|
|
83
|
+
order.push(floatable);
|
|
84
|
+
}
|
|
81
85
|
document.body.append(floatable.content);
|
|
82
86
|
floatable.content.hidden = false;
|
|
83
87
|
update(floatable);
|
|
84
88
|
}
|
|
85
|
-
function onDeactivate(floatable, active) {
|
|
89
|
+
function onDeactivate(floatable, active, order) {
|
|
86
90
|
floatable.active = false;
|
|
87
91
|
active.delete(floatable);
|
|
92
|
+
const index = order?.indexOf(floatable) ?? -1;
|
|
93
|
+
if (index > -1) {
|
|
94
|
+
order?.splice(index, 1);
|
|
95
|
+
}
|
|
88
96
|
floatable.content.hidden = true;
|
|
89
97
|
floatable.anchor.insertAdjacentElement("afterend", floatable.content);
|
|
90
98
|
}
|
|
@@ -99,6 +107,11 @@ function update(floatable) {
|
|
|
99
107
|
}
|
|
100
108
|
floatable.frame = requestAnimationFrame(run);
|
|
101
109
|
}
|
|
110
|
+
function setStyles(content) {
|
|
111
|
+
content.style.position = "fixed";
|
|
112
|
+
content.style.inset = "0 auto auto 0";
|
|
113
|
+
content.style.zIndex = "10";
|
|
114
|
+
}
|
|
102
115
|
function stop(floatable) {
|
|
103
116
|
if (floatable.frame != null) {
|
|
104
117
|
cancelAnimationFrame(floatable.frame);
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const number = require("@oscarpalmer/atoms/number");
|
|
4
|
+
const event = require("@oscarpalmer/toretto/event");
|
|
5
|
+
const find = require("@oscarpalmer/toretto/find");
|
|
6
|
+
const focusable = require("@oscarpalmer/toretto/focusable");
|
|
7
|
+
class FocusTrap {
|
|
8
|
+
constructor(element) {
|
|
9
|
+
this.element = element;
|
|
10
|
+
element.setAttribute("aria-modal", "true");
|
|
11
|
+
element.setAttribute("tabindex", "-1");
|
|
12
|
+
}
|
|
13
|
+
active = true;
|
|
14
|
+
destroy() {
|
|
15
|
+
this.element = void 0;
|
|
16
|
+
}
|
|
17
|
+
disable() {
|
|
18
|
+
if (this.active && !this.element.hasAttribute(`${selector}-noescape`)) {
|
|
19
|
+
setDisabled(this, true);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
enable() {
|
|
23
|
+
if (!this.active) {
|
|
24
|
+
setDisabled(this, false);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function createFocusTrap(element) {
|
|
29
|
+
let focusTrap = focusTraps.get(element);
|
|
30
|
+
if (focusTrap == null) {
|
|
31
|
+
focusTrap = new FocusTrap(element);
|
|
32
|
+
focusTraps.set(element, focusTrap);
|
|
33
|
+
}
|
|
34
|
+
return focusTrap;
|
|
35
|
+
}
|
|
36
|
+
function onEscape(event2) {
|
|
37
|
+
focusTraps.get(
|
|
38
|
+
find.findAncestor(event2.target, `[${selector}]`)
|
|
39
|
+
)?.disable();
|
|
40
|
+
}
|
|
41
|
+
function onFocusIn(event2) {
|
|
42
|
+
lastTarget = find.findAncestor(event2.target, `[${selector}]`) == null ? void 0 : event2.target;
|
|
43
|
+
}
|
|
44
|
+
function onKeydown(event2) {
|
|
45
|
+
switch (event2.key) {
|
|
46
|
+
case "Escape":
|
|
47
|
+
onEscape(event2);
|
|
48
|
+
break;
|
|
49
|
+
case "Tab":
|
|
50
|
+
onTab(event2);
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function onPointerdown(event2) {
|
|
57
|
+
const lastFocusTrap = find.findAncestor(
|
|
58
|
+
lastTarget,
|
|
59
|
+
`[${selector}]`
|
|
60
|
+
);
|
|
61
|
+
const nextFocusTrap = find.findAncestor(
|
|
62
|
+
event2.target,
|
|
63
|
+
`[${selector}]`
|
|
64
|
+
);
|
|
65
|
+
if (lastFocusTrap != null && nextFocusTrap !== lastFocusTrap) {
|
|
66
|
+
event2.preventDefault();
|
|
67
|
+
lastTarget?.focus();
|
|
68
|
+
}
|
|
69
|
+
if (disabled.size === 0) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
for (const focusTrap of disabled) {
|
|
73
|
+
if (focusTrap.element !== nextFocusTrap) {
|
|
74
|
+
focusTrap.enable();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function onTab(event2) {
|
|
80
|
+
const element = find.findAncestor(
|
|
81
|
+
event2.target,
|
|
82
|
+
`[${selector}]`
|
|
83
|
+
);
|
|
84
|
+
const focusTrap = focusTraps.get(element);
|
|
85
|
+
if (focusTrap == null) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (focusTrap.active) {
|
|
89
|
+
event2.preventDefault();
|
|
90
|
+
}
|
|
91
|
+
const elements = focusable.getFocusable(element);
|
|
92
|
+
const index = elements.indexOf(event2.target);
|
|
93
|
+
if (index === -1 || elements.length === 0) {
|
|
94
|
+
tabToTrap(focusTrap, elements, element);
|
|
95
|
+
} else {
|
|
96
|
+
tabToElement(event2, focusTrap, elements, index);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function setDisabled(focusTrap, value) {
|
|
100
|
+
focusTrap.element.setAttribute("aria-modal", String(!value));
|
|
101
|
+
focusTrap.active = !value;
|
|
102
|
+
if (value) {
|
|
103
|
+
disabled.add(focusTrap);
|
|
104
|
+
} else {
|
|
105
|
+
disabled.delete(focusTrap);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function tabToElement(event2, focusTrap, elements, index) {
|
|
109
|
+
const next = number.clamp(
|
|
110
|
+
index + (event2.shiftKey ? -1 : 1),
|
|
111
|
+
0,
|
|
112
|
+
elements.length - 1,
|
|
113
|
+
true
|
|
114
|
+
);
|
|
115
|
+
if (!focusTrap.active) {
|
|
116
|
+
if (index === length - 1 && next === 0 || index === 0 && next === length - 1) {
|
|
117
|
+
lastTarget = void 0;
|
|
118
|
+
focusTrap.enable();
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
elements[next].focus();
|
|
123
|
+
lastTarget = elements[next];
|
|
124
|
+
}
|
|
125
|
+
function tabToTrap(focusTrap, elements, element) {
|
|
126
|
+
if (focusTrap.active) {
|
|
127
|
+
const next = length === 0 ? element : elements[0];
|
|
128
|
+
next.focus();
|
|
129
|
+
lastTarget = next;
|
|
130
|
+
} else {
|
|
131
|
+
lastTarget = void 0;
|
|
132
|
+
focusTrap.enable();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const disabled = /* @__PURE__ */ new Set();
|
|
136
|
+
const eventOptions = {
|
|
137
|
+
capture: true,
|
|
138
|
+
passive: false
|
|
139
|
+
};
|
|
140
|
+
const focusTraps = /* @__PURE__ */ new WeakMap();
|
|
141
|
+
let lastTarget;
|
|
142
|
+
const selector = "oui-focus-trap";
|
|
143
|
+
event.on(document, "focusin", onFocusIn, eventOptions);
|
|
144
|
+
event.on(document, "keydown", onKeydown, eventOptions);
|
|
145
|
+
event.on(document, "mousedown", onPointerdown, eventOptions);
|
|
146
|
+
event.on(document, "touchstart", onPointerdown, eventOptions);
|
|
147
|
+
exports.FocusTrap = FocusTrap;
|
|
148
|
+
exports.createFocusTrap = createFocusTrap;
|
|
149
|
+
exports.focusTraps = focusTraps;
|
|
150
|
+
exports.selector = selector;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { clamp } from "@oscarpalmer/atoms/number";
|
|
2
|
+
import { on } from "@oscarpalmer/toretto/event";
|
|
3
|
+
import { findAncestor } from "@oscarpalmer/toretto/find";
|
|
4
|
+
import { getFocusable } from "@oscarpalmer/toretto/focusable";
|
|
5
|
+
class FocusTrap {
|
|
6
|
+
constructor(element) {
|
|
7
|
+
this.element = element;
|
|
8
|
+
element.setAttribute("aria-modal", "true");
|
|
9
|
+
element.setAttribute("tabindex", "-1");
|
|
10
|
+
}
|
|
11
|
+
active = true;
|
|
12
|
+
destroy() {
|
|
13
|
+
this.element = void 0;
|
|
14
|
+
}
|
|
15
|
+
disable() {
|
|
16
|
+
if (this.active && !this.element.hasAttribute(`${selector}-noescape`)) {
|
|
17
|
+
setDisabled(this, true);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
enable() {
|
|
21
|
+
if (!this.active) {
|
|
22
|
+
setDisabled(this, false);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function createFocusTrap(element) {
|
|
27
|
+
let focusTrap = focusTraps.get(element);
|
|
28
|
+
if (focusTrap == null) {
|
|
29
|
+
focusTrap = new FocusTrap(element);
|
|
30
|
+
focusTraps.set(element, focusTrap);
|
|
31
|
+
}
|
|
32
|
+
return focusTrap;
|
|
33
|
+
}
|
|
34
|
+
function onEscape(event) {
|
|
35
|
+
focusTraps.get(
|
|
36
|
+
findAncestor(event.target, `[${selector}]`)
|
|
37
|
+
)?.disable();
|
|
38
|
+
}
|
|
39
|
+
function onFocusIn(event) {
|
|
40
|
+
lastTarget = findAncestor(event.target, `[${selector}]`) == null ? void 0 : event.target;
|
|
41
|
+
}
|
|
42
|
+
function onKeydown(event) {
|
|
43
|
+
switch (event.key) {
|
|
44
|
+
case "Escape":
|
|
45
|
+
onEscape(event);
|
|
46
|
+
break;
|
|
47
|
+
case "Tab":
|
|
48
|
+
onTab(event);
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function onPointerdown(event) {
|
|
55
|
+
const lastFocusTrap = findAncestor(
|
|
56
|
+
lastTarget,
|
|
57
|
+
`[${selector}]`
|
|
58
|
+
);
|
|
59
|
+
const nextFocusTrap = findAncestor(
|
|
60
|
+
event.target,
|
|
61
|
+
`[${selector}]`
|
|
62
|
+
);
|
|
63
|
+
if (lastFocusTrap != null && nextFocusTrap !== lastFocusTrap) {
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
lastTarget?.focus();
|
|
66
|
+
}
|
|
67
|
+
if (disabled.size === 0) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
for (const focusTrap of disabled) {
|
|
71
|
+
if (focusTrap.element !== nextFocusTrap) {
|
|
72
|
+
focusTrap.enable();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function onTab(event) {
|
|
78
|
+
const element = findAncestor(
|
|
79
|
+
event.target,
|
|
80
|
+
`[${selector}]`
|
|
81
|
+
);
|
|
82
|
+
const focusTrap = focusTraps.get(element);
|
|
83
|
+
if (focusTrap == null) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (focusTrap.active) {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
}
|
|
89
|
+
const elements = getFocusable(element);
|
|
90
|
+
const index = elements.indexOf(event.target);
|
|
91
|
+
if (index === -1 || elements.length === 0) {
|
|
92
|
+
tabToTrap(focusTrap, elements, element);
|
|
93
|
+
} else {
|
|
94
|
+
tabToElement(event, focusTrap, elements, index);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function setDisabled(focusTrap, value) {
|
|
98
|
+
focusTrap.element.setAttribute("aria-modal", String(!value));
|
|
99
|
+
focusTrap.active = !value;
|
|
100
|
+
if (value) {
|
|
101
|
+
disabled.add(focusTrap);
|
|
102
|
+
} else {
|
|
103
|
+
disabled.delete(focusTrap);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function tabToElement(event, focusTrap, elements, index) {
|
|
107
|
+
const next = clamp(
|
|
108
|
+
index + (event.shiftKey ? -1 : 1),
|
|
109
|
+
0,
|
|
110
|
+
elements.length - 1,
|
|
111
|
+
true
|
|
112
|
+
);
|
|
113
|
+
if (!focusTrap.active) {
|
|
114
|
+
if (index === length - 1 && next === 0 || index === 0 && next === length - 1) {
|
|
115
|
+
lastTarget = void 0;
|
|
116
|
+
focusTrap.enable();
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
elements[next].focus();
|
|
121
|
+
lastTarget = elements[next];
|
|
122
|
+
}
|
|
123
|
+
function tabToTrap(focusTrap, elements, element) {
|
|
124
|
+
if (focusTrap.active) {
|
|
125
|
+
const next = length === 0 ? element : elements[0];
|
|
126
|
+
next.focus();
|
|
127
|
+
lastTarget = next;
|
|
128
|
+
} else {
|
|
129
|
+
lastTarget = void 0;
|
|
130
|
+
focusTrap.enable();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const disabled = /* @__PURE__ */ new Set();
|
|
134
|
+
const eventOptions = {
|
|
135
|
+
capture: true,
|
|
136
|
+
passive: false
|
|
137
|
+
};
|
|
138
|
+
const focusTraps = /* @__PURE__ */ new WeakMap();
|
|
139
|
+
let lastTarget;
|
|
140
|
+
const selector = "oui-focus-trap";
|
|
141
|
+
on(document, "focusin", onFocusIn, eventOptions);
|
|
142
|
+
on(document, "keydown", onKeydown, eventOptions);
|
|
143
|
+
on(document, "mousedown", onPointerdown, eventOptions);
|
|
144
|
+
on(document, "touchstart", onPointerdown, eventOptions);
|
|
145
|
+
export {
|
|
146
|
+
FocusTrap,
|
|
147
|
+
createFocusTrap,
|
|
148
|
+
focusTraps,
|
|
149
|
+
selector
|
|
150
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const attributable = require("../attributable.cjs");
|
|
3
|
+
const focusTrap_embedded = require("./embedded.cjs");
|
|
4
|
+
function removeFocusTraps(element) {
|
|
5
|
+
focusTrap_embedded.focusTraps.get(element)?.destroy();
|
|
6
|
+
focusTrap_embedded.focusTraps.delete(element);
|
|
7
|
+
}
|
|
8
|
+
attributable.attributable(focusTrap_embedded.selector, focusTrap_embedded.createFocusTrap, removeFocusTraps);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { attributable } from "../attributable.js";
|
|
2
|
+
import { createFocusTrap, selector, focusTraps } from "./embedded.js";
|
|
3
|
+
function removeFocusTraps(element) {
|
|
4
|
+
focusTraps.get(element)?.destroy();
|
|
5
|
+
focusTraps.delete(element);
|
|
6
|
+
}
|
|
7
|
+
attributable(selector, createFocusTrap, removeFocusTraps);
|
package/dist/js/index.cjs
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
require("./focus-trap/standalone.cjs");
|
|
4
|
+
const popover = require("./popover.cjs");
|
|
5
|
+
require("./tooltip.cjs");
|
|
6
|
+
exports.OuiPopoverElement = popover.OuiPopoverElement;
|
package/dist/js/index.js
CHANGED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const is = require("@oscarpalmer/atoms/is");
|
|
4
|
+
const event = require("@oscarpalmer/toretto/event");
|
|
5
|
+
const find = require("@oscarpalmer/toretto/find");
|
|
6
|
+
const focusable = require("@oscarpalmer/toretto/focusable");
|
|
7
|
+
const floatable = require("./floatable.cjs");
|
|
8
|
+
const focusTrap_embedded = require("./focus-trap/embedded.cjs");
|
|
9
|
+
const mapped = /* @__PURE__ */ new Map();
|
|
10
|
+
let index = 0;
|
|
11
|
+
class OuiPopoverElement extends HTMLElement {
|
|
12
|
+
#floatable;
|
|
13
|
+
#focusTrap;
|
|
14
|
+
constructor() {
|
|
15
|
+
super();
|
|
16
|
+
const content = this.querySelector(":scope > [oui-popover-content]");
|
|
17
|
+
const toggle2 = this.querySelector(":scope > [oui-popover-toggle]");
|
|
18
|
+
if (!(content instanceof HTMLElement)) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
"A oui-popover must have a direct child element with the [oui-popover-content]-attribute"
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
if (!(toggle2 instanceof HTMLButtonElement)) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
"A oui-popover must have a direct child element with the [oui-popover-toggle]-attribute and be a button"
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
content.hidden = true;
|
|
29
|
+
content.setAttribute("role", "dialog");
|
|
30
|
+
content.setAttribute("oui-focus-trap", "");
|
|
31
|
+
content.setAttribute("oui-focus-trap-noescape", "");
|
|
32
|
+
if (is.isNullableOrWhitespace(content.id)) {
|
|
33
|
+
content.setAttribute("id", `oui-popover-content-${++index}`);
|
|
34
|
+
}
|
|
35
|
+
toggle2.setAttribute("aria-controls", content.id);
|
|
36
|
+
toggle2.setAttribute("aria-haspopup", "dialog");
|
|
37
|
+
this.#floatable = new floatable.Floatable(toggle2, content);
|
|
38
|
+
this.#focusTrap = focusTrap_embedded.createFocusTrap(content);
|
|
39
|
+
mapped.set(toggle2, this.#floatable);
|
|
40
|
+
mapped.set(content, this.#floatable);
|
|
41
|
+
}
|
|
42
|
+
close() {
|
|
43
|
+
this.hidePopover();
|
|
44
|
+
}
|
|
45
|
+
hidePopover() {
|
|
46
|
+
if (this.#floatable.active) {
|
|
47
|
+
closeAbove(this.#floatable.content);
|
|
48
|
+
floatable.deactivate(this.#floatable, active, order);
|
|
49
|
+
this.#floatable.anchor.focus();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
open() {
|
|
53
|
+
this.showPopover();
|
|
54
|
+
}
|
|
55
|
+
showPopover() {
|
|
56
|
+
if (!this.#floatable.active) {
|
|
57
|
+
toggle(this.#floatable.anchor);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
toggle() {
|
|
61
|
+
this.togglePopover();
|
|
62
|
+
}
|
|
63
|
+
togglePopover(options) {
|
|
64
|
+
if (typeof options === "boolean") {
|
|
65
|
+
if (options === true) {
|
|
66
|
+
this.showPopover();
|
|
67
|
+
} else {
|
|
68
|
+
this.hidePopover();
|
|
69
|
+
}
|
|
70
|
+
return options;
|
|
71
|
+
}
|
|
72
|
+
if (this.#floatable.active) {
|
|
73
|
+
this.hidePopover();
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
this.showPopover();
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function closeAll() {
|
|
81
|
+
for (const floatable$1 of active) {
|
|
82
|
+
floatable.deactivate(floatable$1, active, order);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function closeAbove(element) {
|
|
86
|
+
const index2 = order.findIndex(
|
|
87
|
+
(floatable2) => floatable2.anchor === element || floatable2.content === element
|
|
88
|
+
);
|
|
89
|
+
if (index2 === -1 || index2 === order.length - 1) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
while (index2 < order.length - 1) {
|
|
93
|
+
const floatable$1 = order.pop();
|
|
94
|
+
if (floatable$1 != null) {
|
|
95
|
+
floatable.deactivate(floatable$1, active, order);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
order[index2].content.focus();
|
|
99
|
+
}
|
|
100
|
+
function focus(content) {
|
|
101
|
+
let target = focusable.getFocusable(content)[0];
|
|
102
|
+
if (!(target instanceof HTMLElement)) {
|
|
103
|
+
target = content;
|
|
104
|
+
}
|
|
105
|
+
requestAnimationFrame(() => {
|
|
106
|
+
target.focus();
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function onClick(event2) {
|
|
110
|
+
const related = find.findAncestor(
|
|
111
|
+
event2.target,
|
|
112
|
+
"[oui-popover-toggle], [oui-popover-content]"
|
|
113
|
+
);
|
|
114
|
+
if (!(related instanceof HTMLElement)) {
|
|
115
|
+
closeAll();
|
|
116
|
+
} else if (related.hasAttribute("oui-popover-content")) {
|
|
117
|
+
closeAbove(related);
|
|
118
|
+
} else {
|
|
119
|
+
toggle(related);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function onKeydown(event2) {
|
|
123
|
+
if (event2.key !== "Escape" || order.length === 0) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const related = find.findAncestor(event2.target, "[oui-popover-content]");
|
|
127
|
+
if (!(related instanceof HTMLElement)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
closeAbove(related);
|
|
131
|
+
const floatable$1 = mapped.get(related);
|
|
132
|
+
if (floatable$1 != null) {
|
|
133
|
+
floatable.deactivate(floatable$1, active, order);
|
|
134
|
+
floatable$1.anchor.focus();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function toggle(anchor) {
|
|
138
|
+
const content = find.findAncestor(anchor, "[oui-popover-content]");
|
|
139
|
+
if (content instanceof HTMLElement) {
|
|
140
|
+
closeAbove(content);
|
|
141
|
+
} else {
|
|
142
|
+
closeAll();
|
|
143
|
+
}
|
|
144
|
+
const floatable$1 = mapped.get(anchor);
|
|
145
|
+
if (floatable$1 == null) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (floatable$1.active) {
|
|
149
|
+
floatable.deactivate(floatable$1, active, order);
|
|
150
|
+
} else {
|
|
151
|
+
floatable.activate(floatable$1, active, order);
|
|
152
|
+
focus(floatable$1.content);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
customElements.define("oui-popover", OuiPopoverElement);
|
|
156
|
+
event.on(document, "click", onClick);
|
|
157
|
+
event.on(document, "keydown", onKeydown);
|
|
158
|
+
const active = /* @__PURE__ */ new Set();
|
|
159
|
+
const order = [];
|
|
160
|
+
exports.OuiPopoverElement = OuiPopoverElement;
|