@hyvnt/hyvui 0.2.0 → 0.4.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.
Files changed (181) hide show
  1. package/README.md +294 -253
  2. package/dist/components/ambient/ArcaneVein.svelte +151 -0
  3. package/dist/components/ambient/ArcaneVein.svelte.d.ts +31 -0
  4. package/dist/components/ambient/BrassFiligree.svelte +109 -0
  5. package/dist/components/ambient/BrassFiligree.svelte.d.ts +20 -0
  6. package/dist/components/ambient/CornerBrackets.svelte +91 -87
  7. package/dist/components/ambient/CornerBrackets.svelte.d.ts +8 -0
  8. package/dist/components/ambient/CrystalShard.svelte +151 -0
  9. package/dist/components/ambient/CrystalShard.svelte.d.ts +19 -0
  10. package/dist/components/ambient/DataStream.svelte +117 -94
  11. package/dist/components/ambient/DataStream.svelte.d.ts +6 -0
  12. package/dist/components/ambient/EnergyArc.svelte +189 -0
  13. package/dist/components/ambient/EnergyArc.svelte.d.ts +32 -0
  14. package/dist/components/ambient/GlyphMark.svelte +75 -69
  15. package/dist/components/ambient/GlyphMark.svelte.d.ts +6 -0
  16. package/dist/components/ambient/GridOverlay.svelte +34 -28
  17. package/dist/components/ambient/GridOverlay.svelte.d.ts +8 -0
  18. package/dist/components/ambient/HexGrid.svelte +119 -0
  19. package/dist/components/ambient/HexGrid.svelte.d.ts +21 -0
  20. package/dist/components/ambient/ParallaxLayer.svelte +45 -41
  21. package/dist/components/ambient/ParallaxLayer.svelte.d.ts +7 -0
  22. package/dist/components/ambient/ScanBand.svelte +103 -91
  23. package/dist/components/ambient/ScanBand.svelte.d.ts +8 -0
  24. package/dist/components/ambient/ShimmerCloud.svelte +180 -0
  25. package/dist/components/ambient/ShimmerCloud.svelte.d.ts +21 -0
  26. package/dist/components/ambient/SignalRing.svelte +106 -100
  27. package/dist/components/ambient/SignalRing.svelte.d.ts +6 -0
  28. package/dist/components/ambient/ThreadLine.svelte +78 -78
  29. package/dist/components/ambient/ThreadLine.svelte.d.ts +7 -0
  30. package/dist/components/ambient/Vignette.svelte +30 -26
  31. package/dist/components/ambient/Vignette.svelte.d.ts +6 -0
  32. package/dist/components/depth/DepthLayer.svelte +30 -27
  33. package/dist/components/depth/DepthLayer.svelte.d.ts +8 -0
  34. package/dist/components/depth/DepthStage.svelte +67 -62
  35. package/dist/components/depth/DepthStage.svelte.d.ts +8 -0
  36. package/dist/components/depth/FloatCard.svelte +129 -104
  37. package/dist/components/depth/FloatCard.svelte.d.ts +8 -0
  38. package/dist/components/depth/HorizonGrid.svelte +241 -160
  39. package/dist/components/depth/HorizonGrid.svelte.d.ts +9 -0
  40. package/dist/components/depth/Plinth.svelte +62 -57
  41. package/dist/components/depth/Plinth.svelte.d.ts +10 -0
  42. package/dist/components/display/Avatar.svelte +69 -69
  43. package/dist/components/display/Avatar.svelte.d.ts +5 -0
  44. package/dist/components/display/Badge.svelte +75 -63
  45. package/dist/components/display/Badge.svelte.d.ts +6 -0
  46. package/dist/components/display/Blockquote.svelte +35 -34
  47. package/dist/components/display/Blockquote.svelte.d.ts +4 -0
  48. package/dist/components/display/CodeBlock.svelte +76 -76
  49. package/dist/components/display/CodeBlock.svelte.d.ts +5 -0
  50. package/dist/components/display/MetricCard.svelte +100 -83
  51. package/dist/components/display/MetricCard.svelte.d.ts +6 -0
  52. package/dist/components/display/Table.svelte +106 -104
  53. package/dist/components/display/Table.svelte.d.ts +7 -0
  54. package/dist/components/feedback/Alert.svelte +95 -76
  55. package/dist/components/feedback/Alert.svelte.d.ts +6 -0
  56. package/dist/components/feedback/EmptyState.svelte +75 -68
  57. package/dist/components/feedback/EmptyState.svelte.d.ts +7 -0
  58. package/dist/components/feedback/ErrorState.svelte +78 -73
  59. package/dist/components/feedback/ErrorState.svelte.d.ts +5 -0
  60. package/dist/components/feedback/Skeleton.svelte +58 -52
  61. package/dist/components/feedback/Skeleton.svelte.d.ts +6 -0
  62. package/dist/components/feedback/StatusDot.svelte +84 -54
  63. package/dist/components/feedback/StatusDot.svelte.d.ts +6 -0
  64. package/dist/components/feedback/StatusLine.svelte +128 -122
  65. package/dist/components/feedback/StatusLine.svelte.d.ts +6 -0
  66. package/dist/components/feedback/Toast.svelte +144 -136
  67. package/dist/components/feedback/Toast.svelte.d.ts +10 -0
  68. package/dist/components/inputs/Button.svelte +310 -237
  69. package/dist/components/inputs/Button.svelte.d.ts +8 -0
  70. package/dist/components/inputs/Checkbox.svelte +109 -105
  71. package/dist/components/inputs/Checkbox.svelte.d.ts +5 -0
  72. package/dist/components/inputs/FileUpload.svelte +170 -163
  73. package/dist/components/inputs/FileUpload.svelte.d.ts +5 -0
  74. package/dist/components/inputs/Input.svelte +153 -147
  75. package/dist/components/inputs/Input.svelte.d.ts +7 -0
  76. package/dist/components/inputs/Select.svelte +164 -150
  77. package/dist/components/inputs/Select.svelte.d.ts +8 -0
  78. package/dist/components/inputs/Textarea.svelte +160 -154
  79. package/dist/components/inputs/Textarea.svelte.d.ts +6 -0
  80. package/dist/components/inputs/Toggle.svelte +125 -120
  81. package/dist/components/inputs/Toggle.svelte.d.ts +5 -0
  82. package/dist/components/layout/Card.svelte +81 -76
  83. package/dist/components/layout/Card.svelte.d.ts +11 -0
  84. package/dist/components/layout/Drawer.svelte +140 -109
  85. package/dist/components/layout/Drawer.svelte.d.ts +6 -0
  86. package/dist/components/layout/Grid.svelte +128 -43
  87. package/dist/components/layout/Grid.svelte.d.ts +18 -2
  88. package/dist/components/layout/Modal.svelte +191 -159
  89. package/dist/components/layout/Modal.svelte.d.ts +10 -0
  90. package/dist/components/layout/Panel.svelte +58 -54
  91. package/dist/components/layout/Panel.svelte.d.ts +9 -0
  92. package/dist/components/layout/Popover.svelte +188 -67
  93. package/dist/components/layout/Popover.svelte.d.ts +19 -1
  94. package/dist/components/layout/Stack.svelte +65 -53
  95. package/dist/components/layout/Stack.svelte.d.ts +12 -0
  96. package/dist/components/navigation/Breadcrumb.svelte +78 -73
  97. package/dist/components/navigation/Breadcrumb.svelte.d.ts +8 -0
  98. package/dist/components/navigation/DropdownMenu.svelte +179 -124
  99. package/dist/components/navigation/DropdownMenu.svelte.d.ts +24 -2
  100. package/dist/components/navigation/SidebarNav.svelte +96 -90
  101. package/dist/components/navigation/SidebarNav.svelte.d.ts +9 -0
  102. package/dist/components/navigation/Tabs.svelte +106 -86
  103. package/dist/components/navigation/Tabs.svelte.d.ts +8 -0
  104. package/dist/components/navigation/Topbar.svelte +94 -85
  105. package/dist/components/navigation/Topbar.svelte.d.ts +9 -0
  106. package/dist/components/patterns/ActionBar.svelte +82 -76
  107. package/dist/components/patterns/ActionBar.svelte.d.ts +10 -0
  108. package/dist/components/patterns/ChapterMark.svelte +152 -0
  109. package/dist/components/patterns/ChapterMark.svelte.d.ts +19 -0
  110. package/dist/components/patterns/ConfirmDialog.svelte +75 -64
  111. package/dist/components/patterns/ConfirmDialog.svelte.d.ts +12 -0
  112. package/dist/components/patterns/DepthPortal.svelte +123 -0
  113. package/dist/components/patterns/DepthPortal.svelte.d.ts +24 -0
  114. package/dist/components/patterns/Manifesto.svelte +171 -0
  115. package/dist/components/patterns/Manifesto.svelte.d.ts +25 -0
  116. package/dist/components/patterns/PageHeader.svelte +117 -114
  117. package/dist/components/patterns/PageHeader.svelte.d.ts +8 -0
  118. package/dist/components/patterns/PullQuote.svelte +145 -0
  119. package/dist/components/patterns/PullQuote.svelte.d.ts +23 -0
  120. package/dist/components/patterns/RegisterSwitcher.svelte +132 -0
  121. package/dist/components/patterns/RegisterSwitcher.svelte.d.ts +21 -0
  122. package/dist/components/patterns/SearchBar.svelte +59 -59
  123. package/dist/components/patterns/SearchBar.svelte.d.ts +5 -0
  124. package/dist/components/patterns/ShowcaseFrame.svelte +117 -0
  125. package/dist/components/patterns/ShowcaseFrame.svelte.d.ts +28 -0
  126. package/dist/components/patterns/TerminalBoot.svelte +118 -104
  127. package/dist/components/patterns/TerminalBoot.svelte.d.ts +12 -0
  128. package/dist/components/primitives/Divider.svelte +56 -29
  129. package/dist/components/primitives/Divider.svelte.d.ts +5 -0
  130. package/dist/components/primitives/Icon.svelte +53 -49
  131. package/dist/components/primitives/Icon.svelte.d.ts +9 -0
  132. package/dist/components/primitives/Label.svelte +45 -44
  133. package/dist/components/primitives/Label.svelte.d.ts +6 -0
  134. package/dist/components/primitives/Surface.svelte +154 -87
  135. package/dist/components/primitives/Surface.svelte.d.ts +7 -0
  136. package/dist/components/primitives/Text.svelte +130 -98
  137. package/dist/components/primitives/Text.svelte.d.ts +7 -0
  138. package/dist/components/scenes/ArchiveScene.svelte +102 -95
  139. package/dist/components/scenes/ArchiveScene.svelte.d.ts +17 -1
  140. package/dist/components/scenes/DepthScene.svelte +128 -0
  141. package/dist/components/scenes/DepthScene.svelte.d.ts +36 -0
  142. package/dist/components/scenes/LogScene.svelte +86 -77
  143. package/dist/components/scenes/LogScene.svelte.d.ts +14 -0
  144. package/dist/components/scenes/NarrativeScene.svelte +100 -92
  145. package/dist/components/scenes/NarrativeScene.svelte.d.ts +9 -0
  146. package/dist/components/scenes/ReadoutScene.svelte +131 -107
  147. package/dist/components/scenes/ReadoutScene.svelte.d.ts +14 -1
  148. package/dist/components/scenes/StageScene.svelte +111 -104
  149. package/dist/components/scenes/StageScene.svelte.d.ts +14 -0
  150. package/dist/components/system/AppShell.svelte +62 -0
  151. package/dist/components/system/AppShell.svelte.d.ts +32 -0
  152. package/dist/examples/ArcaneShard.svelte +364 -0
  153. package/dist/examples/ArcaneShard.svelte.d.ts +3 -0
  154. package/dist/examples/FieldReport.svelte +226 -223
  155. package/dist/examples/HextechForge.svelte +324 -0
  156. package/dist/examples/HextechForge.svelte.d.ts +3 -0
  157. package/dist/examples/ObservationDeck.svelte +333 -317
  158. package/dist/examples/SignalLost.svelte +191 -191
  159. package/dist/index.d.ts +15 -1
  160. package/dist/index.js +16 -1
  161. package/dist/styles.css +115 -0
  162. package/dist/system/actions/echo.js +21 -12
  163. package/dist/system/actions/resolve.js +28 -14
  164. package/dist/system/actions/reveal.js +2 -2
  165. package/dist/system/actions/surface.js +12 -2
  166. package/dist/system/depth/depth.css +49 -49
  167. package/dist/system/depth/depth.js +1 -1
  168. package/dist/system/expressions.css +80 -80
  169. package/dist/system/override-template.css +72 -72
  170. package/dist/system/register.css +74 -74
  171. package/dist/system/register.d.ts +1 -1
  172. package/dist/system/register.js +5 -1
  173. package/dist/system/scroll-lock.d.ts +6 -0
  174. package/dist/system/scroll-lock.js +23 -0
  175. package/dist/tokens/arcane.css +96 -0
  176. package/dist/tokens/hextech.css +96 -0
  177. package/dist/tokens/tokens.css +102 -86
  178. package/dist/tokens/tokens.d.ts +41 -0
  179. package/dist/tokens/tokens.js +44 -3
  180. package/dist/utils/motion.js +1 -1
  181. package/package.json +71 -60
