@txstate-mws/svelte-components 1.3.3 → 1.3.5

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.
@@ -88,8 +88,8 @@ const focusin = (e) => {
88
88
  setInitialFocus();
89
89
  }
90
90
  };
91
- const windowclick = (_) => {
92
- if (state === 'active') {
91
+ const windowclick = (e) => {
92
+ if (state === 'active' && e.target instanceof HTMLElement && e.target.isConnected) {
93
93
  returnfocusto = undefined;
94
94
  dispatch('escape');
95
95
  }
@@ -1,8 +1,8 @@
1
1
  <script>import { createEventDispatcher } from 'svelte';
2
- import { randomid, sleep } from 'txstate-utils';
2
+ import { randomid, Cache } from 'txstate-utils';
3
3
  import ScreenReaderOnly from './ScreenReaderOnly.svelte';
4
4
  import DefaultPopupMenu from './PopupMenu.svelte';
5
- import { debouncedPromise, modifierKey, selectionIsLeft } from '../util';
5
+ import { modifierKey, selectionIsLeft } from '../util';
6
6
  export let id = randomid();
7
7
  export let name;
8
8
  export let disabled = false;
@@ -16,6 +16,7 @@ export let placeholder = '';
16
16
  export let emptyText = undefined;
17
17
  export let PopupMenu = DefaultPopupMenu;
18
18
  let menushown;
19
+ let loading = false;
19
20
  let options = [];
20
21
  let hilitedpill;
21
22
  let inputvalue = '';
@@ -23,20 +24,26 @@ let popupvalue = undefined;
23
24
  let inputelement;
24
25
  const descriptionid = randomid();
25
26
  const dispatch = createEventDispatcher();
26
- $: debouncedGetOptions = debouncedPromise(getOptions, 100, []);
27
+ const optionsCache = new Cache(async (ipt) => {
28
+ return await getOptions(ipt);
29
+ }, { freshseconds: 5 });
27
30
  $: selectedSet = new Set(selected.map(s => s.value));
31
+ let optionsTimer;
28
32
  async function reactToInput(..._) {
29
- const saveval = inputvalue;
30
- const rawOptions = await debouncedGetOptions(saveval);
31
- if (inputvalue !== saveval)
32
- return; // ignore any results that are out of date
33
- options = rawOptions.filter(o => !selectedSet.has(o.value));
34
- await sleep(100);
35
- // needed to sleep a little bit or this will turn the menu on only to have popupmenu's click handler toggle it back off
36
- if (typeof document !== 'undefined' && inputelement === document.activeElement)
37
- menushown = true;
33
+ loading = true;
34
+ clearTimeout(optionsTimer);
35
+ setTimeout(async () => {
36
+ const saveval = inputvalue;
37
+ const rawOptions = await optionsCache.get(saveval);
38
+ if (inputvalue !== saveval)
39
+ return; // ignore any results that are out of date
40
+ options = rawOptions.filter(o => !selectedSet.has(o.value));
41
+ if (typeof document !== 'undefined' && inputelement === document.activeElement && (options.length || inputvalue?.length))
42
+ menushown = true;
43
+ loading = false;
44
+ }, 250);
38
45
  }
39
- $: reactToInput(inputvalue, debouncedGetOptions, selectedSet);
46
+ $: reactToInput(inputvalue, getOptions, selectedSet);
40
47
  $: availablemessage = options.filter(o => o.value).length + ' autocomplete choices available';
41
48
  function addSelection(e) {
42
49
  inputvalue = '';
@@ -83,8 +90,8 @@ function inputkeydown(e) {
83
90
  }
84
91
  }
85
92
  }
86
- function inputfocus() {
87
- reactToInput(true);
93
+ async function inputfocus() {
94
+ reactToInput();
88
95
  }
89
96
  let popuphilited;
90
97
  $: if (popuphilited != null)
@@ -127,7 +134,7 @@ $: reactToHilite(hilitedpill, id);
127
134
  </ScreenReaderOnly>
128
135
  <slot></slot>
129
136
  </fieldset>
130
- <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>
137
+ <svelte:component this={PopupMenu} bind:menushown bind:hilited={popuphilited} bind:value={popupvalue} align='bottomleft' {loading} {emptyText} {menuContainerClass} {menuClass} {menuItemClass} {menuItemHilitedClass} items={options} buttonelement={inputelement} on:change={addSelection}></svelte:component>
131
138
 
132
139
  <style>
133
140
  fieldset {
@@ -18,6 +18,7 @@ export let cover = false;
18
18
  export let showSelected = true;
19
19
  export let width = undefined;
20
20
  export let computedalign = new Store({ valign: 'bottom', halign: 'left' });
21
+ export let loading = false;
21
22
  export let usePortal = undefined;
22
23
  export let emptyText = undefined;
23
24
  export let value = undefined;
@@ -145,7 +146,8 @@ let blurTimer;
145
146
  function onbuttonclick(e) {
146
147
  e.preventDefault();
147
148
  cancelAnimationFrame(blurTimer);
148
- menushown = !menushown;
149
+ if (!loading)
150
+ menushown = !menushown;
149
151
  }
150
152
  async function onblur(e) {
151
153
  // tabindex=-1 on our menu elements means e.relatedTarget will be set
@@ -17,6 +17,7 @@ declare const __propDef: {
17
17
  showSelected?: boolean;
18
18
  width?: string | undefined;
19
19
  computedalign?: Store<GlueAlignStore>;
20
+ loading?: boolean;
20
21
  usePortal?: HTMLElement | true | undefined;
21
22
  emptyText?: string | undefined;
22
23
  value?: string | undefined;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@txstate-mws/svelte-components",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "Svelte components that are generically useful.",
5
5
  "dependencies": {
6
6
  "@txstate-mws/svelte-store": "^1.0.3",
7
7
  "tabbable": "^6.0.0",
8
- "txstate-utils": "^1.7.6"
8
+ "txstate-utils": "^1.7.12"
9
9
  },
10
10
  "devDependencies": {
11
11
  "@playwright/test": "^1.18.1",
package/util/util.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export declare function classes(...classNames: (string | undefined)[]): string;
2
2
  export declare function debounced(fn: Function, timeout: number): (...args: any[]) => void;
3
- export declare function debouncedPromise<T>(fn: (..._: any) => Promise<T> | T, timeout: number, defaultValue: T): (..._: any) => Promise<T>;
4
3
  export declare function modifierKey(e: any): any;
5
4
  export declare function selectionIsLeft(ele: HTMLInputElement | HTMLTextAreaElement): boolean;
package/util/util.js CHANGED
@@ -8,23 +8,6 @@ export function debounced(fn, timeout) {
8
8
  timer = setTimeout(() => fn(...args), timeout);
9
9
  };
10
10
  }
11
- export function debouncedPromise(fn, timeout, defaultValue) {
12
- let version = 0;
13
- return async (...args) => {
14
- const myversion = ++version;
15
- return await new Promise(resolve => {
16
- setTimeout(() => {
17
- if (version === myversion) {
18
- version = 0;
19
- resolve(fn(...args));
20
- }
21
- else {
22
- resolve(defaultValue);
23
- }
24
- }, timeout);
25
- });
26
- };
27
- }
28
11
  export function modifierKey(e) {
29
12
  return e.ctrlKey || e.altKey || e.metaKey || e.key === 'Insert';
30
13
  }