@hyvnt/hyvui 0.1.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 (157) hide show
  1. package/README.md +253 -0
  2. package/dist/components/ambient/CornerBrackets.svelte +87 -0
  3. package/dist/components/ambient/CornerBrackets.svelte.d.ts +11 -0
  4. package/dist/components/ambient/DataStream.svelte +94 -0
  5. package/dist/components/ambient/DataStream.svelte.d.ts +13 -0
  6. package/dist/components/ambient/GlyphMark.svelte +69 -0
  7. package/dist/components/ambient/GlyphMark.svelte.d.ts +13 -0
  8. package/dist/components/ambient/GridOverlay.svelte +28 -0
  9. package/dist/components/ambient/GridOverlay.svelte.d.ts +7 -0
  10. package/dist/components/ambient/ParallaxLayer.svelte +41 -0
  11. package/dist/components/ambient/ParallaxLayer.svelte.d.ts +12 -0
  12. package/dist/components/ambient/ScanBand.svelte +91 -0
  13. package/dist/components/ambient/ScanBand.svelte.d.ts +17 -0
  14. package/dist/components/ambient/SignalRing.svelte +100 -0
  15. package/dist/components/ambient/SignalRing.svelte.d.ts +13 -0
  16. package/dist/components/ambient/ThreadLine.svelte +78 -0
  17. package/dist/components/ambient/ThreadLine.svelte.d.ts +17 -0
  18. package/dist/components/ambient/Vignette.svelte +26 -0
  19. package/dist/components/ambient/Vignette.svelte.d.ts +7 -0
  20. package/dist/components/depth/DepthLayer.svelte +27 -0
  21. package/dist/components/depth/DepthLayer.svelte.d.ts +15 -0
  22. package/dist/components/depth/DepthStage.svelte +62 -0
  23. package/dist/components/depth/DepthStage.svelte.d.ts +19 -0
  24. package/dist/components/depth/FloatCard.svelte +104 -0
  25. package/dist/components/depth/FloatCard.svelte.d.ts +14 -0
  26. package/dist/components/depth/HorizonGrid.svelte +160 -0
  27. package/dist/components/depth/HorizonGrid.svelte.d.ts +15 -0
  28. package/dist/components/depth/Plinth.svelte +57 -0
  29. package/dist/components/depth/Plinth.svelte.d.ts +13 -0
  30. package/dist/components/display/Avatar.svelte +69 -0
  31. package/dist/components/display/Avatar.svelte.d.ts +13 -0
  32. package/dist/components/display/Badge.svelte +63 -0
  33. package/dist/components/display/Badge.svelte.d.ts +12 -0
  34. package/dist/components/display/Blockquote.svelte +34 -0
  35. package/dist/components/display/Blockquote.svelte.d.ts +10 -0
  36. package/dist/components/display/CodeBlock.svelte +76 -0
  37. package/dist/components/display/CodeBlock.svelte.d.ts +13 -0
  38. package/dist/components/display/MetricCard.svelte +83 -0
  39. package/dist/components/display/MetricCard.svelte.d.ts +15 -0
  40. package/dist/components/display/Table.svelte +104 -0
  41. package/dist/components/display/Table.svelte.d.ts +19 -0
  42. package/dist/components/feedback/Alert.svelte +76 -0
  43. package/dist/components/feedback/Alert.svelte.d.ts +14 -0
  44. package/dist/components/feedback/EmptyState.svelte +68 -0
  45. package/dist/components/feedback/EmptyState.svelte.d.ts +14 -0
  46. package/dist/components/feedback/ErrorState.svelte +73 -0
  47. package/dist/components/feedback/ErrorState.svelte.d.ts +15 -0
  48. package/dist/components/feedback/Skeleton.svelte +52 -0
  49. package/dist/components/feedback/Skeleton.svelte.d.ts +13 -0
  50. package/dist/components/feedback/StatusDot.svelte +54 -0
  51. package/dist/components/feedback/StatusDot.svelte.d.ts +13 -0
  52. package/dist/components/feedback/StatusLine.svelte +122 -0
  53. package/dist/components/feedback/StatusLine.svelte.d.ts +17 -0
  54. package/dist/components/feedback/Toast.svelte +136 -0
  55. package/dist/components/feedback/Toast.svelte.d.ts +21 -0
  56. package/dist/components/inputs/Button.svelte +237 -0
  57. package/dist/components/inputs/Button.svelte.d.ts +30 -0
  58. package/dist/components/inputs/Checkbox.svelte +105 -0
  59. package/dist/components/inputs/Checkbox.svelte.d.ts +15 -0
  60. package/dist/components/inputs/FileUpload.svelte +163 -0
  61. package/dist/components/inputs/FileUpload.svelte.d.ts +17 -0
  62. package/dist/components/inputs/Input.svelte +147 -0
  63. package/dist/components/inputs/Input.svelte.d.ts +25 -0
  64. package/dist/components/inputs/Select.svelte +150 -0
  65. package/dist/components/inputs/Select.svelte.d.ts +23 -0
  66. package/dist/components/inputs/Textarea.svelte +154 -0
  67. package/dist/components/inputs/Textarea.svelte.d.ts +25 -0
  68. package/dist/components/inputs/Toggle.svelte +120 -0
  69. package/dist/components/inputs/Toggle.svelte.d.ts +15 -0
  70. package/dist/components/layout/Card.svelte +76 -0
  71. package/dist/components/layout/Card.svelte.d.ts +16 -0
  72. package/dist/components/layout/Drawer.svelte +109 -0
  73. package/dist/components/layout/Drawer.svelte.d.ts +18 -0
  74. package/dist/components/layout/Grid.svelte +43 -0
  75. package/dist/components/layout/Grid.svelte.d.ts +16 -0
  76. package/dist/components/layout/Modal.svelte +159 -0
  77. package/dist/components/layout/Modal.svelte.d.ts +20 -0
  78. package/dist/components/layout/Panel.svelte +54 -0
  79. package/dist/components/layout/Panel.svelte.d.ts +14 -0
  80. package/dist/components/layout/Popover.svelte +67 -0
  81. package/dist/components/layout/Popover.svelte.d.ts +14 -0
  82. package/dist/components/layout/Stack.svelte +53 -0
  83. package/dist/components/layout/Stack.svelte.d.ts +22 -0
  84. package/dist/components/navigation/Breadcrumb.svelte +73 -0
  85. package/dist/components/navigation/Breadcrumb.svelte.d.ts +13 -0
  86. package/dist/components/navigation/DropdownMenu.svelte +124 -0
  87. package/dist/components/navigation/DropdownMenu.svelte.d.ts +19 -0
  88. package/dist/components/navigation/SidebarNav.svelte +90 -0
  89. package/dist/components/navigation/SidebarNav.svelte.d.ts +16 -0
  90. package/dist/components/navigation/Tabs.svelte +86 -0
  91. package/dist/components/navigation/Tabs.svelte.d.ts +17 -0
  92. package/dist/components/navigation/Topbar.svelte +85 -0
  93. package/dist/components/navigation/Topbar.svelte.d.ts +14 -0
  94. package/dist/components/patterns/ActionBar.svelte +76 -0
  95. package/dist/components/patterns/ActionBar.svelte.d.ts +14 -0
  96. package/dist/components/patterns/ConfirmDialog.svelte +64 -0
  97. package/dist/components/patterns/ConfirmDialog.svelte.d.ts +23 -0
  98. package/dist/components/patterns/PageHeader.svelte +114 -0
  99. package/dist/components/patterns/PageHeader.svelte.d.ts +16 -0
  100. package/dist/components/patterns/SearchBar.svelte +59 -0
  101. package/dist/components/patterns/SearchBar.svelte.d.ts +15 -0
  102. package/dist/components/patterns/TerminalBoot.svelte +104 -0
  103. package/dist/components/patterns/TerminalBoot.svelte.d.ts +25 -0
  104. package/dist/components/primitives/Divider.svelte +29 -0
  105. package/dist/components/primitives/Divider.svelte.d.ts +9 -0
  106. package/dist/components/primitives/Icon.svelte +49 -0
  107. package/dist/components/primitives/Icon.svelte.d.ts +14 -0
  108. package/dist/components/primitives/Label.svelte +44 -0
  109. package/dist/components/primitives/Label.svelte.d.ts +14 -0
  110. package/dist/components/primitives/Surface.svelte +87 -0
  111. package/dist/components/primitives/Surface.svelte.d.ts +16 -0
  112. package/dist/components/primitives/Text.svelte +98 -0
  113. package/dist/components/primitives/Text.svelte.d.ts +19 -0
  114. package/dist/components/primitives/text.d.ts +1 -0
  115. package/dist/components/primitives/text.js +1 -0
  116. package/dist/components/scenes/ArchiveScene.svelte +95 -0
  117. package/dist/components/scenes/ArchiveScene.svelte.d.ts +16 -0
  118. package/dist/components/scenes/LogScene.svelte +77 -0
  119. package/dist/components/scenes/LogScene.svelte.d.ts +14 -0
  120. package/dist/components/scenes/NarrativeScene.svelte +92 -0
  121. package/dist/components/scenes/NarrativeScene.svelte.d.ts +16 -0
  122. package/dist/components/scenes/ReadoutScene.svelte +107 -0
  123. package/dist/components/scenes/ReadoutScene.svelte.d.ts +16 -0
  124. package/dist/components/scenes/StageScene.svelte +104 -0
  125. package/dist/components/scenes/StageScene.svelte.d.ts +18 -0
  126. package/dist/examples/FieldReport.svelte +223 -0
  127. package/dist/examples/FieldReport.svelte.d.ts +18 -0
  128. package/dist/examples/ObservationDeck.svelte +317 -0
  129. package/dist/examples/ObservationDeck.svelte.d.ts +3 -0
  130. package/dist/examples/SignalLost.svelte +191 -0
  131. package/dist/examples/SignalLost.svelte.d.ts +3 -0
  132. package/dist/index.d.ts +73 -0
  133. package/dist/index.js +83 -0
  134. package/dist/system/actions/echo.d.ts +10 -0
  135. package/dist/system/actions/echo.js +46 -0
  136. package/dist/system/actions/resolve.d.ts +23 -0
  137. package/dist/system/actions/resolve.js +59 -0
  138. package/dist/system/actions/reveal.d.ts +18 -0
  139. package/dist/system/actions/reveal.js +54 -0
  140. package/dist/system/actions/surface.d.ts +14 -0
  141. package/dist/system/actions/surface.js +25 -0
  142. package/dist/system/depth/depth.css +49 -0
  143. package/dist/system/depth/depth.d.ts +15 -0
  144. package/dist/system/depth/depth.js +24 -0
  145. package/dist/system/expressions.css +80 -0
  146. package/dist/system/override-template.css +72 -0
  147. package/dist/system/register.css +74 -0
  148. package/dist/system/register.d.ts +11 -0
  149. package/dist/system/register.js +16 -0
  150. package/dist/tokens/tokens.css +86 -0
  151. package/dist/tokens/tokens.d.ts +25 -0
  152. package/dist/tokens/tokens.js +25 -0
  153. package/dist/utils/cn.d.ts +2 -0
  154. package/dist/utils/cn.js +4 -0
  155. package/dist/utils/motion.d.ts +17 -0
  156. package/dist/utils/motion.js +11 -0
  157. package/package.json +60 -0
