@dosgato/dialog 0.0.55 → 0.0.57

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,5 +1,4 @@
1
1
  <script>import { onMount } from 'svelte';
2
- import { isBlank } from 'txstate-utils';
3
2
  import { getDescribedBy } from './';
4
3
  import FieldStandard from './FieldStandard.svelte';
5
4
  let className = '';
@@ -17,6 +17,7 @@ interface ITabStore extends ElementSize {
17
17
  visited: Record<string, boolean>;
18
18
  tabids: Record<string, string>;
19
19
  panelids: Record<string, string>;
20
+ accordionOnMobile: boolean;
20
21
  }
21
22
  export declare class TabStore extends Store<ITabStore> {
22
23
  initialTab?: string | undefined;
package/dist/TabStore.js CHANGED
@@ -19,7 +19,8 @@ export class TabStore extends Store {
19
19
  requireNext: false,
20
20
  tabids: tabs.reduce((acc, curr) => ({ ...acc, [curr.name]: randomid() }), {}),
21
21
  panelids: tabs.reduce((acc, curr) => ({ ...acc, [curr.name]: randomid() }), {}),
22
- clientWidth: 1024
22
+ clientWidth: 1024,
23
+ accordionOnMobile: true
23
24
  }));
24
25
  this.initialTab = initialTab;
25
26
  }
@@ -49,7 +50,7 @@ export class TabStore extends Store {
49
50
  return derivedStore(this, v => v.panelids[v.tabs[v.current].name]);
50
51
  }
51
52
  accordion() {
52
- return derivedStore(this, v => v.clientWidth && v.clientWidth < 500);
53
+ return derivedStore(this, v => v.accordionOnMobile && v.clientWidth && v.clientWidth < 500);
53
54
  }
