@dosgato/dialog 1.0.7 → 1.1.0

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,6 +1,8 @@
1
- <script>import { resize } from '@txstate-mws/svelte-components';
1
+ <script>import { Icon } from '..';
2
+ import { resize, PopupMenu } from '@txstate-mws/svelte-components';
2
3
  import { derivedStore, Store } from '@txstate-mws/svelte-store';
3
- import { afterUpdate, beforeUpdate, onDestroy, onMount, setContext } from 'svelte';
4
+ import { afterUpdate, beforeUpdate, onDestroy, onMount, setContext, tick } from 'svelte';
5
+ import dotsIcon from '@iconify-icons/ph/dots-three-outline-vertical-fill';
4
6
  import { isNotBlank } from 'txstate-utils';
5
7
  import LoadIcon from './LoadIcon.svelte';
6
8
  import TreeNode from './TreeNode.svelte';
@@ -12,6 +14,7 @@ export let filter = '';
12
14
  export let nodeClass = undefined;
13
15
  export let singleSelect = undefined;
14
16
  export let enableResize = false;
17
+ export let minColumnSize = 100;
15
18
  /**
16
19
  * this `itemType` prop is here for typescript only
17
20
  *
@@ -35,21 +38,52 @@ const { filteredRootItems, headerOverride } = store;
35
38
  let checkboxelement;
36
39
  const headerelements = [];
37
40
  const treeWidth = new Store({});
41
+ let numShownColumns = headers.length;
42
+ let numNeverHidden = headers.filter(h => h.neverHide).length;
43
+ // Need to keep track of which headers are shown or hidden and which is selected
44
+ let shownHeaders = headers;
45
+ let hiddenHeaders = [];
46
+ let selectedHeader = undefined;
47
+ function updateShownHeaders() {
48
+ numShownColumns = Math.min(headers.length, Math.floor(($treeWidth.clientWidth ?? 1024) / minColumnSize));
49
+ if (numShownColumns < headers.length) {
50
+ shownHeaders = [];
51
+ hiddenHeaders = [];
52
+ let available = (selectedHeader ? numShownColumns - 1 : numShownColumns) - numNeverHidden;
53
+ if (selectedHeader?.neverHide)
54
+ available++;
55
+ for (let i = 0; i < headers.length; i++) {
56
+ if (available > 0 || headers[i].neverHide || headers[i].id === selectedHeader?.id) {
57
+ shownHeaders.push(headers[i]);
58
+ if (headers[i].id !== selectedHeader?.id && !headers[i].neverHide)
59
+ available--;
60
+ }
61
+ else {
62
+ hiddenHeaders.push(headers[i]);
63
+ }
64
+ }
65
+ }
66
+ else {
67
+ shownHeaders = headers;
68
+ hiddenHeaders = [];
69
+ }
70
+ }
38
71
  function calcHeaderSizes() {
72
+ updateShownHeaders();
39
73
  const headerSizes = [];
40
74
  let totalFixed = checkboxelement?.offsetWidth ?? 0;
41
- for (let i = 0; i < headers.length; i++) {
42
- const header = headers[i];
75
+ for (let i = 0; i < shownHeaders.length; i++) {
76
+ const header = shownHeaders[i];
43
77
  if (header.fixed || $headerOverride[header.id]) {
44
- headerSizes[i] = $headerOverride[header.id] ?? header.fixed;
78
+ headerSizes[i] = $headerOverride[header.id] ?? (header.fixed ? (hiddenHeaders.length && i === shownHeaders.length - 1 ? `calc(${header.fixed} + 1em)` : header.fixed) : undefined);
45
79
  totalFixed += headerelements[i]?.offsetWidth ?? 0;
46
80
  }
47
81
  }
48
82
  const remainingWidth = ($treeWidth.clientWidth ?? 1024) - totalFixed;
49
- const growHeaders = headers.filter((h, i) => !h.fixed && !$headerOverride[h.id] && headerelements[i]?.offsetWidth);
83
+ const growHeaders = shownHeaders.filter((h, i) => !h.fixed && !$headerOverride[h.id]);
50
84
  const totalGrowShares = growHeaders.reduce((sum, h) => sum + (h.grow ?? 1), 0);
51
- for (let i = 0; i < headers.length; i++) {
52
- const header = headers[i];
85
+ for (let i = 0; i < shownHeaders.length; i++) {
86
+ const header = shownHeaders[i];
53
87
  if (!header.fixed && !$headerOverride[header.id] && headerelements[i]?.offsetWidth) {
54
88
  headerSizes[i] = `${remainingWidth * (header.grow ?? 1) / totalGrowShares}px`;
55
89
  }
@@ -151,24 +185,42 @@ afterUpdate(() => {
151
185
  });
152
186
  $: myRootItemIds = $store && $filteredRootItems;
153
187
  $: myRootItems = $store?.rootItems?.filter(r => myRootItemIds.has(r.id)) ?? [];
188
+ async function selectHeader(selected) {
189
+ selectedHeader = headers.find(h => h.id === selected.value);
190
+ updateShownHeaders();
191
+ headerSizes.set([]);
192
+ store.resetHeaderOverride();
193
+ await tick();
194
+ headerSizes.set(calcHeaderSizes());
195
+ }
154
196
  </script>
155
197
 
156
198
  <svelte:window on:mouseup={headerDragEnd} />
157
-
158
199
  <div class="tree-header" class:resizing={!!dragtargetid} use:resize={{ store: treeWidth }} aria-hidden="true" on:mouseup={headerDragEnd} on:touchend={headerDragEnd} on:mousemove={dragtargetid ? headerDrag : undefined} on:touchmove={dragtargetid ? headerDrag : undefined}>
159
200
  <div class="checkbox" bind:this={checkboxelement}>&nbsp;</div>
160
- {#each headers as header, i (header.label)}
161
- <div bind:this={headerelements[i]} id={header.id} class="tree-header-cell {header.id}" style:width={$headerOverride[header.id] ?? $headerSizes?.[i]} style:padding-left={i === 0 ? '1.4em' : undefined}>{header.label}{#if i === 0 && $store.loading}<LoadIcon />{/if}{#if i === 0 && isNotBlank(search)}&nbsp;(searching: {search}){/if}</div>
162
- {#if enableResize && i !== headers.length - 1}<div class="tree-separator {header.id}" on:mousedown={headerDragStart(header, i)} on:touchstart={headerDragStart(header, i)} on:dblclick={headerDragReset}>&nbsp;</div>{/if}
201
+ {#each shownHeaders as header, i (header.label)}
202
+ {@const hasDropdown = hiddenHeaders.length && i === shownHeaders.length - 1}
203
+ <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
204
+ <div bind:this={headerelements[i]} id={header.id} class="tree-header-cell {header.id}" class:column-dropdown={hasDropdown} style:width={$headerOverride[header.id] ?? $headerSizes?.[i]} style:padding-left={i === 0 ? '1.4em' : undefined} tabindex={hasDropdown ? 0 : undefined}>{header.label}{#if i === 0 && $store.loading}
205
+ <LoadIcon />{/if}{#if i === 0 && isNotBlank(search)}&nbsp;(searching: {search}){/if}
206
+ {#if hasDropdown}
207
+ <Icon icon={dotsIcon} inline hiddenLabel="Show more columns"/>
208
+ {/if}
209
+ </div>
210
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
211
+ {#if enableResize && i !== shownHeaders.length - 1}<div class="tree-separator {header.id}" on:mousedown={headerDragStart(header, i)} on:touchstart={headerDragStart(header, i)} on:dblclick={headerDragReset}>&nbsp;</div>{/if}
163
212
  {/each}
164
213
  </div>
214
+ {#if hiddenHeaders.length > 0}
215
+ <PopupMenu items={hiddenHeaders.map(h => ({ value: h.id, label: h.label }))} buttonelement={headerelements[shownHeaders.length-1]} on:change={e => {selectHeader(e.detail)}}/>
216
+ {/if}
165
217
  {#if mounted && myRootItems?.length}
166
218
  <!-- svelte-ignore a11y-no-noninteractive-element-to-interactive-role -->
167
219
  <ul bind:this={store.treeElement} role="tree" class:resizing={!!dragtargetid} on:mousemove={dragtargetid ? headerDrag : undefined} on:touchmove={dragtargetid ? headerDrag : undefined} on:mouseup={headerDragEnd} on:touchend={headerDragEnd} on:keyup={onKeyUp}>
168
220
  {#each myRootItems as item, i (item.id)}
169
221
  <TreeNode
170
222
  {item}
171
- {headers}
223
+ headers={shownHeaders}
172
224
  {headerSizes}
173
225
  {nodeClass}
174
226
  posinset={i + 1}
@@ -217,6 +269,11 @@ $: myRootItems = $store?.rootItems?.filter(r => myRootItemIds.has(r.id)) ?? [];
217
269
  height: 100%;
218
270
  background-color: var(--tree-head-text, white);
219
271
  }
272
+ :global(.column-dropdown) {
273
+ display: flex;
274
+ gap: 0.5em;
275
+ justify-content: space-between;
276
+ }
220
277
  :global([data-eq~="650px"]) .tree-header {
221
278
  font-size: 0.8em;
222
279
  }
@@ -10,6 +10,7 @@ declare class __sveltets_Render<T extends TreeItemFromDB> {
10
10
  nodeClass?: ((itm: T) => string) | undefined;
11
11
  singleSelect?: boolean | undefined;
12
12
  enableResize?: boolean | undefined;
13
+ minColumnSize?: number | undefined;
13
14
  /**
14
15
  * this `itemType` prop is here for typescript only
15
16
  *
@@ -41,7 +41,7 @@ function onKeyDown(e) {
41
41
  store.copy();
42
42
  }
43
43
  else if (e.key === 'v') {
44
- store.paste();
44
+ void store.paste();
45
45
  }
46
46
  }
47
47
  else if (e.key === 'Escape') {
@@ -77,7 +77,7 @@ function onKeyDown(e) {
77
77
  store.focus(child);
78
78
  }
79
79
  else {
80
- store.open(item);
80
+ void store.open(item);
81
81
  }
82
82
  }
83
83
  else if (e.key === 'ArrowLeft') {
@@ -158,7 +158,7 @@ function onClick(e) {
158
158
  if (item.open && wasFocused && !item.loading)
159
159
  store.close(item);
160
160
  else if (!item.open)
161
- store.open(item);
161
+ void store.open(item);
162
162
  }
163
163
  }
164
164
  function onCheckClick(e) {
@@ -207,15 +207,15 @@ function onDragOverAbove(e) {
207
207
  }
208
208
  let dragOver = 0;
209
209
  let dragOverAbove = 0;
210
- function onDrop(e) {
210
+ async function onDrop(e) {
211
211
  e.preventDefault();
212
212
  dragOver = 0;
213
- return store.drop(item, false, e.dataTransfer.dropEffect === 'copy');
213
+ return await store.drop(item, false, e.dataTransfer.dropEffect === 'copy');
214
214
  }
215
- function onDropAbove(e) {
215
+ async function onDropAbove(e) {
216
216
  e.preventDefault();
217
217
  dragOverAbove = 0;
218
- return store.drop(item, true, e.dataTransfer.dropEffect === 'copy');
218
+ return await store.drop(item, true, e.dataTransfer.dropEffect === 'copy');
219
219
  }
220
220
  function onDragEnter(e) {
221
221
  if (!dropZone)
@@ -244,7 +244,7 @@ function onDragLeaveAbove(e) {
244
244
  dragOverAbove--;
245
245
  }
246
246
  let display = $focused && $focused.id === item.id;
247
- onMount(async () => {
247
+ onMount(() => {
248
248
  if ($focused && $focused.id === item.id)
249
249
  nodeelement.scrollIntoView({ block: 'center' });
250
250
  nodeelement.addEventListener('lazy', () => { display = true; });
@@ -258,6 +258,7 @@ $: if ($dragging) {
258
258
  </script>
259
259
  <li role="presentation">
260
260
  {#if dropAbove}
261
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
261
262
  <div class="drop-above"
262
263
  class:dragOverAbove
263
264
  on:dragenter={onDragEnterAbove}
@@ -298,6 +299,7 @@ $: if ($dragging) {
298
299
  {#if display}
299
300
  <!-- keyboard users have modifier keys, they don't ever focus the checkbox -->
300
301
  <!-- svelte-ignore a11y-click-events-have-key-events -->
302
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
301
303
  <div class="checkbox" on:click={onCheckClick}>
302
304
  <Icon icon={isSelected ? checkboxOutline : checkboxBlankOutline } width="1.15em" inline />
303
305
  </div>
@@ -310,6 +312,7 @@ $: if ($dragging) {
310
312
  >
311
313
  {#if i === 0 && item.hasChildren}
312
314
  <!-- svelte-ignore a11y-click-events-have-key-events -->
315
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
313
316
  <span class="arrow" on:click={onClick}><Icon icon={item.open ? menuDown : menuRight} width="1.5em" inline /></span>
314
317
  {/if}
315
318
  <TreeCell {header} {item} />
@@ -51,6 +51,7 @@ export interface TreeHeader<T extends TreeItemFromDB> {
51
51
  render?: (item: TypedTreeItem<T>) => string;
52
52
  component?: SvelteComponent;
53
53
  class?: (item: TypedTreeItem<T>) => string | string[];
54
+ neverHide?: boolean;
54
55
  }
55
56
  export declare class TreeStore<T extends TreeItemFromDB> extends ActiveStore<ITreeStore<T>> {
56
57
  #private;
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": "1.0.7",
4
+ "version": "1.1.0",
5
5
  "scripts": {
6
6
  "prepublishOnly": "svelte-package",
7
7
  "dev": "vite dev --force",
@@ -21,7 +21,7 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@codemirror/lang-javascript": "^6.1.7",
24
- "@codemirror/lang-css": "^6.2.0",
24
+ "@codemirror/lang-css": "^6.2.1",
25
25
  "@codemirror/lang-html": "^6.4.3",
26
26
  "@iconify/svelte": "^3.0.0",
27
27
  "@iconify-icons/mdi": "^1.2.22",
@@ -35,8 +35,8 @@
35
35
  "@sveltejs/adapter-auto": "^2.0.0",
36
36
  "@sveltejs/kit": "^1.0.1",
37
37
  "@sveltejs/package": "^2.0.1",
38
- "eslint-config-standard-with-typescript": "^34.0.0",
39
- "eslint-plugin-svelte3": "^4.0.0",
38
+ "eslint-config-standard-with-typescript": "^39.0.0",
39
+ "eslint-plugin-svelte": "^2.0.0",
40
40
  "svelte-check": "^3.0.1",
41
41
  "svelte-preprocess": "^5.0.0",
42
42
  "svelte2tsx": "^0.6.0",