@txstate-mws/svelte-components 1.1.0 → 1.1.1

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.
Files changed (131) hide show
  1. package/actions/buttonify.d.ts +8 -0
  2. package/actions/buttonify.js +26 -0
  3. package/actions/glue.d.ts +16 -0
  4. package/actions/glue.js +82 -0
  5. package/actions/index.d.ts +7 -0
  6. package/actions/index.js +7 -0
  7. package/actions/offset.d.ts +15 -0
  8. package/actions/offset.js +35 -0
  9. package/actions/portal.d.ts +4 -0
  10. package/actions/portal.js +13 -0
  11. package/actions/resize.d.ts +20 -0
  12. package/actions/resize.js +57 -0
  13. package/actions/sticky.d.ts +33 -0
  14. package/actions/sticky.js +85 -0
  15. package/actions/stickyfixed.d.ts +25 -0
  16. package/actions/stickyfixed.js +157 -0
  17. package/components/Card.svelte +23 -0
  18. package/components/Card.svelte.d.ts +18 -0
  19. package/components/CardLayout.svelte +197 -0
  20. package/components/CardLayout.svelte.d.ts +21 -0
  21. package/{src/lib/components → components}/CollapsingTable.svelte +63 -65
  22. package/components/CollapsingTable.svelte.d.ts +59 -0
  23. package/{src/lib/components → components}/ConditionalWrapper.svelte +6 -7
  24. package/components/ConditionalWrapper.svelte.d.ts +23 -0
  25. package/components/FocusLock.svelte +107 -0
  26. package/components/FocusLock.svelte.d.ts +26 -0
  27. package/{src/lib/components → components}/Modal.svelte +22 -24
  28. package/components/Modal.svelte.d.ts +27 -0
  29. package/components/MultiSelect.svelte +179 -0
  30. package/components/MultiSelect.svelte.d.ts +35 -0
  31. package/components/PopupMenu.svelte +287 -0
  32. package/components/PopupMenu.svelte.d.ts +45 -0
  33. package/{src/lib/components → components}/ScreenReaderOnly.svelte +3 -4
  34. package/components/ScreenReaderOnly.svelte.d.ts +20 -0
  35. package/components/index.d.ts +9 -0
  36. package/components/index.js +9 -0
  37. package/index.d.ts +4 -0
  38. package/index.js +4 -0
  39. package/package.json +34 -13
  40. package/types/CardLayout.d.ts +14 -0
  41. package/types/CardLayout.js +1 -0
  42. package/types/CollapsingTable.d.ts +8 -0
  43. package/types/CollapsingTable.js +1 -0
  44. package/types/PopupMenu.d.ts +5 -0
  45. package/types/PopupMenu.js +1 -0
  46. package/types/index.d.ts +3 -0
  47. package/types/index.js +3 -0
  48. package/util/bodyoffset.d.ts +14 -0
  49. package/util/bodyoffset.js +72 -0
  50. package/util/index.d.ts +3 -0
  51. package/util/index.js +3 -0
  52. package/util/repositioned.d.ts +12 -0
  53. package/util/repositioned.js +81 -0
  54. package/util/util.d.ts +5 -0
  55. package/util/util.js +33 -0
  56. package/.eslintrc.cjs +0 -37
  57. package/docs/CollapsingTable.md +0 -79
  58. package/docs/ConditionalWrapper.md +0 -35
  59. package/docs/Modal.md +0 -63
  60. package/docs/PopupMenu.md +0 -31
  61. package/docs/ScreenReaderOnly.md +0 -10
  62. package/docs/actions.md +0 -84
  63. package/docs/stores.md +0 -203
  64. package/playwright.config.js +0 -112
  65. package/src/app.html +0 -14
  66. package/src/global.d.ts +0 -1
  67. package/src/lib/actions/buttonify.ts +0 -27
  68. package/src/lib/actions/glue.ts +0 -96
  69. package/src/lib/actions/index.ts +0 -7
  70. package/src/lib/actions/offset.ts +0 -43
  71. package/src/lib/actions/portal.ts +0 -13
  72. package/src/lib/actions/resize.ts +0 -72
  73. package/src/lib/actions/sticky.ts +0 -105
  74. package/src/lib/actions/stickyfixed.ts +0 -160
  75. package/src/lib/components/Card.svelte +0 -25
  76. package/src/lib/components/CardLayout.svelte +0 -188
  77. package/src/lib/components/FocusLock.svelte +0 -102
  78. package/src/lib/components/MultiSelect.svelte +0 -174
  79. package/src/lib/components/PopupMenu.svelte +0 -285
  80. package/src/lib/components/index.ts +0 -9
  81. package/src/lib/index.ts +0 -4
  82. package/src/lib/types/CardLayout.ts +0 -14
  83. package/src/lib/types/CollapsingTable.ts +0 -8
  84. package/src/lib/types/PopupMenu.ts +0 -5
  85. package/src/lib/types/index.ts +0 -3
  86. package/src/lib/util/bodyoffset.ts +0 -77
  87. package/src/lib/util/index.ts +0 -3
  88. package/src/lib/util/repositioned.ts +0 -91
  89. package/src/lib/util/util.ts +0 -36
  90. package/src/routes/__layout.svelte +0 -125
  91. package/src/routes/actions/index.svelte +0 -1
  92. package/src/routes/actions/offset.svelte +0 -4
  93. package/src/routes/actions/offsetdebounce.svelte +0 -4
  94. package/src/routes/actions/resize.svelte +0 -4
  95. package/src/routes/actions/resizedebounce.svelte +0 -4
  96. package/src/routes/actions/sticky.svelte +0 -4
  97. package/src/routes/cards/index.svelte +0 -4
  98. package/src/routes/cards/preserve.svelte +0 -4
  99. package/src/routes/collapsingtable/index.svelte +0 -4
  100. package/src/routes/collapsingtable/stickyheader.svelte +0 -4
  101. package/src/routes/index.svelte +0 -2
  102. package/src/routes/modal.svelte +0 -4
  103. package/src/routes/multiselect.svelte +0 -4
  104. package/src/routes/popupmenu/body.svelte +0 -4
  105. package/src/routes/popupmenu/complex.svelte +0 -4
  106. package/src/routes/popupmenu/empty.svelte +0 -5
  107. package/src/routes/popupmenu/index.svelte +0 -4
  108. package/src/routes/popupmenu/parent.svelte +0 -4
  109. package/src/routes/popupmenu/scrollable.svelte +0 -27
  110. package/src/routes/popupmenu/showselected.svelte +0 -4
  111. package/src/test/Cards.svelte +0 -64
  112. package/src/test/CollapsingTableTest.svelte +0 -36
  113. package/src/test/ModalTest.svelte +0 -58
  114. package/src/test/MultiSelectTest.svelte +0 -26
  115. package/src/test/Offset.svelte +0 -32
  116. package/src/test/PopupMenuBody.svelte +0 -34
  117. package/src/test/PopupMenuButtonIsParent.svelte +0 -36
  118. package/src/test/PopupMenuComplex.svelte +0 -34
  119. package/src/test/PopupMenuSimple.svelte +0 -36
  120. package/src/test/Resize.svelte +0 -29
  121. package/src/test/Sticky.svelte +0 -65
  122. package/static/layout.css +0 -263
  123. package/svelte.config.js +0 -15
  124. package/tests/.eslintrc.cjs +0 -19
  125. package/tests/CardLayout.test.js +0 -41
  126. package/tests/CollapsingTable.test.js +0 -37
  127. package/tests/Modal.test.js +0 -18
  128. package/tests/MultiSelect.test.js +0 -58
  129. package/tests/PopupMenu.test.js +0 -55
  130. package/tests/common.js +0 -4
  131. package/tsconfig.json +0 -31
