@marianmeres/stuic 1.11.0 → 1.12.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/actions/focus-trap.d.ts +7 -2
- package/dist/actions/focus-trap.js +54 -29
- package/dist/components/Backdrop/Backdrop.svelte +1 -1
- package/dist/components/Button/Button.svelte +13 -11
- package/dist/components/X/X.svelte +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/package.json +1 -1
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
interface FocusTrapOptions {
|
|
2
|
+
enabled?: boolean;
|
|
3
|
+
autoFocusFirst?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function focusTrap(node: HTMLElement, options?: FocusTrapOptions): {
|
|
6
|
+
update(options?: FocusTrapOptions): void;
|
|
3
7
|
destroy(): void;
|
|
4
8
|
};
|
|
9
|
+
export {};
|
|
@@ -1,52 +1,78 @@
|
|
|
1
1
|
// copied from skeleton
|
|
2
|
+
const defaults = { enabled: true, autoFocusFirst: false };
|
|
2
3
|
// Action: Focus Trap
|
|
3
|
-
export function focusTrap(node,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
export function focusTrap(node, options = {}) {
|
|
5
|
+
let { enabled, autoFocusFirst } = { ...defaults, ...(options || {}) };
|
|
6
|
+
const focusableSelectors = [
|
|
7
|
+
'a[href]',
|
|
8
|
+
'area[href]',
|
|
9
|
+
'details',
|
|
10
|
+
'iframe',
|
|
11
|
+
'button:not([disabled])',
|
|
12
|
+
'input:not([disabled])',
|
|
13
|
+
'select:not([disabled])',
|
|
14
|
+
'textarea:not([disabled])',
|
|
15
|
+
'[contentEditable=true]',
|
|
16
|
+
'[tabindex]',
|
|
17
|
+
].join(',');
|
|
18
|
+
let first;
|
|
19
|
+
let last;
|
|
7
20
|
// When the first element is selected, shift+tab pressed, jump to the last selectable item.
|
|
8
21
|
function onFirstElemKeydown(e) {
|
|
9
22
|
if (e.shiftKey && e.code === 'Tab') {
|
|
10
23
|
e.preventDefault();
|
|
11
|
-
|
|
24
|
+
last.focus();
|
|
12
25
|
}
|
|
13
26
|
}
|
|
14
27
|
// When the last item selected, tab pressed, jump to the first selectable item.
|
|
15
28
|
function onLastElemKeydown(e) {
|
|
16
29
|
if (!e.shiftKey && e.code === 'Tab') {
|
|
17
30
|
e.preventDefault();
|
|
18
|
-
|
|
31
|
+
first.focus();
|
|
19
32
|
}
|
|
20
33
|
}
|
|
21
|
-
const
|
|
34
|
+
const queryElements = (fromObserver) => {
|
|
22
35
|
if (enabled === false)
|
|
23
36
|
return;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
const focusable = [...node.querySelectorAll(focusableSelectors)]
|
|
38
|
+
// i am unable to get the selectors right (still getting false positives),
|
|
39
|
+
// but filtering manually works well
|
|
40
|
+
.filter((e) => {
|
|
41
|
+
if (e.getAttribute('disabled') === '')
|
|
42
|
+
return false;
|
|
43
|
+
if ((e.getAttribute('tabindex') || '').startsWith('-'))
|
|
44
|
+
return false;
|
|
45
|
+
return true;
|
|
46
|
+
})
|
|
47
|
+
// sort by tabindex, so the first/last will work as expected
|
|
48
|
+
.toSorted((e1, e2) => {
|
|
49
|
+
let a = parseInt(e1.getAttribute('tabindex') || '0');
|
|
50
|
+
let b = parseInt(e2.getAttribute('tabindex') || '0');
|
|
51
|
+
return a - b;
|
|
52
|
+
});
|
|
53
|
+
if (focusable.length) {
|
|
54
|
+
first = focusable[0];
|
|
55
|
+
last = focusable[focusable.length - 1];
|
|
30
56
|
// Auto-focus first focusable element only when not called from observer
|
|
31
|
-
if (!fromObserver)
|
|
32
|
-
|
|
57
|
+
if (!fromObserver && autoFocusFirst)
|
|
58
|
+
first.focus();
|
|
33
59
|
// Listen for keydown on first & last element
|
|
34
|
-
|
|
35
|
-
|
|
60
|
+
first.addEventListener('keydown', onFirstElemKeydown);
|
|
61
|
+
last.addEventListener('keydown', onLastElemKeydown);
|
|
36
62
|
}
|
|
37
63
|
};
|
|
38
|
-
|
|
39
|
-
function
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
|
|
64
|
+
queryElements(false);
|
|
65
|
+
function cleanup() {
|
|
66
|
+
if (first)
|
|
67
|
+
first.removeEventListener('keydown', onFirstElemKeydown);
|
|
68
|
+
if (last)
|
|
69
|
+
last.removeEventListener('keydown', onLastElemKeydown);
|
|
44
70
|
}
|
|
45
71
|
// When children of node are changed (added or removed)
|
|
46
72
|
const onObservationChange = (mutationRecords, observer) => {
|
|
47
73
|
if (mutationRecords.length) {
|
|
48
|
-
|
|
49
|
-
|
|
74
|
+
cleanup();
|
|
75
|
+
queryElements(true);
|
|
50
76
|
}
|
|
51
77
|
return observer;
|
|
52
78
|
};
|
|
@@ -54,12 +80,11 @@ export function focusTrap(node, enabled) {
|
|
|
54
80
|
observer.observe(node, { childList: true, subtree: true });
|
|
55
81
|
// Lifecycle
|
|
56
82
|
return {
|
|
57
|
-
update(
|
|
58
|
-
enabled
|
|
59
|
-
newArgs ? onScanElements(false) : onCleanUp();
|
|
83
|
+
update(options = {}) {
|
|
84
|
+
options?.enabled ? queryElements(false) : cleanup();
|
|
60
85
|
},
|
|
61
86
|
destroy() {
|
|
62
|
-
|
|
87
|
+
cleanup();
|
|
63
88
|
observer.disconnect();
|
|
64
89
|
},
|
|
65
90
|
};
|
|
@@ -43,7 +43,7 @@ $:
|
|
|
43
43
|
on:keydown={(e) => e.code === 'Escape' && dispatch('escape')}
|
|
44
44
|
in:fade={{ duration: fadeInDuration }}
|
|
45
45
|
out:fade={{ duration: fadeOutDuration }}
|
|
46
|
-
use:focusTrap={useFocusTrap}
|
|
46
|
+
use:focusTrap={{ enabled: useFocusTrap }}
|
|
47
47
|
role="presentation"
|
|
48
48
|
tabindex="-1"
|
|
49
49
|
>
|
|
@@ -4,18 +4,20 @@ export class ButtonConfig {
|
|
|
4
4
|
static defaultShadow = false;
|
|
5
5
|
static defaultRounded = true;
|
|
6
6
|
static defaultVariant = void 0;
|
|
7
|
-
// hover:brightness-[1.15]
|
|
8
7
|
static presetBase = `
|
|
9
|
-
text-base
|
|
10
8
|
text-center whitespace-nowrap
|
|
11
|
-
inline-flex
|
|
12
|
-
|
|
13
|
-
hover:brightness-[1.1]
|
|
9
|
+
inline-flex items-center gap-x-2
|
|
10
|
+
hover:brightness-[1.05]
|
|
14
11
|
active:brightness-[0.95]
|
|
15
12
|
disabled:!cursor-not-allowed disabled:!opacity-50 disabled:hover:brightness-100
|
|
16
13
|
no-underline
|
|
14
|
+
border
|
|
15
|
+
|
|
16
|
+
bg-zinc-200 text-black
|
|
17
|
+
dark:bg-zinc-600 dark:text-white
|
|
18
|
+
border-zinc-400 dark:border-zinc-500
|
|
17
19
|
`.trim();
|
|
18
|
-
static presetSquare = "p-0 aspect-square";
|
|
20
|
+
static presetSquare = "p-0 aspect-square justify-center";
|
|
19
21
|
static presetsRounded = {
|
|
20
22
|
xs: "rounded",
|
|
21
23
|
sm: "rounded",
|
|
@@ -31,11 +33,11 @@ export class ButtonConfig {
|
|
|
31
33
|
xl: "shadow-md dark:shadow-black"
|
|
32
34
|
};
|
|
33
35
|
static presetsSize = {
|
|
34
|
-
xs: "px-2 py-0.5 text-xs",
|
|
35
|
-
sm: "px-
|
|
36
|
-
md: "px-3
|
|
37
|
-
lg: "px-4 py-1.5 text-lg",
|
|
38
|
-
xl: "px-4 py-2 text-xl"
|
|
36
|
+
xs: "px-2 py-0.5 leading-tight text-xs",
|
|
37
|
+
sm: "px-2.5 py-0.5 leading-snug text-sm",
|
|
38
|
+
md: "px-3 py-1 leading-normal text-base",
|
|
39
|
+
lg: "px-4 py-1.5 leading-relaxed text-lg",
|
|
40
|
+
xl: "px-4 py-2 leading text-xl"
|
|
39
41
|
};
|
|
40
42
|
static classBySize = {
|
|
41
43
|
xs: "",
|
package/dist/index.d.ts
CHANGED
|
@@ -7,5 +7,7 @@ export { ColorScheme } from './components/ColorScheme/color-scheme.js';
|
|
|
7
7
|
export { default as Drawer, createDrawerStore } from './components/Drawer/Drawer.svelte';
|
|
8
8
|
export { default as HoverExpandableWidth } from './components/HoverExpandableWidth/HoverExpandableWidth.svelte';
|
|
9
9
|
export { default as X } from './components/X/X.svelte';
|
|
10
|
+
export { clickOutside } from './actions/click-outside.js';
|
|
11
|
+
export { focusTrap } from './actions/focus-trap.js';
|
|
10
12
|
export { windowSize, breakpoint } from './utils/window-size.js';
|
|
11
13
|
export { DevicePointer } from './utils/device-pointer.js';
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,9 @@ export { default as Drawer, createDrawerStore } from './components/Drawer/Drawer
|
|
|
15
15
|
export { default as HoverExpandableWidth } from './components/HoverExpandableWidth/HoverExpandableWidth.svelte';
|
|
16
16
|
//
|
|
17
17
|
export { default as X } from './components/X/X.svelte';
|
|
18
|
+
// actions
|
|
19
|
+
export { clickOutside } from './actions/click-outside.js';
|
|
20
|
+
export { focusTrap } from './actions/focus-trap.js';
|
|
18
21
|
// utils
|
|
19
22
|
export { windowSize, breakpoint } from './utils/window-size.js';
|
|
20
23
|
export { DevicePointer } from './utils/device-pointer.js';
|