@txstate-mws/svelte-components 0.0.67 → 1.0.3
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 +39 -4
- package/{dist/ts/actions/a11y.d.ts → actions/buttonify.d.ts} +0 -0
- package/{dist/actions/a11y.js → actions/buttonify.js} +1 -6
- package/{dist/ts/actions → actions}/glue.d.ts +2 -2
- package/{dist/actions → actions}/glue.js +9 -16
- package/actions/index.d.ts +7 -0
- package/actions/index.js +7 -0
- package/actions/offset.d.ts +15 -0
- package/actions/offset.js +35 -0
- package/{dist/ts/actions → actions}/portal.d.ts +0 -0
- package/actions/portal.js +13 -0
- package/{dist/ts/actions → actions}/resize.d.ts +6 -2
- package/actions/resize.js +45 -0
- package/{dist/ts/actions → actions}/sticky.d.ts +2 -2
- package/{dist/actions → actions}/sticky.js +10 -18
- package/{dist/ts/actions → actions}/stickyfixed.d.ts +7 -7
- package/{dist/actions → actions}/stickyfixed.js +19 -28
- package/{dist/components → components}/CollapsingTable.svelte +13 -14
- package/components/CollapsingTable.svelte.d.ts +59 -0
- package/{dist/ts/components → components}/ConditionalWrapper.svelte +6 -7
- package/components/ConditionalWrapper.svelte.d.ts +23 -0
- package/{dist/components → components}/FocusLock.svelte +21 -19
- package/components/FocusLock.svelte.d.ts +26 -0
- package/components/Modal.svelte +53 -0
- package/components/Modal.svelte.d.ts +27 -0
- package/{dist/components → components}/PopupMenu.svelte +28 -30
- package/components/PopupMenu.svelte.d.ts +44 -0
- package/{dist/ts/components → components}/ScreenReaderOnly.svelte +4 -5
- package/components/ScreenReaderOnly.svelte.d.ts +20 -0
- package/{dist/ts/components → components}/index.d.ts +0 -0
- package/components/index.js +6 -0
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/package.json +47 -68
- package/{dist/ts/types → types}/CollapsingTable.d.ts +0 -0
- package/{dist/ts/tests/Actions/Actions.stories.d.ts → types/CollapsingTable.js} +0 -0
- package/{dist/ts/types → types}/PopupMenu.d.ts +0 -0
- package/{dist/ts/tests/CollapsingTable/CollapsingTable.stories.d.ts → types/PopupMenu.js} +0 -0
- package/types/index.d.ts +2 -0
- package/types/index.js +2 -0
- package/{dist/ts/lib → util}/bodyoffset.d.ts +2 -1
- package/{dist/lib → util}/bodyoffset.js +10 -14
- package/util/index.d.ts +3 -0
- package/util/index.js +3 -0
- package/{dist/ts/lib → util}/repositioned.d.ts +1 -2
- package/{dist/lib → util}/repositioned.js +14 -24
- package/{dist/ts/lib → util}/util.d.ts +0 -0
- package/util/util.js +10 -0
- package/LICENSE +0 -21
- package/dist/actions/a11y.js.map +0 -1
- package/dist/actions/glue.js.map +0 -1
- package/dist/actions/index.js +0 -20
- package/dist/actions/index.js.map +0 -1
- package/dist/actions/offset.js +0 -35
- package/dist/actions/offset.js.map +0 -1
- package/dist/actions/portal.js +0 -23
- package/dist/actions/portal.js.map +0 -1
- package/dist/actions/resize.js +0 -48
- package/dist/actions/resize.js.map +0 -1
- package/dist/actions/sticky.js.map +0 -1
- package/dist/actions/stickyfixed.js.map +0 -1
- package/dist/components/ConditionalWrapper.svelte +0 -38
- package/dist/components/Modal.svelte +0 -44
- package/dist/components/ScreenReaderOnly.svelte +0 -26
- package/dist/components/index.js +0 -19
- package/dist/components/index.js.map +0 -1
- package/dist/index.js +0 -17
- package/dist/index.js.map +0 -1
- package/dist/lib/bodyoffset.js.map +0 -1
- package/dist/lib/deepstore.js +0 -80
- package/dist/lib/deepstore.js.map +0 -1
- package/dist/lib/derivedstore.js +0 -53
- package/dist/lib/derivedstore.js.map +0 -1
- package/dist/lib/index.js +0 -20
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/repositioned.js.map +0 -1
- package/dist/lib/safestore.js +0 -16
- package/dist/lib/safestore.js.map +0 -1
- package/dist/lib/substore.js +0 -46
- package/dist/lib/substore.js.map +0 -1
- package/dist/lib/util.js +0 -16
- package/dist/lib/util.js.map +0 -1
- package/dist/ts/actions/index.d.ts +0 -7
- package/dist/ts/actions/offset.d.ts +0 -15
- package/dist/ts/components/CollapsingTable.svelte +0 -147
- package/dist/ts/components/FocusLock.svelte +0 -100
- package/dist/ts/components/Modal.svelte +0 -45
- package/dist/ts/components/PopupMenu.svelte +0 -278
- package/dist/ts/index.d.ts +0 -4
- package/dist/ts/lib/deepstore.d.ts +0 -42
- package/dist/ts/lib/derivedstore.d.ts +0 -16
- package/dist/ts/lib/index.d.ts +0 -7
- package/dist/ts/lib/safestore.d.ts +0 -4
- package/dist/ts/lib/substore.d.ts +0 -23
- package/dist/ts/tests/Actions/Offset.svelte +0 -34
- package/dist/ts/tests/Actions/Resize.svelte +0 -31
- package/dist/ts/tests/Actions/Sticky.svelte +0 -65
- package/dist/ts/tests/CollapsingTable/CollapsingTable.test.d.ts +0 -1
- package/dist/ts/tests/CollapsingTable/CollapsingTableTest.svelte +0 -36
- package/dist/ts/tests/Modal/Modal.stories.d.ts +0 -1
- package/dist/ts/tests/Modal/Modal.test.d.ts +0 -1
- package/dist/ts/tests/Modal/ModalTest.svelte +0 -58
- package/dist/ts/tests/PopupMenu/PopupMenu.stories.d.ts +0 -1
- package/dist/ts/tests/PopupMenu/PopupMenu.test.d.ts +0 -1
- package/dist/ts/tests/PopupMenu/PopupMenuBody.svelte +0 -34
- package/dist/ts/tests/PopupMenu/PopupMenuButtonIsParent.svelte +0 -36
- package/dist/ts/tests/PopupMenu/PopupMenuComplex.svelte +0 -34
- package/dist/ts/tests/PopupMenu/PopupMenuSimple.svelte +0 -30
- package/dist/ts/tests/stores/deepstore.test.d.ts +0 -1
- package/dist/ts/types/index.d.ts +0 -2
- package/dist/types/CollapsingTable.js +0 -3
- package/dist/types/CollapsingTable.js.map +0 -1
- package/dist/types/PopupMenu.js +0 -3
- package/dist/types/PopupMenu.js.map +0 -1
- package/dist/types/index.js +0 -15
- package/dist/types/index.js.map +0 -1
- package/docs/CollapsingTable.md +0 -80
- package/docs/actions.md +0 -10
- package/esm/index.js +0 -1
- package/esm/package.json +0 -3
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
import type { CollapsingTableColumn } from '../types';
|
|
3
|
+
import DefaultPopupMenu from './PopupMenu.svelte';
|
|
4
|
+
declare const __propDef: {
|
|
5
|
+
props: {
|
|
6
|
+
tableClass?: string;
|
|
7
|
+
bodyRowClass?: string;
|
|
8
|
+
bodyCellClass?: string;
|
|
9
|
+
headerRowClass?: string;
|
|
10
|
+
headerCellClass?: string;
|
|
11
|
+
menuContainerClass?: string;
|
|
12
|
+
menuClass?: string;
|
|
13
|
+
menuItemClass?: string;
|
|
14
|
+
menuItemHilitedClass?: string;
|
|
15
|
+
items: any[];
|
|
16
|
+
config?: CollapsingTableColumn[] | undefined;
|
|
17
|
+
defaultCellWidth?: number;
|
|
18
|
+
PopupMenu?: typeof DefaultPopupMenu;
|
|
19
|
+
slots?: Record<string, boolean>;
|
|
20
|
+
stickyheader?: boolean;
|
|
21
|
+
};
|
|
22
|
+
events: {
|
|
23
|
+
[evt: string]: CustomEvent<any>;
|
|
24
|
+
};
|
|
25
|
+
slots: {
|
|
26
|
+
headercell: {
|
|
27
|
+
column: CollapsingTableColumn & {
|
|
28
|
+
widthPercent?: number;
|
|
29
|
+
};
|
|
30
|
+
key: string;
|
|
31
|
+
title: string;
|
|
32
|
+
item: any;
|
|
33
|
+
value: any;
|
|
34
|
+
};
|
|
35
|
+
dropicon: {
|
|
36
|
+
column: CollapsingTableColumn & {
|
|
37
|
+
widthPercent?: number;
|
|
38
|
+
};
|
|
39
|
+
item: any;
|
|
40
|
+
value: any;
|
|
41
|
+
};
|
|
42
|
+
default: {
|
|
43
|
+
key: string;
|
|
44
|
+
value: any;
|
|
45
|
+
item: any;
|
|
46
|
+
};
|
|
47
|
+
footer: {
|
|
48
|
+
item: any;
|
|
49
|
+
key: any;
|
|
50
|
+
value: any;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
export declare type CollapsingTableProps = typeof __propDef.props;
|
|
55
|
+
export declare type CollapsingTableEvents = typeof __propDef.events;
|
|
56
|
+
export declare type CollapsingTableSlots = typeof __propDef.slots;
|
|
57
|
+
export default class CollapsingTable extends SvelteComponentTyped<CollapsingTableProps, CollapsingTableEvents, CollapsingTableSlots> {
|
|
58
|
+
}
|
|
59
|
+
export {};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
<script
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
$: passthrough = (({ condition, component, a, span, element, ...passthrough }) => passthrough)($$props)
|
|
1
|
+
<script >export let condition;
|
|
2
|
+
export let component = undefined;
|
|
3
|
+
export let a = false;
|
|
4
|
+
export let span = false;
|
|
5
|
+
export let element = undefined;
|
|
6
|
+
$: passthrough = (({ condition, component, a, span, element, ...passthrough }) => passthrough)($$props);
|
|
8
7
|
</script>
|
|
9
8
|
|
|
10
9
|
{#if condition || (typeof condition === 'undefined' && component)}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
[x: string]: any;
|
|
5
|
+
condition: boolean | undefined;
|
|
6
|
+
component?: Function | undefined;
|
|
7
|
+
a?: boolean;
|
|
8
|
+
span?: boolean;
|
|
9
|
+
element?: HTMLElement | undefined;
|
|
10
|
+
};
|
|
11
|
+
events: {
|
|
12
|
+
[evt: string]: CustomEvent<any>;
|
|
13
|
+
};
|
|
14
|
+
slots: {
|
|
15
|
+
default: {};
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export declare type ConditionalWrapperProps = typeof __propDef.props;
|
|
19
|
+
export declare type ConditionalWrapperEvents = typeof __propDef.events;
|
|
20
|
+
export declare type ConditionalWrapperSlots = typeof __propDef.slots;
|
|
21
|
+
export default class ConditionalWrapper extends SvelteComponentTyped<ConditionalWrapperProps, ConditionalWrapperEvents, ConditionalWrapperSlots> {
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
<script context="module">const FocusLockStack = [];
|
|
1
|
+
<script context="module" >const FocusLockStack = [];
|
|
2
2
|
</script>
|
|
3
3
|
|
|
4
|
-
<script>export let returnfocusto = undefined;
|
|
4
|
+
<script >export let returnfocusto = undefined;
|
|
5
5
|
export let initialfocus = undefined;
|
|
6
6
|
export let hidefocus = true;
|
|
7
|
-
export let hidefocuslabel =
|
|
7
|
+
export let hidefocuslabel = 'focus is above modal dialog, start tabbing';
|
|
8
8
|
export let escapable = true;
|
|
9
9
|
let className = '';
|
|
10
10
|
export { className as class };
|
|
@@ -17,6 +17,7 @@ const dispatch = createEventDispatcher();
|
|
|
17
17
|
let lockelement;
|
|
18
18
|
let abovelockelement;
|
|
19
19
|
let active = true;
|
|
20
|
+
let listenforescape = false;
|
|
20
21
|
if (initialfocus)
|
|
21
22
|
hidefocus = false;
|
|
22
23
|
onMount(async () => {
|
|
@@ -30,7 +31,9 @@ onMount(async () => {
|
|
|
30
31
|
if (typeof returnfocusto === 'undefined') {
|
|
31
32
|
returnfocusto = document.querySelector(':focus');
|
|
32
33
|
}
|
|
33
|
-
await sleep(1);
|
|
34
|
+
await sleep(1); // sapper resets focus on page load, so we'll wait a millisecond in case our modal is on screen at load
|
|
35
|
+
if (!lockelement)
|
|
36
|
+
return; // in case we already unmounted
|
|
34
37
|
if (initialfocus) {
|
|
35
38
|
const focusEl = lockelement.querySelector(initialfocus);
|
|
36
39
|
if (focusEl instanceof HTMLElement)
|
|
@@ -39,20 +42,13 @@ onMount(async () => {
|
|
|
39
42
|
else {
|
|
40
43
|
setInitialFocus();
|
|
41
44
|
}
|
|
45
|
+
setTimeout(() => { listenforescape = true; }, 0);
|
|
42
46
|
});
|
|
43
47
|
onDestroy(async () => {
|
|
44
48
|
const wasactive = active;
|
|
45
|
-
let focuswasinside;
|
|
46
|
-
try {
|
|
47
|
-
focuswasinside = lockelement.matches(':focus-within');
|
|
48
|
-
}
|
|
49
|
-
catch (e) {
|
|
50
|
-
// browser doesn't support :focus-within
|
|
51
|
-
focuswasinside = true;
|
|
52
|
-
}
|
|
53
49
|
active = false;
|
|
54
50
|
await tick();
|
|
55
|
-
if (returnfocusto && wasactive
|
|
51
|
+
if (returnfocusto && wasactive) {
|
|
56
52
|
returnfocusto.focus();
|
|
57
53
|
}
|
|
58
54
|
FocusLockStack.pop();
|
|
@@ -61,37 +57,43 @@ onDestroy(async () => {
|
|
|
61
57
|
prevFocusLock.unpause();
|
|
62
58
|
});
|
|
63
59
|
const setInitialFocus = () => {
|
|
64
|
-
const firstfocus = tabbable(lockelement)[0];
|
|
60
|
+
const firstfocus = lockelement ? tabbable(lockelement)[0] : undefined;
|
|
65
61
|
if (firstfocus && firstfocus.focus) {
|
|
66
62
|
firstfocus.focus();
|
|
67
63
|
}
|
|
68
64
|
};
|
|
69
65
|
const setLastFocus = () => {
|
|
70
|
-
const lastfocus = tabbable(lockelement).slice(-1)[0];
|
|
66
|
+
const lastfocus = lockelement ? tabbable(lockelement).slice(-1)[0] : undefined;
|
|
71
67
|
if (lastfocus && lastfocus.focus) {
|
|
72
68
|
lastfocus.focus();
|
|
73
69
|
}
|
|
74
70
|
};
|
|
75
71
|
const keydown = (e) => {
|
|
76
|
-
if (active && e.key ===
|
|
72
|
+
if (active && e.key === 'Escape' && escapable) {
|
|
77
73
|
e.preventDefault();
|
|
78
74
|
dispatch('escape');
|
|
79
75
|
}
|
|
80
76
|
};
|
|
81
77
|
const focusin = (e) => {
|
|
82
|
-
if (active && e.target instanceof HTMLElement && !
|
|
78
|
+
if (active && e.target instanceof HTMLElement && !lockelement?.contains(e.target)) {
|
|
83
79
|
if (e.target === abovelockelement)
|
|
84
80
|
setLastFocus();
|
|
85
81
|
else
|
|
86
82
|
setInitialFocus();
|
|
87
83
|
}
|
|
88
84
|
};
|
|
85
|
+
const windowclick = (_) => {
|
|
86
|
+
if (listenforescape && active) {
|
|
87
|
+
returnfocusto = undefined;
|
|
88
|
+
dispatch('escape');
|
|
89
|
+
}
|
|
90
|
+
};
|
|
89
91
|
</script>
|
|
90
|
-
|
|
92
|
+
<svelte:window on:click={windowclick} />
|
|
91
93
|
<div class={className} role="alertdialog" aria-modal="true" on:click|stopPropagation on:keydown|stopPropagation={keydown} on:focusin={focusin}>
|
|
92
94
|
<div bind:this={abovelockelement} tabindex="0"></div>
|
|
93
95
|
<div bind:this={lockelement} on:keydown={keydown}>
|
|
94
|
-
{#if hidefocus}<div class="hiddenfocus" use:buttonify on:blur={() =>{ hidefocus = false }} on:click={() => escapable && dispatch('escape')}><ScreenReaderOnly>{hidefocuslabel}{#if escapable}, click to escape or use escape key at any time{/if}</ScreenReaderOnly></div>{/if}
|
|
96
|
+
{#if hidefocus}<div class="hiddenfocus" use:buttonify on:blur={() => { hidefocus = false }} on:click={() => escapable && dispatch('escape')}><ScreenReaderOnly>{hidefocuslabel}{#if escapable}, click to escape or use escape key at any time{/if}</ScreenReaderOnly></div>{/if}
|
|
95
97
|
<slot></slot>
|
|
96
98
|
</div>
|
|
97
99
|
<div tabindex="0"></div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
returnfocusto?: HTMLElement | null | undefined;
|
|
5
|
+
initialfocus?: string | undefined;
|
|
6
|
+
hidefocus?: boolean;
|
|
7
|
+
hidefocuslabel?: string;
|
|
8
|
+
escapable?: boolean;
|
|
9
|
+
class?: string;
|
|
10
|
+
};
|
|
11
|
+
events: {
|
|
12
|
+
click: MouseEvent;
|
|
13
|
+
escape: CustomEvent<any>;
|
|
14
|
+
} & {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
};
|
|
17
|
+
slots: {
|
|
18
|
+
default: {};
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export declare type FocusLockProps = typeof __propDef.props;
|
|
22
|
+
export declare type FocusLockEvents = typeof __propDef.events;
|
|
23
|
+
export declare type FocusLockSlots = typeof __propDef.slots;
|
|
24
|
+
export default class FocusLock extends SvelteComponentTyped<FocusLockProps, FocusLockEvents, FocusLockSlots> {
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script >export let escapable = true;
|
|
2
|
+
export let hidefocus = true;
|
|
3
|
+
export let hidefocuslabel = undefined;
|
|
4
|
+
export let returnfocusto = undefined;
|
|
5
|
+
export let initialfocus = undefined;
|
|
6
|
+
export let containerClass = '';
|
|
7
|
+
export let opaque = false;
|
|
8
|
+
export let usePortal = undefined;
|
|
9
|
+
import { createEventDispatcher, onMount } from 'svelte';
|
|
10
|
+
import FocusLock from './FocusLock.svelte';
|
|
11
|
+
import { portal } from '../actions';
|
|
12
|
+
const dispatch = createEventDispatcher();
|
|
13
|
+
const endmodal = () => {
|
|
14
|
+
dispatch('escape');
|
|
15
|
+
};
|
|
16
|
+
onMount(() => {
|
|
17
|
+
document.body.style.marginRight = (window.innerWidth - document.body.clientWidth) + 'px';
|
|
18
|
+
document.body.style.overflow = 'hidden';
|
|
19
|
+
return () => {
|
|
20
|
+
document.body.style.overflow = '';
|
|
21
|
+
document.body.style.marginRight = '';
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<div use:portal={usePortal} class="modal-backdrop" class:opaque on:click|stopPropagation|preventDefault={() => escapable && endmodal()}>
|
|
27
|
+
<FocusLock class="modal-container {containerClass}" {escapable} on:escape {hidefocus} {hidefocuslabel} {returnfocusto} {initialfocus}>
|
|
28
|
+
<slot></slot>
|
|
29
|
+
</FocusLock>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<style>
|
|
33
|
+
.modal-backdrop {
|
|
34
|
+
position: fixed;
|
|
35
|
+
top: 0;
|
|
36
|
+
left: 0;
|
|
37
|
+
width: 100vw;
|
|
38
|
+
height: 100vh;
|
|
39
|
+
background-color: var(--modal-bg, rgba(0, 0, 0, 0.7));
|
|
40
|
+
z-index: 2147483000;
|
|
41
|
+
display: flex;
|
|
42
|
+
justify-content: center;
|
|
43
|
+
align-items: center
|
|
44
|
+
}
|
|
45
|
+
.modal-backdrop.opaque {
|
|
46
|
+
background-color: var(--modal-bg-opaque, #4c4c4c);
|
|
47
|
+
}
|
|
48
|
+
.modal-backdrop :global(.modal-container) {
|
|
49
|
+
max-width: 100vw;
|
|
50
|
+
max-height: 100vh;
|
|
51
|
+
overflow: auto;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
escapable?: boolean;
|
|
5
|
+
hidefocus?: boolean;
|
|
6
|
+
hidefocuslabel?: string | undefined;
|
|
7
|
+
returnfocusto?: HTMLElement | undefined;
|
|
8
|
+
initialfocus?: string | undefined;
|
|
9
|
+
containerClass?: string;
|
|
10
|
+
opaque?: boolean;
|
|
11
|
+
usePortal?: HTMLElement | undefined;
|
|
12
|
+
};
|
|
13
|
+
events: {
|
|
14
|
+
escape: CustomEvent<any>;
|
|
15
|
+
} & {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
};
|
|
18
|
+
slots: {
|
|
19
|
+
default: {};
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export declare type ModalProps = typeof __propDef.props;
|
|
23
|
+
export declare type ModalEvents = typeof __propDef.events;
|
|
24
|
+
export declare type ModalSlots = typeof __propDef.slots;
|
|
25
|
+
export default class Modal extends SvelteComponentTyped<ModalProps, ModalEvents, ModalSlots> {
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<script>import {
|
|
2
|
-
import { createEventDispatcher, onDestroy, tick } from 'svelte';
|
|
1
|
+
<script >import { createEventDispatcher, onDestroy, tick } from 'svelte';
|
|
3
2
|
import { randomid } from 'txstate-utils';
|
|
4
|
-
import {
|
|
3
|
+
import { Store } from '@txstate-mws/svelte-store';
|
|
4
|
+
import { glue, portal } from '../actions';
|
|
5
5
|
import ScreenReaderOnly from './ScreenReaderOnly.svelte';
|
|
6
6
|
const dispatch = createEventDispatcher();
|
|
7
7
|
export let menushown = false;
|
|
@@ -16,27 +16,26 @@ export let align = 'auto';
|
|
|
16
16
|
export let cover = false;
|
|
17
17
|
export let showSelected = true;
|
|
18
18
|
export let width = undefined;
|
|
19
|
-
export let computedalign = new
|
|
19
|
+
export let computedalign = new Store({ valign: 'bottom', halign: 'left' });
|
|
20
20
|
export let usePortal = undefined;
|
|
21
21
|
export let emptyText = undefined;
|
|
22
|
-
export let
|
|
23
|
-
|
|
22
|
+
export let value = undefined;
|
|
23
|
+
let hilited = undefined;
|
|
24
24
|
let menuelement;
|
|
25
|
-
|
|
25
|
+
const itemelements = [];
|
|
26
26
|
const menuid = randomid();
|
|
27
27
|
let firstactive = 0;
|
|
28
28
|
let lastactive = items.length - 1;
|
|
29
29
|
function hiddenItem(item) {
|
|
30
|
-
return !!item && !showSelected && item.value ===
|
|
30
|
+
return !!item && !showSelected && item.value === value;
|
|
31
31
|
}
|
|
32
32
|
async function reactToItems(..._) {
|
|
33
|
-
var _a;
|
|
34
33
|
firstactive = items.findIndex(itm => !itm.disabled && !hiddenItem(itm));
|
|
35
34
|
lastactive = items.length - [...items].reverse().findIndex(itm => !itm.disabled && !hiddenItem(itm)) - 1;
|
|
36
|
-
if (hilited &&
|
|
35
|
+
if (hilited && items[hilited]?.disabled)
|
|
37
36
|
hilited = firstactive;
|
|
38
37
|
}
|
|
39
|
-
$: reactToItems(items,
|
|
38
|
+
$: reactToItems(items, value);
|
|
40
39
|
async function reactToMenuShown(_) {
|
|
41
40
|
if (!buttonelement) {
|
|
42
41
|
hilited = undefined;
|
|
@@ -56,22 +55,20 @@ async function reactToMenuShown(_) {
|
|
|
56
55
|
}
|
|
57
56
|
$: reactToMenuShown(menushown);
|
|
58
57
|
function move(idx) {
|
|
59
|
-
var _a;
|
|
60
58
|
if (!menushown)
|
|
61
59
|
return;
|
|
62
|
-
if (
|
|
60
|
+
if (items[idx]?.disabled)
|
|
63
61
|
return;
|
|
64
62
|
hilited = Math.max(firstactive, Math.min(lastactive, idx));
|
|
65
63
|
itemelements[hilited].scrollIntoView({ block: 'center' });
|
|
66
64
|
buttonelement.setAttribute('aria-activedescendant', `${menuid}-${hilited}`);
|
|
67
65
|
}
|
|
68
66
|
function onkeydown(e) {
|
|
69
|
-
var _a, _b;
|
|
70
67
|
if (e.key === 'ArrowDown') {
|
|
71
68
|
e.preventDefault();
|
|
72
69
|
if (menushown) {
|
|
73
|
-
let i = (hilited
|
|
74
|
-
while (
|
|
70
|
+
let i = (hilited ?? firstactive - 1) + 1;
|
|
71
|
+
while (items[i]?.disabled || hiddenItem(items[i]))
|
|
75
72
|
i++;
|
|
76
73
|
move(i);
|
|
77
74
|
}
|
|
@@ -83,8 +80,8 @@ function onkeydown(e) {
|
|
|
83
80
|
else if (e.key === 'ArrowUp') {
|
|
84
81
|
e.preventDefault();
|
|
85
82
|
if (menushown) {
|
|
86
|
-
let i = (hilited
|
|
87
|
-
while (
|
|
83
|
+
let i = (hilited ?? lastactive + 1) - 1;
|
|
84
|
+
while (items[i]?.disabled || hiddenItem(items[i]))
|
|
88
85
|
i--;
|
|
89
86
|
move(i);
|
|
90
87
|
}
|
|
@@ -98,8 +95,8 @@ function onkeydown(e) {
|
|
|
98
95
|
if (menushown) {
|
|
99
96
|
menushown = false;
|
|
100
97
|
if (typeof hilited !== 'undefined') {
|
|
101
|
-
|
|
102
|
-
dispatch('change', items[hilited]
|
|
98
|
+
value = items[hilited]?.value;
|
|
99
|
+
dispatch('change', items[hilited]);
|
|
103
100
|
}
|
|
104
101
|
}
|
|
105
102
|
else {
|
|
@@ -114,8 +111,8 @@ function onkeydown(e) {
|
|
|
114
111
|
if (typeof hilited !== 'undefined') {
|
|
115
112
|
e.preventDefault();
|
|
116
113
|
menushown = false;
|
|
117
|
-
|
|
118
|
-
dispatch('change', items[hilited]
|
|
114
|
+
value = items[hilited]?.value;
|
|
115
|
+
dispatch('change', items[hilited]);
|
|
119
116
|
}
|
|
120
117
|
else {
|
|
121
118
|
if (buttonelement.tagName !== 'INPUT') {
|
|
@@ -144,7 +141,7 @@ function onbuttonclick(e) {
|
|
|
144
141
|
}
|
|
145
142
|
async function onblur(e) {
|
|
146
143
|
// tabindex=-1 on our menu elements means e.relatedTarget will be set
|
|
147
|
-
if (!(e.relatedTarget instanceof HTMLElement &&
|
|
144
|
+
if (!(e.relatedTarget instanceof HTMLElement && menuelement?.contains(e.relatedTarget)))
|
|
148
145
|
menushown = false;
|
|
149
146
|
}
|
|
150
147
|
function cleanup(element) {
|
|
@@ -152,6 +149,7 @@ function cleanup(element) {
|
|
|
152
149
|
element.removeEventListener('click', onbuttonclick);
|
|
153
150
|
element.removeEventListener('keydown', onkeydown);
|
|
154
151
|
element.removeEventListener('blur', onblur);
|
|
152
|
+
element.removeAttribute('aria-disabled');
|
|
155
153
|
element.removeAttribute('aria-haspopup');
|
|
156
154
|
element.removeAttribute('aria-expanded');
|
|
157
155
|
element.removeAttribute('aria-controls');
|
|
@@ -178,29 +176,29 @@ const onclick = (item) => (e) => {
|
|
|
178
176
|
if (item.disabled)
|
|
179
177
|
return;
|
|
180
178
|
menushown = false;
|
|
181
|
-
|
|
182
|
-
dispatch('change', item
|
|
179
|
+
value = item.value;
|
|
180
|
+
dispatch('change', item);
|
|
183
181
|
};
|
|
184
|
-
$: hasSelected = showSelected &&
|
|
182
|
+
$: hasSelected = showSelected && items.some(itm => itm.value === value);
|
|
185
183
|
</script>
|
|
186
184
|
|
|
187
185
|
{#if menushown}
|
|
188
186
|
<div use:portal={usePortal === true ? document.body : (usePortal || null)} use:glue={{ target: buttonelement, align, cover, store: computedalign }} class={menuContainerClass}>
|
|
189
187
|
<ul bind:this={menuelement} id={menuid} role='listbox' style={width ? `width: ${width}` : ''} class={menuClass} class:hasSelected class:defaultmenu={!menuClass && !menuContainerClass} on:keydown={onkeydown}>
|
|
190
188
|
{#each items as item, i (item.value)}
|
|
191
|
-
{#if showSelected ||
|
|
189
|
+
{#if showSelected || item.value !== value}
|
|
192
190
|
<li
|
|
193
191
|
id={`${menuid}-${i}`}
|
|
194
192
|
bind:this={itemelements[i]}
|
|
195
|
-
class={`${menuItemClass} ${i === hilited ? menuItemHilitedClass || '' : ''} ${
|
|
193
|
+
class={`${menuItemClass} ${i === hilited ? menuItemHilitedClass || '' : ''} ${value === item.value ? menuItemSelectedClass || '' : ''}`}
|
|
196
194
|
class:disabled={!!item.disabled}
|
|
197
195
|
class:hilited={!menuItemHilitedClass && i === hilited}
|
|
198
|
-
class:selected={showSelected && !menuItemSelectedClass &&
|
|
196
|
+
class:selected={showSelected && !menuItemSelectedClass && value === item.value}
|
|
199
197
|
on:click={onclick(item)}
|
|
200
198
|
role="option"
|
|
201
199
|
tabindex=-1
|
|
202
200
|
aria-disabled={item.disabled}
|
|
203
|
-
><slot {item} label={item.label || item.value} hilited={i === hilited} selected={
|
|
201
|
+
><slot {item} label={item.label || item.value} hilited={i === hilited} selected={value === item.value}>{item.label || item.value}</slot></li>
|
|
204
202
|
{/if}
|
|
205
203
|
{/each}
|
|
206
204
|
{#if items.length === 0}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
import { Store } from '@txstate-mws/svelte-store';
|
|
3
|
+
import type { GlueAlignOpts, GlueAlignStore } from '../actions';
|
|
4
|
+
import type { PopupMenuItem } from '../types';
|
|
5
|
+
declare const __propDef: {
|
|
6
|
+
props: {
|
|
7
|
+
menushown?: boolean;
|
|
8
|
+
menuContainerClass?: string;
|
|
9
|
+
menuClass?: string;
|
|
10
|
+
menuItemClass?: string;
|
|
11
|
+
menuItemHilitedClass?: string;
|
|
12
|
+
menuItemSelectedClass?: string;
|
|
13
|
+
items?: PopupMenuItem[];
|
|
14
|
+
buttonelement: HTMLElement;
|
|
15
|
+
align?: GlueAlignOpts;
|
|
16
|
+
cover?: boolean;
|
|
17
|
+
showSelected?: boolean;
|
|
18
|
+
width?: string | undefined;
|
|
19
|
+
computedalign?: Store<GlueAlignStore>;
|
|
20
|
+
usePortal?: HTMLElement | true | undefined;
|
|
21
|
+
emptyText?: string | undefined;
|
|
22
|
+
value?: string | undefined;
|
|
23
|
+
};
|
|
24
|
+
events: {
|
|
25
|
+
change: CustomEvent<any>;
|
|
26
|
+
} & {
|
|
27
|
+
[evt: string]: CustomEvent<any>;
|
|
28
|
+
};
|
|
29
|
+
slots: {
|
|
30
|
+
default: {
|
|
31
|
+
item: PopupMenuItem;
|
|
32
|
+
label: string;
|
|
33
|
+
hilited: boolean;
|
|
34
|
+
selected: boolean;
|
|
35
|
+
};
|
|
36
|
+
noresults: {};
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export declare type PopupMenuProps = typeof __propDef.props;
|
|
40
|
+
export declare type PopupMenuEvents = typeof __propDef.events;
|
|
41
|
+
export declare type PopupMenuSlots = typeof __propDef.slots;
|
|
42
|
+
export default class PopupMenu extends SvelteComponentTyped<PopupMenuProps, PopupMenuEvents, PopupMenuSlots> {
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export let ariaatomic = ''
|
|
1
|
+
<script >export let id = undefined;
|
|
2
|
+
export let arialive = undefined;
|
|
3
|
+
export let ariaatomic = undefined;
|
|
5
4
|
</script>
|
|
6
5
|
|
|
7
6
|
<style>
|
|
@@ -23,4 +22,4 @@
|
|
|
23
22
|
}
|
|
24
23
|
</style>
|
|
25
24
|
|
|
26
|
-
<span
|
|
25
|
+
<span {id} class="sr-only" aria-live={arialive} aria-atomic={ariaatomic}><slot/></span>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
id?: string | undefined;
|
|
5
|
+
arialive?: 'off' | 'assertive' | 'polite' | undefined;
|
|
6
|
+
ariaatomic?: boolean | 'false' | 'true' | undefined;
|
|
7
|
+
};
|
|
8
|
+
events: {
|
|
9
|
+
[evt: string]: CustomEvent<any>;
|
|
10
|
+
};
|
|
11
|
+
slots: {
|
|
12
|
+
default: {};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export declare type ScreenReaderOnlyProps = typeof __propDef.props;
|
|
16
|
+
export declare type ScreenReaderOnlyEvents = typeof __propDef.events;
|
|
17
|
+
export declare type ScreenReaderOnlySlots = typeof __propDef.slots;
|
|
18
|
+
export default class ScreenReaderOnly extends SvelteComponentTyped<ScreenReaderOnlyProps, ScreenReaderOnlyEvents, ScreenReaderOnlySlots> {
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as CollapsingTable } from './CollapsingTable.svelte';
|
|
2
|
+
export { default as FocusLock } from './FocusLock.svelte';
|
|
3
|
+
export { default as ConditionalWrapper } from './ConditionalWrapper.svelte';
|
|
4
|
+
export { default as Modal } from './Modal.svelte';
|
|
5
|
+
export { default as PopupMenu } from './PopupMenu.svelte';
|
|
6
|
+
export { default as ScreenReaderOnly } from './ScreenReaderOnly.svelte';
|
package/index.d.ts
ADDED
package/index.js
ADDED