@axel669/zephyr 0.4.7 → 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.
Files changed (58) hide show
  1. package/package.json +12 -10
  2. package/readme.md +8 -1
  3. package/src/async/button.svelte +17 -13
  4. package/src/async/suspend.svelte +38 -0
  5. package/src/changed.js +10 -0
  6. package/src/composed/data-table/th.svelte +2 -2
  7. package/src/composed/data-table.svelte +154 -180
  8. package/src/composed/entry-button.svelte +13 -16
  9. package/src/control/button.svelte +26 -22
  10. package/src/control/input.svelte +52 -50
  11. package/src/control/label.svelte +28 -24
  12. package/src/control/link.svelte +36 -30
  13. package/src/control/radio-group.svelte +13 -10
  14. package/src/control/select.svelte +21 -16
  15. package/src/control/tabs.svelte +18 -14
  16. package/src/control/toggle.svelte +33 -22
  17. package/src/custom-macros.js +9 -0
  18. package/src/data-display/avatar.svelte +12 -11
  19. package/src/data-display/badge.svelte +9 -7
  20. package/src/data-display/chip.svelte +27 -17
  21. package/src/data-display/details.svelte +22 -14
  22. package/src/data-display/popover.svelte +12 -9
  23. package/src/data-display/progress.svelte +17 -13
  24. package/src/data-display/table.svelte +29 -17
  25. package/src/data-display/tooltip.svelte +11 -9
  26. package/src/dialogs/alert.svelte +18 -12
  27. package/src/dialogs/confirm.svelte +22 -16
  28. package/src/{hash.js → hash.svelte.js} +9 -0
  29. package/src/{index.mjs → index.js} +10 -10
  30. package/src/{composed/element-toast.svelte → info/element-toaster.svelte} +41 -27
  31. package/src/info/icon.svelte +4 -4
  32. package/src/info/titlebar.svelte +20 -14
  33. package/src/info/toast.svelte +35 -0
  34. package/src/info/toaster/message.svelte +42 -0
  35. package/src/info/toaster.svelte +64 -6
  36. package/src/layout/dialog.svelte +10 -12
  37. package/src/layout/drawer.svelte +45 -18
  38. package/src/layout/flex.svelte +13 -11
  39. package/src/layout/grid.svelte +17 -15
  40. package/src/layout/modal.svelte +34 -26
  41. package/src/layout/paper.svelte +31 -33
  42. package/src/layout/screen.svelte +13 -9
  43. package/src/props.js +24 -0
  44. package/src/routing/route.svelte +29 -16
  45. package/src/routing/{routing.js → routing.svelte.js} +10 -1
  46. package/src/routing/title.svelte +8 -6
  47. package/src/spinner/circle-spinner.svelte +10 -8
  48. package/src/spinner/hexagon-spinner.svelte +10 -8
  49. package/src/text.svelte +6 -10
  50. package/src/wind.js +4 -0
  51. package/src/{wsx.mjs → wsx.js} +1 -1
  52. package/src/async/load-zone.svelte +0 -22
  53. package/src/composed/toast/message.svelte +0 -33
  54. package/src/composed/toast.svelte +0 -70
  55. package/src/info/notification.svelte +0 -28
  56. package/src/inline-dialog.svelte +0 -23
  57. /package/src/{handler$.mjs → handler$.js} +0 -0
  58. /package/src/{variant.mjs → variant.js} +0 -0
@@ -1,14 +1,72 @@
1
+ <script module>
2
+ const genID = () => `${Date.now()}:${Math.random().toString(16)}`
3
+
4
+ const delays = {}
5
+ const delay = ({time, action, id}) => {
6
+ const trigger = () => {
7
+ const info = delays[id]
8
+ clearTimeout(info.id)
9
+ action()
10
+ delete delays[id]
11
+ }
12
+ const timeoutID = setTimeout(trigger, time)
13
+ delays[id] = { trigger, id: timeoutID }
14
+ }
15
+ delay.trigger = (id) => delays[id].trigger()
16
+ </script>
17
+
1
18
  <script>