54
55
  left() {
55
56
  this.update(v => ({ ...v, current: Math.max(0, v.current - 1) }));
package/dist/Tabs.svelte CHANGED
@@ -10,7 +10,7 @@ export let active = undefined;
10
10
  export let store = new TabStore(tabs, active);
11
11
  export let disableDialogControl = false;
12
12
  export let accordionOnMobile = true;
13
- $: store.update(v => ({ ...v, tabs }));
13
+ $: store.update(v => ({ ...v, tabs, accordionOnMobile }));
14
14
  const activeStore = new Store({});
15
15
  const tabelements = [];
16
16
  let activeelement;
@@ -30,7 +30,7 @@ const accordion = store.accordion();
30
30
  $: cols = Math.min(Math.floor(($store.clientWidth ?? 1024) / 90), $store.tabs.length);
31
31
  $: scalefactor = Math.min(roundTo(($store.clientWidth ?? 1024) / (cols * 130), 4), 1);
32
32
  $: wrapping = cols !== $store.tabs.length;
33
- $: dialogContext.hasTabs = !$accordion || !accordionOnMobile;
33
+ $: dialogContext.hasTabs = !$accordion;
34
34
  function onClick(idx) {
35
35
  return () => store.activate(idx);
36
36
  }
@@ -87,7 +87,7 @@ onMount(reactToCurrent);
87
87
  </script>
88
88
 
89
89
  {#if $store.tabs.length > 1}
90
- {#if !$accordion || !accordionOnMobile}
90
+ {#if !$accordion}
91
91
  <!-- svelte-ignore a11y-no-noninteractive-element-to-interactive-role -->
92
92
  <ul use:resize={{ store }} class="tabs-buttons" role="tablist">
93
93
  {#each $store.tabs as tab, idx (tab.name)}
@@ -1,12 +1,13 @@
1
1
  <script>import { resize } from '@txstate-mws/svelte-components';
2
2
  import { derivedStore, Store } from '@txstate-mws/svelte-store';
3
3
  import { afterUpdate, beforeUpdate, onDestroy, onMount, setContext } from 'svelte';
4
- import { toArray } from 'txstate-utils';
4
+ import { isNotBlank } from 'txstate-utils';
5
5
  import LoadIcon from './LoadIcon.svelte';
6
6
  import TreeNode from './TreeNode.svelte';
7
- import { getHashId, TreeStore, TREE_STORE_CONTEXT } from './treestore';
7
+ import { getHashId, transformSearchable, TreeStore, TREE_STORE_CONTEXT } from './treestore';
8
8
  export let headers;
9
9
  export let searchable = undefined;
10
+ export let filter = '';
10
11
  export let nodeClass = undefined;
11
12
  export let singleSelect = undefined;
12
13
  export let enableResize = false;
@@ -24,8 +25,10 @@ export let moveHandler = undefined;
24
25
  export let copyHandler = undefined;
25
26
  export let dropEffect = undefined;
26
27
  export let store = new TreeStore(fetchChildren, { copyHandler, dragEligible, dropEffect, moveHandler });
28
+ if (searchable)
29
+ store.searchableFn = transformSearchable(searchable);
27
30
  setContext(TREE_STORE_CONTEXT, store);
28
- const { rootItems, headerOverride } = store;
31
+ const { filteredRootItems, headerOverride } = store;
29
32
  let checkboxelement;
30
33
  const headerelements = [];
31
34
  const treeWidth = new Store({});
@@ -57,14 +60,14 @@ function onDragEnd() {
57
60
  }
58
61
  let search = '';
59
62
  let searchTimer = 0;
60
- $: searchableFn = searchable == null ? undefined : (typeof searchable === 'function' ? (itm) => toArray(searchable(itm)) : (itm) => [itm[searchable]]);
63
+ $: store.filter(filter);
61
64
  function onKeyUp(e) {
62
- if (!searchableFn)
65
+ if (!store.searchableFn)
63
66
  return;
64
67
  if (e.key.length === 1) {
65
- search += e.key;
68
+ search += e.key.toLocaleLowerCase();
66
69
  const searchItems = $store.focused?.parent ? $store.focused.parent.children : $store.rootItems;
67
- const newFocus = searchItems?.find(itm => searchableFn(itm).some(str => str.toLocaleLowerCase().startsWith(search.toLocaleLowerCase())));
70
+ const newFocus = searchItems?.find(itm => store.searchableFn(itm).some(str => str.toLocaleLowerCase().startsWith(search)));
68
71
  if (newFocus)
69
72
  store.focus(newFocus);
70
73
  clearTimeout(searchTimer);
@@ -150,24 +153,24 @@ afterUpdate(() => {
150
153
  <div class="tree-header" class:resizing={!!dragheaderid} use:resize={{ store: treeWidth }} aria-hidden="true" on:mouseup={headerDragEnd} on:touchend={headerDragEnd} on:mousemove={dragheaderid ? headerDrag : undefined} on:touchmove={dragheaderid ? headerDrag : undefined}>
151
154
  <div class="checkbox" bind:this={checkboxelement}>&nbsp;</div>
152
155
  {#each headers as header, i (header.label)}
153
- <div bind:this={headerelements[i]} id={header.id} class="tree-header-cell {header.id}" style:width={$headerOverride[header.id] ?? $headerSizes?.[i]}>{header.label}{#if i === 0 && $store.loading}<LoadIcon />{/if}{#if i === 0 && search}&nbsp;(searching: {search}){/if}</div>
156
+ <div bind:this={headerelements[i]} id={header.id} class="tree-header-cell {header.id}" style:width={$headerOverride[header.id] ?? $headerSizes?.[i]}>{header.label}{#if i === 0 && $store.loading}<LoadIcon />{/if}{#if i === 0 && isNotBlank(search)}&nbsp;(searching: {search}){/if}</div>
154
157
  {#if enableResize && i !== headers.length - 1}<div class="tree-separator" on:mousedown={headerDragStart(header, i)} on:touchstart={headerDragStart(header, i)} on:dblclick={headerDragReset}>&nbsp;</div>{/if}
155
158
  {/each}
156
159
  </div>
157
- {#if mounted && $rootItems?.length}
160
+ {#if mounted && $filteredRootItems?.length}
158
161
  <!-- svelte-ignore a11y-no-noninteractive-element-to-interactive-role -->
159
162
  <ul bind:this={store.treeElement} role="tree" class:resizing={!!dragheaderid} on:mousemove={dragheaderid ? headerDrag : undefined} on:touchmove={dragheaderid ? headerDrag : undefined} on:mouseup={headerDragEnd} on:touchend={headerDragEnd} on:keyup={onKeyUp}>
160
- {#each $rootItems as item, i (item.id)}
163
+ {#each $filteredRootItems as item, i (item.id)}
161
164
  <TreeNode
162
165
  {item}
163
166
  {headers}
164
167
  {headerSizes}
165
168
  {nodeClass}
166
169
  posinset={i + 1}
167
- setsize={$rootItems.length}
170
+ setsize={$filteredRootItems.length}
168
171
  level={item.level}
169
- prev={$rootItems[i - 1]}
170
- next={$rootItems[i + 1]}
172
+ prev={$filteredRootItems[i - 1]}
173
+ next={$filteredRootItems[i + 1]}
171
174
  on:choose
172
175
  on:deselect
173
176
  />
@@ -1,10 +1,11 @@
1
1
  import { SvelteComponentTyped } from "svelte";
2
2
  import { TreeStore } from './treestore';
3
- import type { DragEligibleFn, CopyHandlerFn, DropEffectFn, FetchChildrenFn, MoveHandlerFn, TreeHeader, TreeItemFromDB } from './treestore';
3
+ import type { DragEligibleFn, CopyHandlerFn, DropEffectFn, FetchChildrenFn, MoveHandlerFn, TreeHeader, TreeItemFromDB, SearchableType } from './treestore';
4
4
  declare class __sveltets_Render<T extends TreeItemFromDB> {
5
5
  props(): {
6
6
  headers: TreeHeader<T>[];
7
- searchable?: keyof T | ((item: T) => string | string[]) | undefined;
7
+ searchable?: SearchableType<T>;
8
+ filter?: string | undefined;
8
9
  nodeClass?: ((itm: T) => string) | undefined;
9
10
  singleSelect?: boolean | undefined;
10
11
  enableResize?: boolean | undefined;
@@ -18,6 +18,7 @@ export interface ITreeStore<T extends TreeItemFromDB> {
18
18
  loading?: boolean;
19
19
  rootItems?: TypedTreeItem<T>[];
20
20
  itemsById: Record<string, TypedTreeItem<T> | undefined>;
21
+ filter?: string;
21
22
  focused?: TypedTreeItem<T>;
22
23
  selected: Map<string, TypedTreeItem<T>>;
23
24
  selectedItems: TypedTreeItem<T>[];
@@ -33,6 +34,7 @@ export type DragEligibleFn<T extends TreeItemFromDB> = (selectedItems: TypedTree
33
34
  export type DropEffectFn<T extends TreeItemFromDB> = (selectedItems: TypedTreeItem<T>[], dropTarget: TypedTreeItem<T>, above: boolean, userWantsCopy: boolean) => 'move' | 'copy' | 'none';
34
35
  export type MoveHandlerFn<T extends TreeItemFromDB> = (selectedItems: TypedTreeItem<T>[], dropTarget: TypedTreeItem<T>, above: boolean) => boolean | Promise<boolean>;
35
36
  export type CopyHandlerFn<T extends TreeItemFromDB> = (selectedItems: TypedTreeItem<T>[], dropTarget: TypedTreeItem<T>, above: boolean, userWantsRecursive: boolean | undefined) => boolean | Promise<boolean>;
37
+ export type SearchableFn<T extends TreeItemFromDB> = (item: TypedTreeItem<T>) => string[];
36
38
  export interface TreeHeader<T extends TreeItemFromDB> {
37
39
  id: string;
38
40
  label: string;
@@ -48,7 +50,9 @@ export declare class TreeStore<T extends TreeItemFromDB> extends ActiveStore<ITr
48
50
  #private;
49
51
  fetchChildren: FetchChildrenFn<T>;
50
52
  treeElement?: HTMLElement;
51
- rootItems: import("svelte/store").Readable<any>;
53
+ rootItems: import("@txstate-mws/svelte-store").DerivedStore<TypedTreeItem<T>[] | undefined, ITreeStore<T>>;
54
+ filterTerm: import("@txstate-mws/svelte-store").DerivedStore<string | undefined, ITreeStore<T>>;
55
+ filteredRootItems: import("svelte/store").Readable<TypedTreeItem<T>[] | undefined>;
52
56
  draggable: import("@txstate-mws/svelte-store").DerivedStore<boolean, ITreeStore<T>>;
53
57
  dragging: import("@txstate-mws/svelte-store").DerivedStore<boolean, ITreeStore<T>>;
54
58
  selectedUndraggable: import("@txstate-mws/svelte-store").DerivedStore<boolean | undefined, ITreeStore<T>>;
@@ -60,13 +64,15 @@ export declare class TreeStore<T extends TreeItemFromDB> extends ActiveStore<ITr
60
64
  copyHandler?: CopyHandlerFn<T>;
61
65
  dragEligibleHandler?: DragEligibleFn<T>;
62
66
  dropEffectHandler?: DropEffectFn<T>;
67
+ searchableFn?: SearchableFn<T>;
63
68
  singleSelect?: boolean;
64
69
  private refreshPromise?;
65
- constructor(fetchChildren: FetchChildrenFn<T>, { moveHandler, copyHandler, dragEligible, dropEffect, singleSelect }?: {
70
+ constructor(fetchChildren: FetchChildrenFn<T>, { moveHandler, copyHandler, dragEligible, dropEffect, searchableFn, singleSelect }?: {
66
71
  moveHandler?: MoveHandlerFn<T>;
67
72
  copyHandler?: CopyHandlerFn<T>;
68
73
  dragEligible?: DragEligibleFn<T>;
69
74
  dropEffect?: DropEffectFn<T>;
75
+ searchableFn?: SearchableFn<T>;
70
76
  singleSelect?: boolean;
71
77
  });
72
78
  visit(item: TypedTreeItem<T>, cb: (item: TypedTreeItem<T>) => Promise<void>): Promise<void>;
@@ -77,6 +83,7 @@ export declare class TreeStore<T extends TreeItemFromDB> extends ActiveStore<ITr
77
83
  trigger(): void;
78
84
  fetch(item?: TypedTreeItem<T>): Promise<TypedTreeItem<T>[]>;
79
85
  refresh(item?: TypedTreeItem<T>, skipNotify?: boolean): Promise<void>;
86
+ filter(term: string | undefined, notify?: boolean): void;
80
87
  focus(item: TypedTreeItem<T> | undefined, notify?: boolean): void;
81
88
  select(item: TypedTreeItem<T>, { clear, notify, toggle }: {
82
89
  clear?: boolean | undefined;
@@ -118,3 +125,5 @@ export declare class TreeStore<T extends TreeItemFromDB> extends ActiveStore<ITr
118
125
  export declare const hashedIds: Record<string, string>;
119
126
  export declare function getHashId(str: string): string;
120
127
  export declare const lazyObserver: IntersectionObserver | undefined;
128
+ export type SearchableType<T> = keyof T | (keyof T)[] | ((item: T) => string | string[]) | undefined;
129
+ export declare function transformSearchable<T>(searchable: SearchableType<T>): undefined | ((itm: T) => string[]);
@@ -1,12 +1,27 @@
1
1
  import { ActiveStore, derivedStore } from '@txstate-mws/svelte-store';
2
2
  import { derived } from 'svelte/store';
3
- import { hashid, keyby } from 'txstate-utils';
3
+ import { hashid, isBlank, keyby, toArray } from 'txstate-utils';
4
4
  export const TREE_STORE_CONTEXT = {};
5
- const rootItems = v => v.rootItems;
6
5
  export class TreeStore extends ActiveStore {
7
6
  fetchChildren;
8
7
  treeElement;
9
- rootItems = derived(this, rootItems);
8
+ rootItems = derivedStore(this, 'rootItems');
9
+ filterTerm = derivedStore(this, 'filter');
10
+ filteredRootItems = derived([this.rootItems, this.filterTerm], ([rootItems, filter]) => {
11
+ if (!this.searchableFn || !rootItems?.length || isBlank(filter))
12
+ return this.value.rootItems;
13
+ const ret = [];
14
+ for (const itm of this.value.rootItems ?? []) {
15
+ let found = false;
16
+ for (const val of this.searchableFn(itm)) {
17
+ if (val.toLocaleLowerCase().startsWith(filter))
18
+ found = true;
19
+ }
20
+ if (found)
21
+ ret.push(itm);
22
+ }
23
+ return ret;
24
+ });
10
25
  draggable = derivedStore(this, v => v.draggable && !v.loading);
11
26
  dragging = derivedStore(this, 'dragging');
12
27
  selectedUndraggable = derivedStore(this, 'selectedUndraggable');
@@ -18,15 +33,17 @@ export class TreeStore extends ActiveStore {
18
33
  copyHandler;
19
34
  dragEligibleHandler;
20
35
  dropEffectHandler;
36
+ searchableFn;
21
37
  singleSelect;
22
38
  refreshPromise;
23
- constructor(fetchChildren, { moveHandler, copyHandler, dragEligible, dropEffect, singleSelect } = {}) {
39
+ constructor(fetchChildren, { moveHandler, copyHandler, dragEligible, dropEffect, searchableFn, singleSelect } = {}) {
24
40
  super({ itemsById: {}, selected: new Map(), selectedItems: [], copied: new Map(), dragging: false, draggable: !!moveHandler, headerWidthOverrides: {} });
25
41
  this.fetchChildren = fetchChildren;
26
42
  this.moveHandler = moveHandler;
27
43
  this.copyHandler = copyHandler;
28
44
  this.dragEligibleHandler = dragEligible;
29
45
  this.dropEffectHandler = dropEffect;
46
+ this.searchableFn = searchableFn;
30
47
  this.singleSelect = singleSelect;
31
48
  }
32
49
  async visit(item, cb) {
@@ -125,6 +142,11 @@ export class TreeStore extends ActiveStore {
125
142
  await this.refreshPromise;
126
143
  this.refreshPromise = undefined;
127
144
  }
145
+ filter(term, notify = true) {
146
+ this.value.filter = term?.toLocaleLowerCase();
147
+ if (notify)
148
+ this.trigger();
149
+ }
128
150
  focus(item, notify = true) {
129
151
  this.value.focused = item;
130
152
  if (notify)
@@ -357,3 +379,13 @@ export const lazyObserver = typeof IntersectionObserver !== 'undefined'
357
379
  }
358
380
  }, { rootMargin: '500px' })
359
381
  : undefined;
382
+ export function transformSearchable(searchable) {
383
+ console.log('transformSearchable', searchable);
384
+ return searchable == null
385
+ ? undefined
386
+ : (typeof searchable === 'function'
387
+ ? (itm) => toArray(searchable(itm))
388
+ : (Array.isArray(searchable)
389
+ ? (itm) => searchable.map(k => itm[k])
390
+ : (itm) => [itm[searchable]]));
391
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dosgato/dialog",
3
3
  "description": "A component library for building forms that edit a JSON document.",
4
- "version": "0.0.55",
4
+ "version": "0.0.57",
5
5
  "scripts": {
6
6
  "prepublishOnly": "svelte-package",
7
7
  "dev": "vite dev --force",