@axel669/zephyr 0.5.5 → 1.0.0-beta.1

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.
Files changed (89) hide show
  1. package/package.json +48 -42
  2. package/readme.md +0 -44
  3. package/src/changed.svelte.js +23 -0
  4. package/src/component/adorn.svelte +9 -0
  5. package/src/component/alert.svelte +32 -0
  6. package/src/component/application.svelte +17 -0
  7. package/src/component/async-button.svelte +53 -0
  8. package/src/component/avatar.svelte +15 -0
  9. package/src/component/badge.svelte +7 -0
  10. package/src/component/button.svelte +7 -0
  11. package/src/component/chip.svelte +10 -0
  12. package/src/component/circle-spinner.svelte +6 -0
  13. package/src/component/confirm.svelte +40 -0
  14. package/src/component/control-label.svelte +41 -0
  15. package/src/component/data-table.svelte +227 -0
  16. package/src/component/details.svelte +22 -0
  17. package/src/component/dialog-element.svelte +36 -0
  18. package/src/component/dialog.svelte +43 -0
  19. package/src/component/drawer.svelte +20 -0
  20. package/src/component/flex.svelte +7 -0
  21. package/src/component/grid.svelte +7 -0
  22. package/src/component/hexagon-spinner.svelte +6 -0
  23. package/src/component/icon.svelte +7 -0
  24. package/src/component/link.svelte +11 -0
  25. package/src/component/modal.svelte +32 -0
  26. package/src/component/paper.svelte +33 -0
  27. package/src/component/popover.svelte +19 -0
  28. package/src/component/progress.svelte +6 -0
  29. package/src/component/prompt.svelte +58 -0
  30. package/src/{routing → component}/routing.svelte.js +4 -6
  31. package/src/component/screen.svelte +30 -0
  32. package/src/component/select.svelte +40 -0
  33. package/src/{async → component}/suspend.svelte +14 -8
  34. package/src/component/table.svelte +49 -0
  35. package/src/component/tabs.svelte +2 -0
  36. package/src/component/text.svelte +26 -0
  37. package/src/component/titlebar.svelte +23 -0
  38. package/src/component/toast.svelte +7 -0
  39. package/src/component/toaster.svelte +60 -0
  40. package/src/component/toggle-label.svelte +11 -0
  41. package/src/component/tooltip.svelte +11 -0
  42. package/src/main.js +40 -0
  43. package/src/ws.js +10 -0
  44. package/src/async/button.svelte +0 -41
  45. package/src/changed.js +0 -10
  46. package/src/composed/data-table/th.svelte +0 -61
  47. package/src/composed/data-table.svelte +0 -174
  48. package/src/composed/entry-button.svelte +0 -29
  49. package/src/composed/form.svelte +0 -60
  50. package/src/composed/table-functions.js +0 -15
  51. package/src/control/button.svelte +0 -39
  52. package/src/control/input.svelte +0 -103
  53. package/src/control/label.svelte +0 -49
  54. package/src/control/link.svelte +0 -47
  55. package/src/control/radio-group.svelte +0 -24
  56. package/src/control/select.svelte +0 -69
  57. package/src/control/tabs.svelte +0 -35
  58. package/src/control/toggle.svelte +0 -60
  59. package/src/custom-macros.js +0 -9
  60. package/src/data-display/avatar.svelte +0 -29
  61. package/src/data-display/badge.svelte +0 -16
  62. package/src/data-display/chip.svelte +0 -38
  63. package/src/data-display/details.svelte +0 -33
  64. package/src/data-display/popover.svelte +0 -28
  65. package/src/data-display/progress.svelte +0 -25
  66. package/src/data-display/table.svelte +0 -50
  67. package/src/data-display/tooltip.svelte +0 -19
  68. package/src/dialogs/alert.svelte +0 -41
  69. package/src/dialogs/confirm.svelte +0 -52
  70. package/src/dialogs/prompt.svelte +0 -67
  71. package/src/info/element-toaster.svelte +0 -110
  72. package/src/info/icon.svelte +0 -9
  73. package/src/info/titlebar.svelte +0 -40
  74. package/src/info/toast.svelte +0 -35
  75. package/src/info/toaster/message.svelte +0 -42
  76. package/src/info/toaster.svelte +0 -72
  77. package/src/layout/dialog.svelte +0 -25
  78. package/src/layout/drawer.svelte +0 -70
  79. package/src/layout/flex.svelte +0 -24
  80. package/src/layout/grid.svelte +0 -32
  81. package/src/layout/modal.svelte +0 -71
  82. package/src/layout/paper.svelte +0 -54
  83. package/src/layout/screen.svelte +0 -38
  84. package/src/spinner/circle-spinner.svelte +0 -18
  85. package/src/spinner/hexagon-spinner.svelte +0 -18
  86. package/src/text.svelte +0 -16
  87. package/src/wsx.js +0 -29
  88. /package/src/{routing → component}/route.svelte +0 -0
  89. /package/src/{routing → component}/title.svelte +0 -0
