@dosgato/dialog 1.0.8 → 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,25 +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>
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>
162
210
  <!-- svelte-ignore a11y-no-static-element-interactions -->
163
- {#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}
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}
164
212
  {/each}
165
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}
166
217
  {#if mounted && myRootItems?.length}
167
218
  <!-- svelte-ignore a11y-no-noninteractive-element-to-interactive-role -->
168
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}>
169
220
  {#each myRootItems as item, i (item.id)}
170
221
  <TreeNode
171
222
  {item}
172
- {headers}
223
+ headers={shownHeaders}
173
224
  {headerSizes}
174
225
  {nodeClass}
175
226
  posinset={i + 1}
@@ -218,6 +269,11 @@ $: myRootItems = $store?.rootItems?.filter(r => myRootItemIds.has(r.id)) ?? [];
218
269
  height: 100%;
219
270
  background-color: var(--tree-head-text, white);
220
271
  }
272
+ :global(.column-dropdown) {
273
+ display: flex;
274
+ gap: 0.5em;
275
+ justify-content: space-between;
276
+ }
221
277
  :global([data-eq~="650px"]) .tree-header {
222
278
  font-size: 0.8em;
223
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
  *
@@ -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.8",
4
+ "version": "1.1.0",
5
5
  "scripts": {
6
6
  "prepublishOnly": "svelte-package",
7
7
  "dev": "vite dev --force",