@@ -0,0 +1,136 @@
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>
@@ -0,0 +1,21 @@
1
+ interface ToastItem {
2
+ id: number;
3
+ message: string;
4
+ variant: 'ok' | 'pend' | 'warn' | 'fail';
5
+ duration: number;
6
+ }
7
+ /** Toast store. Use toastStore.push() to show a toast. */
8
+ export declare const toastStore: {
9
+ subscribe: (this: void, run: import("svelte/store").Subscriber<ToastItem[]>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
10
+ /** Push a new toast notification. */
11
+ push(message: string, variant?: "ok" | "pend" | "warn" | "fail", duration?: number): void;
12
+ };
13
+ interface Props {
14
+ /** Position of the toast stack. */
15
+ position?: 'bottom-right' | 'bottom-center';
16
+ /** Additional CSS classes. */
17
+ class?: string;
18
+ }
19
+ declare const Toast: import("svelte").Component<Props, {}, "">;
20
+ type Toast = ReturnType<typeof Toast>;
21
+ export default Toast;
@@ -0,0 +1,237 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+ import type { Snippet } from 'svelte';
4
+ import { echo as echoAction } from '../../system/actions/echo.js';
5
+
6
+ interface Props {
7
+ /** Button visual style. */
8
+ variant?: 'primary' | 'secondary' | 'ghost' | 'destructive';
9
+ /** Button size. */
10
+ size?: 'sm' | 'md';
11
+ /** Disables the button. */
12
+ disabled?: boolean;
13
+ /** Shows a pending status indicator instead of the label. */
14
+ loading?: boolean;
15
+ /** Enables the click echo ripple. */
16
+ echo?: boolean;
17
+ /** Additional CSS classes. */
18
+ class?: string;
19
+ /** Button type attribute. */
20
+ type?: 'button' | 'submit' | 'reset';
21
+ /** Optional href to render an anchor instead of a button. */
22
+ href?: string;
23
+ /** Anchor target. */
24
+ target?: string;
25
+ /** Anchor rel. */
26
+ rel?: string;
27
+ /** Click handler. */
28
+ onclick?: (e: MouseEvent) => void;
29
+ /** Button label content. */
30
+ children?: Snippet;
31
+ }
32
+
33
+ let {
34
+ variant = 'secondary',
35
+ size = 'md',
36
+ disabled = false,
37
+ loading = false,
38
+ echo = false,
39
+ class: className = '',
40
+ type = 'button',
41
+ href,
42
+ target,
43
+ rel,
44
+ onclick,
45
+ children,
46
+ }: Props = $props();
47
+
48
+ function activeEcho(node: HTMLElement) {
49
+ if (!echo) return {};
50
+ return echoAction(node);
51
+ }
52
+
53
+ function handleAnchorClick(e: MouseEvent) {
54
+ if (disabled || loading) {
55
+ e.preventDefault();
56
+ return;
57
+ }
58
+
59
+ onclick?.(e);
60
+ }
61
+ </script>
62
+
63
+ {#if href}
64
+ <a
65
+ {href}
66
+ {target}
67
+ {rel}
68
+ use:activeEcho
69
+ class={cn(
70
+ 'hyvui-btn',
71
+ `hyvui-btn-${variant}`,
72
+ `hyvui-btn-${size}`,
73
+ loading && 'hyvui-btn-loading',
74
+ disabled && 'hyvui-btn-disabled',
75
+ className
76
+ )}
77
+ aria-disabled={disabled || loading}
78
+ tabindex={disabled || loading ? -1 : undefined}
79
+ onclick={handleAnchorClick}
80
+ >
81
+ {#if loading}
82
+ <span class="hyvui-btn-dot" aria-label="loading"></span>
83
+ {:else if children}
84
+ {@render children()}
85
+ {/if}
86
+ </a>
87
+ {:else}
88
+ <button
89
+ {type}
90
+ use:activeEcho
91
+ class={cn(
92
+ 'hyvui-btn',
93
+ `hyvui-btn-${variant}`,
94
+ `hyvui-btn-${size}`,
95
+ loading && 'hyvui-btn-loading',
96
+ className
97
+ )}
98
+ disabled={disabled || loading}
99
+ {onclick}
100
+ >
101
+ {#if loading}
102
+ <span class="hyvui-btn-dot" aria-label="loading"></span>
103
+ {:else if children}
104
+ {@render children()}
105
+ {/if}
106
+ </button>
107
+ {/if}
108
+
109
+ <style>
110
+ .hyvui-btn {
111
+ position: relative;
112
+ overflow: clip;
113
+ font-family: var(--font-mono);
114
+ font-size: 0.72rem;
115
+ font-weight: 400;
116
+ letter-spacing: 0.16em;
117
+ text-transform: uppercase;
118
+ border: 1px solid var(--line);
119
+ border-radius: var(--radius-md);
120
+ cursor: pointer;
121
+ display: inline-flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ gap: var(--control-gap);
125
+ transition:
126
+ color var(--transition-smooth),
127
+ border-color var(--transition-smooth),
128
+ background var(--transition-smooth),
129
+ transform var(--transition-smooth),
130
+ box-shadow var(--transition-smooth);
131
+ white-space: nowrap;
132
+ text-decoration: none;
133
+ line-height: 1;
134
+ max-width: 100%;
135
+ box-shadow: var(--surface-stroke);
136
+ }
137
+
138
+ .hyvui-btn:disabled,
139
+ .hyvui-btn-disabled {
140
+ opacity: 0.46;
141
+ cursor: not-allowed;
142
+ transform: none !important;
143
+ box-shadow: none;
144
+ }
145
+
146
+ .hyvui-btn-md {
147
+ min-height: var(--control-height-md);
148
+ padding: 0.75rem 1.15rem;
149
+ }
150
+
151
+ .hyvui-btn-sm {
152
+ min-height: var(--control-height-sm);
153
+ padding: 0.5rem 0.8rem;
154
+ font-size: 0.68rem;
155
+ }
156
+
157
+ /* primary */
158
+ .hyvui-btn-primary {
159
+ background:
160
+ linear-gradient(180deg, color-mix(in srgb, var(--accent-strong) 22%, transparent), transparent 70%),
161
+ linear-gradient(135deg, var(--accent), var(--accent-strong));
162
+ color: var(--bg);
163
+ border-color: color-mix(in srgb, var(--accent-strong) 45%, var(--accent));
164
+ box-shadow:
165
+ inset 0 1px 0 rgba(255, 255, 255, 0.16),
166
+ 0 14px 26px rgba(199, 156, 87, 0.16);
167
+ }
168
+
169
+ .hyvui-btn-primary:hover:not(:disabled):not(.hyvui-btn-disabled) {
170
+ transform: translateY(-2px);
171
+ filter: brightness(1.03);
172
+ }
173
+
174
+ /* secondary */
175
+ .hyvui-btn-secondary {
176
+ background:
177
+ linear-gradient(180deg, rgba(121, 166, 163, 0.06), transparent 62%),
178
+ rgba(10, 12, 14, 0.74);
179
+ color: var(--text-soft);
180
+ border-color: var(--line-strong);
181
+ }
182
+
183
+ .hyvui-btn-secondary:hover:not(:disabled):not(.hyvui-btn-disabled) {
184
+ transform: translateY(-2px);
185
+ border-color: color-mix(in srgb, var(--accent) 46%, var(--line-strong));
186
+ color: var(--text);
187
+ }
188
+
189
+ /* ghost */
190
+ .hyvui-btn-ghost {
191
+ background: transparent;
192
+ color: var(--muted);
193
+ border-color: transparent;
194
+ box-shadow: none;
195
+ }
196
+
197
+ .hyvui-btn-ghost:hover:not(:disabled):not(.hyvui-btn-disabled) {
198
+ background: linear-gradient(90deg, rgba(199, 156, 87, 0.12), transparent 78%);
199
+ color: var(--text);
200
+ transform: translateX(2px);
201
+ }
202
+
203
+ /* destructive */
204
+ .hyvui-btn-destructive {
205
+ background: rgba(10, 12, 14, 0.74);
206
+ color: var(--text-soft);
207
+ border-color: rgba(182, 106, 72, 0.34);
208
+ }
209
+
210
+ .hyvui-btn-destructive:hover:not(:disabled):not(.hyvui-btn-disabled) {
211
+ background-color: rgba(182, 106, 72, 0.1);
212
+ transform: translateY(-2px);
213
+ }
214
+
215
+ /* loading dot */
216
+ .hyvui-btn-dot {
217
+ width: 6px;
218
+ height: 6px;
219
+ border-radius: 50%;
220
+ background-color: var(--status-pend);
221
+ animation: pulse-dot 2s ease-in-out infinite;
222
+ }
223
+
224
+ @media (prefers-reduced-motion: reduce) {
225
+ .hyvui-btn {
226
+ transition: none;
227
+ }
228
+
229
+ .hyvui-btn:hover:not(:disabled):not(.hyvui-btn-disabled) {
230
+ transform: none;
231
+ }
232
+
233
+ .hyvui-btn-dot {
234
+ animation: none;
235
+ }
236
+ }
237
+ </style>
@@ -0,0 +1,30 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /** Button visual style. */
4
+ variant?: 'primary' | 'secondary' | 'ghost' | 'destructive';
5
+ /** Button size. */
6
+ size?: 'sm' | 'md';
7
+ /** Disables the button. */
8
+ disabled?: boolean;
9
+ /** Shows a pending status indicator instead of the label. */
10
+ loading?: boolean;
11
+ /** Enables the click echo ripple. */
12
+ echo?: boolean;
13
+ /** Additional CSS classes. */
14
+ class?: string;
15
+ /** Button type attribute. */
16
+ type?: 'button' | 'submit' | 'reset';
17
+ /** Optional href to render an anchor instead of a button. */
18
+ href?: string;
19
+ /** Anchor target. */
20
+ target?: string;
21
+ /** Anchor rel. */
22
+ rel?: string;
23
+ /** Click handler. */
24
+ onclick?: (e: MouseEvent) => void;
25
+ /** Button label content. */
26
+ children?: Snippet;
27
+ }
28
+ declare const Button: import("svelte").Component<Props, {}, "">;
29
+ type Button = ReturnType<typeof Button>;
30
+ export default Button;
@@ -0,0 +1,105 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+
4
+ interface Props {
5
+ /** Whether the checkbox is checked (bindable). */
6
+ checked?: boolean;
7
+ /** Label text. */
8
+ label?: string;
9
+ /** Disables the checkbox. */
10
+ disabled?: boolean;
11
+ /** Additional CSS classes. */
12
+ class?: string;
13
+ /** Change handler. */
14
+ onchange?: (e: Event) => void;
15
+ }
16
+
17
+ let {
18
+ checked = $bindable(false),
19
+ label = '',
20
+ disabled = false,
21
+ class: className = '',
22
+ onchange,
23
+ }: Props = $props();
24
+ </script>
25
+
26
+ <label class={cn('hyvui-checkbox', disabled && 'hyvui-checkbox-disabled', className)}>
27
+ <input
28
+ type="checkbox"
29
+ bind:checked
30
+ {disabled}
31
+ class="hyvui-checkbox-input"
32
+ {onchange}
33
+ />
34
+ <span class="hyvui-checkbox-box" class:hyvui-checkbox-checked={checked}>
35
+ {#if checked}
36
+ <svg width="10" height="10" viewBox="0 0 10 10" aria-hidden="true">
37
+ <path d="M2 5.5L4 7.5L8 3" stroke="var(--bg)" stroke-width="1.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
38
+ </svg>
39
+ {/if}
40
+ </span>
41
+ {#if label}
42
+ <span class="hyvui-checkbox-label">{label}</span>
43
+ {/if}
44
+ </label>
45
+
46
+ <style>
47
+ .hyvui-checkbox {
48
+ display: inline-flex;
49
+ align-items: flex-start;
50
+ gap: var(--space-sm);
51
+ cursor: pointer;
52
+ min-width: 0;
53
+ }
54
+
55
+ .hyvui-checkbox-disabled {
56
+ opacity: 0.4;
57
+ cursor: not-allowed;
58
+ }
59
+
60
+ .hyvui-checkbox-input {
61
+ position: absolute;
62
+ opacity: 0;
63
+ width: 0;
64
+ height: 0;
65
+ pointer-events: none;
66
+ }
67
+
68
+ .hyvui-checkbox-box {
69
+ width: 16px;
70
+ height: 16px;
71
+ margin-top: 0.1rem;
72
+ border: 1px solid var(--line-strong);
73
+ border-radius: var(--radius-sm);
74
+ background:
75
+ linear-gradient(180deg, rgba(240, 232, 218, 0.02), transparent 48%),
76
+ var(--bg-elev);
77
+ display: inline-flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ flex-shrink: 0;
81
+ transition:
82
+ background-color var(--transition-fast),
83
+ border-color var(--transition-fast),
84
+ box-shadow var(--transition-fast);
85
+ box-shadow: inset 0 1px 0 rgba(240, 232, 218, 0.03);
86
+ }
87
+
88
+ .hyvui-checkbox-checked {
89
+ background-color: var(--accent);
90
+ border-color: var(--accent);
91
+ }
92
+
93
+ .hyvui-checkbox-label {
94
+ font-family: var(--font-body);
95
+ font-size: 0.98rem;
96
+ color: var(--text-soft);
97
+ line-height: 1.5;
98
+ }
99
+
100
+ @media (prefers-reduced-motion: reduce) {
101
+ .hyvui-checkbox-box {
102
+ transition: none;
103
+ }
104
+ }
105
+ </style>
@@ -0,0 +1,15 @@
1
+ interface Props {
2
+ /** Whether the checkbox is checked (bindable). */
3
+ checked?: boolean;
4
+ /** Label text. */
5
+ label?: string;
6
+ /** Disables the checkbox. */
7
+ disabled?: boolean;
8
+ /** Additional CSS classes. */
9
+ class?: string;
10
+ /** Change handler. */
11
+ onchange?: (e: Event) => void;
12
+ }
13
+ declare const Checkbox: import("svelte").Component<Props, {}, "checked">;
14
+ type Checkbox = ReturnType<typeof Checkbox>;
15
+ export default Checkbox;
@@ -0,0 +1,163 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+
4
+ interface Props {
5
+ /** Accepted file types (e.g. "image/*"). */
6
+ accept?: string;
7
+ /** Allow multiple file selection. */
8
+ multiple?: boolean;
9
+ /** Disables the upload zone. */
10
+ disabled?: boolean;
11
+ /** Label text displayed in the zone. */
12
+ label?: string;
13
+ /** Additional CSS classes. */
14
+ class?: string;
15
+ /** Fires with the selected files. */
16
+ onfiles?: (files: File[]) => void;
17
+ }
18
+
19
+ let {
20
+ accept = '',
21
+ multiple = false,
22
+ disabled = false,
23
+ label = 'drop files here or click to browse',
24
+ class: className = '',
25
+ onfiles,
26
+ }: Props = $props();
27
+
28
+ let dragging = $state(false);
29
+ let fileNames = $state<string[]>([]);
30
+ let inputEl: HTMLInputElement | undefined = $state();
31
+
32
+ function handleFiles(fileList: FileList | null) {
33
+ if (!fileList) return;
34
+ const files = Array.from(fileList);
35
+ fileNames = files.map((f) => f.name);
36
+ onfiles?.(files);
37
+ }
38
+
39
+ function onDrop(e: DragEvent) {
40
+ e.preventDefault();
41
+ dragging = false;
42
+ if (disabled) return;
43
+ handleFiles(e.dataTransfer?.files ?? null);
44
+ }
45
+
46
+ function onDragOver(e: DragEvent) {
47
+ e.preventDefault();
48
+ if (!disabled) dragging = true;
49
+ }
50
+
51
+ function onDragLeave() {
52
+ dragging = false;
53
+ }
54
+
55
+ function onClick() {
56
+ if (!disabled) inputEl?.click();
57
+ }
58
+
59
+ function onInputChange(e: Event) {
60
+ const target = e.target as HTMLInputElement;
61
+ handleFiles(target.files);
62
+ }
63
+ </script>
64
+
65
+ <div class={cn('hyvui-file-upload', className)}>
66
+ <button
67
+ type="button"
68
+ class={cn(
69
+ 'hyvui-file-zone',
70
+ dragging && 'hyvui-file-zone-active',
71
+ disabled && 'hyvui-file-zone-disabled'
72
+ )}
73
+ ondrop={onDrop}
74
+ ondragover={onDragOver}
75
+ ondragleave={onDragLeave}
76
+ onclick={onClick}
77
+ {disabled}
78
+ >
79
+ <span class="hyvui-file-label">{label}</span>
80
+ </button>
81
+ <input
82
+ bind:this={inputEl}
83
+ type="file"
84
+ {accept}
85
+ {multiple}
86
+ class="hyvui-file-input"
87
+ onchange={onInputChange}
88
+ tabindex="-1"
89
+ />
90
+ {#if fileNames.length > 0}
91
+ <div class="hyvui-file-names">
92
+ {#each fileNames as name}
93
+ <span class="hyvui-file-name">{name}</span>
94
+ {/each}
95
+ </div>
96
+ {/if}
97
+ </div>
98
+
99
+ <style>
100
+ .hyvui-file-upload {
101
+ display: flex;
102
+ flex-direction: column;
103
+ gap: 0.5rem;
104
+ }
105
+
106
+ .hyvui-file-zone {
107
+ border: 1px dashed var(--line);
108
+ background: transparent;
109
+ padding: 2rem;
110
+ display: flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ cursor: pointer;
114
+ transition: border-color var(--transition-fast), background-color var(--transition-fast);
115
+ width: 100%;
116
+ }
117
+
118
+ .hyvui-file-zone:hover:not(:disabled),
119
+ .hyvui-file-zone-active {
120
+ border-color: var(--line-strong);
121
+ background-color: rgba(199, 156, 87, 0.04);
122
+ }
123
+
124
+ .hyvui-file-zone-disabled {
125
+ opacity: 0.4;
126
+ cursor: not-allowed;
127
+ }
128
+
129
+ .hyvui-file-label {
130
+ font-family: var(--font-mono);
131
+ font-size: 0.75rem;
132
+ color: var(--muted);
133
+ letter-spacing: 0.08em;
134
+ }
135
+
136
+ .hyvui-file-input {
137
+ position: absolute;
138
+ width: 0;
139
+ height: 0;
140
+ opacity: 0;
141
+ pointer-events: none;
142
+ }
143
+
144
+ .hyvui-file-names {
145
+ display: flex;
146
+ flex-wrap: wrap;
147
+ gap: 0.375rem;
148
+ }
149
+
150
+ .hyvui-file-name {
151
+ font-family: var(--font-mono);
152
+ font-size: 0.68rem;
153
+ letter-spacing: 0.14em;
154
+ text-transform: uppercase;
155
+ color: var(--muted-strong);
156
+ }
157
+
158
+ @media (prefers-reduced-motion: reduce) {
159
+ .hyvui-file-zone {
160
+ transition: none;
161
+ }
162
+ }
163
+ </style>