package/package.json CHANGED
@@ -1,42 +1,48 @@
1
- {
2
- "name": "@axel669/zephyr",
3
- "version": "0.5.5",
4
- "description": "",
5
- "main": "src/index.js",
6
- "type": "module",
7
- "scripts": {
8
- "site/start": "wrangler pages dev site --port 45067",
9
- "site/build": "rollup -c docsite/rollup.config.js",
10
- "site/watch": "rollup -c docsite/rollup.config.js --watch"
11
- },
12
- "keywords": [],
13
- "author": "",
14
- "license": "MIT",
15
- "dependencies": {
16
- "@axel669/windstorm": "0.5.5",
17
- "svelte": "^5.30.1"
18
- },
19
- "devDependencies": {
20
- "@axel669/asuid": "^0.2.1",
21
- "@axel669/ipsen": "0.4.0-beta.7",
22
- "@axel669/rollup-copy-static": "^0.2.0",
23
- "@axel669/rollup-html-input": "^0.1.2",
24
- "@rollup/plugin-node-resolve": "^15.0.2",
25
- "@rollup/plugin-terser": "^0.4.0",
26
- "marked": "^12.0.2",
27
- "prism-svelte": "^0.5.0",
28
- "prismjs": "^1.29.0",
29
- "rollup": "^3.20.2",
30
- "rollup-plugin-delete": "^2.0.0",
31
- "rollup-plugin-svelte": "^7.2.2"
32
- },
33
- "files": [
34
- "src/"
35
- ],
36
- "imports": {
37
- "#lib": "./src/index.js",
38
- "#comp/*": "./docsite/comp/*.svelte",
39
- "#state/*": "./docsite/state/*.js",
40
- "#README": "./readme.md"
41
- }
42
- }
1
+ {
2
+ "name": "@axel669/zephyr",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "",
5
+ "main": "src/main.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "site/start": "wrangler dev",
9
+ "site/build": "rollup -c docsite/rollup.config.js",
10
+ "site/watch": "rollup -c docsite/rollup.config.js --watch"
11
+ },
12
+ "keywords": [],
13
+ "author": "",
14
+ "license": "MIT",
15
+ "dependencies": {
16
+ "@axel669/windstorm": "1.0.0-beta.1",
17
+ "svelte": "^5.30.1"
18
+ },
19
+ "devDependencies": {
20
+ "@axel669/asuid": "^0.2.1",
21
+ "@axel669/ipsen": "0.4.0-beta.7",
22
+ "@axel669/rollup-copy-static": "^0.2.0",
23
+ "@axel669/rollup-html-input": "^0.1.2",
24
+ "@rollup/plugin-node-resolve": "^15.0.2",
25
+ "@rollup/plugin-terser": "^0.4.0",
26
+ "fs-jetpack": "^5.1.0",
27
+ "markdown-it": "^14.1.1",
28
+ "marked": "^12.0.2",
29
+ "prism-svelte": "^0.5.0",
30
+ "prismjs": "^1.29.0",
31
+ "rollup": "^3.20.2",
32
+ "rollup-plugin-delete": "^2.0.0",
33
+ "rollup-plugin-svelte": "^7.2.2",
34
+ "yaml": "^2.8.3"
35
+ },
36
+ "files": [
37
+ "src/"
38
+ ],
39
+ "exports": {
40
+ ".": "./src/main.js"
41
+ },
42
+ "imports": {
43
+ "#lib": "./src/main.js",
44
+ "#comp/*": "./docsite/comp/*.svelte",
45
+ "#state/*": "./docsite/state/*.js",
46
+ "#README": "./readme.md"
47
+ }
48
+ }
package/readme.md CHANGED
@@ -17,50 +17,6 @@ Zephyr can be installed through npm (or the variants like yarn, pnpm, etc).
17
17
  npm install @axel669/zephyr
