@axel669/zephyr 0.5.0-alpha.0 → 0.5.4

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.
package/package.json CHANGED
@@ -1,41 +1,42 @@
1
- {
2
- "name": "@axel669/zephyr",
3
- "version": "0.5.0-alpha.0",
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.0",
17
- "svelte": "^5.9.0"
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
- }
41
- }
1
+ {
2
+ "name": "@axel669/zephyr",
3
+ "version": "0.5.4",
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.4",
17
+ "svelte": "^5.9.0"
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
+ }
package/readme.md CHANGED
@@ -4,6 +4,12 @@ library, with some additional parts to make it look nice in the markup.
4
4
 
5
5
  [Docs Here](https://zephyr.axel669.net)
6
6
 
7
+ ## Todo
8
+ These are things that I plan to do, but didn't want to hold up the 0.5.x major
9
+ changes for svelte 5/runes/improvements.
10
+ - Add filter/sort back into data table
11
+ - Form element
12
+
7
13
  ## Installation
8
14
  Zephyr can be installed through npm (or the variants like yarn, pnpm, etc).
9
15
 
@@ -15,7 +21,8 @@ npm install @axel669/zephyr
15
21
  Components can be imported individually from the library for tree-shaking, or
16
22
  the entire lib can imported at once, but I dunno how big that bundle will be.
17
23
  Components can also take any windstorm macro as a prop, including custom macros,
18
- using the same rules as the `wsx` covered in [Functions](./src/functions.md).
24
+ using the same rules as the `wsx` covered in
25
+ [Functions](./docsite/docs/functions.md).
19
26
 
20
27
  ### Theming
21
28
  Windstorm expects a theme to be defined on an ancestor of the html elements
package/src/changed.js CHANGED
@@ -1,10 +1,10 @@
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
- }
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
+ }
@@ -1,200 +1,174 @@
1
- <svelte:options immutable />
2
-
3
- <script context="module">
4
- // find the first filter in the list that returns false
5
- const applyFilters = (data, filters) => {
6
- if (Array.isArray(data) === false) {
7
- return null
1
+ <script module>
2
+ const itemRange = (info) => {
3
+ const {
4
+ scrollPos,
5
+ scrollMax,
6
+ rowSize,
7
+ length,
8
+ containerHeight,
9
+ } = info
10
+ if (scrollMax === null) {
11
+ return [0, 0]
8
12
  }
9
- return data.filter(
10
- (row) => {
11
- for (const [filter, value] of filters) {
12
- if (filter(row, value) === false) {
13
- return false
14
- }
15
- }
16
- return true
17
- }
13
+ const displayed = Math.ceil(containerHeight / rowSize) - 1
14
+ const itemPos = Math.floor(
15
+ (scrollPos / scrollMax) * (length - displayed)
18
16
  )
17
+ return [
18
+ itemPos - 1,
19
+ itemPos + displayed + 1,
20
+ displayed + 2,
21
+ ]
19
22
  }
20
- const sliceData = (data, page, pageSize, sortFunc) => {
21
- if (data === null) {
22
- return []
23
- }
24
- const sorted = data.sort(sortFunc)
25
- return Array.from(
26
- { length: pageSize },
27
- (_, i) => sorted[page * pageSize + i]
23
+ const emptyRow = Symbol("empty row")
24
+ const nullRow = Symbol("null row")
25
+
26
+ const sliceData = (data, range) => {
27
+ const displayed = data.slice(
28
+ Math.max(range[0], 0),
29
+ range[1]
28
30
  )
31
+ if (range[0] === -1) {
32
+ return [emptyRow, ...displayed]
33
+ }
34
+ if (displayed.length < range[2]) {
35
+ return [
36
+ ...displayed,
37
+ ...Array.from(
38
+ { length: range[2] - displayed.length },
39
+ () => nullRow
40
+ )
41
+ ]
42
+ }
43
+ return displayed
29
44
  }
30
- const noSort = {
31
- direction: null,
32
- func: (a, b) => 0,
33
- base: null,
34
- }
35
-
36
- export const dtContext = Symbol("table key")
37
45
  </script>
38
46
 
39
47
  <script>
40
- import { createEventDispatcher, setContext } from "svelte"
41
- import { writable } from "svelte/store"
42
-
43
- import Button from "../control/button.svelte"
44
- import Icon from "../info/icon.svelte"
45
- import Paper from "../layout/paper.svelte"
46
- import Table from "../data-display/table.svelte"
47
- import Text from "../text.svelte"
48
+ import { on } from "svelte/events"
48
49
 
49
50
  import Grid from "../layout/grid.svelte"
50
51
 
51
52
  import wsx from "../wsx.js"
52
- import { handler$, eventHandler$ } from "../handler$.js"
53
-
54
- export let color = "@primary"
55
- export let fillHeader = true
56
- export let data
57
- export let pageSize = 10
58
- export let page = 0
59
- export let rowHeight = "40px"
60
- export let scrollable = false
61
- export let height = null
62
-
63
- let sorting = noSort
64
- let filters = new Map()
65
- let filterFunctions = []
66
- let jumpTarget = "1"
67
-
68
- $: filteredData = applyFilters(data, filterFunctions, sorting.func)
69
- $: rows = sliceData(filteredData, page, pageSize, sorting.func)
70
- $: rowCount = filteredData?.length ?? 0
71
- $: pageCount = Math.ceil(rowCount / pageSize)
72
- $: maxPage = Math.max(pageCount - 1, 0)
73
- $: jumpTarget = (page + 1).toString()
74
-
75
- $: if (filteredData === null) {
76
- console.warn("DataTable: data is not an array")
77
- }
78
-
79
- const prev = () => page = Math.max(0, page - 1)
80
- const next = () => page = Math.min(maxPage, page + 1)
81
-
82
- const updateSort = (func) => {
83
- if (sorting.base === func) {
84
- if (sorting.direction === "desc") {
85
- sorting = noSort
86
- return
87
- }
88
- sorting = {
89
- func: (a, b) => -func(a, b),
90
- base: func,
91
- direction: "desc"
92
- }
93
- return
94
- }
95
- sorting = {
96
- func,
97
- base: func,
98
- direction: "asc",
99
- }
100
- }
101
- const updateFilter = (func, value) => {
102
- if (func === null) {
103
- return
104
- }
105
- filters.set(func, value)
106
- filterFunctions = [
107
- ...filters.entries()
108
- ]
109
- }
110
53
 
111
- const context = writable({})
112
- $: $context = {
113
- updateSort,
114
- fillHeader,
115
- sorting,
116
- updateFilter,
54
+ const {
55
+ data,
56
+ rowSize = 40,
57
+ headerSize = rowSize,
58
+ cols = "1fr",
59
+ gap = 2,
60
+ colGap = gap,
61
+ rowGap = gap,
62
+ header,
63
+ row,
117
64
  color,
118
- }
119
- setContext(dtContext, context)
65
+ fillHeader = false,
66
+ ...rest
67
+ } = $props()
120
68
 
121
- const fire = createEventDispatcher()
122
- const pass = handler$(
123
- (row) => fire("row-click", row)
69
+ const rowPx = rowSize + rowGap
70
+ const height = $derived(
71
+ rowPx * data.length - rowGap
124
72
  )
73
+ const tableHeight = $derived(height + rowPx)
125
74
 
126
- let scroller = null
127
- $: if (scroller !== null && isNaN(page) === false) {
128
- scroller.scrollTop = 0
75
+ let scrollPos = $state(0)
76
+ let scrollMax = $state(null)
77
+ let containerHeight = $state(null)
78
+ const scrollHandler = (evt) => {
79
+ scrollPos = container.scrollTop
129
80
  }
130
- const jump = (evt) => {
131
- if (evt.type === "keypress" && evt.key !== "Enter") {
132
- return
133
- }
134
- const target = parseInt(evt.target.value)
135
- if (isNaN(target) === true) {
136
- jumpTarget = (page + 1).toString()
137
- return
138
- }
139
- page = Math.max(
140
- Math.min(jumpTarget - 1, maxPage),
141
- 0
81
+ const range = $derived(
82
+ itemRange({
83
+ scrollPos,
84
+ scrollMax,
85
+ containerHeight,
86
+ rowSize: rowSize + rowGap,
87
+ length: data.length,
88
+ })
89
+ )
90
+ let container = $state(null)
91
+
92
+ $effect(() => {
93
+ containerHeight = container.clientHeight
94
+ scrollMax = container.scrollHeight - containerHeight
95
+ on(container, "scroll", scrollHandler, { passive: true })
96
+ const observer = new ResizeObserver(
97
+ () => {
98
+ containerHeight = container.clientHeight
99
+ scrollMax = container.scrollHeight - containerHeight
100
+ }
142
101
  )
143
- jumpTarget = (page + 1).toString()
144
- }
102
+ observer.observe(container)
103
+ return () => observer.unobserve(container)
104
+ })
145
105
 
146
- $: content = {
147
- "fl.cross": "stretch",
148
- p: "0px",
149
- gap: "0px",
150
- over: (scrollable === true) ? "auto" : null,
151
- }
152
- $: header = {
153
- "h.min": rowHeight,
154
- y: "0px",
155
- z: "+10",
156
- pos: (scrollable === true) ? "sticky" : null,
157
- }
106
+ const displayed = $derived(range[2])
107
+ const displayedRows = $derived(
108
+ sliceData(data, range)
109
+ )
110
+ const tbodyHeight = $derived(displayRows.length * rowSize)
111
+
112
+ const tableWSX = $derived({
113
+ "$fill-header": fillHeader,
114
+ $color: color,
115
+ "gr.cols": cols,
116
+ "gap.col": `${colGap}px`,
117
+ "gap.row": `${rowGap}px`,
118
+ "@header-size": `${headerSize}px`,
119
+ "@row-size": `${rowSize}px`,
120
+ "$sticky-header": true,
121
+ w: "100%",
122
+ h: `${tableHeight}px`,
123
+ over: "visible",
124
+ con: "paint",
125
+ })
126
+ const tbodyWSX = $derived({
127
+ disp: "grid",
128
+ "gr.rows": "subgrid",
129
+ "gr.cols": "subgrid",
130
+ row: `span ${displayed}`,
131
+ tf: `translateY(${range[0] * rowPx}px)`,
132
+ })
133
+ const footerWSX = $derived({
134
+ h: "40px",
135
+ bg: "@background-element",
136
+ $color: color,
137
+ "b.y": "2px solid @ripple-base-color",
138
+ disp: "flex",
139
+ "fl-center": true,
140
+ })
141
+
142
+ const show = $derived({
143
+ start: Math.max(1, range[0] + 2),
144
+ end: Math.min(data.length, range[1] - 1),
145
+ total: data.length,
146
+ })
158
147
  </script>
159
148
 
160
- <Paper h={height} r="0px">
161
- <ws-flex use:wsx={content} slot="content" bind:this={scroller}>
162
- <Table data={rows} {color} {fillHeader} b.t.w="2px" {...$$restProps}>
163
- <tr use:wsx={header} slot="header">
164
- <slot name="header">
165
- <th>No Header Defined</th>
166
- </slot>
167
- </tr>
168
- <tr ws-x="[h {rowHeight}]" slot="row" let:row>
169
- <slot name="row" {row}>
170
- <th>No Row Defined</th>
171
- </slot>
172
- </tr>
173
- <tr ws-x="[h {rowHeight}]" slot="empty-row" />
174
- </Table>
175
- </ws-flex>
176
- <Grid slot="footer" gr.cols="min-content min-content min-content 1fr"
177
- rows="32px" b="1px solid {color}" b.b.w="2px">
178
- {#if pageCount > 0}
179
- <Button on:click={prev} disabled={page === 0} ground>
180
- <Icon name="caret-left" />
181
- </Button>
182
- <Button on:click={next} disabled={page === maxPage} ground>
183
- <Icon name="caret-right" />
184
- </Button>
185
- <Text adorn t.ws="nowrap">
186
- Page
187
- <input ws-x="[b 1px solid @text-color-normal] [w 36px] [r 4px]
188
- [h 24px] [bg.c transaprent] [t.a center] [m.l 4px] [m.r 4px]"
189
- type="text" bind:value={jumpTarget}
190
- on:keypress={jump} on:blur={jump} />
191
- / {pageCount}
192
- </Text>
193
- {:else}
194
- <div ws-x="[col span 3] [p.l 4px]">
195
- No data to show
196
- </div>
197
- {/if}
198
- <slot name="action" />
199
- </Grid>
200
- </Paper>
149
+ <Grid cols="1fr" rows="1fr min-content" pos="relative" {...rest} gap="0px">
150
+ <div ws-x="[over auto]" bind:this={container}>
151
+ <table use:wsx={tableWSX}>
152
+ <thead>
153
+ {@render header()}
154
+ </thead>
155
+ <tbody use:wsx={tbodyWSX}>
156
+ {#each displayedRows as dataRow}
157
+ {#if dataRow === nullRow}
158
+ <tr>
159
+ <td ws-x="[col 1 / -1]"></td>
160
+ </tr>
161
+ {:else if dataRow === emptyRow}
162
+ <tr></tr>
163
+ {:else}
164
+ {@render row(dataRow)}
165
+ {/if}
166
+ {/each}
167
+ </tbody>
168
+ </table>
169
+ </div>
170
+
171
+ <div use:wsx={footerWSX}>
172
+ Showing: {show.start} - {show.end} of {show.total}
173
+ </div>
174
+ </Grid>
@@ -0,0 +1,9 @@
1
+ const head = document.head
2
+ const macros = document.createElement("style")
3
+ macros.setAttribute("ws-root", "")
4
+ macros.innerHTML = `
5
+ .ws-style {
6
+ --con: "contain: {$}";
7
+ }
8
+ `
9
+ head.append(macros)
@@ -5,6 +5,8 @@
5
5
  color = "@default",
6
6
  data = [],
7
7
  fillHeader = false,
8
+ stickyHeader = false,
9
+ cols = "1fr",
8
10
  header,
9
11
  row,
10
12
  emptyRow,
@@ -13,7 +15,9 @@
13
15
 
14
16
  const wind = $derived({
15
17
  "$color": color,
16
- "$header-fill": fillHeader,
18
+ "$fill-header": fillHeader,
19
+ "$sticky-header": stickyHeader,
20
+ "gr.cols": cols,
17
21
  ...rest,
18
22
  })
19
23
  </script>
@@ -12,7 +12,6 @@
12
12
  message,
13
13
  okText = "OK",
14
14
  titleText = "Alert",
15
- ...rest
16
15
  } = $props()
17
16
  </script>
18
17
 
package/src/index.js CHANGED
@@ -39,13 +39,12 @@ export { default as HexagonSpinner } from "./spinner/hexagon-spinner.svelte"
39
39
  export { default as Alert } from "./dialogs/alert.svelte"
40
40
  export { default as Confirm } from "./dialogs/confirm.svelte"
41
41
 
42
- // export { default as DataTable } from "./composed/data-table.svelte"
42
+ export { default as DataTable } from "./composed/data-table.svelte"
43
43
  // export { default as TH } from "./composed/data-table/th.svelte"
44
44
  export { default as EntryButton } from "./composed/entry-button.svelte"
45
45
  // export { default as Form } from "./composed/form.svelte"
46
46
 
47
47
  export { default as AsyncButton } from "./async/button.svelte"
48
- // export { default as LoadZone } from "./async/load-zone.svelte"
49
48
  export { default as Suspend } from "./async/suspend.svelte"
50
49
 
51
50
  export { default as Route } from "./routing/route.svelte"
@@ -47,8 +47,6 @@
47
47
  content,
48
48
  ...rest
49
49
  } = $props()
50
- // export let height
51
- // export let type = "menu"
52
50
 
53
51
  const animTime = getContext(modalContext)
54
52
 
@@ -23,7 +23,6 @@
23
23
  resolver = null
24
24
  modalProps = null
25
25
  Component = null
26
- // visible.checked = false
27
26
  if (topClose !== close) {
28
27
  return
29
28
  }
@@ -47,7 +46,6 @@
47
46
  }
48
47
  )
49
48
 
50
- // let visible = null
51
49
  const animationTime = writable(animTime)
52
50
  const wind = $derived({
53
51
  "@anim-time": $animationTime,
@@ -16,17 +16,6 @@
16
16
  ...rest
17
17
  } = $props()
18
18
 
19
- // $: props = Object.entries($$restProps).reduce(
20
- // (p, [key, value]) => {
21
- // const [target, name] =
22
- // (key.startsWith("l!") === true)
23
- // ? [ p.layout, key.slice(2) ]
24
- // : [ p.paper, key ]
25
- // target[name] = value
26
- // return p
27
- // },
28
- // { layout: {}, paper: {} }
29
- // )
30
19
  const props = $derived(
31
20
  splitProps(rest, "l!")
32
21
  )
package/src/props.js CHANGED
@@ -1,24 +1,24 @@
1
- export const filterProps = (props, filter) => Object.entries(props).reduce(
2
- (p, [name, value]) => {
3
- if (name.startsWith(filter) === true) {
4
- p[name.slice(filter.length)] = value
5
- }
6
- return p
7
- },
8
- {}
9
- )
10
-
11
- export const splitProps = (props, ...splits) => Object.entries(props).reduce(
12
- (groups, [name, value]) => {
13
- for (const split of splits) {
14
- if (name.startsWith(split) === true) {
15
- groups[split] = groups[split] ?? {}
16
- groups[split][name.slice(split.length)] = value
17
- return groups
18
- }
19
- }
20
- groups.rest[name] = value
21
- return groups
22
- },
23
- { rest: {} }
24
- )
1
+ export const filterProps = (props, filter) => Object.entries(props).reduce(
2
+ (p, [name, value]) => {
3
+ if (name.startsWith(filter) === true) {
4
+ p[name.slice(filter.length)] = value
5
+ }
6
+ return p
7
+ },
8
+ {}
9
+ )
10
+
11
+ export const splitProps = (props, ...splits) => Object.entries(props).reduce(
12
+ (groups, [name, value]) => {
13
+ for (const split of splits) {
14
+ if (name.startsWith(split) === true) {
15
+ groups[split] = groups[split] ?? {}
16
+ groups[split][name.slice(split.length)] = value
17
+ return groups
18
+ }
19
+ }
20
+ groups.rest[name] = value
21
+ return groups
22
+ },
23
+ { rest: {} }
24
+ )
@@ -58,8 +58,5 @@
58
58
  {:else}
59
59
  {@render (content ?? children)?.(routeInfo, props)}
60
60
  {/if}
61
- <!-- <slot {routeInfo}>
62
- <svelte:component this={component} {...props} {routeInfo} />
63
- </slot> -->
64
61
  {/key}
65
62
  {/if}
package/src/wind.js ADDED
@@ -0,0 +1,4 @@
1
+ import "./custom-macros.js"
2
+ import ws from "@axel669/windstorm"
3
+
4
+ export default ws
package/src/wsx.js CHANGED
@@ -1,4 +1,4 @@
1
- import ws from "@axel669/windstorm"
1
+ import ws from "./wind.js"
2
2
 
3
3
  export const none = Symbol("none")
4
4
  export default (node, props) => {
@@ -1,22 +0,0 @@
1
- <svelte:options immutable />
2
-
3
- <script>
4
- import Spinner from "../spinner/hexagon-spinner.svelte"
5
- export let source = null
6
- export let message = "Loading"
7
- </script>
8
-
9
- {#if source !== null}
10
- {#await source}
11
- <slot name="loading">
12
- <ws-flex ws-x="[fl-center]">
13
- <span>{message}</span>
14
- <Spinner size="100px" />
15
- </ws-flex>
16
- </slot>
17
- {:then result}
18
- <slot {result} />
19
- {:catch error}
20
- <slot name="error" {error} />
21
- {/await}
22
- {/if}