2
- import wsx from "../wsx.mjs"
19
+ import { fade } from "svelte/transition"
3
20
 
4
- export let position = "tc"
21
+ import { eventHandler$ } from "../handler$.js"
22
+ import wsx from "../wsx.js"
5
23
 
6
- $: wind = {
7
- [`$${position}`]: true,
8
- ...$$restProps,
24
+ import ToastMessage from "./toaster/message.svelte"
25
+
26
+ const {
27
+ message = ToastMessage,
28
+ position = "tc",
29
+ onaction,
30
+ ...rest
31
+ } = $props()
32
+
33
+ let items = $state([])
34
+
35
+ const act = eventHandler$(
36
+ (evt, id, props) => {
37
+ delay.trigger(id)
38
+ evt.props = props
39
+ onaction?.(evt)
40
+ }
41
+ )
42
+
43
+ export const show = (duration, props) => {
44
+ const id = genID()
45
+ items = [...items, { id, props }]
46
+ delay({
47
+ action: () => items = items.filter(
48
+ item => item.id !== id
49
+ ),
50
+ time: duration,
51
+ id,
52
+ })
9
53
  }
54
+ export const clear = () => items = []
55
+
56
+ const wind = $derived({
57
+ [`$${position}`]: true,
58
+ ...rest,
59
+ })
60
+ const Message = $derived(message)
10
61
  </script>
11
62
 
12
63
  <ws-toaster use:wsx={wind}>
13
- <slot />
64
+ {#each items as {props, id} (id)}
65
+ <zephyr-toast-wrapper ws-x="[grid]" transition:fade={{duration: 200}}>
66
+ <Message
67
+ {...props}
68
+ onaction={act(id, {...props})}
69
+ />
70
+ </zephyr-toast-wrapper>
71
+ {/each}
14
72
  </ws-toaster>
@@ -1,26 +1,24 @@
1
- <svelte:options immutable />
2
-
3
1
  <script>
4
2
  import { fade } from "svelte/transition"
5
3
 
6
- import wsx from "../wsx.mjs"
4
+ import wsx from "../wsx.js"
7
5
 
8
6
  import Paper from "./paper.svelte"
9
7
 
8
+ const props = $props()
9
+
10
10
  const trick = (node, options) => ({
11
11
  delay: 0,
12
12
  duration: 250,
13
13
  css: () => "",
14
14
  })
15
+ const onclick = (evt) => {
16
+ evt.stopPropagation()
17
+ }
15
18
  </script>
16
19
 
17
- <!-- svelte-ignore a11y-click-events-have-key-events -->
18
- <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
19
- <wind-wrapper transition:trick ws-x="@@dialog [grid]" on:click|stopPropagation
20
- role="dialog">
21
- <Paper {...$$props}>
22
- <slot name="header" slot="header" />
23
- <slot />
24
- <slot name="footer" slot="footer" />
25
- </Paper>
20
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
21
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
22
+ <wind-wrapper transition:trick ws-x="@@dialog [grid]" {onclick} role="dialog">
23
+ <Paper {...props} />
26
24
  </wind-wrapper>
@@ -1,6 +1,4 @@
1
- <svelte:options immutable />
2
-
3
- <script context="module">
1
+ <script module>
4
2
  const defs = {
5
3
  select: {
6
4
  "@@select": true,
@@ -15,35 +13,64 @@
15
13
  "@@action": true,
16
14
  }
17
15
  }
16
+ const parseTime = (timestring) => {
17
+ if (!timestring) {
18
+ return 200
19
+ }
20
+ const { base, scale } =
21
+ timestring.match(/(?<base>\d+)(?<scale>s|ms)/)?.groups ?? {}
22
+ if (base === undefined) {
23
+ return 200
24
+ }
25
+ const numeric = parseInt(base)
26
+ if (scale === "ms") {
27
+ return numeric
28
+ }
29
+ return numeric * 1000
30
+ }
18
31
  </script>
19
32
 
20
33
  <script>
21
- import wsx from "../wsx.mjs"
34
+ import { getContext } from "svelte"
35
+
36
+ import wsx from "../wsx.js"
22
37
 
23
38
  import Paper from "./paper.svelte"
39
+ import { modalContext } from "./modal.svelte"
40
+
41
+ const {
42
+ height,
43
+ type = "menu",
44
+ children,
45
+ header,
46
+ footer,
47
+ content,
48
+ ...rest
49
+ } = $props()
24
50
 
25
- export let height
26
- export let type = "menu"
51
+ const animTime = getContext(modalContext)
27
52
 
53
+ // When Svelte removes things from the DOM they are removed immediately
54
+ // unless Svelte has a transition running. Adding this prevents the drawer
55
+ // from disappearing without first letting the built in animations run.
28
56
  const trick = (node, options) => ({
29
57
  delay: 0,
30
- duration: 250,
58
+ duration: parseTime($animTime),
31
59
  css: () => "",
32
60
  })
33
61
 
34
- $: container = {
62
+ const container = $derived({
35
63
  ...defs[type],
64
+ $show: true,
36
65
  h: (type === "select") ? height : "100%",
37
66
  grid: true,
38
- }
67
+ })
68
+ const onclick = (evt) => evt.stopPropagation()
39
69
  </script>
40
- <!-- svelte-ignore a11y-click-events-have-key-events -->
41
- <!-- svelte-ignore a11y-interactive-supports-focus -->
42
- <wind-drawer-container use:wsx={container} on:click|stopPropagation
43
- role="menubar" transition:trick>
44
- <Paper r="0px" {...$$restProps}>
45
- <slot name="header" slot="header" />
46
- <slot />
47
- <slot name="footer" slot="footer" />
48
- </Paper>
70
+
71
+ <!-- svelte-ignore a11y_interactive_supports_focus -->
72
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
73
+ <wind-drawer-container use:wsx={container} {onclick} role="menubar"
74
+ transition:trick>
75
+ <Paper r="0px" {...rest} {header} {footer} {content} {children} />
49
76
  </wind-drawer-container>
@@ -1,22 +1,24 @@
1
- <svelte:options immutable />
2
-
3
1
  <script>
4
- import wsx from "../wsx.mjs"
2
+ import wsx from "../wsx.js"
5
3
 
6
- export let direction = false
7
- export let cross = "stretch"
8
- export let main = "start"
9
- export let scrollable = false
4
+ const {
5
+ direction = false,
6
+ cross = "stretch",
7
+ main = "start",
8
+ scrollable = false,
9
+ children,
10
+ ...rest
11
+ } = $props()
10
12
 
11
- $: wind = {
13
+ const wind = $derived({
12
14
  "fl.dir": direction,
13
15
  "fl.cross": cross,
14
16
  "fl.main": main,
15
17
  over: (scrollable === true) ? "auto" : null,
16
- ...$$restProps
17
- }
18
+ ...rest
19
+ })
18
20
  </script>
19
21
 
20
22
  <ws-flex use:wsx={wind}>
21
- <slot />
23
+ {@render children?.()}
22
24
  </ws-flex>
@@ -1,18 +1,20 @@
1
- <svelte:options immutable />
2
-
3
1
  <script>
4
- import wsx from "../wsx.mjs"
2
+ import wsx from "../wsx.js"
5
3
 
6
- export let direction = false
7
- export let cols = null
8
- export let rows = null
9
- export let autoCol = false
10
- export let autoRow = false
11
- export let colsFit = false
12
- export let colsFill = false
13
- export let scrollable = false
4
+ const {
5
+ direction = false,
6
+ cols = null,
7
+ rows = null,
8
+ autoCol = false,
9
+ autoRow = false,
10
+ colsFit = false,
11
+ colsFill = false,
12
+ scrollable = false,
13
+ children,
14
+ ...rest
15
+ } = $props()
14
16
 
15
- $: wind = {
17
+ const wind = $derived({
16
18
  "gr.flow": direction,
17
19
  "gr.cols": cols?.join?.(" ") ?? cols ?? false,
18
20
  "gr.rows": rows?.join?.(" ") ?? rows ?? false,
@@ -21,10 +23,10 @@
21
23
  "gr.cols-fit": colsFit,
22
24
  "gr.cols-fill": colsFill,
23
25
  over: (scrollable === true) ? "auto" : null,
24
- ...$$restProps
25
- }
26
+ ...rest
27
+ })
26
28
  </script>
27
29
 
28
30
  <ws-grid use:wsx={wind}>
29
- <slot />
31
+ {@render children?.()}
30
32
  </ws-grid>
@@ -1,23 +1,28 @@
1
- <svelte:options immutable />
2
-
3
- <script context="module">
1
+ <script module>
4
2
  let topClose = null
3
+ export const modalContext = Symbol("modal context")
5
4
  </script>
6
5
 
7
6
  <script>
8
- import { tick } from "svelte"
7
+ import { tick, setContext } from "svelte"
8
+ import { writable } from "svelte/store"
9
+
10
+ import wsx from "../wsx.js"
9
11
 
10
- export let component
12
+ const {
13
+ component,
14
+ animTime = "200ms"
15
+ } = $props()
11
16
 
12
- let modalProps = null
13
- let resolver = null
14
- let displayed = null
17
+ let modalProps = $state(null)
18
+ let resolver = $state(null)
19
+ let displayed = $state(null)
15
20
 
16
21
  const close = (value) => {
17
22
  resolver(value)
18
23
  resolver = null
19
24
  modalProps = null
20
- visible.checked = false
25
+ Component = null
21
26
  if (topClose !== close) {
22
27
  return
23
28
  }
@@ -31,30 +36,33 @@
31
36
  export const show = (props) => new Promise(
32
37
  async (resolve) => {
33
38
  modalProps = props ?? {}
34
- resolver = resolve
35
39
  topClose = topClose ?? close
40
+ Component = component
36
41
  await tick()
37
42
  setTimeout(
38
- () => visible.checked = true,
43
+ () => resolver = resolve,
39
44
  0
40
45
  )
41
46
  }
42
47
  )
43
48
 
44
- let visible = null
49
+ const animationTime = writable(animTime)
50
+ const wind = $derived({
51
+ "@anim-time": $animationTime,
52
+ $show: resolver !== null,
53
+ })
54
+ let Component = $state(null)
55
+ setContext(modalContext, animationTime)
45
56
  </script>
46
57
 
47
- <input type="checkbox" bind:this={visible} ws-x="[disp none]" />
48
- {#if resolver !== null}
49
- <!-- svelte-ignore a11y-click-events-have-key-events -->
50
- <!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
51
- <ws-modal on:click={cancel} role="dialog">
52
- <svelte:component
53
- bind:this={displayed}
54
- this={component}
55
- {...modalProps}
56
- {close}
57
- {closeToTop}
58
- />
59
- </ws-modal>
60
- {/if}
58
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
59
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
60
+ <ws-modal onclick={cancel} role="dialog" use:wsx={wind}>
61
+ <Component
62
+ bind:this={displayed}
63
+ this={component}
64
+ {...modalProps}
65
+ {close}
66
+ {closeToTop}
67
+ />
68
+ </ws-modal>
@@ -1,56 +1,54 @@
1
- <svelte:options immutable />
2
-
3
1
  <script>
4
2
  import Flex from "./flex.svelte"
5
- import wsx from "../wsx.mjs"
3
+ import wsx from "../wsx.js"
4
+ import { splitProps } from "../props.js"
6
5
 
7
- export let color
8
- export let card = false
9
- export let square = false
10
- export let layout = Flex
11
- export let scrollable = true
6
+ const {
7
+ color,
8
+ card = false,
9
+ square = false,
10
+ layout = Flex,
11
+ scrollable = true,
12
+ children,
13
+ content,
14
+ header,
15
+ footer,
16
+ ...rest
17
+ } = $props()
12
18
 
13
- $: props = Object.entries($$restProps).reduce(
14
- (p, [key, value]) => {
15
- const [target, name] =
16
- (key.startsWith("l-") === true)
17
- ? [ p.layout, key.slice(2) ]
18
- : [ p.paper, key ]
19
- target[name] = value
20
- return p
21
- },
22
- { layout: {}, paper: {} }
19
+ const props = $derived(
20
+ splitProps(rest, "l!")
23
21
  )
24
22
 
25
- $: layoutProps = {
23
+ const layoutProps = $derived({
26
24
  over: scrollable ? "auto" : false,
27
25
  $content: true,
28
- ...props.layout,
29
- }
30
- $: wind = {
26
+ ...props["l!"],
27
+ })
28
+ const wind = $derived({
31
29
  "$color": color,
32
30
  "$outline": card,
33
31
  r: square && "0px",
34
- ...props.paper,
35
- }
32
+ ...props.rest,
33
+ })
36
34
  </script>
37
35
 
38
36
  <ws-paper use:wsx={wind}>
39
- {#if $$slots.header}
37
+ {#if header}
40
38
  <div ws-x="[$header] [grid] [p 0px]">
41
- <slot name="header" />
39
+ {@render header()}
42
40
  </div>
43
41
  {/if}
44
- {#if $$slots.content}
45
- <slot name="content" />
42
+ {#if content}
43
+ {@render content()}
46
44
  {:else}
47
- <svelte:component this={layout} {...layoutProps}>
48
- <slot />
49
- </svelte:component>
45
+ <Flex {...layoutProps}>
46
+ {@render children?.()}
47
+ </Flex>
50
48
  {/if}
51
- {#if $$slots.footer}
49
+ {#if footer}
52
50
  <div ws-x="[$footer] [grid] [p 0px]">
53
- <slot name="footer" />
51
+ {@render footer()}
54
52
  </div>
55
53
  {/if}
56
54
  </ws-paper>
@@ -1,6 +1,4 @@
1
- <svelte:options immutable />
2
-
3
- <script context="module">
1
+ <script module>
4
2
  const ctxStack = Symbol("stack context")
5
3
  const ctxClose = Symbol("close context")
6
4
  </script>
@@ -9,9 +7,14 @@
9
7
  import { getContext, setContext } from "svelte"
10
8
  import { fly } from "svelte/transition"
11
9
 
12
- import wsx from "../wsx.mjs"
10
+ import wsx from "../wsx.js"
13
11
 
14
- export let width = false
12
+ const {
13
+ alignLeft = false,
14
+ width = false,
15
+ children,
16
+ ...rest
17
+ } = $props()
15
18
 
16
19
  const stack = getContext(ctxStack) ?? 0
17
20
  const animation = {
@@ -21,14 +24,15 @@
21
24
 
22
25
  setContext(ctxStack, stack + 1)
23
26
 
24
- $: wind = {
27
+ const wind = $derived({
25
28
  "@stack": stack.toString(),
26
29
  "@screen-width": width,
27
30
  "bg.c": "transparent",
28
- ...$$restProps
29
- }
31
+ $left: alignLeft,
32
+ ...rest
33
+ })
30
34
  </script>
31
35
 
32
36
  <ws-screen use:wsx={wind} transition:fly={animation}>
33
- <slot />
37
+ {@render children?.()}
34
38
  </ws-screen>
package/src/props.js ADDED
@@ -0,0 +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,4 +1,4 @@
1
- <script context="module">
1
+ <script module>
2
2
  const urlChars = "[\\w\\-\\%\\(\\)\\.@!\\*\\^\\$]"
3
3
  const urlPattern = (path, exact) => {
4
4
  const regexSource = path.replace(
@@ -15,35 +15,48 @@
15
15
  <script>
16
16
  import { getContext, setContext } from "svelte"
17
17
 
18
- import { ctx, resolve, route } from "./routing.js"
18
+ import { ctx, resolve, route } from "./routing.svelte.js"
19
19
 
20
- export let path = ""
21
- export let component = null
22
- export let props = {}
23
- export let exact = false
20
+ const {
21
+ path = "",
22
+ component = null,
23
+ props = {},
24
+ exact = false,
25
+ content,
26
+ children,
27
+ } = $props()
24
28
 
25
29
  const parent = getContext(ctx.parent) ?? "/"
26
30
  const fullPath = resolve(parent, path)
27
31
 
28
32
  setContext(ctx.parent, fullPath)
29
- $: pattern = urlPattern(fullPath, exact)
30
- $: match = $route.match(pattern)
31
- $: routeParams = Object.fromEntries(
32
- Object.entries({ ...match?.groups }).map(
33
- ([key, value]) => [key, decodeURIComponent(value)]
33
+ const pattern = $derived(
34
+ urlPattern(fullPath, exact)
35
+ )
36
+ const match = $derived(
37
+ $route.match(pattern)
38
+ )
39
+ const routeParams = $derived(
40
+ Object.fromEntries(
41
+ Object.entries({ ...match?.groups }).map(
42
+ ([key, value]) => [key, decodeURIComponent(value)]
43
+ )
34
44
  )
35
45
  )
36
- $: routeInfo = {
46
+ const routeInfo = $derived({
37
47
  params: routeParams,
38
48
  match: routeParams._path,
39
49
  route: $route,
40
- }
50
+ })
51
+ const Content = $derived(component)
41
52
  </script>
42
53
 
43
54
  {#if match !== null}
44
55
  {#key routeParams._path}
45
- <slot {routeInfo}>
46
- <svelte:component this={component} {...props} {routeInfo} />
47
- </slot>
56
+ {#if Content !== null}
57
+ <Content {...props} {routeInfo} />
58
+ {:else}
59
+ {@render (content ?? children)?.(routeInfo, props)}
60
+ {/if}
48
61
  {/key}
49
62
  {/if}
@@ -1,7 +1,7 @@
1
1
  import { onMount, getContext } from "svelte"
2
2
  import { writable, derived } from "svelte/store"
3
3
 
4
- import { hash } from "../hash.js"
4
+ import { hash } from "../hash.svelte.js"
5
5
 
6
6
  export const route = derived(
7
7
  hash,
@@ -12,6 +12,15 @@ export const route = derived(
12
12
  return hash
13
13
  }
14
14
  )
15
+ route.set = (path) => {
16
+ hash.set(path)
17
+ }
18
+ // export const route = $derived(
19
+ // hash.startsWith("/") === false ? `/${hash}` : hash
20
+ // )
21
+ // $effect(
22
+ // () => hash = route
23
+ // )
15
24
 
16
25
  export const ctx = {
17
26
  router: Symbol("Router Context"),
@@ -1,9 +1,8 @@
1
- <script context="module">
1
+ <script module>
2
2
  import { writable } from "svelte/store"
3
3
 
4
- import { stackStore } from "./routing.js"
4
+ import { stackStore } from "./routing.svelte.js"
5
5
 
6
- // const stack = writable([])
7
6
  const stack = stackStore()
8
7
 
9
8
  const updateTitle = (format, data) => {
@@ -17,10 +16,13 @@
17
16
 
18
17
  <script>
19
18
  import { onMount } from "svelte"
20
- export let data = ""
21
- export let format = null
19
+
20
+ const {
21
+ data = "",
22
+ format = null,
23
+ } = $props()
22
24
 
23
25
  stack.push(data)
24
26
 
25
- $: updateTitle(format, $stack)
27
+ $effect(() => updateTitle(format, $stack))
26
28
  </script>