18
18
  ```
19
19
 
20
- ## Components
21
- Components can be imported individually from the library for tree-shaking, or
22
- the entire lib can imported at once, but I dunno how big that bundle will be.
23
- Components can also take any windstorm macro as a prop, including custom macros,
24
- using the same rules as the `wsx` covered in
25
- [Functions](./docsite/docs/functions.md).
26
-
27
- ### Theming
28
- Windstorm expects a theme to be defined on an ancestor of the html elements
29
- that use it, and Zephyr does not automatically set the theme because it can
30
- used anywhere in the page. This means the wsx action (or setting the ws-x
31
- attribute directly) will be needed for components to look right. Any valid
32
- Windstorm theme is usable, including custom ones.
33
-
34
- ### Example
35
- ```svelte
36
- <script>
37
- // individual import for tree shaking
38
- import {
39
- Button,
40
- wsx
41
- } from "@axel669/zephyr"
42
-
43
- // bring the whole thing to keep it simple
44
- import * as Wind from "@axel669/zephyr"
45
-
46
- const theme = "dark"
47
- const handler = () => {
48
- console.log("button clicked!")
49
- }
50
- </script>
51
-
52
- <!-- the wsx action can be used to setup the ws-x needed for the body -->
53
- <svelte:body use:wsx={{"@theme": theme, "@app": true}} />
54
-
55
- <Button on:click={handler}>
56
- I'm a button!
57
- </Button>
58
-
59
- <Wind.Button on:click={handler}>
60
- I'm also button!
61
- </Wind.Button>
62
- ```
63
-
64
20
  ## Svelte Config
65
21
  Zephyr does not output any css of its own, so no configuration is required to
66
22
  make it styled as expected. For people using Svelte 4+ you will need to make
@@ -0,0 +1,23 @@
1
+ // export const trackChanges = (initial) => {
2
+ // let current = initial
3
+ // return (next) => {
4
+ // if (next !== current) {
5
+ // current = next
6
+ // return true
7
+ // }
8
+ // return false
9
+ // }
10
+ // }
11
+
12
+ export const track = (...args) => {
13
+ let called = false
14
+ const f = args[args.length - 1]
15
+ const params = args.slice(0, -1)
16
+ $effect(() => {
17
+ if (called === false) {
18
+ called = true
19
+ return
20
+ }
21
+ f(...params)
22
+ })
23
+ }
@@ -0,0 +1,9 @@
1
+ <script>
2
+ const {
3
+ children
4
+ } = $props()
5
+ </script>
6
+
7
+ <ze-adorn data-ws="adorn;">
8
+ {@render children?.()}
9
+ </ze-adorn>
@@ -0,0 +1,32 @@
1
+ <script>
2
+ import Button from "./button.svelte"
3
+ import Paper from "./paper.svelte"
4
+ import Text from "./text.svelte"
5
+ import Titlebar from "./titlebar.svelte"
6
+
7
+ const {
8
+ message,
9
+ close,
10
+ color = "@primary",
11
+ } = $props()
12
+ </script>
13
+
14
+ <Paper ws="@color: {color}; variant.outline;">
15
+ {#snippet header()}
16
+ <Titlebar ws="@color: {color};">
17
+ <Text header>
18
+ Alert
19
+ </Text>
20
+ </Titlebar>
21
+ {/snippet}
22
+
23
+ <Text ws="p: 8px;">
24
+ {message}
25
+ </Text>
26
+
27
+ {#snippet footer()}
28
+ <Button ws="@color: @success;" onclick={() => close(true)}>
29
+ OK
30
+ </Button>
31
+ {/snippet}
32
+ </Paper>
@@ -0,0 +1,17 @@
1
+ <script>
2
+ import Dialog from "./dialog.svelte"
3
+ import Toaster from "./toaster.svelte"
4
+ import { wsAttr } from "../ws.js"
5
+
6
+ const {
7
+ baseline = "",
8
+ theme = "tron",
9
+ children,
10
+ } = $props()
11
+ </script>
12
+
13
+ <svelte:body use:wsAttr={`#theme.${theme}; ${baseline}`} />
14
+
15
+ <Toaster />
16
+ <Dialog />
17
+ {@render children?.()}
@@ -0,0 +1,53 @@
1
+ <script module>
2
+ import { ws } from "../ws.js"
3
+
4
+ ws.component("ze-async-button")`
5
+ disp: inline-grid;
6
+ pos.rel;
7
+ `
8
+
9
+ ws.macro("async.button.spinner")`
10
+ @size: 100%;
11
+ pos.abs;
12
+ x: 50%;
13
+ y: 0px;
14
+ tf: translateX(-50%);
15
+ h: 100%;
16
+ `
17
+ </script>
18
+
19
+ <script>
20
+ import Button from "./button.svelte"
21
+ import CircleSpinner from "./circle-spinner.svelte"
22
+
23
+ const {
24
+ onclick,
25
+ onresolve,
26
+ hideSpinner = false,
27
+ children,
28
+ disabled,
29
+ spinnerWS = "",
30
+ ...props
31
+ } = $props()
32
+
33
+ let waiting = $state(false)
34
+ const handler = async (e) => {
35
+ waiting = true
36
+ const value = await onclick?.(e)
37
+ waiting = false
38
+ onresolve?.(value)
39
+ }
40
+ const dis = $derived(
41
+ disabled === true
42
+ || waiting === true
43
+ )
44
+ </script>
45
+
46
+ <ze-async-button>
47
+ <Button {...props} onclick={handler} disabled={dis}>
48
+ {@render children?.()}
49
+ </Button>
50
+ {#if waiting === true && hideSpinner === false}
51
+ <CircleSpinner ws="async.button.spinner; {spinnerWS}" />
52
+ {/if}
53
+ </ze-async-button>
@@ -0,0 +1,15 @@
1
+ <script>
2
+ const { ws, image, type = "image/png", text, children, ...rest } = $props()
3
+ </script>
4
+
5
+ <ws-avatar data-ws={ws} {...rest}>
6
+ {#if children !== undefined}
7
+ {@render children()}
8
+ {:else if text !== undefined}
9
+ {text}
10
+ {:else}
11
+ <!-- svelte-ignore a11y_missing_attribute -->
12
+ <!-- svelte-ignore element_invalid_self_closing_tag -->
13
+ <object data={image} {type} />
14
+ {/if}
15
+ </ws-avatar>
@@ -0,0 +1,7 @@
1
+ <script>
2
+ const { ws, text, children, ...rest } = $props()
3
+ </script>
4
+
5
+ <ws-badge ws-text={text} data-ws={ws} {...rest}>
6
+ {@render children()}
7
+ </ws-badge>
@@ -0,0 +1,7 @@
1
+ <script>
2
+ const { ws = "", children, ...rest } = $props()
3
+ </script>
4
+
5
+ <button data-ws={ws} {...rest}>
6
+ {@render children?.()}
7
+ </button>
@@ -0,0 +1,10 @@
1
+ <script>
2
+ const { ws, clickable = false, children, ...rest } = $props()
3
+ const clicking = $derived(
4
+ (clickable === true) ? "" : undefined
5
+ )
6
+ </script>
7
+
8
+ <ws-chip data-ws={ws} clickable={clicking} {...rest}>
9
+ {@render children()}
10
+ </ws-chip>
@@ -0,0 +1,6 @@
1
+ <script>
2
+ const { ws, ...rest } = $props()
3
+ </script>
4
+
5
+ <!-- svelte-ignore element_invalid_self_closing_tag -->
6
+ <ws-circle-spinner data-ws={ws} {...rest} />
@@ -0,0 +1,40 @@
1
+ <script>
2
+ import Button from "./button.svelte"
3
+ import Grid from "./grid.svelte"
4
+ import Paper from "./paper.svelte"
5
+ import Text from "./text.svelte"
6
+ import Titlebar from "./titlebar.svelte"
7
+
8
+ const {
9
+ message,
10
+ close,
11
+ color = "@primary",
12
+ okText = "OK",
13
+ cancelText = "Cancel",
14
+ } = $props()
15
+ </script>
16
+
17
+ <Paper ws="@color: {color}; variant.outline;">
18
+ {#snippet header()}
19
+ <Titlebar ws="@color: {color};">
20
+ <Text header>
21
+ Confirm
22
+ </Text>
23
+ </Titlebar>
24
+ {/snippet}
25
+
26
+ <Text ws="p: 8px;">
27
+ {message}
28
+ </Text>
29
+
30
+ {#snippet footer()}
31
+ <Grid ws="gr.cols: 1fr 1fr; p: 0px;">
32
+ <Button ws="@color: @error;" onclick={() => close(false)}>
33
+ {cancelText}
34
+ </Button>
35
+ <Button ws="@color: @success;" onclick={() => close(true)}>
36
+ {okText}
37
+ </Button>
38
+ </Grid>
39
+ {/snippet}
40
+ </Paper>
@@ -0,0 +1,41 @@
1
+ <script>
2
+ const {
3
+ ws,
4
+ labelWS,
5
+ label,
6
+ children,
7
+ start,
8
+ end,
9
+ extra,
10
+ ...rest
11
+ } = $props()
12
+ </script>
13
+
14
+ <label control data-ws={ws ?? ""} {...rest}>
15
+ {#if label}
16
+ <ws-text label-text data-ws={labelWS}>
17
+ {#if typeof label !== "function"}
18
+ {label}
19
+ {:else}
20
+ {@render label()}
21
+ {/if}
22
+ </ws-text>
23
+ {/if}
24
+ {@render children()}
25
+
26
+ {#if start}
27
+ <ze-input-start data-ws="area: start; grid;">
28
+ {@render start()}
29
+ </ze-input-start>
30
+ {/if}
31
+ {#if end}
32
+ <ze-input-end data-ws="area: end; grid;">
33
+ {@render end()}
34
+ </ze-input-end>
35
+ {/if}
36
+ {#if extra}
37
+ <ze-input-extra data-ws="area: extra; grid;">
38
+ {@render extra()}
39
+ </ze-input-extra>
40
+ {/if}
41
+ </label>
@@ -0,0 +1,227 @@
1
+ <script module>
2
+ import { ws as wind } from "../ws.js"
3
+
4
+ wind.component("ze-data-table")`
5
+ pos.rel;
6
+ grid;
7
+ gr.cols: 1fr;
8
+ gr.rows: 1fr min-content;
9
+ p: 0px;
10
+ h: 100%;
11
+ w: 100%;
12
+ h.max: 100%;
13
+ w.max: 100%;
14
+ `
15
+ wind.component("ze-data-scroller")`
16
+ pos.rel;
17
+ over: auto;
18
+ ! &::before {
19
+ *content: "";
20
+ pos.abs;
21
+ w: 1px;
22
+ h: @h;
23
+ z: -1;
24
+ }
25
+ `
26
+ wind.component("ze-fake-scroll")`
27
+ disp: block;
28
+ w: 100%;
29
+ h: @h;
30
+ pos.stick;
31
+ y: 0px;
32
+ over: hidden;
33
+ `
34
+ wind.component("ze-data-footer")`
35
+ pos: rel;
36
+ grid;
37
+ `
38
+
39
+ const itemRange = (info) => {
40
+ const {
41
+ scrollPos,
42
+ scrollMax,
43
+ rowSize,
44
+ length,
45
+ containerHeight,
46
+ } = info
47
+ if (scrollMax === null) {
48
+ return [0, 0]
49
+ }
50
+ const displayed = Math.ceil(containerHeight / rowSize) - 1
51
+ const itemPos = Math.floor(
52
+ (scrollPos / scrollMax) * (length - displayed)
53
+ )
54
+ return [
55
+ itemPos - 1,
56
+ itemPos + displayed + 1,
57
+ displayed + 2,
58
+ ]
59
+ }
60
+ const emptyRow = Symbol("empty row")
61
+ const nullRow = Symbol("null row")
62
+
63
+ const sliceData = (data, range) => {
64
+ const displayed = data.slice(
65
+ Math.max(range[0], 0),
66
+ range[1]
67
+ )
68
+ if (range[0] === -1) {
69
+ return [emptyRow, ...displayed]
70
+ }
71
+ if (displayed.length < range[2]) {
72
+ return [
73
+ ...displayed,
74
+ ...Array.from(
75
+ { length: range[2] - displayed.length },
76
+ () => nullRow
77
+ )
78
+ ]
79
+ }
80
+ return displayed
81
+ }
82
+ </script>
83
+ <script>
84
+ import { onMount } from "svelte"
85
+ import { on } from "svelte/events"
86
+
87
+ const {
88
+ rowSize = 30,
89
+ headerSize = rowSize,
90
+ header = Header,
91
+ row = Row,
92
+ ws = "",
93
+ data,
94
+ footer = Footer,
95
+ } = $props()
96
+
97
+ const keys = $derived(
98
+ Object.keys(data?.[0] ?? {})
99
+ )
100
+
101
+ const itemsHeight = $derived(
102
+ rowSize * data.length
103
+ )
104
+ const tableHeight = $derived(itemsHeight + headerSize)
105
+
106
+ let scrollPos = $state(0)
107
+ let scrollMax = $state(null)
108
+ let containerHeight = $state(null)
109
+ const scrollHandler = (evt) => {
110
+ scrollPos = container.scrollTop
111
+ }
112
+ const range = $derived(
113
+ itemRange({
114
+ scrollPos,
115
+ scrollMax,
116
+ containerHeight,
117
+ rowSize,
118
+ length: data.length,
119
+ })
120
+ )
121
+ let container = $state(null)
122
+
123
+ $inspect(scrollMax, containerHeight)
124
+
125
+ onMount(() => {
126
+ containerHeight = container.clientHeight
127
+ scrollMax = container.scrollHeight - containerHeight
128
+ on(container, "scroll", scrollHandler, { passive: true })
129
+ const observer = new ResizeObserver(
130
+ () => {
131
+ containerHeight = container.clientHeight
132
+ scrollMax = container.scrollHeight - containerHeight
133
+ }
134
+ )
135
+ observer.observe(container)
136
+ return () => observer.unobserve(container)
137
+ })
138
+
139
+ const displayed = $derived(range[2])
140
+ const displayedRows = $derived(
141
+ sliceData(data, range)
142
+ )
143
+ const show = $derived({
144
+ start: Math.max(1, range[0] + 2),
145
+ end: Math.min(data.length, range[1] - 1),
146
+ total: data.length,
147
+ })
148
+ const correction = $derived(
149
+ (data.length - displayed) * rowSize
150
+ )
151
+
152
+ const itemOffset = $derived(
153
+ range[0] * rowSize - scrollPos
154
+ )
155
+
156
+ const containerWS = $derived(
157
+ wind.x({
158
+ "! & :where(thead, tbody)": {
159
+ w: "100%",
160
+ },
161
+ "! & thead > tr": {
162
+ h: "@header",
163
+ z: "+5",
164
+ "pos.rel": true,
165
+ },
166
+ "! & tbody > tr": {
167
+ h: "@row",
168
+ },
169
+ })
170
+ )
171
+ </script>
172
+
173
+ {#snippet Header(keys)}
174
+ <tr>
175
+ {#each keys as key}
176
+ <th>{key}</th>
177
+ {/each}
178
+ </tr>
179
+ {/snippet}
180
+ {#snippet Row(item, keys)}
181
+ <tr>
182
+ {#each keys as key}
183
+ <td>{item[key]}</td>
184
+ {/each}
185
+ </tr>
186
+ {/snippet}
187
+ {#snippet Footer(show, displayed)}
188
+ <div data-ws="flex; fl.cn; h.min: 32px;">
189
+ <div>
190
+ Showing: {show.start} - {show.end} of {show.total}
191
+ </div>
192
+ </div>
193
+ {/snippet}
194
+
195
+ <ze-data-table data-ws={containerWS}>
196
+ <ze-data-scroller
197
+ bind:this={container}
198
+ data-ws="@h: {tableHeight}px; @header: {headerSize}px; @row: {rowSize}px;"
199
+ >
200
+ <ze-fake-scroll data-ws="@h: {containerHeight}px;">
201
+ <table data-ws="w: 100%; {ws}">
202
+ <thead>
203
+ {#if data?.length > 0}
204
+ {@render header(keys)}
205
+ {:else}
206
+ <tr>
207
+ <td>N/A</td>
208
+ </tr>
209
+ {/if}
210
+ </thead>
211
+ <tbody data-ws="tf: translateY({itemOffset}px);">
212
+ {#each displayedRows as item}
213
+ {@render row(item, keys)}
214
+ {:else}
215
+ <tr>
216
+ <td>No Items</td>
217
+ </tr>
218
+ {/each}
219
+ </tbody>
220
+ </table>
221
+ </ze-fake-scroll>
222
+ </ze-data-scroller>
223
+
224
+ <ze-data-footer>
225
+ {@render footer(show, displayedRows)}
226
+ </ze-data-footer>
227
+ </ze-data-table>
@@ -0,0 +1,22 @@
1
+ <script>
2
+ let {
3
+ open = $bindable(false),
4
+ ws,
5
+ summaryWS,
6
+ children,
7
+ label = "",
8
+ summary,
9
+ ...rest
10
+ } = $props()
11
+ </script>
12
+
13
+ <details data-ws={ws ?? ""} bind:open {...rest}>
14
+ <summary data-ws={summaryWS}>
15
+ {#if summary !== undefined}
16
+ {@render summary()}
17
+ {:else}
18
+ {label}
19
+ {/if}
20
+ </summary>
21
+ {@render children()}
22
+ </details>