@@ -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 {};
@@ -0,0 +1,179 @@
1
+ <script >import { createEventDispatcher } from 'svelte';
2
+ import { randomid } from 'txstate-utils';
3
+ import ScreenReaderOnly from './ScreenReaderOnly.svelte';
4
+ import DefaultPopupMenu from './PopupMenu.svelte';
5
+ import { debouncedPromise, modifierKey, selectionIsLeft } from '../util';
6
+ export let id = randomid();
7
+ export let name;
8
+ export let getOptions;
9
+ export let menuContainerClass = undefined;
10
+ export let menuClass = undefined;
11
+ export let menuItemClass = undefined;
12
+ export let menuItemHilitedClass = undefined;
13
+ export let selected = [];
14
+ export let placeholder = '';
15
+ export let emptyText = undefined;
16
+ export let PopupMenu = DefaultPopupMenu;
17
+ let menushown;
18
+ let options = [];
19
+ let hilitedpill;
20
+ let inputvalue = '';
21
+ let popupvalue = undefined;
22
+ let inputelement;
23
+ const descriptionid = randomid();
24
+ $: debouncedGetOptions = debouncedPromise(getOptions, 100, []);
25
+ $: selectedSet = new Set(selected.map(s => s.value));
26
+ async function reactToInput(..._) {
27
+ const saveval = inputvalue;
28
+ const rawOptions = await debouncedGetOptions(saveval);
29
+ if (inputvalue !== saveval)
30
+ return; // ignore any results that are out of date
31
+ options = rawOptions.filter(o => !selectedSet.has(o.value));
32
+ if (typeof document !== 'undefined' && inputelement === document.activeElement)
33
+ menushown = true;
34
+ }
35
+ $: reactToInput(inputvalue, debouncedGetOptions, selectedSet);
36
+ $: availablemessage = options.filter(o => o.value).length + ' autocomplete choices available';
37
+ function addSelection(e) {
38
+ inputvalue = '';
39
+ const opt = { ...e.detail, label: e.detail.label || e.detail.value };
40
+ selected = [...selected, opt];
41
+ popupvalue = undefined;
42
+ }
43
+ function removeSelection(opt, idx, nextfocus = 1) {
44
+ if (hilitedpill === opt.value) {
45
+ hilitedpill = selected[idx + nextfocus]?.value;
46
+ }
47
+ selected = selected.filter(s => s.value !== opt.value);
48
+ }
49
+ function inputkeydown(e) {
50
+ if (modifierKey(e))
51
+ return;
52
+ if (e.key === 'ArrowLeft' && selectionIsLeft(inputelement)) {
53
+ const idx = hilitedpill ? selected.findIndex(s => s.value === hilitedpill) : selected.length;
54
+ if (idx !== 0)
55
+ hilitedpill = selected[idx - 1].value;
56
+ }
57
+ else if (e.key === 'ArrowRight' && selectionIsLeft(inputelement)) {
58
+ if (hilitedpill)
59
+ e.preventDefault();
60
+ const idx = hilitedpill ? selected.findIndex(s => s.value === hilitedpill) : undefined;
61
+ if (idx != null) {
62
+ if (idx === selected.length - 1)
63
+ hilitedpill = undefined;
64
+ else
65
+ hilitedpill = selected[idx + 1].value;
66
+ }
67
+ }
68
+ else if (['Backspace', 'Delete', 'Clear', ' ', 'Enter'].includes(e.key)) {
69
+ if (hilitedpill) {
70
+ const idx = selected.findIndex(s => s.value === hilitedpill);
71
+ removeSelection(selected[idx], idx, e.key === 'Delete' ? 1 : -1);
72
+ e.preventDefault();
73
+ }
74
+ else if (e.key === 'Backspace' && selectionIsLeft(inputelement)) {
75
+ removeSelection(selected[selected.length - 1], selected.length - 1, -1);
76
+ e.preventDefault();
77
+ }
78
+ }
79
+ }
80
+ function inputfocus() {
81
+ reactToInput();
82
+ }
83
+ let popuphilited;
84
+ $: if (popuphilited != null)
85
+ hilitedpill = undefined;
86
+ function reactToHilite(..._) {
87
+ if (!inputelement)
88
+ return;
89
+ if (hilitedpill) {
90
+ popuphilited = undefined;
91
+ inputelement.setAttribute('aria-activedescendant', id + hilitedpill);
92
+ }
93
+ else
94
+ inputelement.removeAttribute('aria-activedescendant');
95
+ }
96
+ $: reactToHilite(hilitedpill, id);
97
+ const dispatch = createEventDispatcher();
98
+ $: dispatch('change', selected);
99
+ </script>
100
+
101
+ <fieldset>
102
+ <ul class="multiselect-selected" role="listbox" on:click={() => inputelement.focus()}>
103
+ {#each selected as option, i}
104
+ <li id={id + option.value} role="option" tabindex="-1" class="multiselect-pill" class:hilited={hilitedpill === option.value}
105
+ on:click|preventDefault|stopPropagation={() => removeSelection(option, i, 1)}
106
+ aria-selected="true">
107
+ {option.label || option.value}
108
+ <ScreenReaderOnly>, click to remove</ScreenReaderOnly>
109
+ </li>
110
+ {/each}
111
+ <li class="input">
112
+ <input type="text" id={id} name={name} placeholder={placeholder}
113
+ bind:this={inputelement} bind:value={inputvalue}
114
+ on:focus={inputfocus} on:keydown={inputkeydown}
115
+ autocomplete="off" autocorrect="off" spellcheck="false" aria-autocomplete="list"
116
+ aria-describedby="{descriptionid}">
117
+ </li>
118
+ </ul>
119
+ <ScreenReaderOnly id={descriptionid} arialive="assertive">
120
+ <span>{selected.length ? selected.length + ' selected' : 'select multiple, none selected'}, up down to choose, left right to hilite existing choices</span>
121
+ {#if menushown}<span>{availablemessage}, touch users explore to find autocomplete menu</span>{/if}
122
+ </ScreenReaderOnly>
123
+ <slot></slot>
124
+ </fieldset>
125
+ <svelte:component this={PopupMenu} bind:menushown bind:hilited={popuphilited} bind:value={popupvalue} align='bottomleft' {emptyText} {menuContainerClass} {menuClass} {menuItemClass} {menuItemHilitedClass} items={options} buttonelement={inputelement} on:change={addSelection}></svelte:component>
126
+
127
+ <style>
128
+ fieldset {
129
+ border: 0;
130
+ padding: 0;
131
+ margin: 0;
132
+ }
133
+ fieldset * {
134
+ box-sizing: border-box;
135
+ }
136
+ .multiselect-selected {
137
+ margin: 0;
138
+ padding: 0;
139
+ list-style: none;
140
+ display: flex;
141
+ flex-wrap: wrap;
142
+ align-items: stretch;
143
+ padding: var(--multiselect-padding, 0.3em);
144
+ border: var(--multiselect-border, 1px solid #666666);
145
+ border-radius: var(--multiselect-radius, 0.3em);
146
+ }
147
+ .multiselect-selected:focus-within {
148
+ outline-width: var(--multiselect-focus-width, 2px);
149
+ outline-style: solid;
150
+ outline-color: var(--multiselect-focus-color, Highlight);
151
+ }
152
+ li.input {
153
+ flex-grow: 1;
154
+ }
155
+ input {
156
+ outline: 0;
157
+ border: 0;
158
+ min-width: 0;
159
+ width: 100%;
160
+ height: 100%;
161
+ }
162
+ .multiselect-pill {
163
+ cursor: pointer;
164
+ flex-grow: 0;
165
+ margin-right: 0.3em;
166
+ line-height: 1;
167
+ border-radius: var(--multiselect-pill-radius, 0.8em);
168
+ border: var(--multiselect-pill-border, 1px solid gray);
169
+ background-color: var(--multiselect-pill-bg, transparent);
170
+ color: var(--multiselect-pill-text, black);
171
+ padding: var(--multiselect-pill-padding, 0.3em 0.5em);
172
+ }
173
+ .multiselect-pill.hilited {
174
+ outline: 0;
175
+ background-color: var(--multiselect-pill-selected, gray);
176
+ border: var(--multiselect-pill-selected-border, 1px solid transparent);
177
+ color: var(--multiselect-pill-selected-text, white);
178
+ }
179
+ </style>
@@ -0,0 +1,35 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import DefaultPopupMenu from './PopupMenu.svelte';
3
+ import type { PopupMenuItem } from '../types';
4
+ declare const __propDef: {
5
+ props: {
6
+ id?: string;
7
+ name: string;
8
+ getOptions: (search: string) => Promise<PopupMenuItem[]> | PopupMenuItem[];
9
+ menuContainerClass?: string | undefined;
10
+ menuClass?: string | undefined;
11
+ menuItemClass?: string | undefined;
12
+ menuItemHilitedClass?: string | undefined;
13
+ selected?: {
14
+ value: string;
15
+ label: string;
16
+ }[];
17
+ placeholder?: string;
18
+ emptyText?: string | undefined;
19
+ PopupMenu?: typeof DefaultPopupMenu;
20
+ };
21
+ events: {
22
+ change: CustomEvent<any>;
23
+ } & {
24
+ [evt: string]: CustomEvent<any>;
25
+ };
26
+ slots: {
27
+ default: {};
28
+ };
29
+ };
30
+ export declare type MultiSelectProps = typeof __propDef.props;
31
+ export declare type MultiSelectEvents = typeof __propDef.events;
32
+ export declare type MultiSelectSlots = typeof __propDef.slots;
33
+ export default class MultiSelect extends SvelteComponentTyped<MultiSelectProps, MultiSelectEvents, MultiSelectSlots> {
34
+ }
35
+ export {};
@@ -0,0 +1,287 @@
1
+ <script >import { createEventDispatcher, onDestroy, tick } from 'svelte';
2
+ import { randomid } from 'txstate-utils';
3
+ import { Store } from '@txstate-mws/svelte-store';
4
+ import { glue, portal } from '../actions';
5
+ import ScreenReaderOnly from './ScreenReaderOnly.svelte';
6
+ import { modifierKey } from '../util';
7
+ const dispatch = createEventDispatcher();
8
+ export let menushown = false;
9
+ export let menuContainerClass = '';
10
+ export let menuClass = '';
11
+ export let menuItemClass = '';
12
+ export let menuItemHilitedClass = '';
13
+ export let menuItemSelectedClass = '';
14
+ export let items = [];
15
+ export let buttonelement;
16
+ export let align = 'auto';
17
+ export let cover = false;
18
+ export let showSelected = true;
19
+ export let width = undefined;
20
+ export let computedalign = new Store({ valign: 'bottom', halign: 'left' });
21
+ export let usePortal = undefined;
22
+ export let emptyText = undefined;
23
+ export let value = undefined;
24
+ export let hilited = undefined;
25
+ let menuelement;
26
+ const itemelements = [];
27
+ const menuid = randomid();
28
+ let firstactive = 0;
29
+ let lastactive = items.length - 1;
30
+ function hiddenItem(item) {
31
+ return !!item && !showSelected && item.value === value;
32
+ }
33
+ async function reactToItems(..._) {
34
+ firstactive = items.findIndex(itm => !itm.disabled && !hiddenItem(itm));
35
+ lastactive = items.length - [...items].reverse().findIndex(itm => !itm.disabled && !hiddenItem(itm)) - 1;
36
+ if (hilited && items[hilited]?.disabled)
37
+ hilited = firstactive;
38
+ }
39
+ $: reactToItems(items, value);
40
+ async function reactToMenuShown(_) {
41
+ if (!buttonelement) {
42
+ hilited = undefined;
43
+ return;
44
+ }
45
+ await tick();
46
+ if (!menushown) {
47
+ buttonelement.removeAttribute('aria-controls');
48
+ buttonelement.setAttribute('aria-expanded', 'false');
49
+ if (hilited)
50
+ buttonelement.removeAttribute('aria-activedescendant');
51
+ hilited = undefined;
52
+ }
53
+ else {
54
+ buttonelement.setAttribute('aria-controls', menuid);
55
+ buttonelement.setAttribute('aria-expanded', 'true');
56
+ buttonelement.focus();
57
+ }
58
+ }
59
+ $: reactToMenuShown(menushown);
60
+ function move(idx) {
61
+ if (!menushown)
62
+ return;
63
+ if (items[idx]?.disabled)
64
+ return;
65
+ hilited = Math.max(firstactive, Math.min(lastactive, idx));
66
+ itemelements[hilited].scrollIntoView({ block: 'center' });
67
+ buttonelement.setAttribute('aria-activedescendant', `${menuid}-${hilited}`);
68
+ }
69
+ function onkeydown(e) {
70
+ if (modifierKey(e))
71
+ return;
72
+ if (e.key === 'ArrowDown') {
73
+ e.preventDefault();
74
+ if (menushown) {
75
+ let i = (hilited ?? firstactive - 1) + 1;
76
+ while (items[i]?.disabled || hiddenItem(items[i]))
77
+ i++;
78
+ move(i);
79
+ }
80
+ else {
81
+ menushown = true;
82
+ tick().then(() => move(firstactive));
83
+ }
84
+ }
85
+ else if (e.key === 'ArrowUp') {
86
+ e.preventDefault();
87
+ if (menushown) {
88
+ let i = (hilited ?? lastactive + 1) - 1;
89
+ while (items[i]?.disabled || hiddenItem(items[i]))
90
+ i--;
91
+ move(i);
92
+ }
93
+ else {
94
+ menushown = true;
95
+ tick().then(() => move(lastactive));
96
+ }
97
+ }
98
+ else if (e.key === 'Enter') {
99
+ e.preventDefault();
100
+ if (menushown) {
101
+ menushown = false;
102
+ if (typeof hilited !== 'undefined') {
103
+ value = items[hilited]?.value;
104
+ dispatch('change', items[hilited]);
105
+ }
106
+ }
107
+ else {
108
+ menushown = true;
109
+ tick().then(() => move(firstactive));
110
+ }
111
+ }
112
+ else if (e.key === ' ') {
113
+ // buttonelement might be a text input if this popup is searchable,
114
+ // so we want to allow spaces to be entered without changing the menu situation
115
+ if (menushown) {
116
+ if (typeof hilited !== 'undefined') {
117
+ e.preventDefault();
118
+ menushown = false;
119
+ value = items[hilited]?.value;
120
+ dispatch('change', items[hilited]);
121
+ }
122
+ else {
123
+ if (buttonelement.tagName !== 'INPUT') {
124
+ e.preventDefault();
125
+ menushown = false;
126
+ }
127
+ }
128
+ }
129
+ else {
130
+ if (buttonelement.tagName !== 'INPUT') {
131
+ e.preventDefault();
132
+ menushown = true;
133
+ tick().then(() => move(firstactive));
134
+ }
135
+ }
136
+ }
137
+ else if (menushown && e.key === 'Escape') {
138
+ e.preventDefault();
139
+ e.stopPropagation();
140
+ menushown = false;
141
+ }
142
+ }
143
+ let blurTimer;
144
+ function onbuttonclick(e) {
145
+ e.preventDefault();
146
+ cancelAnimationFrame(blurTimer);
147
+ menushown = !menushown;
148
+ }
149
+ async function onblur(e) {
150
+ // tabindex=-1 on our menu elements means e.relatedTarget will be set
151
+ if (!(e.relatedTarget instanceof HTMLElement && menuelement?.contains(e.relatedTarget))) {
152
+ blurTimer = requestAnimationFrame(() => { menushown = false; });
153
+ }
154
+ }
155
+ function cleanup(element) {
156
+ if (element) {
157
+ element.removeEventListener('click', onbuttonclick);
158
+ element.removeEventListener('keydown', onkeydown);
159
+ element.removeEventListener('blur', onblur);
160
+ element.removeAttribute('aria-disabled');
161
+ element.removeAttribute('aria-haspopup');
162
+ element.removeAttribute('aria-expanded');
163
+ element.removeAttribute('aria-controls');
164
+ element.removeAttribute('aria-activedescendant');
165
+ }
166
+ }
167
+ onDestroy(() => cleanup(buttonelement));
168
+ // if buttonelement changes we need to handle listeners and aria
169
+ let lastbuttonelement;
170
+ function reactToButtonElement(buttonelement) {
171
+ cleanup(lastbuttonelement);
172
+ lastbuttonelement = buttonelement;
173
+ if (buttonelement) {
174
+ buttonelement.setAttribute('aria-haspopup', 'listbox');
175
+ buttonelement.addEventListener('click', onbuttonclick);
176
+ buttonelement.addEventListener('keydown', onkeydown);
177
+ buttonelement.addEventListener('blur', onblur);
178
+ }
179
+ }
180
+ $: reactToButtonElement(buttonelement);
181
+ const onclick = (item) => (e) => {
182
+ e.stopPropagation();
183
+ e.preventDefault();
184
+ if (item.disabled)
185
+ return;
186
+ menushown = false;
187
+ value = item.value;
188
+ dispatch('change', item);
189
+ };
190
+ $: hasSelected = showSelected && items.some(itm => itm.value === value);
191
+ </script>
192
+
193
+ {#if menushown}
194
+ <div use:portal={usePortal === true ? document.body : (usePortal || null)} use:glue={{ target: buttonelement, align, cover, store: computedalign }} class={menuContainerClass}>
195
+ <ul bind:this={menuelement} id={menuid} role='listbox' style={width ? `width: ${width}` : ''} class={menuClass} class:hasSelected class:defaultmenu={!menuClass && !menuContainerClass} on:keydown={onkeydown}>
196
+ {#each items as item, i (item.value)}
197
+ {#if showSelected || item.value !== value}
198
+ <li
199
+ id={`${menuid}-${i}`}
200
+ bind:this={itemelements[i]}
201
+ class={`${menuItemClass} ${i === hilited ? menuItemHilitedClass || '' : ''} ${value === item.value ? menuItemSelectedClass || '' : ''}`}
202
+ class:disabled={!!item.disabled}
203
+ class:hilited={!menuItemHilitedClass && i === hilited}
204
+ class:selected={showSelected && !menuItemSelectedClass && value === item.value}
205
+ on:click={onclick(item)}
206
+ role="option"
207
+ tabindex=-1
208
+ aria-disabled={item.disabled}
209
+ ><slot {item} label={item.label || item.value} hilited={i === hilited} selected={value === item.value}>{item.label || item.value}</slot></li>
210
+ {/if}
211
+ {/each}
212
+ {#if items.length === 0}
213
+ <li role="option" class={`${menuItemClass} disabled`} aria-live="assertive">
214
+ <slot name="noresults">
215
+ {#if !emptyText}
216
+ <span aria-hidden="true">{'¯\\_(ツ)_/¯'}</span><ScreenReaderOnly>{emptyText || 'empty popup menu'}</ScreenReaderOnly>
217
+ {:else}
218
+ {emptyText}
219
+ {/if}
220
+ </slot>
221
+ </li>
222
+ {/if}
223
+ </ul>
224
+ </div>
225
+ {/if}
226
+
227
+ <style>
228
+ div {
229
+ position: absolute !important;
230
+ }
231
+ ul {
232
+ list-style: none;
233
+ }
234
+ ul.defaultmenu {
235
+ margin: 0;
236
+ padding: 0.4em;
237
+ background: white;
238
+ border: 1px solid slategray;
239
+ border-radius: 3px;
240
+ min-width: 10em;
241
+ max-height: 20em;
242
+ overflow-y: auto;
243
+ }
244
+ ul.defaultmenu li.disabled {
245
+ color: rgba(0,0,0,0.6);
246
+ }
247
+ ul.defaultmenu::-webkit-scrollbar {
248
+ appearance: none;
249
+ width: 7px;
250
+ }
251
+ ul.defaultmenu::-webkit-scrollbar-thumb {
252
+ border-radius: 4px;
253
+ background-color: rgba(0,0,0,.5);
254
+ box-shadow: 0 0 1px rgba(255,255,255,.5);
255
+ }
256
+ ul.defaultmenu::-webkit-scrollbar-track {
257
+ background-color: rgba(0,0,0,.1);
258
+ }
259
+ li {
260
+ cursor: pointer;
261
+ }
262
+ li.hilited {
263
+ background: lightblue;
264
+ }
265
+ li.disabled {
266
+ cursor: auto;
267
+ }
268
+ li.selected {
269
+ position: relative;
270
+ }
271
+ ul.hasSelected li {
272
+ padding-left: 1.4em;
273
+ }
274
+ li.selected:after {
275
+ content: ' ';
276
+ position: absolute;
277
+ left: 0.5em;
278
+ top: calc(50% - 0.11em);
279
+ transform: translateY(-50%) rotate(38deg);
280
+ display: block;
281
+ height: 0.786em;
282
+ width: 0.4286em;
283
+ border-bottom: 0.2em solid;
284
+ border-right: 0.2em solid;
285
+ box-sizing: border-box;
286
+ }
287
+ </style>
@@ -0,0 +1,45 @@
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
+ hilited?: number | undefined;
24
+ };
25
+ events: {
26
+ change: CustomEvent<any>;
27
+ } & {
28
+ [evt: string]: CustomEvent<any>;
29
+ };
30
+ slots: {
31
+ default: {
32
+ item: PopupMenuItem;
33
+ label: string;
34
+ hilited: boolean;
35
+ selected: boolean;
36
+ };
37
+ noresults: {};
38
+ };
39
+ };
40
+ export declare type PopupMenuProps = typeof __propDef.props;
41
+ export declare type PopupMenuEvents = typeof __propDef.events;
42
+ export declare type PopupMenuSlots = typeof __propDef.slots;
43
+ export default class PopupMenu extends SvelteComponentTyped<PopupMenuProps, PopupMenuEvents, PopupMenuSlots> {
44
+ }
45
+ export {};
@@ -1,7 +1,6 @@
1
- <script lang="ts">
2
- export let id: string|undefined = undefined
3
- export let arialive: 'off' | 'assertive' | 'polite' | undefined = undefined
4
- export let ariaatomic: boolean | 'false' | 'true' | undefined = undefined
1
+ <script >export let id = undefined;
2
+ export let arialive = undefined;
3
+ export let ariaatomic = undefined;
5
4
  </script>
6
5
 
7
6
  <style>
@@ -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 {};
@@ -0,0 +1,9 @@
1
+ export { default as CardLayout } from './CardLayout.svelte';
2
+ export { default as Card } from './Card.svelte';
3
+ export { default as CollapsingTable } from './CollapsingTable.svelte';
4
+ export { default as FocusLock } from './FocusLock.svelte';
5
+ export { default as ConditionalWrapper } from './ConditionalWrapper.svelte';
6
+ export { default as Modal } from './Modal.svelte';
7
+ export { default as MultiSelect } from './MultiSelect.svelte';
8
+ export { default as PopupMenu } from './PopupMenu.svelte';
9
+ export { default as ScreenReaderOnly } from './ScreenReaderOnly.svelte';
@@ -0,0 +1,9 @@
1
+ export { default as CardLayout } from './CardLayout.svelte';
2
+ export { default as Card } from './Card.svelte';
3
+ export { default as CollapsingTable } from './CollapsingTable.svelte';
4
+ export { default as FocusLock } from './FocusLock.svelte';
5
+ export { default as ConditionalWrapper } from './ConditionalWrapper.svelte';
6
+ export { default as Modal } from './Modal.svelte';
7
+ export { default as MultiSelect } from './MultiSelect.svelte';
8
+ export { default as PopupMenu } from './PopupMenu.svelte';
9
+ export { default as ScreenReaderOnly } from './ScreenReaderOnly.svelte';
package/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './components/index.js';
2
+ export * from './actions/index.js';
3
+ export * from './types/index.js';
4
+ export * from './util/index.js';
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './components/index.js';
2
+ export * from './actions/index.js';
3
+ export * from './types/index.js';
4
+ export * from './util/index.js';