@hyvnt/hyvui 0.2.0 → 0.3.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.
- package/README.md +264 -253
- package/dist/components/ambient/CornerBrackets.svelte +83 -87
- package/dist/components/ambient/DataStream.svelte +111 -94
- package/dist/components/ambient/GlyphMark.svelte +69 -69
- package/dist/components/ambient/GridOverlay.svelte +26 -28
- package/dist/components/ambient/ParallaxLayer.svelte +37 -41
- package/dist/components/ambient/ScanBand.svelte +95 -91
- package/dist/components/ambient/SignalRing.svelte +100 -100
- package/dist/components/ambient/ThreadLine.svelte +71 -78
- package/dist/components/ambient/Vignette.svelte +24 -26
- package/dist/components/depth/DepthLayer.svelte +22 -27
- package/dist/components/depth/DepthStage.svelte +63 -62
- package/dist/components/depth/FloatCard.svelte +113 -104
- package/dist/components/depth/HorizonGrid.svelte +216 -160
- package/dist/components/depth/Plinth.svelte +52 -57
- package/dist/components/display/Avatar.svelte +64 -69
- package/dist/components/display/Badge.svelte +59 -63
- package/dist/components/display/Blockquote.svelte +31 -34
- package/dist/components/display/CodeBlock.svelte +71 -76
- package/dist/components/display/MetricCard.svelte +77 -83
- package/dist/components/display/Table.svelte +99 -104
- package/dist/components/feedback/Alert.svelte +71 -76
- package/dist/components/feedback/EmptyState.svelte +68 -68
- package/dist/components/feedback/ErrorState.svelte +73 -73
- package/dist/components/feedback/Skeleton.svelte +52 -52
- package/dist/components/feedback/StatusDot.svelte +49 -54
- package/dist/components/feedback/StatusLine.svelte +122 -122
- package/dist/components/feedback/Toast.svelte +130 -136
- package/dist/components/inputs/Button.svelte +240 -237
- package/dist/components/inputs/Checkbox.svelte +104 -105
- package/dist/components/inputs/FileUpload.svelte +165 -163
- package/dist/components/inputs/Input.svelte +145 -147
- package/dist/components/inputs/Select.svelte +156 -150
- package/dist/components/inputs/Textarea.svelte +153 -154
- package/dist/components/inputs/Toggle.svelte +120 -120
- package/dist/components/layout/Card.svelte +70 -76
- package/dist/components/layout/Drawer.svelte +133 -109
- package/dist/components/layout/Grid.svelte +118 -43
- package/dist/components/layout/Grid.svelte.d.ts +8 -2
- package/dist/components/layout/Modal.svelte +176 -159
- package/dist/components/layout/Panel.svelte +49 -54
- package/dist/components/layout/Popover.svelte +178 -67
- package/dist/components/layout/Popover.svelte.d.ts +10 -1
- package/dist/components/layout/Stack.svelte +53 -53
- package/dist/components/navigation/Breadcrumb.svelte +70 -73
- package/dist/components/navigation/DropdownMenu.svelte +167 -124
- package/dist/components/navigation/DropdownMenu.svelte.d.ts +12 -2
- package/dist/components/navigation/SidebarNav.svelte +86 -90
- package/dist/components/navigation/Tabs.svelte +81 -86
- package/dist/components/navigation/Topbar.svelte +85 -85
- package/dist/components/patterns/ActionBar.svelte +71 -76
- package/dist/components/patterns/ConfirmDialog.svelte +63 -64
- package/dist/components/patterns/PageHeader.svelte +109 -114
- package/dist/components/patterns/SearchBar.svelte +54 -59
- package/dist/components/patterns/TerminalBoot.svelte +104 -104
- package/dist/components/primitives/Divider.svelte +26 -29
- package/dist/components/primitives/Icon.svelte +44 -49
- package/dist/components/primitives/Label.svelte +39 -44
- package/dist/components/primitives/Surface.svelte +89 -87
- package/dist/components/primitives/Text.svelte +98 -98
- package/dist/components/scenes/ArchiveScene.svelte +92 -95
- package/dist/components/scenes/ArchiveScene.svelte.d.ts +7 -1
- package/dist/components/scenes/LogScene.svelte +72 -77
- package/dist/components/scenes/NarrativeScene.svelte +91 -92
- package/dist/components/scenes/ReadoutScene.svelte +120 -107
- package/dist/components/scenes/ReadoutScene.svelte.d.ts +3 -1
- package/dist/components/scenes/StageScene.svelte +97 -104
- package/dist/examples/FieldReport.svelte +226 -223
- package/dist/examples/ObservationDeck.svelte +333 -317
- package/dist/examples/SignalLost.svelte +191 -191
- package/dist/styles.css +113 -0
- package/dist/system/actions/echo.js +9 -9
- package/dist/system/actions/resolve.js +9 -9
- package/dist/system/actions/reveal.js +1 -1
- package/dist/system/actions/surface.js +13 -1
- package/dist/system/depth/depth.css +49 -49
- package/dist/system/depth/depth.js +1 -1
- package/dist/system/expressions.css +80 -80
- package/dist/system/override-template.css +72 -72
- package/dist/system/register.css +74 -74
- package/dist/system/scroll-lock.d.ts +6 -0
- package/dist/system/scroll-lock.js +23 -0
- package/dist/tokens/tokens.css +100 -86
- package/dist/tokens/tokens.js +4 -4
- package/dist/utils/motion.js +1 -1
- package/package.json +67 -60
|
@@ -1,76 +1,70 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
+
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 { staggerOffset = '', class: className = '', header, children, footer }: Props = $props();
|
|
20
|
+
|
|
21
|
+
const style = $derived(staggerOffset ? `transform: translateY(${staggerOffset})` : '');
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<div class={cn('hyvui-card-wrap', className)} {style}>
|
|
25
|
+
<Surface variant="card" class="hyvui-card-inner">
|
|
26
|
+
{#if header}
|
|
27
|
+
<div class="hyvui-card-header">
|
|
28
|
+
{@render header()}
|
|
29
|
+
</div>
|
|
30
|
+
{/if}
|
|
31
|
+
{#if children}
|
|
32
|
+
<div class="hyvui-card-body">
|
|
33
|
+
{@render children()}
|
|
34
|
+
</div>
|
|
35
|
+
{/if}
|
|
36
|
+
{#if footer}
|
|
37
|
+
<div class="hyvui-card-footer">
|
|
38
|
+
{@render footer()}
|
|
39
|
+
</div>
|
|
40
|
+
{/if}
|
|
41
|
+
</Surface>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<style>
|
|
45
|
+
.hyvui-card-wrap {
|
|
46
|
+
display: block;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
:global(.hyvui-card-inner) {
|
|
50
|
+
padding: var(--space-card);
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
gap: var(--space-lg);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.hyvui-card-header {
|
|
57
|
+
padding-bottom: var(--space-sm);
|
|
58
|
+
border-bottom: 1px solid color-mix(in srgb, var(--line) 92%, transparent);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.hyvui-card-body {
|
|
62
|
+
flex: 1;
|
|
63
|
+
min-width: 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.hyvui-card-footer {
|
|
67
|
+
padding-top: var(--space-sm);
|
|
68
|
+
border-top: 1px solid color-mix(in srgb, var(--line) 92%, transparent);
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
@@ -1,109 +1,133 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
{
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
+
interface Props {
|
|
8
|
+
/** Controls drawer visibility. */
|
|
9
|
+
open?: boolean;
|
|
10
|
+
/** Side the drawer slides in from. */
|
|
11
|
+
side?: 'left' | 'right';
|
|
12
|
+
/** Drawer width. */
|
|
13
|
+
width?: string;
|
|
14
|
+
/** Additional CSS classes. */
|
|
15
|
+
class?: string;
|
|
16
|
+
/** Drawer content. */
|
|
17
|
+
children?: Snippet;
|
|
18
|
+
/** Fires when the drawer is dismissed. */
|
|
19
|
+
onclose?: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let {
|
|
23
|
+
open = false,
|
|
24
|
+
side = 'right',
|
|
25
|
+
width = '320px',
|
|
26
|
+
class: className = '',
|
|
27
|
+
children,
|
|
28
|
+
onclose
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
$effect(() => {
|
|
32
|
+
if (!open) return;
|
|
33
|
+
return lockScroll();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
$effect(() => {
|
|
37
|
+
if (typeof window === 'undefined') return;
|
|
38
|
+
if (!open) return;
|
|
39
|
+
|
|
40
|
+
function onKeydown(e: KeyboardEvent) {
|
|
41
|
+
if (e.key === 'Escape') onclose?.();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
window.addEventListener('keydown', onKeydown);
|
|
45
|
+
return () => window.removeEventListener('keydown', onKeydown);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
function handleBackdropClick(e: MouseEvent) {
|
|
49
|
+
// Only close when the backdrop itself was clicked.
|
|
50
|
+
if (e.target !== e.currentTarget) return;
|
|
51
|
+
onclose?.();
|
|
52
|
+
}
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
{#if open}
|
|
56
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
57
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
58
|
+
<div class="hyvui-drawer-backdrop" onclick={handleBackdropClick}>
|
|
59
|
+
<div
|
|
60
|
+
class={cn(
|
|
61
|
+
'hyvui-drawer',
|
|
62
|
+
side === 'left' ? 'hyvui-drawer-left' : 'hyvui-drawer-right',
|
|
63
|
+
className
|
|
64
|
+
)}
|
|
65
|
+
style:--hyvui-drawer-w={width}
|
|
66
|
+
>
|
|
67
|
+
<Surface variant="panel" class="hyvui-drawer-surface">
|
|
68
|
+
{#if children}{@render children()}{/if}
|
|
69
|
+
</Surface>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
{/if}
|
|
73
|
+
|
|
74
|
+
<style>
|
|
75
|
+
.hyvui-drawer-backdrop {
|
|
76
|
+
position: fixed;
|
|
77
|
+
inset: 0;
|
|
78
|
+
z-index: var(--z-modal);
|
|
79
|
+
background: rgba(0, 0, 0, 0.72);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.hyvui-drawer {
|
|
83
|
+
position: fixed;
|
|
84
|
+
top: 0;
|
|
85
|
+
bottom: 0;
|
|
86
|
+
z-index: var(--z-modal);
|
|
87
|
+
inline-size: min(var(--hyvui-drawer-w, 320px), 100dvw);
|
|
88
|
+
max-inline-size: 100dvw;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.hyvui-drawer-left {
|
|
92
|
+
left: 0;
|
|
93
|
+
animation: drawer-left-in 0.35s cubic-bezier(0.22, 1, 0.36, 1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.hyvui-drawer-right {
|
|
97
|
+
right: 0;
|
|
98
|
+
animation: drawer-right-in 0.35s cubic-bezier(0.22, 1, 0.36, 1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
:global(.hyvui-drawer-surface) {
|
|
102
|
+
height: 100%;
|
|
103
|
+
padding: calc(var(--space-card) + var(--safe-top)) calc(var(--space-card) + var(--safe-right))
|
|
104
|
+
calc(var(--space-card) + var(--safe-bottom)) calc(var(--space-card) + var(--safe-left));
|
|
105
|
+
overflow-y: auto;
|
|
106
|
+
overscroll-behavior: contain;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@keyframes drawer-left-in {
|
|
110
|
+
from {
|
|
111
|
+
transform: translateX(-100%);
|
|
112
|
+
}
|
|
113
|
+
to {
|
|
114
|
+
transform: translateX(0);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@keyframes drawer-right-in {
|
|
119
|
+
from {
|
|
120
|
+
transform: translateX(100%);
|
|
121
|
+
}
|
|
122
|
+
to {
|
|
123
|
+
transform: translateX(0);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@media (prefers-reduced-motion: reduce) {
|
|
128
|
+
.hyvui-drawer-left,
|
|
129
|
+
.hyvui-drawer-right {
|
|
130
|
+
animation: none;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
@@ -1,43 +1,118 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../../utils/cn.js';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Grid mode. `auto` computes columns from container width. */
|
|
7
|
+
mode?: 'auto' | 'template';
|
|
8
|
+
/** CSS grid-template-columns value (used when mode = 'template'). */
|
|
9
|
+
cols?: string | number;
|
|
10
|
+
/** Minimum column width (used when mode = 'auto'). */
|
|
11
|
+
minColWidth?: string;
|
|
12
|
+
/** Maximum number of columns (used when mode = 'auto'). */
|
|
13
|
+
maxCols?: number;
|
|
14
|
+
/** Gap between grid items. */
|
|
15
|
+
gap?: string;
|
|
16
|
+
/** HTML tag to render. */
|
|
17
|
+
as?: string;
|
|
18
|
+
/** Additional CSS classes. */
|
|
19
|
+
class?: string;
|
|
20
|
+
/** Grid children. */
|
|
21
|
+
children?: Snippet;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
mode = 'auto',
|
|
26
|
+
cols = 1,
|
|
27
|
+
minColWidth = '16rem',
|
|
28
|
+
maxCols,
|
|
29
|
+
gap = 'var(--space-md)',
|
|
30
|
+
as = 'div',
|
|
31
|
+
class: className = '',
|
|
32
|
+
children
|
|
33
|
+
}: Props = $props();
|
|
34
|
+
|
|
35
|
+
let gridEl: HTMLElement | undefined = $state();
|
|
36
|
+
let gridCols = $state('repeat(1, minmax(0, 1fr))');
|
|
37
|
+
|
|
38
|
+
function readGapPx(el: HTMLElement): number {
|
|
39
|
+
const cs = getComputedStyle(el);
|
|
40
|
+
const raw = cs.columnGap || cs.gap || '0';
|
|
41
|
+
const px = Number.parseFloat(raw);
|
|
42
|
+
return Number.isFinite(px) ? px : 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function measureWidthPx(el: HTMLElement, width: string): number {
|
|
46
|
+
const probe = document.createElement('div');
|
|
47
|
+
probe.style.position = 'absolute';
|
|
48
|
+
probe.style.visibility = 'hidden';
|
|
49
|
+
probe.style.pointerEvents = 'none';
|
|
50
|
+
probe.style.width = width;
|
|
51
|
+
probe.style.height = '0';
|
|
52
|
+
probe.style.padding = '0';
|
|
53
|
+
probe.style.border = '0';
|
|
54
|
+
el.appendChild(probe);
|
|
55
|
+
const px = probe.getBoundingClientRect().width;
|
|
56
|
+
probe.remove();
|
|
57
|
+
return px > 0 ? px : 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
$effect(() => {
|
|
61
|
+
if (typeof window === 'undefined') return;
|
|
62
|
+
if (!gridEl) return;
|
|
63
|
+
const el = gridEl;
|
|
64
|
+
|
|
65
|
+
if (mode === 'template') {
|
|
66
|
+
gridCols = typeof cols === 'string' ? cols : `repeat(${cols}, minmax(0, 1fr))`;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Back-compat: if callers pass a numeric `cols` in auto mode, treat it as maxCols.
|
|
71
|
+
const max = maxCols ?? (typeof cols === 'number' ? cols : undefined);
|
|
72
|
+
|
|
73
|
+
let minPx = measureWidthPx(el, minColWidth);
|
|
74
|
+
if (minPx <= 0) minPx = 16 * 16; // fallback: 16rem at 16px root
|
|
75
|
+
|
|
76
|
+
let last = 0;
|
|
77
|
+
const ro = new ResizeObserver((entries) => {
|
|
78
|
+
const entry = entries[0];
|
|
79
|
+
if (!entry) return;
|
|
80
|
+
const width = entry.contentRect.width;
|
|
81
|
+
const gapPx = readGapPx(el);
|
|
82
|
+
const next = Math.max(1, Math.floor((width + gapPx) / (minPx + gapPx)));
|
|
83
|
+
const clamped = max ? Math.min(next, max) : next;
|
|
84
|
+
if (clamped === last) return;
|
|
85
|
+
last = clamped;
|
|
86
|
+
gridCols = `repeat(${clamped}, minmax(0, 1fr))`;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
ro.observe(el);
|
|
90
|
+
|
|
91
|
+
// Seed with initial measurement.
|
|
92
|
+
const width = el.getBoundingClientRect().width;
|
|
93
|
+
const gapPx = readGapPx(el);
|
|
94
|
+
const next = Math.max(1, Math.floor((width + gapPx) / (minPx + gapPx)));
|
|
95
|
+
last = max ? Math.min(next, max) : next;
|
|
96
|
+
gridCols = `repeat(${last}, minmax(0, 1fr))`;
|
|
97
|
+
|
|
98
|
+
return () => ro.disconnect();
|
|
99
|
+
});
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<svelte:element
|
|
103
|
+
this={as}
|
|
104
|
+
bind:this={gridEl}
|
|
105
|
+
class={cn('hyvui-grid', className)}
|
|
106
|
+
style:--hyv-grid-cols={gridCols}
|
|
107
|
+
style:gap
|
|
108
|
+
>
|
|
109
|
+
{#if children}{@render children()}{/if}
|
|
110
|
+
</svelte:element>
|
|
111
|
+
|
|
112
|
+
<style>
|
|
113
|
+
.hyvui-grid {
|
|
114
|
+
display: grid;
|
|
115
|
+
min-width: 0;
|
|
116
|
+
grid-template-columns: var(--hyv-grid-cols);
|
|
117
|
+
}
|
|
118
|
+
</style>
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
interface Props {
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/** Grid mode. `auto` computes columns from container width. */
|
|
4
|
+
mode?: 'auto' | 'template';
|
|
5
|
+
/** CSS grid-template-columns value (used when mode = 'template'). */
|
|
6
|
+
cols?: string | number;
|
|
7
|
+
/** Minimum column width (used when mode = 'auto'). */
|
|
8
|
+
minColWidth?: string;
|
|
9
|
+
/** Maximum number of columns (used when mode = 'auto'). */
|
|
10
|
+
maxCols?: number;
|
|
5
11
|
/** Gap between grid items. */
|
|
6
12
|
gap?: string;
|
|
7
13
|
/** HTML tag to render. */
|