@txstate-mws/svelte-components 1.6.11 → 1.6.13

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,184 +1,177 @@
1
1
  <!-- @component
2
2
  [`CardLayout`](https://github.com/txstate-etc/svelte-components/blob/main/src/lib/components/CardLayout.svelte)
3
3
  -->
4
- <script lang="ts">import { onDestroy, tick, setContext, onMount } from 'svelte';
5
- import { writable } from 'svelte/store';
6
- import { resize, passActions } from '../actions';
7
- import { CARDLAYOUT } from '../types';
8
- export let maxwidth = 500;
9
- export let preserveorder = false;
10
- export let gutter = 10;
11
- export let className = '';
12
- export let use = [];
13
- const ssr = typeof navigator === 'undefined' || navigator.userAgent.includes('jsdom');
14
- const blocks = [];
15
- const gutterstore = writable(gutter);
16
- let defaultOrder = 0;
17
- setContext(CARDLAYOUT, {
18
- registerBlock: (block) => {
19
- blocks.push(block);
20
- hardrecalc();
21
- onDestroy(() => {
22
- blocks.splice(blocks.indexOf(block), 1);
23
- hardrecalc();
24
- });
25
- block.order = writable(defaultOrder++);
26
- block.linebreak = writable(false);
27
- const cols = savecolumns || Math.ceil(1024 / maxwidth);
28
- block.width = writable(`calc(${100.0 / cols}% - ${gutter * (cols - 1) / cols}px)`);
29
- return block;
4
+ <script lang="ts">
5
+ import { onDestroy, tick, setContext, onMount } from 'svelte'
6
+ import { writable } from 'svelte/store'
7
+ import { type ElementSize, resize, type HTMLActionEntry, passActions } from '../actions'
8
+ import { CARDLAYOUT } from '../types'
9
+ import type { Block } from '../types'
10
+ export let maxwidth = 500
11
+ export let preserveorder = false
12
+ export let gutter = 10
13
+ export let className = ''
14
+ export let use: HTMLActionEntry[] = []
15
+ const ssr = typeof navigator === 'undefined' || navigator.userAgent.includes('jsdom')
16
+ const blocks: Block[] = []
17
+ const gutterstore = writable(gutter)
18
+ let defaultOrder = 0
19
+ setContext(CARDLAYOUT, {
20
+ registerBlock: (block: Block) => {
21
+ blocks.push(block)
22
+ hardrecalc()
23
+ onDestroy(() => {
24
+ blocks.splice(blocks.indexOf(block), 1)
25
+ hardrecalc()
26
+ })
27
+ block.order = writable(defaultOrder)
28
+ defaultOrder += 1
29
+ block.linebreak = writable(false)
30
+ const cols = savecolumns || Math.ceil(1024 / maxwidth)
31
+ block.width = writable(`calc(${100.0 / cols}% - ${gutter * (cols - 1) / cols}px)`)
32
+ return block
30
33
  },
31
34
  recalculate: () => {
32
- hardrecalc();
35
+ hardrecalc()
33
36
  },
34
37
  gutter: gutterstore
35
- });
36
- $: gutterstore.set(gutter);
37
- let layoutelement;
38
- let cycle1 = 0;
39
- let cycle2 = 0;
40
- function detectcycle(w) {
38
+ })
39
+ $: gutterstore.set(gutter)
40
+ let layoutelement: HTMLElement
41
+ let cycle1 = 0
42
+ let cycle2 = 0
43
+ function detectcycle (w: number) {
41
44
  if (cycle1 === 0 && Math.abs(w - cycle2) > 5) {
42
- cycle1 = w;
45
+ cycle1 = w
46
+ } else if (cycle2 === 0 && Math.abs(w - cycle1) > 5) {
47
+ cycle2 = w
48
+ } else if (w === cycle1 || w === cycle2) {
49
+ return true
50
+ } else {
51
+ cycle1 = 0
52
+ cycle2 = 0
43
53
  }
44
- else if (cycle2 === 0 && Math.abs(w - cycle1) > 5) {
45
- cycle2 = w;
46
- }
47
- else if (w === cycle1 || w === cycle2) {
48
- return true;
49
- }
50
- else {
51
- cycle1 = 0;
52
- cycle2 = 0;
53
- }
54
- return false;
55
- }
56
- let savecolumns = 0;
57
- let optimal;
58
- let fullheight = 0;
59
- let hardrequired = false;
60
- async function recalculate(realw) {
61
- const columns = Math.ceil(realw / maxwidth);
62
- const guttereach = gutter * (columns - 1) / columns;
63
- const cycling = detectcycle(realw);
54
+ return false
55
+ }
56
+ let savecolumns = 0
57
+ let optimal: Block[][]
58
+ let fullheight = 0
59
+ let hardrequired = false
60
+ async function recalculate (realw: number) {
61
+ const columns = Math.ceil(realw / maxwidth)
62
+ const guttereach = gutter * (columns - 1) / columns
63
+ const cycling = detectcycle(realw)
64
64
  if (columns !== savecolumns) {
65
- for (const block of blocks)
66
- block.width.set(`calc(${100.0 / columns}% - ${guttereach}px`);
67
- await tick();
65
+ for (const block of blocks) block.width!.set(`calc(${100.0 / columns}% - ${guttereach}px)`)
66
+ await tick()
68
67
  }
69
68
  // collect all the card heights at this new column width
70
- for (const block of blocks)
71
- block.height = block.element?.offsetHeight ?? 200;
69
+ for (const block of blocks) block.height = block.element?.offsetHeight ?? 200
72
70
  if (columns !== savecolumns || hardrequired) { // only do real work if number of columns has changed, recalculate triggers on resize
73
- if (preserveorder) {
74
- optimal = [blocks];
75
- if (columns > 1) {
76
- const totalheight = blocks.reduce((totalheight, block) => totalheight + block.height + gutter, 0);
77
- const tallestblock = Math.max(...blocks.map(b => b.height));
78
- const minheight = Math.max(totalheight / columns, tallestblock);
79
- let shortestoverall = totalheight;
80
- // 2d packing problem is NP-hard so this is an O(n) heuristic
81
- // optimal configuration would be if each column is `minheight` so start there
82
- // then relax it in increments and see if the overall height shrinks
83
- const increment = Math.max(1, Math.floor(Math.min(minheight, tallestblock) * 0.05));
84
- for (let colmaxheight = minheight; colmaxheight < minheight + tallestblock + gutter; colmaxheight += increment) {
85
- let colheight = 0;
86
- let colidx = 0;
87
- let tallestcol = 0;
88
- let tallestcolidx = 0;
89
- const arrangement = [];
90
- for (const block of blocks) {
91
- if (colheight + block.height > colmaxheight && colidx < columns - 1) {
92
- if (colheight > tallestcol) {
93
- tallestcol = colheight;
94
- tallestcolidx = colidx;
95
- }
96
- colidx++;
97
- colheight = 0;
98
- }
99
- if (!arrangement[colidx])
100
- arrangement[colidx] = [];
101
- arrangement[colidx].push(block);
102
- colheight += block.height + gutter;
103
- }
104
- if (colheight > tallestcol) {
105
- tallestcol = colheight;
106
- tallestcolidx = colidx;
107
- }
108
- if (tallestcol < shortestoverall) {
109
- shortestoverall = tallestcol;
110
- optimal = arrangement;
111
- }
112
- // if leftmost column is tallest, increasing the allowable height can never make the overall height shorter
113
- if (tallestcolidx === 0)
114
- break;
71
+ if (preserveorder) {
72
+ optimal = [blocks]
73
+ if (columns > 1) {
74
+ const totalheight = blocks.reduce((totalheight, block) => totalheight + block.height! + gutter, 0)
75
+ const tallestblock = Math.max(...blocks.map(b => b.height!))
76
+ const minheight = Math.max(totalheight / columns, tallestblock)
77
+ let shortestoverall = totalheight
78
+ // 2d packing problem is NP-hard so this is an O(n) heuristic
79
+ // optimal configuration would be if each column is `minheight` so start there
80
+ // then relax it in increments and see if the overall height shrinks
81
+ const increment = Math.max(1, Math.floor(Math.min(minheight, tallestblock) * 0.05))
82
+ for (let colmaxheight = minheight; colmaxheight < minheight + tallestblock + gutter; colmaxheight += increment) {
83
+ let colheight = 0
84
+ let colidx = 0
85
+ let tallestcol = 0
86
+ let tallestcolidx = 0
87
+ const arrangement: Block[][] = []
88
+ for (const block of blocks) {
89
+ if (colheight + block.height! > colmaxheight && colidx < columns - 1) {
90
+ if (colheight > tallestcol) {
91
+ tallestcol = colheight
92
+ tallestcolidx = colidx
115
93
  }
94
+ colidx += 1
95
+ colheight = 0
96
+ }
97
+ arrangement[colidx] ??= []
98
+ arrangement[colidx].push(block)
99
+ colheight += block.height! + gutter
116
100
  }
117
- }
118
- else {
119
- optimal = [];
120
- const heights = Array.apply(null, Array(columns)).map((_) => 0); // initializes heights to an array of zeroes
121
- // begin sorting blocks into columns
122
- for (const block of blocks) {
123
- // find the column with the smallest current height
124
- const colidx = heights.reduce((acc, curr, curridx) => curr < heights[acc] ? curridx : acc, 0);
125
- // record the height we are adding to the chosen column
126
- heights[colidx] += block.height + gutter;
127
- // move the current card to the chosen column
128
- if (!optimal[colidx])
129
- optimal[colidx] = [];
130
- optimal[colidx].push(block);
101
+ if (colheight > tallestcol) {
102
+ tallestcol = colheight
103
+ tallestcolidx = colidx
131
104
  }
132
- }
133
- // we created the optimal arrangement, now move the block elements to
134
- // reflect it
135
- let order = 0;
136
- for (let i = 0; i < optimal.length; i++) {
137
- for (let j = 0; j < optimal[i].length; j++) {
138
- const block = optimal[i][j];
139
- block.order.set(order++);
140
- block.linebreak.set(j === optimal[i].length - 1 && i < optimal.length - 1);
105
+ if (tallestcol < shortestoverall) {
106
+ shortestoverall = tallestcol
107
+ optimal = arrangement
141
108
  }
109
+ // if leftmost column is tallest, increasing the allowable height can never make the overall height shorter
110
+ if (tallestcolidx === 0) break
111
+ }
142
112
  }
143
- savecolumns = columns;
144
- hardrequired = false;
113
+ } else {
114
+ optimal = []
115
+ const heights: number[] = [...Array(columns)].map((_: any) => 0) // initializes heights to an array of zeroes
116
+ // begin sorting blocks into columns
117
+ for (const block of blocks) {
118
+ // find the column with the smallest current height
119
+ const colidx = heights.reduce((acc, curr, curridx) => curr < heights[acc] ? curridx : acc, 0)
120
+ // record the height we are adding to the chosen column
121
+ heights[colidx] += block.height! + gutter
122
+ // move the current card to the chosen column
123
+ optimal[colidx] ??= []
124
+ optimal[colidx].push(block)
125
+ }
126
+ }
127
+ // we created the optimal arrangement, now move the block elements to
128
+ // reflect it
129
+ let order = 0
130
+ for (let i = 0; i < optimal.length; i++) {
131
+ for (let j = 0; j < optimal[i].length; j++) {
132
+ const block = optimal[i][j]
133
+ block.order!.set(order)
134
+ order += 1
135
+ block.linebreak!.set(j === optimal[i].length - 1 && i < optimal.length - 1)
136
+ }
137
+ }
138
+ savecolumns = columns
139
+ hardrequired = false
145
140
  }
146
141
  // re-adjust container height
147
- const saveheight = fullheight;
148
- fullheight = 0;
142
+ const saveheight = fullheight
143
+ fullheight = 0
149
144
  for (let i = 0; i < optimal.length; i++) {
150
- let top = 0;
151
- for (const block of optimal[i]) {
152
- top += block.height + gutter;
153
- }
154
- fullheight = Math.max(fullheight, top);
145
+ let top = 0
146
+ for (const block of optimal[i]) {
147
+ top += block.height! + gutter
148
+ }
149
+ fullheight = Math.max(fullheight, top)
155
150
  }
156
- if (cycling && saveheight < fullheight)
157
- fullheight = saveheight;
158
- }
159
- let savewidth = 0;
160
- let timer = 0;
161
- function triggerrecalc(width) {
162
- if (width === savewidth && !hardrequired)
163
- return;
164
- cancelAnimationFrame(timer);
151
+ if (cycling && saveheight < fullheight) fullheight = saveheight
152
+ }
153
+
154
+ let savewidth = 0
155
+ let timer = 0
156
+ function triggerrecalc (width: number) {
157
+ if (width === savewidth && !hardrequired) return
158
+ cancelAnimationFrame(timer)
165
159
  timer = requestAnimationFrame(() => {
166
- void recalculate(width);
167
- savewidth = width;
168
- });
169
- }
170
- function hardrecalc() {
171
- if (!layoutelement)
172
- return;
173
- hardrequired = true;
174
- cycle1 = 0;
175
- cycle2 = 0;
176
- triggerrecalc(layoutelement.clientWidth);
177
- }
178
- function onResize(e) {
179
- triggerrecalc(e.detail.clientWidth ?? 0);
180
- }
181
- onMount(() => { triggerrecalc(layoutelement.clientWidth); });
160
+ void recalculate(width)
161
+ savewidth = width
162
+ })
163
+ }
164
+ function hardrecalc () {
165
+ if (!layoutelement) return
166
+ hardrequired = true
167
+ cycle1 = 0
168
+ cycle2 = 0
169
+ triggerrecalc(layoutelement.clientWidth)
170
+ }
171
+ function onResize (e: UIEvent & { currentTarget: any, detail: ElementSize }) {
172
+ triggerrecalc(e.detail.clientWidth ?? 0)
173
+ }
174
+ onMount(() => { triggerrecalc(layoutelement.clientWidth) })
182
175
  </script>
183
176
 
184
177
  <ul class="cardlayout {className}" class:ssr style:height={ssr ? undefined : `${fullheight + gutter}px`} bind:this={layoutelement} use:resize on:resize={onResize} use:passActions={use}>
@@ -5,78 +5,80 @@ This component is meant to help make tables responsive by eliminating columns (f
5
5
  When there are hidden columns, the last column header becomes a dropdown button allowing the selection of another column
6
6
  to be displayed instead.
7
7
  -->
8
- <script lang="ts">import { Store } from '@txstate-mws/svelte-store';
9
- import { derived } from 'svelte/store';
10
- import { classes } from '../util';
11
- import DefaultPopupMenu from './PopupMenu.svelte';
12
- import ScreenReaderOnly from './ScreenReaderOnly.svelte';
13
- export let tableClass = '';
14
- export let bodyRowClass = '';
15
- export let bodyCellClass = '';
16
- export let headerRowClass = '';
17
- export let headerCellClass = '';
18
- export let menuContainerClass = '';
19
- export let menuClass = '';
20
- export let menuItemClass = '';
21
- export let menuItemHilitedClass = '';
22
- export let items;
23
- export let config = undefined;
24
- export let defaultCellWidth = 150;
25
- export let PopupMenu = DefaultPopupMenu;
26
- export let slots = $$slots;
27
- export let stickyheader = false;
28
- let columns;
29
- let width = 320;
30
- let selected;
31
- let identifyingkeys = [];
32
- $: firstrow = items?.[0] ?? {};
33
- $: columns = config ?? Object.keys(firstrow).map(key => ({ key }));
34
- function reactToColumns(columns) {
8
+ <script lang="ts">
9
+ import { Store } from '@txstate-mws/svelte-store'
10
+ import { derived } from 'svelte/store'
11
+ import type { CollapsingTableColumn, PopupMenuItem } from '../types'
12
+ import { classes } from '../util'
13
+ import DefaultPopupMenu from './PopupMenu.svelte'
14
+ import ScreenReaderOnly from './ScreenReaderOnly.svelte'
15
+
16
+ export let tableClass = ''
17
+ export let bodyRowClass = ''
18
+ export let bodyCellClass = ''
19
+ export let headerRowClass = ''
20
+ export let headerCellClass = ''
21
+ export let menuContainerClass = ''
22
+ export let menuClass = ''
23
+ export let menuItemClass = ''
24
+ export let menuItemHilitedClass = ''
25
+ export let items: any[]
26
+ export let config: CollapsingTableColumn[] | undefined = undefined
27
+ export let defaultCellWidth = 150
28
+ export let PopupMenu = DefaultPopupMenu
29
+ export let slots: Record<string, boolean> = $$slots
30
+ export let stickyheader = false
31
+ let columns: CollapsingTableColumn[]
32
+ let width = 320
33
+ let selected: PopupMenuItem | undefined
34
+ let identifyingkeys: string[] = []
35
+ $: firstrow = items?.[0] ?? {}
36
+ $: columns = config ?? Object.keys(firstrow as object).map(key => ({ key }))
37
+ function reactToColumns (columns: CollapsingTableColumn[]) {
35
38
  if (!columns.some(c => c.neverhide)) {
36
- columns[0].neverhide = true;
39
+ columns[0].neverhide = true
37
40
  }
38
41
  const sortedcolumns = columns // stable sort
39
- .map((c, i) => ({ col: c, idx: i }))
40
- .sort((a, b) => ((a.col.neverhide ? 0 : 1) - (b.col.neverhide ? 0 : 1)) || a.idx - b.idx)
41
- .map(o => o.col);
42
+ .map((c, i) => ({ col: c, idx: i }))
43
+ .sort((a, b) => ((a.col.neverhide ? 0 : 1) - (b.col.neverhide ? 0 : 1)) || a.idx - b.idx)
44
+ .map(o => o.col)
42
45
  if (!selected) {
43
- const firstcol = sortedcolumns.find(c => !c.neverhide);
44
- selected = firstcol ? { value: firstcol.key } : undefined;
46
+ const firstcol = sortedcolumns.find(c => !c.neverhide)
47
+ selected = firstcol ? { value: firstcol.key } : undefined
45
48
  }
46
- identifyingkeys = sortedcolumns.filter(c => c.neverhide).map(c => c.key);
47
- return sortedcolumns;
48
- }
49
- $: sortedcolumns = reactToColumns(columns);
50
- $: itemkeys = items.map(item => item.id ?? item._id ?? identifyingkeys.map(k => item[k]).join('.'));
51
- const state = new Store({
52
- keepcolumns: [],
53
- hiddencolumns: [],
54
- dropdowncolumn: undefined
55
- });
56
- const menuitems = derived(state, obj => obj.hiddencolumns.map(c => ({ value: c.key, label: c.title })));
57
- function react(..._) {
58
- const selectedcol = sortedcolumns.find(c => c.key === selected?.value) ?? columns[1];
59
- const keepcolumns = [];
60
- const hiddencolumns = [];
61
- let used = sortedcolumns.filter(c => c.neverhide || c === selectedcol).reduce((sum, col) => sum + (col.width ?? defaultCellWidth), 0);
49
+ identifyingkeys = sortedcolumns.filter(c => c.neverhide).map(c => c.key)
50
+ return sortedcolumns
51
+ }
52
+ $: sortedcolumns = reactToColumns(columns)
53
+
54
+ $: itemkeys = items.map(item => item.id ?? item._id ?? identifyingkeys.map(k => item[k]).join('.'))
55
+
56
+ const state = new Store({
57
+ keepcolumns: [] as (CollapsingTableColumn & { widthPercent?: number })[],
58
+ hiddencolumns: [] as CollapsingTableColumn[],
59
+ dropdowncolumn: undefined as (CollapsingTableColumn & { widthPercent?: number }) | undefined
60
+ })
61
+ const menuitems = derived(state, obj => obj.hiddencolumns.map(c => ({ value: c.key, label: c.title })))
62
+ function react (..._: any[]) {
63
+ const selectedcol = sortedcolumns.find(c => c.key === selected?.value) ?? columns[1]
64
+ const keepcolumns: CollapsingTableColumn[] = []
65
+ const hiddencolumns: CollapsingTableColumn[] = []
66
+ let used = sortedcolumns.filter(c => c.neverhide || c === selectedcol).reduce((sum, col) => sum + (col.width ?? defaultCellWidth), 0)
62
67
  for (const col of sortedcolumns) {
63
- if (col.neverhide || col === selectedcol || used + (col.width ?? defaultCellWidth) < width) {
64
- keepcolumns.push(col);
65
- if (col !== selectedcol && !col.neverhide)
66
- used += col.width ?? defaultCellWidth;
67
- }
68
- else
69
- hiddencolumns.push(col);
68
+ if (col.neverhide || col === selectedcol || used + (col.width ?? defaultCellWidth) < width) {
69
+ keepcolumns.push(col)
70
+ if (col !== selectedcol && !col.neverhide) used += col.width ?? defaultCellWidth
71
+ } else hiddencolumns.push(col)
70
72
  }
71
73
  for (const col of keepcolumns) {
72
- col.widthPercent = 100 * (col.width ?? defaultCellWidth) / used;
74
+ (col as any).widthPercent = 100 * (col.width ?? defaultCellWidth) / used
73
75
  }
74
- const dropdowncolumn = hiddencolumns.length ? keepcolumns[keepcolumns.length - 1] : undefined;
75
- state.set({ keepcolumns, hiddencolumns, dropdowncolumn });
76
- }
77
- $: react(width, selected);
78
- const headers = [];
79
- $: menubuttonelement = headers[$state.keepcolumns.length - 1];
76
+ const dropdowncolumn = hiddencolumns.length ? keepcolumns[keepcolumns.length - 1] : undefined
77
+ state.set({ keepcolumns, hiddencolumns, dropdowncolumn })
78
+ }
79
+ $: react(width, selected)
80
+ const headers: HTMLElement[] = []
81
+ $: menubuttonelement = headers[$state.keepcolumns.length - 1]
80
82
  </script>
81
83
 
82
84
  <div bind:clientWidth={width}>
@@ -22,12 +22,13 @@ With this component, you can de-duplicate like this:
22
22
  Any props you provide (except the ConditionalWrapper props like `condition`) will pass through
23
23
  to the wrapper element/component, if it gets inserted.
24
24
  -->
25
- <script lang="ts">export let condition;
26
- export let component = undefined;
27
- export let a = false;
28
- export let span = false;
29
- export let element = undefined;
30
- $: passthrough = (({ condition, component, a, span, element, ...passthrough }) => passthrough)($$props);
25
+ <script lang="ts">
26
+ export let condition: boolean | undefined
27
+ export let component: any = undefined
28
+ export let a = false
29
+ export let span = false
30
+ export let element: HTMLElement | undefined = undefined
31
+ $: passthrough = (({ condition, component, a, span, element, ...passthrough }) => passthrough)($$props)
31
32
  </script>
32
33
 
33
34
  {#if condition || (typeof condition === 'undefined' && component)}
@@ -35,7 +36,6 @@ $: passthrough = (({ condition, component, a, span, element, ...passthrough }) =
35
36
  <svelte:component this={component} bind:element {...passthrough}><slot></slot></svelte:component>
36
37
  {:else}
37
38
  {#if a}
38
- <!-- svelte-ignore a11y-missing-attribute -->
39
39
  <a bind:this={element} {...passthrough}><slot></slot></a>
40
40
  {:else if span}
41
41
  <span bind:this={element} {...passthrough}><slot></slot></span>
@@ -3,7 +3,7 @@ declare const __propDef: {
3
3
  props: {
4
4
  [x: string]: any;
5
5
  condition: boolean | undefined;
6
- component?: any | undefined;
6
+ component?: any;
7
7
  a?: boolean | undefined;
8
8
  span?: boolean | undefined;
9
9
  element?: HTMLElement | undefined;