@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,76 +1,81 @@
1
- <script lang="ts">
2
- import { cn } from '../../utils/cn.js';
3
- import Surface from '../primitives/Surface.svelte';
4
- import type { Snippet } from 'svelte';
5
-
6
- interface Props {
7
- /** TranslateY offset for staggered card grids. */
8
- staggerOffset?: string;
9
- /** Additional CSS classes. */
10
- class?: string;
11
- /** Card header slot. */
12
- header?: Snippet;
13
- /** Card body content. */
14
- children?: Snippet;
15
- /** Card footer slot. */
16
- footer?: Snippet;
17
- }
18
-
19
- let {
20
- staggerOffset = '',
21
- class: className = '',
22
- header,
23
- children,
24
- footer,
25
- }: Props = $props();
26
-
27
- const style = $derived(staggerOffset ? `transform: translateY(${staggerOffset})` : '');
28
- </script>
29
-
30
- <div class={cn('hyvui-card-wrap', className)} {style}>
31
- <Surface variant="card" class="hyvui-card-inner">
32
- {#if header}
33
- <div class="hyvui-card-header">
34
- {@render header()}
35
- </div>
36
- {/if}
37
- {#if children}
38
- <div class="hyvui-card-body">
39
- {@render children()}
40
- </div>
41
- {/if}
42
- {#if footer}
43
- <div class="hyvui-card-footer">
44
- {@render footer()}
45
- </div>
46
- {/if}
47
- </Surface>
48
- </div>
49
-
50
- <style>
51
- .hyvui-card-wrap {
52
- display: block;
53
- }
54
-
55
- :global(.hyvui-card-inner) {
56
- padding: var(--space-card);
57
- display: flex;
58
- flex-direction: column;
59
- gap: var(--space-lg);
60
- }
61
-
62
- .hyvui-card-header {
63
- padding-bottom: var(--space-sm);
64
- border-bottom: 1px solid color-mix(in srgb, var(--line) 92%, transparent);
65
- }
66
-
67
- .hyvui-card-body {
68
- flex: 1;
69
- min-width: 0;
70
- }
71
-
72
- .hyvui-card-footer {
73
- padding-top: var(--space-sm);
74
- border-top: 1px solid color-mix(in srgb, var(--line) 92%, transparent);
75
- }
76
- </style>
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+ import Surface from '../primitives/Surface.svelte';
4
+ import type { Snippet } from 'svelte';
5
+
6
+ /**
7
+ * @see surface — add `use:surface` on Card for an entrance animation on mount.
8
+ * @see reveal — add `use:reveal={{ target: '.meta' }}` on the Card for hover-reveal of child elements.
9
+ * @example
10
+ * <Card>
11
+ * {#snippet header()}<Label>project alpha</Label>{/snippet}
12
+ * <Text>card body content here.</Text>
13
+ * {#snippet footer()}<Button variant="ghost" size="sm">view details</Button>{/snippet}
14
+ * </Card>
15
+ * <Card staggerOffset="1.2rem">offset card in a grid</Card>
16
+ */
17
+ interface Props {
18
+ /** TranslateY offset for staggered card grids. */
19
+ staggerOffset?: string;
20
+ /** Additional CSS classes. */
21
+ class?: string;
22
+ /** Card header slot. */
23
+ header?: Snippet;
24
+ /** Card body content. */
25
+ children?: Snippet;
26
+ /** Card footer slot. */
27
+ footer?: Snippet;
28
+ }
29
+
30
+ let { staggerOffset = '', class: className = '', header, children, footer }: Props = $props();
31
+
32
+ const style = $derived(staggerOffset ? `transform: translateY(${staggerOffset})` : '');
33
+ </script>
34
+
35
+ <div class={cn('hyvui-card-wrap', className)} {style}>
36
+ <Surface variant="card" class="hyvui-card-inner">
37
+ {#if header}
38
+ <div class="hyvui-card-header">
39
+ {@render header()}
40
+ </div>
41
+ {/if}
42
+ {#if children}
43
+ <div class="hyvui-card-body">
44
+ {@render children()}
45
+ </div>
46
+ {/if}
47
+ {#if footer}
48
+ <div class="hyvui-card-footer">
49
+ {@render footer()}
50
+ </div>
51
+ {/if}
52
+ </Surface>
53
+ </div>
54
+
55
+ <style>
56
+ .hyvui-card-wrap {
57
+ display: block;
58
+ }
59
+
60
+ :global(.hyvui-card-inner) {
61
+ padding: var(--space-card);
62
+ display: flex;
63
+ flex-direction: column;
64
+ gap: var(--space-lg);
65
+ }
66
+
67
+ .hyvui-card-header {
68
+ padding-bottom: var(--space-sm);
69
+ border-bottom: 1px solid color-mix(in srgb, var(--line) 92%, transparent);
70
+ }
71
+
72
+ .hyvui-card-body {
73
+ flex: 1;
74
+ min-width: 0;
75
+ }
76
+
77
+ .hyvui-card-footer {
78
+ padding-top: var(--space-sm);
79
+ border-top: 1px solid color-mix(in srgb, var(--line) 92%, transparent);
80
+ }
81
+ </style>
@@ -1,4 +1,15 @@
1
1
  import type { Snippet } from 'svelte';
2
+ /**
3
+ * @see surface — add `use:surface` on Card for an entrance animation on mount.
4
+ * @see reveal — add `use:reveal={{ target: '.meta' }}` on the Card for hover-reveal of child elements.
5
+ * @example
6
+ * <Card>
7
+ * {#snippet header()}<Label>project alpha</Label>{/snippet}
8
+ * <Text>card body content here.</Text>
9
+ * {#snippet footer()}<Button variant="ghost" size="sm">view details</Button>{/snippet}
10
+ * </Card>
11
+ * <Card staggerOffset="1.2rem">offset card in a grid</Card>
12
+ */
2
13
  interface Props {
3
14
  /** TranslateY offset for staggered card grids. */
4
15
  staggerOffset?: string;
@@ -1,109 +1,140 @@
1
- <script lang="ts">
2
- import { cn } from '../../utils/cn.js';
3
- import Surface from '../primitives/Surface.svelte';
4
- import type { Snippet } from 'svelte';
5
-
6
- interface Props {
7
- /** Controls drawer visibility. */
8
- open?: boolean;
9
- /** Side the drawer slides in from. */
10
- side?: 'left' | 'right';
11
- /** Drawer width. */
12
- width?: string;
13
- /** Additional CSS classes. */
14
- class?: string;
15
- /** Drawer content. */
16
- children?: Snippet;
17
- /** Fires when the drawer is dismissed. */
18
- onclose?: () => void;
19
- }
20
-
21
- let {
22
- open = false,
23
- side = 'right',
24
- width = '320px',
25
- class: className = '',
26
- children,
27
- onclose,
28
- }: Props = $props();
29
-
30
- function handleKeydown(e: KeyboardEvent) {
31
- if (e.key === 'Escape') {
32
- onclose?.();
33
- }
34
- }
35
-
36
- function handleBackdropClick() {
37
- onclose?.();
38
- }
39
- </script>
40
-
41
- {#if open}
42
- <!-- svelte-ignore a11y_no_static_element_interactions -->
43
- <div class="hyvui-drawer-backdrop" onclick={handleBackdropClick} onkeydown={handleKeydown}>
44
- <!-- svelte-ignore a11y_no_static_element_interactions -->
45
- <div
46
- class={cn(
47
- 'hyvui-drawer',
48
- side === 'left' ? 'hyvui-drawer-left' : 'hyvui-drawer-right',
49
- className
50
- )}
51
- style:width={width}
52
- onclick={(e) => e.stopPropagation()}
53
- onkeydown={(e) => e.stopPropagation()}
54
- >
55
- <Surface variant="panel" class="hyvui-drawer-surface">
56
- {#if children}{@render children()}{/if}
57
- </Surface>
58
- </div>
59
- </div>
60
- {/if}
61
-
62
- <style>
63
- .hyvui-drawer-backdrop {
64
- position: fixed;
65
- inset: 0;
66
- z-index: var(--z-modal);
67
- background: rgba(0, 0, 0, 0.72);
68
- }
69
-
70
- .hyvui-drawer {
71
- position: fixed;
72
- top: 0;
73
- bottom: 0;
74
- z-index: var(--z-modal);
75
- }
76
-
77
- .hyvui-drawer-left {
78
- left: 0;
79
- animation: drawer-left-in 0.35s cubic-bezier(0.22, 1, 0.36, 1);
80
- }
81
-
82
- .hyvui-drawer-right {
83
- right: 0;
84
- animation: drawer-right-in 0.35s cubic-bezier(0.22, 1, 0.36, 1);
85
- }
86
-
87
- :global(.hyvui-drawer-surface) {
88
- height: 100%;
89
- padding: var(--space-card);
90
- overflow-y: auto;
91
- }
92
-
93
- @keyframes drawer-left-in {
94
- from { transform: translateX(-100%); }
95
- to { transform: translateX(0); }
96
- }
97
-
98
- @keyframes drawer-right-in {
99
- from { transform: translateX(100%); }
100
- to { transform: translateX(0); }
101
- }
102
-
103
- @media (prefers-reduced-motion: reduce) {
104
- .hyvui-drawer-left,
105
- .hyvui-drawer-right {
106
- animation: none;
107
- }
108
- }
109
- </style>
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+ import { lockScroll } from '../../system/scroll-lock.js';
4
+ import Surface from '../primitives/Surface.svelte';
5
+ import type { Snippet } from 'svelte';
6
+
7
+ /**
8
+ * @example
9
+ * <Drawer open={showDrawer} side="right" onclose={() => showDrawer = false}>
10
+ * <SidebarNav items={navItems} />
11
+ * </Drawer>
12
+ */
13
+ interface Props {
14
+ /** Controls drawer visibility. */
15
+ open?: boolean;
16
+ /** Side the drawer slides in from. */
17
+ side?: 'left' | 'right';
18
+ /** Drawer width. */
19
+ width?: string;
20
+ /** Additional CSS classes. */
21
+ class?: string;
22
+ /** Drawer content. */
23
+ children?: Snippet;
24
+ /** Fires when the drawer is dismissed. */
25
+ onclose?: () => void;
26
+ }
27
+
28
+ let {
29
+ open = false,
30
+ side = 'right',
31
+ width = '320px',
32
+ class: className = '',
33
+ children,
34
+ onclose
35
+ }: Props = $props();
36
+
37
+ $effect(() => {
38
+ if (!open) return;
39
+ return lockScroll();
40
+ });
41
+
42
+ $effect(() => {
43
+ if (typeof window === 'undefined') return;
44
+ if (!open) return;
45
+
46
+ function onKeydown(e: KeyboardEvent) {
47
+ if (e.key === 'Escape') onclose?.();
48
+ }
49
+
50
+ window.addEventListener('keydown', onKeydown);
51
+ return () => window.removeEventListener('keydown', onKeydown);
52
+ });
53
+
54
+ function handleBackdropClick(e: MouseEvent) {
55
+ // Only close when the backdrop itself was clicked.
56
+ if (e.target !== e.currentTarget) return;
57
+ onclose?.();
58
+ }
59
+ </script>
60
+
61
+ {#if open}
62
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
63
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
64
+ <div class="hyvui-drawer-backdrop" onclick={handleBackdropClick}>
65
+ <div
66
+ class={cn(
67
+ 'hyvui-drawer',
68
+ side === 'left' ? 'hyvui-drawer-left' : 'hyvui-drawer-right',
69
+ className
70
+ )}
71
+ style:--hyvui-drawer-w={width}
72
+ >
73
+ <Surface variant="panel" class="hyvui-drawer-surface">
74
+ {#if children}{@render children()}{/if}
75
+ </Surface>
76
+ </div>
77
+ </div>
78
+ {/if}
79
+
80
+ <style>
81
+ .hyvui-drawer-backdrop {
82
+ position: fixed;
83
+ inset: 0;
84
+ z-index: var(--z-modal);
85
+ background: rgba(0, 0, 0, 0.72);
86
+ }
87
+
88
+ .hyvui-drawer {
89
+ position: fixed;
90
+ top: 0;
91
+ bottom: 0;
92
+ z-index: var(--z-modal);
93
+ inline-size: min(var(--hyvui-drawer-w, 320px), 100dvw);
94
+ max-inline-size: 100dvw;
95
+ backface-visibility: hidden;
96
+ }
97
+
98
+ .hyvui-drawer-left {
99
+ left: 0;
100
+ animation: drawer-left-in 0.35s cubic-bezier(0.22, 1, 0.36, 1);
101
+ }
102
+
103
+ .hyvui-drawer-right {
104
+ right: 0;
105
+ animation: drawer-right-in 0.35s cubic-bezier(0.22, 1, 0.36, 1);
106
+ }
107
+
108
+ :global(.hyvui-drawer-surface) {
109
+ height: 100%;
110
+ padding: calc(var(--space-card) + var(--safe-top)) calc(var(--space-card) + var(--safe-right))
111
+ calc(var(--space-card) + var(--safe-bottom)) calc(var(--space-card) + var(--safe-left));
112
+ overflow-y: auto;
113
+ overscroll-behavior: contain;
114
+ }
115
+
116
+ @keyframes drawer-left-in {
117
+ from {
118
+ transform: translateX(-100%);
119
+ }
120
+ to {
121
+ transform: translateX(0);
122
+ }
123
+ }
124
+
125
+ @keyframes drawer-right-in {
126
+ from {
127
+ transform: translateX(100%);
128
+ }
129
+ to {
130
+ transform: translateX(0);
131
+ }
132
+ }
133
+
134
+ @media (prefers-reduced-motion: reduce) {
135
+ .hyvui-drawer-left,
136
+ .hyvui-drawer-right {
137
+ animation: none;
138
+ }
139
+ }
140
+ </style>
@@ -1,4 +1,10 @@
1
1
  import type { Snippet } from 'svelte';
2
+ /**
3
+ * @example
4
+ * <Drawer open={showDrawer} side="right" onclose={() => showDrawer = false}>
5
+ * <SidebarNav items={navItems} />
6
+ * </Drawer>
7
+ */
2
8
  interface Props {
3
9
  /** Controls drawer visibility. */
4
10
  open?: boolean;
@@ -1,43 +1,128 @@
1
- <script lang="ts">
2
- import { cn } from '../../utils/cn.js';
3
- import type { Snippet } from 'svelte';
4
-
5
- interface Props {
6
- /** Number of columns or a CSS grid-template-columns value. */
7
- cols?: number | string;
8
- /** Gap between grid items. */
9
- gap?: string;
10
- /** HTML tag to render. */
11
- as?: string;
12
- /** Additional CSS classes. */
13
- class?: string;
14
- /** Grid children. */
15
- children?: Snippet;
16
- }
17
-
18
- let {
19
- cols = 1,
20
- gap = 'var(--space-md)',
21
- as = 'div',
22
- class: className = '',
23
- children,
24
- }: Props = $props();
25
-
26
- const gridCols = $derived(typeof cols === 'number' ? `repeat(${cols}, 1fr)` : cols);
27
- </script>
28
-
29
- <svelte:element
30
- this={as}
31
- class={cn('hyvui-grid', className)}
32
- style:grid-template-columns={gridCols}
33
- style:gap={gap}
34
- >
35
- {#if children}{@render children()}{/if}
36
- </svelte:element>
37
-
38
- <style>
39
- .hyvui-grid {
40
- display: grid;
41
- min-width: 0;
42
- }
43
- </style>
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+ import type { Snippet } from 'svelte';
4
+
5
+ /**
6
+ * @example
7
+ * <Grid minColWidth="18rem" maxCols={3}>
8
+ * {#each items as item}<Card>{item.name}</Card>{/each}
9
+ * </Grid>
10
+ * <Grid mode="template" cols="1fr 2fr">
11
+ * <aside>sidebar</aside>
12
+ * <main>content</main>
13
+ * </Grid>
14
+ */
15
+ interface Props {
16
+ /** Grid mode. `auto` computes columns from container width. */
17
+ mode?: 'auto' | 'template';
18
+ /** CSS grid-template-columns value (used when mode = 'template'). */
19
+ cols?: string | number;
20
+ /** Minimum column width (used when mode = 'auto'). */
21
+ minColWidth?: string;
22
+ /** Maximum number of columns (used when mode = 'auto'). */
23
+ maxCols?: number;
24
+ /** Gap between grid items. */
25
+ gap?: string;
26
+ /** HTML tag to render. */
27
+ as?: string;
28
+ /** Additional CSS classes. */
29
+ class?: string;
30
+ /** Grid children. */
31
+ children?: Snippet;
32
+ }
33
+
34
+ let {
35
+ mode = 'auto',
36
+ cols = 1,
37
+ minColWidth = '16rem',
38
+ maxCols,
39
+ gap = 'var(--space-md)',
40
+ as = 'div',
41
+ class: className = '',
42
+ children
43
+ }: Props = $props();
44
+
45
+ let gridEl: HTMLElement | undefined = $state();
46
+ let gridCols = $state('repeat(1, minmax(0, 1fr))');
47
+
48
+ function readGapPx(el: HTMLElement): number {
49
+ const cs = getComputedStyle(el);
50
+ const raw = cs.columnGap || cs.gap || '0';
51
+ const px = Number.parseFloat(raw);
52
+ return Number.isFinite(px) ? px : 0;
53
+ }
54
+
55
+ function measureWidthPx(el: HTMLElement, width: string): number {
56
+ const probe = document.createElement('div');
57
+ probe.style.position = 'absolute';
58
+ probe.style.visibility = 'hidden';
59
+ probe.style.pointerEvents = 'none';
60
+ probe.style.width = width;
61
+ probe.style.height = '0';
62
+ probe.style.padding = '0';
63
+ probe.style.border = '0';
64
+ el.appendChild(probe);
65
+ const px = probe.getBoundingClientRect().width;
66
+ probe.remove();
67
+ return px > 0 ? px : 0;
68
+ }
69
+
70
+ $effect(() => {
71
+ if (typeof window === 'undefined') return;
72
+ if (!gridEl) return;
73
+ const el = gridEl;
74
+
75
+ if (mode === 'template') {
76
+ gridCols = typeof cols === 'string' ? cols : `repeat(${cols}, minmax(0, 1fr))`;
77
+ return;
78
+ }
79
+
80
+ // Back-compat: if callers pass a numeric `cols` in auto mode, treat it as maxCols.
81
+ const max = maxCols ?? (typeof cols === 'number' ? cols : undefined);
82
+
83
+ let minPx = measureWidthPx(el, minColWidth);
84
+ if (minPx <= 0) minPx = 16 * 16; // fallback: 16rem at 16px root
85
+
86
+ let last = 0;
87
+ const ro = new ResizeObserver((entries) => {
88
+ const entry = entries[0];
89
+ if (!entry) return;
90
+ const width = entry.contentRect.width;
91
+ const gapPx = readGapPx(el);
92
+ const next = Math.max(1, Math.floor((width + gapPx) / (minPx + gapPx)));
93
+ const clamped = max ? Math.min(next, max) : next;
94
+ if (clamped === last) return;
95
+ last = clamped;
96
+ gridCols = `repeat(${clamped}, minmax(0, 1fr))`;
97
+ });
98
+
99
+ ro.observe(el);
100
+
101
+ // Seed with initial measurement.
102
+ const width = el.getBoundingClientRect().width;
103
+ const gapPx = readGapPx(el);
104
+ const next = Math.max(1, Math.floor((width + gapPx) / (minPx + gapPx)));
105
+ last = max ? Math.min(next, max) : next;
106
+ gridCols = `repeat(${last}, minmax(0, 1fr))`;
107
+
108
+ return () => ro.disconnect();
109
+ });
110
+ </script>
111
+
112
+ <svelte:element
113
+ this={as}
114
+ bind:this={gridEl}
115
+ class={cn('hyvui-grid', className)}
116
+ style:--hyv-grid-cols={gridCols}
117
+ style:gap
118
+ >
119
+ {#if children}{@render children()}{/if}
120
+ </svelte:element>
121
+
122
+ <style>
123
+ .hyvui-grid {
124
+ display: grid;
125
+ min-width: 0;
126
+ grid-template-columns: var(--hyv-grid-cols);
127
+ }
128
+ </style>
@@ -1,7 +1,23 @@
1
1
  import type { Snippet } from 'svelte';
2
+ /**
3
+ * @example
4
+ * <Grid minColWidth="18rem" maxCols={3}>
5
+ * {#each items as item}<Card>{item.name}</Card>{/each}
6
+ * </Grid>
7
+ * <Grid mode="template" cols="1fr 2fr">
8
+ * <aside>sidebar</aside>
9
+ * <main>content</main>
10
+ * </Grid>
11
+ */
2
12
  interface Props {
3
- /** Number of columns or a CSS grid-template-columns value. */
4
- cols?: number | string;
13
+ /** Grid mode. `auto` computes columns from container width. */
14
+ mode?: 'auto' | 'template';
15
+ /** CSS grid-template-columns value (used when mode = 'template'). */
16
+ cols?: string | number;
17
+ /** Minimum column width (used when mode = 'auto'). */
18
+ minColWidth?: string;
19
+ /** Maximum number of columns (used when mode = 'auto'). */
20
+ maxCols?: number;
5
21
  /** Gap between grid items. */
6
22
  gap?: string;
7
23
  /** HTML tag to render. */