@sc4rfurryx/proteusjs 1.1.1 → 2.0.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 +684 -899
- package/dist/.tsbuildinfo +1 -1
- package/dist/modules/a11y-audit.d.ts +1 -1
- package/dist/modules/a11y-audit.esm.js +3 -3
- package/dist/modules/a11y-primitives.d.ts +2 -2
- package/dist/modules/a11y-primitives.esm.js +2 -2
- package/dist/modules/a11y-primitives.esm.js.map +1 -1
- package/dist/modules/anchor.d.ts +1 -1
- package/dist/modules/anchor.esm.js +2 -2
- package/dist/modules/container.d.ts +1 -1
- package/dist/modules/container.esm.js +34 -34
- package/dist/modules/container.esm.js.map +1 -1
- package/dist/modules/perf.d.ts +1 -1
- package/dist/modules/perf.esm.js +2 -2
- package/dist/modules/popover.d.ts +1 -1
- package/dist/modules/popover.esm.js +2 -2
- package/dist/modules/scroll.d.ts +1 -1
- package/dist/modules/scroll.esm.js +14 -14
- package/dist/modules/scroll.esm.js.map +1 -1
- package/dist/modules/transitions.d.ts +1 -1
- package/dist/modules/transitions.esm.js +12 -12
- package/dist/modules/transitions.esm.js.map +1 -1
- package/dist/modules/typography.d.ts +1 -1
- package/dist/modules/typography.esm.js +2 -2
- package/dist/proteus.cjs.js +68 -68
- package/dist/proteus.cjs.js.map +1 -1
- package/dist/proteus.d.ts +13 -13
- package/dist/proteus.esm.js +68 -68
- package/dist/proteus.esm.js.map +1 -1
- package/dist/proteus.esm.min.js +2 -2
- package/dist/proteus.esm.min.js.map +1 -1
- package/dist/proteus.js +68 -68
- package/dist/proteus.js.map +1 -1
- package/dist/proteus.min.js +2 -2
- package/dist/proteus.min.js.map +1 -1
- package/package.json +40 -8
- package/src/adapters/react.ts +607 -264
- package/src/adapters/svelte.ts +321 -321
- package/src/adapters/vue.ts +268 -268
- package/src/core/ProteusJS.ts +6 -6
- package/src/index.ts +2 -2
- package/src/modules/a11y-audit/index.ts +84 -84
- package/src/modules/a11y-primitives/index.ts +151 -151
- package/src/modules/anchor/index.ts +259 -259
- package/src/modules/container/index.ts +230 -230
- package/src/modules/perf/index.ts +291 -291
- package/src/modules/popover/index.ts +238 -238
- package/src/modules/scroll/index.ts +251 -251
- package/src/modules/transitions/index.ts +145 -145
- package/src/modules/typography/index.ts +239 -239
- package/src/utils/version.ts +1 -1
- package/dist/adapters/react.d.ts +0 -140
- package/dist/adapters/react.esm.js +0 -849
- package/dist/adapters/react.esm.js.map +0 -1
- package/dist/adapters/svelte.d.ts +0 -181
- package/dist/adapters/svelte.esm.js +0 -909
- package/dist/adapters/svelte.esm.js.map +0 -1
- package/dist/adapters/vue.d.ts +0 -205
- package/dist/adapters/vue.esm.js +0 -873
- package/dist/adapters/vue.esm.js.map +0 -1
|
@@ -1,238 +1,238 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @sc4rfurryx/proteusjs/popover
|
|
3
|
-
* HTML Popover API wrapper with robust focus/inert handling
|
|
4
|
-
*
|
|
5
|
-
* @version
|
|
6
|
-
* @author sc4rfurry
|
|
7
|
-
* @license MIT
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export interface PopoverOptions {
|
|
11
|
-
type?: 'menu' | 'dialog' | 'tooltip';
|
|
12
|
-
trapFocus?: boolean;
|
|
13
|
-
restoreFocus?: boolean;
|
|
14
|
-
closeOnEscape?: boolean;
|
|
15
|
-
onOpen?: () => void;
|
|
16
|
-
onClose?: () => void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface PopoverController {
|
|
20
|
-
open(): void;
|
|
21
|
-
close(): void;
|
|
22
|
-
toggle(): void;
|
|
23
|
-
destroy(): void;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Unified API for menus, tooltips, and dialogs using the native Popover API
|
|
28
|
-
* with robust focus/inert handling
|
|
29
|
-
*/
|
|
30
|
-
export function attach(
|
|
31
|
-
trigger: Element | string,
|
|
32
|
-
panel: Element | string,
|
|
33
|
-
opts: PopoverOptions = {}
|
|
34
|
-
): PopoverController {
|
|
35
|
-
const triggerEl = typeof trigger === 'string' ? document.querySelector(trigger) : trigger;
|
|
36
|
-
const panelEl = typeof panel === 'string' ? document.querySelector(panel) : panel;
|
|
37
|
-
|
|
38
|
-
if (!triggerEl || !panelEl) {
|
|
39
|
-
throw new Error('Both trigger and panel elements must exist');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const {
|
|
43
|
-
type = 'menu',
|
|
44
|
-
trapFocus = type === 'dialog',
|
|
45
|
-
restoreFocus = true,
|
|
46
|
-
closeOnEscape = true,
|
|
47
|
-
onOpen,
|
|
48
|
-
onClose
|
|
49
|
-
} = opts;
|
|
50
|
-
|
|
51
|
-
let isOpen = false;
|
|
52
|
-
let previousFocus: Element | null = null;
|
|
53
|
-
let focusTrap: FocusTrap | null = null;
|
|
54
|
-
|
|
55
|
-
// Check for native Popover API support
|
|
56
|
-
const hasPopoverAPI = 'popover' in HTMLElement.prototype;
|
|
57
|
-
|
|
58
|
-
// Set up ARIA attributes
|
|
59
|
-
const setupAria = () => {
|
|
60
|
-
const panelId = panelEl.id || `popover-${Math.random().toString(36).substr(2, 9)}`;
|
|
61
|
-
panelEl.id = panelId;
|
|
62
|
-
|
|
63
|
-
triggerEl.setAttribute('aria-expanded', 'false');
|
|
64
|
-
triggerEl.setAttribute('aria-controls', panelId);
|
|
65
|
-
|
|
66
|
-
if (type === 'menu') {
|
|
67
|
-
triggerEl.setAttribute('aria-haspopup', 'menu');
|
|
68
|
-
panelEl.setAttribute('role', 'menu');
|
|
69
|
-
} else if (type === 'dialog') {
|
|
70
|
-
triggerEl.setAttribute('aria-haspopup', 'dialog');
|
|
71
|
-
panelEl.setAttribute('role', 'dialog');
|
|
72
|
-
panelEl.setAttribute('aria-modal', 'true');
|
|
73
|
-
} else if (type === 'tooltip') {
|
|
74
|
-
triggerEl.setAttribute('aria-describedby', panelId);
|
|
75
|
-
panelEl.setAttribute('role', 'tooltip');
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// Set up native popover if supported
|
|
80
|
-
const setupNativePopover = () => {
|
|
81
|
-
if (hasPopoverAPI) {
|
|
82
|
-
(panelEl as any).popover = type === 'dialog' ? 'manual' : 'auto';
|
|
83
|
-
triggerEl.setAttribute('popovertarget', panelEl.id);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
// Focus trap implementation
|
|
88
|
-
class FocusTrap {
|
|
89
|
-
private focusableElements: Element[] = [];
|
|
90
|
-
|
|
91
|
-
constructor(private container: Element) {
|
|
92
|
-
this.updateFocusableElements();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
private updateFocusableElements() {
|
|
96
|
-
const selector = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
97
|
-
this.focusableElements = Array.from(this.container.querySelectorAll(selector));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
activate() {
|
|
101
|
-
this.updateFocusableElements();
|
|
102
|
-
if (this.focusableElements.length > 0) {
|
|
103
|
-
(this.focusableElements[0] as HTMLElement).focus();
|
|
104
|
-
}
|
|
105
|
-
document.addEventListener('keydown', this.handleKeyDown);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
deactivate() {
|
|
109
|
-
document.removeEventListener('keydown', this.handleKeyDown);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
private handleKeyDown = (e: KeyboardEvent) => {
|
|
113
|
-
if (e.key !== 'Tab') return;
|
|
114
|
-
|
|
115
|
-
const firstElement = this.focusableElements[0] as HTMLElement;
|
|
116
|
-
const lastElement = this.focusableElements[this.focusableElements.length - 1] as HTMLElement;
|
|
117
|
-
|
|
118
|
-
if (e.shiftKey) {
|
|
119
|
-
if (document.activeElement === firstElement) {
|
|
120
|
-
e.preventDefault();
|
|
121
|
-
lastElement.focus();
|
|
122
|
-
}
|
|
123
|
-
} else {
|
|
124
|
-
if (document.activeElement === lastElement) {
|
|
125
|
-
e.preventDefault();
|
|
126
|
-
firstElement.focus();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const open = () => {
|
|
133
|
-
if (isOpen) return;
|
|
134
|
-
|
|
135
|
-
if (restoreFocus) {
|
|
136
|
-
previousFocus = document.activeElement;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (hasPopoverAPI) {
|
|
140
|
-
(panelEl as any).showPopover();
|
|
141
|
-
} else {
|
|
142
|
-
(panelEl as HTMLElement).style.display = 'block';
|
|
143
|
-
panelEl.setAttribute('data-popover-open', 'true');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
triggerEl.setAttribute('aria-expanded', 'true');
|
|
147
|
-
isOpen = true;
|
|
148
|
-
|
|
149
|
-
if (trapFocus) {
|
|
150
|
-
focusTrap = new FocusTrap(panelEl);
|
|
151
|
-
focusTrap.activate();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (onOpen) {
|
|
155
|
-
onOpen();
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const close = () => {
|
|
160
|
-
if (!isOpen) return;
|
|
161
|
-
|
|
162
|
-
if (hasPopoverAPI) {
|
|
163
|
-
(panelEl as any).hidePopover();
|
|
164
|
-
} else {
|
|
165
|
-
(panelEl as HTMLElement).style.display = 'none';
|
|
166
|
-
panelEl.removeAttribute('data-popover-open');
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
triggerEl.setAttribute('aria-expanded', 'false');
|
|
170
|
-
isOpen = false;
|
|
171
|
-
|
|
172
|
-
if (focusTrap) {
|
|
173
|
-
focusTrap.deactivate();
|
|
174
|
-
focusTrap = null;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (restoreFocus && previousFocus) {
|
|
178
|
-
(previousFocus as HTMLElement).focus();
|
|
179
|
-
previousFocus = null;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (onClose) {
|
|
183
|
-
onClose();
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
const toggle = () => {
|
|
188
|
-
if (isOpen) {
|
|
189
|
-
close();
|
|
190
|
-
} else {
|
|
191
|
-
open();
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const handleKeyDown = (e: KeyboardEvent) => {
|
|
196
|
-
if (closeOnEscape && e.key === 'Escape' && isOpen) {
|
|
197
|
-
e.preventDefault();
|
|
198
|
-
close();
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const handleClick = (e: Event) => {
|
|
203
|
-
e.preventDefault();
|
|
204
|
-
toggle();
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
const destroy = () => {
|
|
208
|
-
triggerEl.removeEventListener('click', handleClick);
|
|
209
|
-
document.removeEventListener('keydown', handleKeyDown);
|
|
210
|
-
|
|
211
|
-
if (focusTrap) {
|
|
212
|
-
focusTrap.deactivate();
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (isOpen) {
|
|
216
|
-
close();
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
// Initialize
|
|
221
|
-
setupAria();
|
|
222
|
-
setupNativePopover();
|
|
223
|
-
|
|
224
|
-
triggerEl.addEventListener('click', handleClick);
|
|
225
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
open,
|
|
229
|
-
close,
|
|
230
|
-
toggle,
|
|
231
|
-
destroy
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Export default object for convenience
|
|
236
|
-
export default {
|
|
237
|
-
attach
|
|
238
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* @sc4rfurryx/proteusjs/popover
|
|
3
|
+
* HTML Popover API wrapper with robust focus/inert handling
|
|
4
|
+
*
|
|
5
|
+
* @version 2.0.0
|
|
6
|
+
* @author sc4rfurry
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface PopoverOptions {
|
|
11
|
+
type?: 'menu' | 'dialog' | 'tooltip';
|
|
12
|
+
trapFocus?: boolean;
|
|
13
|
+
restoreFocus?: boolean;
|
|
14
|
+
closeOnEscape?: boolean;
|
|
15
|
+
onOpen?: () => void;
|
|
16
|
+
onClose?: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PopoverController {
|
|
20
|
+
open(): void;
|
|
21
|
+
close(): void;
|
|
22
|
+
toggle(): void;
|
|
23
|
+
destroy(): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Unified API for menus, tooltips, and dialogs using the native Popover API
|
|
28
|
+
* with robust focus/inert handling
|
|
29
|
+
*/
|
|
30
|
+
export function attach(
|
|
31
|
+
trigger: Element | string,
|
|
32
|
+
panel: Element | string,
|
|
33
|
+
opts: PopoverOptions = {}
|
|
34
|
+
): PopoverController {
|
|
35
|
+
const triggerEl = typeof trigger === 'string' ? document.querySelector(trigger) : trigger;
|
|
36
|
+
const panelEl = typeof panel === 'string' ? document.querySelector(panel) : panel;
|
|
37
|
+
|
|
38
|
+
if (!triggerEl || !panelEl) {
|
|
39
|
+
throw new Error('Both trigger and panel elements must exist');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
type = 'menu',
|
|
44
|
+
trapFocus = type === 'dialog',
|
|
45
|
+
restoreFocus = true,
|
|
46
|
+
closeOnEscape = true,
|
|
47
|
+
onOpen,
|
|
48
|
+
onClose
|
|
49
|
+
} = opts;
|
|
50
|
+
|
|
51
|
+
let isOpen = false;
|
|
52
|
+
let previousFocus: Element | null = null;
|
|
53
|
+
let focusTrap: FocusTrap | null = null;
|
|
54
|
+
|
|
55
|
+
// Check for native Popover API support
|
|
56
|
+
const hasPopoverAPI = 'popover' in HTMLElement.prototype;
|
|
57
|
+
|
|
58
|
+
// Set up ARIA attributes
|
|
59
|
+
const setupAria = () => {
|
|
60
|
+
const panelId = panelEl.id || `popover-${Math.random().toString(36).substr(2, 9)}`;
|
|
61
|
+
panelEl.id = panelId;
|
|
62
|
+
|
|
63
|
+
triggerEl.setAttribute('aria-expanded', 'false');
|
|
64
|
+
triggerEl.setAttribute('aria-controls', panelId);
|
|
65
|
+
|
|
66
|
+
if (type === 'menu') {
|
|
67
|
+
triggerEl.setAttribute('aria-haspopup', 'menu');
|
|
68
|
+
panelEl.setAttribute('role', 'menu');
|
|
69
|
+
} else if (type === 'dialog') {
|
|
70
|
+
triggerEl.setAttribute('aria-haspopup', 'dialog');
|
|
71
|
+
panelEl.setAttribute('role', 'dialog');
|
|
72
|
+
panelEl.setAttribute('aria-modal', 'true');
|
|
73
|
+
} else if (type === 'tooltip') {
|
|
74
|
+
triggerEl.setAttribute('aria-describedby', panelId);
|
|
75
|
+
panelEl.setAttribute('role', 'tooltip');
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Set up native popover if supported
|
|
80
|
+
const setupNativePopover = () => {
|
|
81
|
+
if (hasPopoverAPI) {
|
|
82
|
+
(panelEl as any).popover = type === 'dialog' ? 'manual' : 'auto';
|
|
83
|
+
triggerEl.setAttribute('popovertarget', panelEl.id);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Focus trap implementation
|
|
88
|
+
class FocusTrap {
|
|
89
|
+
private focusableElements: Element[] = [];
|
|
90
|
+
|
|
91
|
+
constructor(private container: Element) {
|
|
92
|
+
this.updateFocusableElements();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private updateFocusableElements() {
|
|
96
|
+
const selector = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
|
|
97
|
+
this.focusableElements = Array.from(this.container.querySelectorAll(selector));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
activate() {
|
|
101
|
+
this.updateFocusableElements();
|
|
102
|
+
if (this.focusableElements.length > 0) {
|
|
103
|
+
(this.focusableElements[0] as HTMLElement).focus();
|
|
104
|
+
}
|
|
105
|
+
document.addEventListener('keydown', this.handleKeyDown);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
deactivate() {
|
|
109
|
+
document.removeEventListener('keydown', this.handleKeyDown);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private handleKeyDown = (e: KeyboardEvent) => {
|
|
113
|
+
if (e.key !== 'Tab') return;
|
|
114
|
+
|
|
115
|
+
const firstElement = this.focusableElements[0] as HTMLElement;
|
|
116
|
+
const lastElement = this.focusableElements[this.focusableElements.length - 1] as HTMLElement;
|
|
117
|
+
|
|
118
|
+
if (e.shiftKey) {
|
|
119
|
+
if (document.activeElement === firstElement) {
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
lastElement.focus();
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
if (document.activeElement === lastElement) {
|
|
125
|
+
e.preventDefault();
|
|
126
|
+
firstElement.focus();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const open = () => {
|
|
133
|
+
if (isOpen) return;
|
|
134
|
+
|
|
135
|
+
if (restoreFocus) {
|
|
136
|
+
previousFocus = document.activeElement;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (hasPopoverAPI) {
|
|
140
|
+
(panelEl as any).showPopover();
|
|
141
|
+
} else {
|
|
142
|
+
(panelEl as HTMLElement).style.display = 'block';
|
|
143
|
+
panelEl.setAttribute('data-popover-open', 'true');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
triggerEl.setAttribute('aria-expanded', 'true');
|
|
147
|
+
isOpen = true;
|
|
148
|
+
|
|
149
|
+
if (trapFocus) {
|
|
150
|
+
focusTrap = new FocusTrap(panelEl);
|
|
151
|
+
focusTrap.activate();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (onOpen) {
|
|
155
|
+
onOpen();
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const close = () => {
|
|
160
|
+
if (!isOpen) return;
|
|
161
|
+
|
|
162
|
+
if (hasPopoverAPI) {
|
|
163
|
+
(panelEl as any).hidePopover();
|
|
164
|
+
} else {
|
|
165
|
+
(panelEl as HTMLElement).style.display = 'none';
|
|
166
|
+
panelEl.removeAttribute('data-popover-open');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
triggerEl.setAttribute('aria-expanded', 'false');
|
|
170
|
+
isOpen = false;
|
|
171
|
+
|
|
172
|
+
if (focusTrap) {
|
|
173
|
+
focusTrap.deactivate();
|
|
174
|
+
focusTrap = null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (restoreFocus && previousFocus) {
|
|
178
|
+
(previousFocus as HTMLElement).focus();
|
|
179
|
+
previousFocus = null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (onClose) {
|
|
183
|
+
onClose();
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const toggle = () => {
|
|
188
|
+
if (isOpen) {
|
|
189
|
+
close();
|
|
190
|
+
} else {
|
|
191
|
+
open();
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
196
|
+
if (closeOnEscape && e.key === 'Escape' && isOpen) {
|
|
197
|
+
e.preventDefault();
|
|
198
|
+
close();
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const handleClick = (e: Event) => {
|
|
203
|
+
e.preventDefault();
|
|
204
|
+
toggle();
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const destroy = () => {
|
|
208
|
+
triggerEl.removeEventListener('click', handleClick);
|
|
209
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
210
|
+
|
|
211
|
+
if (focusTrap) {
|
|
212
|
+
focusTrap.deactivate();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (isOpen) {
|
|
216
|
+
close();
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// Initialize
|
|
221
|
+
setupAria();
|
|
222
|
+
setupNativePopover();
|
|
223
|
+
|
|
224
|
+
triggerEl.addEventListener('click', handleClick);
|
|
225
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
open,
|
|
229
|
+
close,
|
|
230
|
+
toggle,
|
|
231
|
+
destroy
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Export default object for convenience
|
|
236
|
+
export default {
|
|
237
|
+
attach
|
|
238
|
+
};
|