@txstate-mws/svelte-components 1.3.7 → 1.3.9
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script context="module">const FocusLockStack = [];
|
|
1
|
+
<script context="module">export const FocusLockStack = [];
|
|
2
2
|
const waitAtick = typeof requestAnimationFrame !== 'undefined' ? resolve => requestAnimationFrame(resolve) : resolve => resolve(0);
|
|
3
3
|
</script>
|
|
4
4
|
|
|
@@ -7,6 +7,13 @@ export let initialfocus = undefined;
|
|
|
7
7
|
export let hidefocus = true;
|
|
8
8
|
export let hidefocuslabel = 'focus is above modal dialog, start tabbing';
|
|
9
9
|
export let escapable = true;
|
|
10
|
+
/**
|
|
11
|
+
* If you expect any popup menus to be added to the body, we need to know that they
|
|
12
|
+
* are considered to be part of the focus lock, or else the modal will be dismissed
|
|
13
|
+
* when the user clicks inside
|
|
14
|
+
* use commas to include multiple selectors
|
|
15
|
+
*/
|
|
16
|
+
export let includeselector = undefined;
|
|
10
17
|
let className = '';
|
|
11
18
|
export { className as class };
|
|
12
19
|
import { onMount, onDestroy, createEventDispatcher, tick } from 'svelte';
|
|
@@ -89,7 +96,7 @@ const focusin = (e) => {
|
|
|
89
96
|
}
|
|
90
97
|
};
|
|
91
98
|
const windowclick = (e) => {
|
|
92
|
-
if (state === 'active' && e.target instanceof HTMLElement && e.target.isConnected) {
|
|
99
|
+
if (state === 'active' && e.target instanceof HTMLElement && e.target.isConnected && (!includeselector || !e.target.closest(includeselector))) {
|
|
93
100
|
returnfocusto = undefined;
|
|
94
101
|
dispatch('escape');
|
|
95
102
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
export declare const FocusLockStack: {
|
|
3
|
+
pause: () => void;
|
|
4
|
+
unpause: () => void;
|
|
5
|
+
}[];
|
|
2
6
|
declare const __propDef: {
|
|
3
7
|
props: {
|
|
4
8
|
returnfocusto?: HTMLElement | null | undefined;
|
|
@@ -6,6 +10,12 @@ declare const __propDef: {
|
|
|
6
10
|
hidefocus?: boolean;
|
|
7
11
|
hidefocuslabel?: string;
|
|
8
12
|
escapable?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* If you expect any popup menus to be added to the body, we need to know that they
|
|
15
|
+
* are considered to be part of the focus lock, or else the modal will be dismissed
|
|
16
|
+
* when the user clicks inside
|
|
17
|
+
* use commas to include multiple selectors
|
|
18
|
+
*/ includeselector?: string | undefined;
|
|
9
19
|
class?: string;
|
|
10
20
|
};
|
|
11
21
|
events: {
|
package/components/Modal.svelte
CHANGED
|
@@ -6,14 +6,23 @@ export let initialfocus = undefined;
|
|
|
6
6
|
export let containerClass = '';
|
|
7
7
|
export let opaque = false;
|
|
8
8
|
export let usePortal = undefined;
|
|
9
|
+
/**
|
|
10
|
+
* If you expect any popup menus to be added to the body, we need to know that they
|
|
11
|
+
* are considered to be part of the focus lock, or else the modal will be dismissed
|
|
12
|
+
* when the user clicks inside
|
|
13
|
+
* use commas to include multiple selectors
|
|
14
|
+
*/
|
|
15
|
+
export let includeselector = undefined;
|
|
9
16
|
import { createEventDispatcher, onMount } from 'svelte';
|
|
10
|
-
import FocusLock from './FocusLock.svelte';
|
|
17
|
+
import FocusLock, { FocusLockStack } from './FocusLock.svelte';
|
|
11
18
|
import { portal } from '../actions';
|
|
12
19
|
const dispatch = createEventDispatcher();
|
|
13
20
|
const endmodal = () => {
|
|
14
21
|
dispatch('escape');
|
|
15
22
|
};
|
|
23
|
+
let stackPosition;
|
|
16
24
|
onMount(() => {
|
|
25
|
+
stackPosition = FocusLockStack.length - 1;
|
|
17
26
|
document.body.style.marginRight = (window.innerWidth - document.body.clientWidth) + 'px';
|
|
18
27
|
document.body.style.overflow = 'hidden';
|
|
19
28
|
return () => {
|
|
@@ -24,8 +33,8 @@ onMount(() => {
|
|
|
24
33
|
</script>
|
|
25
34
|
|
|
26
35
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
27
|
-
<div use:portal={usePortal} class="modal-backdrop" class:opaque on:mousedown|stopPropagation|preventDefault={() => escapable && endmodal()}>
|
|
28
|
-
<FocusLock class="modal-container {containerClass}" {escapable} on:escape {hidefocus} {hidefocuslabel} {returnfocusto} {initialfocus}>
|
|
36
|
+
<div use:portal={usePortal} class="modal-backdrop" style:--modal-z={stackPosition * 10 + 3000} class:opaque on:mousedown|stopPropagation|preventDefault={() => escapable && endmodal()}>
|
|
37
|
+
<FocusLock class="modal-container {containerClass}" {includeselector} {escapable} on:escape {hidefocus} {hidefocuslabel} {returnfocusto} {initialfocus}>
|
|
29
38
|
<slot></slot>
|
|
30
39
|
</FocusLock>
|
|
31
40
|
</div>
|
|
@@ -38,7 +47,7 @@ onMount(() => {
|
|
|
38
47
|
width: 100vw;
|
|
39
48
|
height: 100vh;
|
|
40
49
|
background-color: var(--modal-bg, rgba(0, 0, 0, 0.7));
|
|
41
|
-
z-index: var(--modal-z
|
|
50
|
+
z-index: var(--modal-z);
|
|
42
51
|
display: flex;
|
|
43
52
|
justify-content: center;
|
|
44
53
|
align-items: center
|
|
@@ -9,6 +9,12 @@ declare const __propDef: {
|
|
|
9
9
|
containerClass?: string;
|
|
10
10
|
opaque?: boolean;
|
|
11
11
|
usePortal?: HTMLElement | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* If you expect any popup menus to be added to the body, we need to know that they
|
|
14
|
+
* are considered to be part of the focus lock, or else the modal will be dismissed
|
|
15
|
+
* when the user clicks inside
|
|
16
|
+
* use commas to include multiple selectors
|
|
17
|
+
*/ includeselector?: string | undefined;
|
|
12
18
|
};
|
|
13
19
|
events: {
|
|
14
20
|
escape: CustomEvent<any>;
|
|
@@ -11,6 +11,7 @@ export let menuContainerClass = undefined;
|
|
|
11
11
|
export let menuClass = undefined;
|
|
12
12
|
export let menuItemClass = undefined;
|
|
13
13
|
export let menuItemHilitedClass = undefined;
|
|
14
|
+
export let maxSelections = 0;
|
|
14
15
|
export let selected = [];
|
|
15
16
|
export let placeholder = '';
|
|
16
17
|
export let emptyText = undefined;
|
|
@@ -29,6 +30,8 @@ const optionsCache = new Cache(async (ipt) => {
|
|
|
29
30
|
return await getOptions(ipt);
|
|
30
31
|
}, { freshseconds: 5 });
|
|
31
32
|
$: selectedSet = new Set(selected.map(s => s.value));
|
|
33
|
+
$: if (maxSelections > 1 && selected.length >= maxSelections && menushown)
|
|
34
|
+
menushown = false;
|
|
32
35
|
let optionsTimer;
|
|
33
36
|
async function reactToInput(..._) {
|
|
34
37
|
loading = true;
|
|
@@ -49,7 +52,7 @@ $: availablemessage = options.filter(o => o.value).length + ' autocomplete choic
|
|
|
49
52
|
function addSelection(e) {
|
|
50
53
|
inputvalue = '';
|
|
51
54
|
const opt = { ...e.detail, label: e.detail.label || e.detail.value };
|
|
52
|
-
selected = [...selected, opt];
|
|
55
|
+
selected = maxSelections === 1 ? [opt] : [...selected, opt];
|
|
53
56
|
popupvalue = undefined;
|
|
54
57
|
dispatch('change', selected);
|
|
55
58
|
}
|
|
@@ -90,6 +93,9 @@ function inputkeydown(e) {
|
|
|
90
93
|
e.preventDefault();
|
|
91
94
|
}
|
|
92
95
|
}
|
|
96
|
+
else if (e.key !== 'Tab' && maxSelections > 1 && selected.length >= maxSelections) {
|
|
97
|
+
e.preventDefault();
|
|
98
|
+
}
|
|
93
99
|
}
|
|
94
100
|
async function inputfocus() {
|
|
95
101
|
reactToInput();
|
|
@@ -130,7 +136,7 @@ $: reactToHilite(hilitedpill, id);
|
|
|
130
136
|
</li>
|
|
131
137
|
</ul>
|
|
132
138
|
<ScreenReaderOnly id={descriptionid} arialive="assertive">
|
|
133
|
-
<span>{selected.length ? selected.length + ' selected' : '
|
|
139
|
+
<span>{selected.length ? selected.length + ' selected' : 'none selected'}, select {maxSelections ? 'up to ' + maxSelections : 'multiple'}, up down to choose, left right to hilite existing choices</span>
|
|
134
140
|
{#if menushown}<span>{availablemessage}, touch users explore to find autocomplete menu</span>{/if}
|
|
135
141
|
</ScreenReaderOnly>
|
|
136
142
|
<slot></slot>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@txstate-mws/svelte-components",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.9",
|
|
4
4
|
"description": "Svelte components that are generically useful.",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@txstate-mws/svelte-store": "^1.0.3",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"@playwright/test": "^1.18.1",
|
|
12
|
-
"@sveltejs/adapter-auto": "^
|
|
12
|
+
"@sveltejs/adapter-auto": "^2.0.0",
|
|
13
13
|
"@sveltejs/kit": "^1.0.1",
|
|
14
14
|
"@sveltejs/package": "^1.0.1",
|
|
15
15
|
"@types/tabbable": "^3.1.2",
|