@@ -1,122 +1,128 @@
1
- <script lang="ts">
2
- import { cn } from '../../utils/cn.js';
3
-
4
- interface Props {
5
- /** Status determining the glyph and color. */
6
- status?: 'ok' | 'pend' | 'warn' | 'fail';
7
- /** Message text displayed after the status glyph. */
8
- message?: string;
9
- /** Controls visibility with stagger animation. */
10
- visible?: boolean;
11
- /** When true, the full line inherits the status tone. */
12
- tone?: 'split' | 'line';
13
- /** Shows a blinking cursor after the message. */
14
- cursor?: boolean;
15
- /** Additional CSS classes. */
16
- class?: string;
17
- }
18
-
19
- let {
20
- status = 'ok',
21
- message = '',
22
- visible = false,
23
- tone = 'split',
24
- cursor = false,
25
- class: className = '',
26
- }: Props = $props();
27
-
28
- const glyphMap: Record<string, string> = {
29
- ok: '[ OK ]',
30
- pend: '[ .. ]',
31
- warn: '[WARN]',
32
- fail: '[FAIL]',
33
- };
34
-
35
- const colorMap: Record<string, string> = {
36
- ok: 'var(--status-ok)',
37
- pend: 'var(--status-pend)',
38
- warn: 'var(--status-warn)',
39
- fail: 'var(--status-fail)',
40
- };
41
- </script>
42
-
43
- <div
44
- class={cn(
45
- 'hyvui-status-line',
46
- visible && 'hyvui-status-line-visible',
47
- tone === 'line' && 'hyvui-status-line-tone-line',
48
- className
49
- )}
50
- style:color={tone === 'line' ? colorMap[status] : undefined}
51
- aria-live="polite"
52
- >
53
- <span class="hyvui-status-glyph" style:color={tone === 'split' ? colorMap[status] : undefined}>
54
- {glyphMap[status]}
55
- </span>
56
- <span class="hyvui-status-message">{message}</span>
57
- {#if cursor}
58
- <span class="hyvui-status-cursor" aria-hidden="true">_</span>
59
- {/if}
60
- </div>
61
-
62
- <style>
63
- .hyvui-status-line {
64
- display: flex;
65
- align-items: baseline;
66
- gap: 0.75rem;
67
- font-family: var(--font-mono);
68
- font-size: 0.82rem;
69
- opacity: 0;
70
- transform: translateY(6px);
71
- transition:
72
- opacity 0.5s cubic-bezier(0.22, 1, 0.36, 1),
73
- transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
74
- }
75
-
76
- .hyvui-status-line-visible {
77
- opacity: 1;
78
- transform: translateY(0);
79
- }
80
-
81
- .hyvui-status-glyph {
82
- font-weight: 400;
83
- white-space: nowrap;
84
- flex-shrink: 0;
85
- }
86
-
87
- .hyvui-status-message {
88
- color: var(--text-soft);
89
- }
90
-
91
- .hyvui-status-line-tone-line .hyvui-status-message,
92
- .hyvui-status-line-tone-line .hyvui-status-glyph {
93
- color: inherit;
94
- }
95
-
96
- .hyvui-status-cursor {
97
- color: var(--muted-strong);
98
- animation: blink 1s step-end infinite;
99
- margin-left: 2px;
100
- }
101
-
102
- @keyframes blink {
103
- 50% {
104
- opacity: 0;
105
- }
106
- }
107
-
108
- @media (prefers-reduced-motion: reduce) {
109
- .hyvui-status-line {
110
- transition: none;
111
- transform: none;
112
- }
113
-
114
- .hyvui-status-line-visible {
115
- opacity: 1;
116
- }
117
-
118
- .hyvui-status-cursor {
119
- animation: none;
120
- }
121
- }
122
- </style>
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+
4
+ /**
5
+ * @example
6
+ * <StatusLine status="ok" message="database connected" visible={true} />
7
+ * <StatusLine status="pend" message="waiting for signal" visible={show} cursor />
8
+ * <StatusLine status="fail" message="stream interrupted" visible={true} tone="line" />
9
+ */
10
+ interface Props {
11
+ /** Status determining the glyph and color. */
12
+ status?: 'ok' | 'pend' | 'warn' | 'fail';
13
+ /** Message text displayed after the status glyph. */
14
+ message?: string;
15
+ /** Controls visibility with stagger animation. */
16
+ visible?: boolean;
17
+ /** When true, the full line inherits the status tone. */
18
+ tone?: 'split' | 'line';
19
+ /** Shows a blinking cursor after the message. */
20
+ cursor?: boolean;
21
+ /** Additional CSS classes. */
22
+ class?: string;
23
+ }
24
+
25
+ let {
26
+ status = 'ok',
27
+ message = '',
28
+ visible = false,
29
+ tone = 'split',
30
+ cursor = false,
31
+ class: className = ''
32
+ }: Props = $props();
33
+
34
+ const glyphMap: Record<string, string> = {
35
+ ok: '[ OK ]',
36
+ pend: '[ .. ]',
37
+ warn: '[WARN]',
38
+ fail: '[FAIL]'
39
+ };
40
+
41
+ const colorMap: Record<string, string> = {
42
+ ok: 'var(--status-ok)',
43
+ pend: 'var(--status-pend)',
44
+ warn: 'var(--status-warn)',
45
+ fail: 'var(--status-fail)'
46
+ };
47
+ </script>
48
+
49
+ <div
50
+ class={cn(
51
+ 'hyvui-status-line',
52
+ visible && 'hyvui-status-line-visible',
53
+ tone === 'line' && 'hyvui-status-line-tone-line',
54
+ className
55
+ )}
56
+ style:color={tone === 'line' ? colorMap[status] : undefined}
57
+ aria-live="polite"
58
+ >
59
+ <span class="hyvui-status-glyph" style:color={tone === 'split' ? colorMap[status] : undefined}>
60
+ {glyphMap[status]}
61
+ </span>
62
+ <span class="hyvui-status-message">{message}</span>
63
+ {#if cursor}
64
+ <span class="hyvui-status-cursor" aria-hidden="true">_</span>
65
+ {/if}
66
+ </div>
67
+
68
+ <style>
69
+ .hyvui-status-line {
70
+ display: flex;
71
+ align-items: baseline;
72
+ gap: 0.75rem;
73
+ font-family: var(--font-mono);
74
+ font-size: 0.82rem;
75
+ opacity: 0;
76
+ transform: translateY(6px);
77
+ transition:
78
+ opacity var(--transition-smooth),
79
+ transform var(--transition-smooth);
80
+ }
81
+
82
+ .hyvui-status-line-visible {
83
+ opacity: 1;
84
+ transform: translateY(0);
85
+ }
86
+
87
+ .hyvui-status-glyph {
88
+ font-weight: 400;
89
+ white-space: nowrap;
90
+ flex-shrink: 0;
91
+ }
92
+
93
+ .hyvui-status-message {
94
+ color: var(--text-soft);
95
+ }
96
+
97
+ .hyvui-status-line-tone-line .hyvui-status-message,
98
+ .hyvui-status-line-tone-line .hyvui-status-glyph {
99
+ color: inherit;
100
+ }
101
+
102
+ .hyvui-status-cursor {
103
+ color: var(--muted-strong);
104
+ animation: blink 1s step-end infinite;
105
+ margin-left: 2px;
106
+ }
107
+
108
+ @keyframes blink {
109
+ 50% {
110
+ opacity: 0;
111
+ }
112
+ }
113
+
114
+ @media (prefers-reduced-motion: reduce) {
115
+ .hyvui-status-line {
116
+ transition: none;
117
+ transform: none;
118
+ }
119
+
120
+ .hyvui-status-line-visible {
121
+ opacity: 1;
122
+ }
123
+
124
+ .hyvui-status-cursor {
125
+ animation: none;
126
+ }
127
+ }
128
+ </style>
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @example
3
+ * <StatusLine status="ok" message="database connected" visible={true} />
4
+ * <StatusLine status="pend" message="waiting for signal" visible={show} cursor />
5
+ * <StatusLine status="fail" message="stream interrupted" visible={true} tone="line" />
6
+ */
1
7
  interface Props {
2
8
  /** Status determining the glyph and color. */
3
9
  status?: 'ok' | 'pend' | 'warn' | 'fail';
@@ -1,136 +1,144 @@
1
- <script lang="ts" module>
2
- import { writable } from 'svelte/store';
3
-
4
- interface ToastItem {
5
- id: number;
6
- message: string;
7
- variant: 'ok' | 'pend' | 'warn' | 'fail';
8
- duration: number;
9
- }
10
-
11
- let nextId = 0;
12
- const _items = writable<ToastItem[]>([]);
13
-
14
- /** Toast store. Use toastStore.push() to show a toast. */
15
- export const toastStore = {
16
- subscribe: _items.subscribe,
17
- /** Push a new toast notification. */
18
- push(message: string, variant: 'ok' | 'pend' | 'warn' | 'fail' = 'ok', duration = 4000) {
19
- const id = nextId++;
20
- _items.update((items) => [...items, { id, message, variant, duration }]);
21
- setTimeout(() => {
22
- _items.update((items) => items.filter((t) => t.id !== id));
23
- }, duration);
24
- },
25
- };
26
- </script>
27
-
28
- <script lang="ts">
29
- import { cn } from '../../utils/cn.js';
30
- import { flip } from 'svelte/animate';
31
-
32
- interface Props {
33
- /** Position of the toast stack. */
34
- position?: 'bottom-right' | 'bottom-center';
35
- /** Additional CSS classes. */
36
- class?: string;
37
- }
38
-
39
- let {
40
- position = 'bottom-right',
41
- class: className = '',
42
- }: Props = $props();
43
-
44
- let items: ToastItem[] = $state([]);
45
-
46
- toastStore.subscribe((v) => {
47
- items = v;
48
- });
49
-
50
- const statusColorMap: Record<string, string> = {
51
- ok: 'var(--status-ok)',
52
- pend: 'var(--status-pend)',
53
- warn: 'var(--status-warn)',
54
- fail: 'var(--status-fail)',
55
- };
56
- </script>
57
-
58
- <div
59
- class={cn(
60
- 'hyvui-toast-container',
61
- position === 'bottom-center' && 'hyvui-toast-center',
62
- className
63
- )}
64
- aria-live="polite"
65
- >
66
- {#each items as item (item.id)}
67
- <div class="hyvui-toast" animate:flip={{ duration: 200 }}>
68
- <span
69
- class="hyvui-toast-dot"
70
- style:background-color={statusColorMap[item.variant]}
71
- ></span>
72
- <span class="hyvui-toast-message">{item.message}</span>
73
- </div>
74
- {/each}
75
- </div>
76
-
77
- <style>
78
- .hyvui-toast-container {
79
- position: fixed;
80
- bottom: 1.5rem;
81
- right: 1.5rem;
82
- z-index: var(--z-toast);
83
- display: flex;
84
- flex-direction: column;
85
- gap: 0.5rem;
86
- pointer-events: none;
87
- }
88
-
89
- .hyvui-toast-center {
90
- right: auto;
91
- left: 50%;
92
- transform: translateX(-50%);
93
- }
94
-
95
- .hyvui-toast {
96
- background: var(--surface-card);
97
- border: 1px solid rgba(255, 255, 255, 0.05);
98
- box-shadow: var(--shadow-veil);
99
- padding: 0.625rem 1rem;
100
- display: flex;
101
- align-items: center;
102
- gap: 0.625rem;
103
- pointer-events: auto;
104
- animation: toast-in 0.35s cubic-bezier(0.22, 1, 0.36, 1);
105
- }
106
-
107
- .hyvui-toast-dot {
108
- width: 6px;
109
- height: 6px;
110
- border-radius: 50%;
111
- flex-shrink: 0;
112
- }
113
-
114
- .hyvui-toast-message {
115
- font-family: var(--font-body);
116
- font-size: 0.88rem;
117
- color: var(--text-soft);
118
- }
119
-
120
- @keyframes toast-in {
121
- from {
122
- opacity: 0;
123
- transform: translateY(8px);
124
- }
125
- to {
126
- opacity: 1;
127
- transform: translateY(0);
128
- }
129
- }
130
-
131
- @media (prefers-reduced-motion: reduce) {
132
- .hyvui-toast {
133
- animation: none;
134
- }
135
- }
136
- </style>
1
+ <script lang="ts" module>
2
+ import { writable } from 'svelte/store';
3
+
4
+ interface ToastItem {
5
+ id: number;
6
+ message: string;
7
+ variant: 'ok' | 'pend' | 'warn' | 'fail';
8
+ duration: number;
9
+ }
10
+
11
+ let nextId = 0;
12
+ const _items = writable<ToastItem[]>([]);
13
+
14
+ /** Toast store. Use toastStore.push() to show a toast. */
15
+ export const toastStore = {
16
+ subscribe: _items.subscribe,
17
+ /** Push a new toast notification. */
18
+ push(message: string, variant: 'ok' | 'pend' | 'warn' | 'fail' = 'ok', duration = 4000) {
19
+ const id = nextId++;
20
+ _items.update((items) => [...items, { id, message, variant, duration }]);
21
+ setTimeout(() => {
22
+ _items.update((items) => items.filter((t) => t.id !== id));
23
+ }, duration);
24
+ }
25
+ };
26
+ </script>
27
+
28
+ <script lang="ts">
29
+ import { cn } from '../../utils/cn.js';
30
+ import { flip } from 'svelte/animate';
31
+
32
+ /**
33
+ * Place once in your root layout. Trigger toasts anywhere via `toastStore.push(message, variant)`.
34
+ * Variants: 'ok' | 'pend' | 'warn' | 'fail'
35
+ * @example
36
+ * <Toast position="bottom-right" />
37
+ * @example
38
+ * toastStore.push('file saved', 'ok');
39
+ * toastStore.push('connection lost', 'fail');
40
+ * toastStore.push('uploading...', 'pend', 8000);
41
+ */
42
+ interface Props {
43
+ /** Position of the toast stack. */
44
+ position?: 'bottom-right' | 'bottom-center';
45
+ /** Additional CSS classes. */
46
+ class?: string;
47
+ }
48
+
49
+ let { position = 'bottom-right', class: className = '' }: Props = $props();
50
+
51
+ let items: ToastItem[] = $state([]);
52
+
53
+ $effect(() => {
54
+ const unsub = toastStore.subscribe((v) => {
55
+ items = v;
56
+ });
57
+ return unsub;
58
+ });
59
+
60
+ const statusColorMap: Record<string, string> = {
61
+ ok: 'var(--status-ok)',
62
+ pend: 'var(--status-pend)',
63
+ warn: 'var(--status-warn)',
64
+ fail: 'var(--status-fail)'
65
+ };
66
+ </script>
67
+
68
+ <div
69
+ class={cn(
70
+ 'hyvui-toast-container',
71
+ position === 'bottom-center' && 'hyvui-toast-center',
72
+ className
73
+ )}
74
+ aria-live="polite"
75
+ >
76
+ {#each items as item (item.id)}
77
+ <div class="hyvui-toast" animate:flip={{ duration: 200 }}>
78
+ <span class="hyvui-toast-dot" style:background-color={statusColorMap[item.variant]}></span>
79
+ <span class="hyvui-toast-message">{item.message}</span>
80
+ </div>
81
+ {/each}
82
+ </div>
83
+
84
+ <style>
85
+ .hyvui-toast-container {
86
+ position: fixed;
87
+ bottom: 1.5rem;
88
+ right: 1.5rem;
89
+ z-index: var(--z-toast);
90
+ display: flex;
91
+ flex-direction: column;
92
+ gap: 0.5rem;
93
+ pointer-events: none;
94
+ }
95
+
96
+ .hyvui-toast-center {
97
+ right: auto;
98
+ left: 50%;
99
+ transform: translateX(-50%);
100
+ }
101
+
102
+ .hyvui-toast {
103
+ background: var(--surface-card);
104
+ border: 1px solid rgba(255, 255, 255, 0.05);
105
+ box-shadow: var(--shadow-veil);
106
+ padding: 0.625rem 1rem;
107
+ display: flex;
108
+ align-items: center;
109
+ gap: 0.625rem;
110
+ pointer-events: auto;
111
+ backface-visibility: hidden;
112
+ animation: toast-in 0.35s cubic-bezier(0.22, 1, 0.36, 1);
113
+ }
114
+
115
+ .hyvui-toast-dot {
116
+ width: 6px;
117
+ height: 6px;
118
+ border-radius: 50%;
119
+ flex-shrink: 0;
120
+ }
121
+
122
+ .hyvui-toast-message {
123
+ font-family: var(--font-body);
124
+ font-size: 0.88rem;
125
+ color: var(--text-soft);
126
+ }
127
+
128
+ @keyframes toast-in {
129
+ from {
130
+ opacity: 0;
131
+ transform: translateY(8px);
132
+ }
133
+ to {
134
+ opacity: 1;
135
+ transform: translateY(0);
136
+ }
137
+ }
138
+
139
+ @media (prefers-reduced-motion: reduce) {
140
+ .hyvui-toast {
141
+ animation: none;
142
+ }
143
+ }
144
+ </style>
@@ -10,6 +10,16 @@ export declare const toastStore: {
10
10
  /** Push a new toast notification. */
11
11
  push(message: string, variant?: "ok" | "pend" | "warn" | "fail", duration?: number): void;
12
12
  };
13
+ /**
14
+ * Place once in your root layout. Trigger toasts anywhere via `toastStore.push(message, variant)`.
15
+ * Variants: 'ok' | 'pend' | 'warn' | 'fail'
16
+ * @example
17
+ * <Toast position="bottom-right" />
18
+ * @example
19
+ * toastStore.push('file saved', 'ok');
20
+ * toastStore.push('connection lost', 'fail');
21
+ * toastStore.push('uploading...', 'pend', 8000);
22
+ */
13
23
  interface Props {
14
24
  /** Position of the toast stack. */
15
25
  position?: 'bottom-right' | 'bottom-center';