@miozu/jera 0.7.2 → 0.8.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.
@@ -0,0 +1,266 @@
1
+ <!--
2
+ @component MetricCard
3
+
4
+ A card displaying a metric value with label, icon, and optional status indicator.
5
+ Ideal for dashboards and overview sections.
6
+
7
+ @example Basic metric
8
+ <MetricCard
9
+ label="Total Users"
10
+ value={1234}
11
+ />
12
+
13
+ @example With icon and status
14
+ <MetricCard
15
+ label="Services"
16
+ value={4}
17
+ unit="/5"
18
+ status="success"
19
+ >
20
+ {#snippet icon()}
21
+ <ActivityIcon size={16} />
22
+ {/snippet}
23
+ </MetricCard>
24
+
25
+ @example Clickable with href
26
+ <MetricCard
27
+ label="Errors"
28
+ value={3}
29
+ status="error"
30
+ href="/errors"
31
+ >
32
+ {#snippet badge()}
33
+ <StatusBadge variant="error">ALERT</StatusBadge>
34
+ {/snippet}
35
+ </MetricCard>
36
+
37
+ @example With progress bar
38
+ <MetricCard
39
+ label="Storage"
40
+ value={75}
41
+ unit="%"
42
+ progress={75}
43
+ />
44
+ -->
45
+ <script>
46
+ let {
47
+ label = '',
48
+ value = 0,
49
+ unit = '',
50
+ sublabel = '',
51
+ status = '',
52
+ progress = null,
53
+ href = null,
54
+ class: className = '',
55
+ icon,
56
+ badge,
57
+ onclick
58
+ } = $props();
59
+
60
+ const isClickable = $derived(!!href || !!onclick);
61
+
62
+ const progressVariant = $derived.by(() => {
63
+ if (progress === null) return '';
64
+ if (progress > 80) return 'progress-error';
65
+ if (progress > 60) return 'progress-warning';
66
+ return 'progress-primary';
67
+ });
68
+ </script>
69
+
70
+ {#if href}
71
+ <a
72
+ {href}
73
+ class="metric-card {status ? `metric-card-${status}` : ''} metric-card-clickable {className}"
74
+ >
75
+ <div class="metric-header">
76
+ {#if icon}
77
+ <span class="metric-icon">
78
+ {@render icon()}
79
+ </span>
80
+ {/if}
81
+ <span class="metric-label">{label}</span>
82
+ {#if badge}
83
+ <span class="metric-badge">
84
+ {@render badge()}
85
+ </span>
86
+ {/if}
87
+ </div>
88
+
89
+ <div class="metric-body">
90
+ <span class="metric-value {status ? `metric-value-${status}` : ''}">
91
+ {value}{#if unit}<span class="metric-unit">{unit}</span>{/if}
92
+ </span>
93
+ {#if sublabel}
94
+ <span class="metric-sublabel">{sublabel}</span>
95
+ {/if}
96
+ </div>
97
+
98
+ {#if progress !== null}
99
+ <div class="metric-progress">
100
+ <div class="metric-progress-bar {progressVariant}" style="width: {Math.min(100, Math.max(0, progress))}%"></div>
101
+ </div>
102
+ {/if}
103
+ </a>
104
+ {:else}
105
+ <div
106
+ class="metric-card {status ? `metric-card-${status}` : ''} {isClickable ? 'metric-card-clickable' : ''} {className}"
107
+ {onclick}
108
+ role={onclick ? 'button' : undefined}
109
+ tabindex={onclick ? 0 : undefined}
110
+ >
111
+ <div class="metric-header">
112
+ {#if icon}
113
+ <span class="metric-icon">
114
+ {@render icon()}
115
+ </span>
116
+ {/if}
117
+ <span class="metric-label">{label}</span>
118
+ {#if badge}
119
+ <span class="metric-badge">
120
+ {@render badge()}
121
+ </span>
122
+ {/if}
123
+ </div>
124
+
125
+ <div class="metric-body">
126
+ <span class="metric-value {status ? `metric-value-${status}` : ''}">
127
+ {value}{#if unit}<span class="metric-unit">{unit}</span>{/if}
128
+ </span>
129
+ {#if sublabel}
130
+ <span class="metric-sublabel">{sublabel}</span>
131
+ {/if}
132
+ </div>
133
+
134
+ {#if progress !== null}
135
+ <div class="metric-progress">
136
+ <div class="metric-progress-bar {progressVariant}" style="width: {Math.min(100, Math.max(0, progress))}%"></div>
137
+ </div>
138
+ {/if}
139
+ </div>
140
+ {/if}
141
+
142
+ <style>
143
+ .metric-card {
144
+ display: flex;
145
+ flex-direction: column;
146
+ gap: var(--space-3);
147
+ padding: var(--space-5);
148
+ background: var(--color-base01);
149
+ border: 1px solid var(--color-base02);
150
+ border-radius: var(--radius-xl);
151
+ text-decoration: none;
152
+ transition: all 0.15s ease;
153
+ }
154
+
155
+ .metric-card-clickable {
156
+ cursor: pointer;
157
+ }
158
+
159
+ .metric-card-clickable:hover {
160
+ border-color: var(--color-base03);
161
+ background: color-mix(in srgb, var(--color-base01) 80%, var(--color-base02));
162
+ }
163
+
164
+ .metric-card-error {
165
+ border-color: color-mix(in srgb, var(--color-base08) 30%, transparent);
166
+ }
167
+
168
+ .metric-card-warning {
169
+ border-color: color-mix(in srgb, var(--color-base0A) 30%, transparent);
170
+ }
171
+
172
+ .metric-card-success {
173
+ border-color: color-mix(in srgb, var(--color-base0B) 30%, transparent);
174
+ }
175
+
176
+ .metric-header {
177
+ display: flex;
178
+ align-items: center;
179
+ gap: var(--space-2);
180
+ }
181
+
182
+ .metric-icon {
183
+ color: var(--color-base04);
184
+ }
185
+
186
+ .metric-label {
187
+ flex: 1;
188
+ font-size: var(--text-xs);
189
+ font-weight: 500;
190
+ text-transform: uppercase;
191
+ letter-spacing: 0.05em;
192
+ color: var(--color-base04);
193
+ }
194
+
195
+ .metric-badge {
196
+ flex-shrink: 0;
197
+ }
198
+
199
+ .metric-body {
200
+ display: flex;
201
+ align-items: baseline;
202
+ gap: var(--space-2);
203
+ }
204
+
205
+ .metric-value {
206
+ font-size: var(--text-3xl);
207
+ font-weight: 600;
208
+ color: var(--color-base06);
209
+ line-height: 1;
210
+ }
211
+
212
+ .metric-value-success {
213
+ color: var(--color-base0B);
214
+ }
215
+
216
+ .metric-value-warning {
217
+ color: var(--color-base0A);
218
+ }
219
+
220
+ .metric-value-error {
221
+ color: var(--color-base08);
222
+ }
223
+
224
+ .metric-unit {
225
+ font-size: var(--text-lg);
226
+ font-weight: 500;
227
+ color: var(--color-base04);
228
+ }
229
+
230
+ .metric-sublabel {
231
+ font-size: var(--text-sm);
232
+ color: var(--color-base04);
233
+ }
234
+
235
+ .metric-progress {
236
+ height: 4px;
237
+ background: var(--color-base02);
238
+ border-radius: 2px;
239
+ overflow: hidden;
240
+ margin-top: var(--space-1);
241
+ }
242
+
243
+ .metric-progress-bar {
244
+ height: 100%;
245
+ border-radius: 2px;
246
+ transition: width 0.3s ease;
247
+ }
248
+
249
+ .progress-primary {
250
+ background: var(--color-base0D);
251
+ }
252
+
253
+ .progress-warning {
254
+ background: var(--color-base0A);
255
+ }
256
+
257
+ .progress-error {
258
+ background: var(--color-base08);
259
+ }
260
+
261
+ /* Focus state for clickable */
262
+ .metric-card-clickable:focus-visible {
263
+ outline: 2px solid var(--color-base0D);
264
+ outline-offset: 2px;
265
+ }
266
+ </style>
@@ -0,0 +1,121 @@
1
+ <!--
2
+ @component StatusLine
3
+
4
+ A horizontal line of status items with separators.
5
+ Ideal for displaying metadata, timestamps, and badges inline.
6
+
7
+ @example Basic status line
8
+ <StatusLine>
9
+ <span>Created by John</span>
10
+ <span>2 hours ago</span>
11
+ <Badge variant="success">Active</Badge>
12
+ </StatusLine>
13
+
14
+ @example With custom separator
15
+ <StatusLine separator="|">
16
+ <span>Version 1.2.3</span>
17
+ <span>Released Jan 15</span>
18
+ </StatusLine>
19
+
20
+ @example Compact size
21
+ <StatusLine size="sm">
22
+ <span>3 tasks</span>
23
+ <span>2 completed</span>
24
+ </StatusLine>
25
+ -->
26
+ <script>
27
+ let {
28
+ separator = '·',
29
+ size = 'md',
30
+ class: className = '',
31
+ items,
32
+ children
33
+ } = $props();
34
+ </script>
35
+
36
+ <div class="status-line status-line-{size} {className}">
37
+ {#if items}
38
+ {#each items as item, i}
39
+ {#if i > 0}
40
+ <span class="status-separator">{separator}</span>
41
+ {/if}
42
+ <span class="status-item">
43
+ {#if typeof item === 'string'}
44
+ {item}
45
+ {:else if item.icon}
46
+ <span class="status-icon">
47
+ {@render item.icon()}
48
+ </span>
49
+ {item.text}
50
+ {:else}
51
+ {item.text || item}
52
+ {/if}
53
+ </span>
54
+ {/each}
55
+ {:else if children}
56
+ {@render children()}
57
+ {/if}
58
+ </div>
59
+
60
+ <style>
61
+ .status-line {
62
+ display: flex;
63
+ align-items: center;
64
+ flex-wrap: wrap;
65
+ gap: var(--space-2);
66
+ color: var(--color-base04);
67
+ }
68
+
69
+ .status-line-sm {
70
+ font-size: var(--text-xs);
71
+ gap: var(--space-1);
72
+ }
73
+
74
+ .status-line-md {
75
+ font-size: var(--text-sm);
76
+ }
77
+
78
+ .status-line-lg {
79
+ font-size: var(--text-base);
80
+ gap: var(--space-3);
81
+ }
82
+
83
+ .status-separator {
84
+ color: var(--color-base03);
85
+ user-select: none;
86
+ }
87
+
88
+ .status-item {
89
+ display: inline-flex;
90
+ align-items: center;
91
+ gap: var(--space-1);
92
+ }
93
+
94
+ .status-icon {
95
+ display: flex;
96
+ align-items: center;
97
+ justify-content: center;
98
+ }
99
+
100
+ /* Allow children to use these utility classes */
101
+ :global(.status-line .status-highlight) {
102
+ color: var(--color-base06);
103
+ font-weight: 500;
104
+ }
105
+
106
+ :global(.status-line .status-success) {
107
+ color: var(--color-base0B);
108
+ }
109
+
110
+ :global(.status-line .status-warning) {
111
+ color: var(--color-base0A);
112
+ }
113
+
114
+ :global(.status-line .status-error) {
115
+ color: var(--color-base08);
116
+ }
117
+
118
+ :global(.status-line .status-muted) {
119
+ color: var(--color-base03);
120
+ }
121
+ </style>
@@ -0,0 +1,201 @@
1
+ <!--
2
+ @component Tooltip
3
+
4
+ A fast, accessible tooltip that appears on hover.
5
+ Supports multiple positions and custom content.
6
+
7
+ @example Basic tooltip
8
+ <Tooltip text="Edit this item">
9
+ <Button>Edit</Button>
10
+ </Tooltip>
11
+
12
+ @example Different positions
13
+ <Tooltip text="Top tooltip" position="top">
14
+ <span>Hover me</span>
15
+ </Tooltip>
16
+
17
+ <Tooltip text="Right tooltip" position="right">
18
+ <span>Hover me</span>
19
+ </Tooltip>
20
+
21
+ @example With custom delay
22
+ <Tooltip text="Delayed tooltip" delay={500}>
23
+ <Button>Hover</Button>
24
+ </Tooltip>
25
+
26
+ @example Rich content
27
+ <Tooltip>
28
+ {#snippet content()}
29
+ <div class="tooltip-rich">
30
+ <strong>Title</strong>
31
+ <p>Description text here</p>
32
+ </div>
33
+ {/snippet}
34
+ <Button>Info</Button>
35
+ </Tooltip>
36
+ -->
37
+ <script>
38
+ let {
39
+ text = '',
40
+ position = 'top',
41
+ delay = 50,
42
+ disabled = false,
43
+ class: className = '',
44
+ content,
45
+ children
46
+ } = $props();
47
+
48
+ let visible = $state(false);
49
+ let timeout = null;
50
+
51
+ function show() {
52
+ if (disabled) return;
53
+ timeout = setTimeout(() => {
54
+ visible = true;
55
+ }, delay);
56
+ }
57
+
58
+ function hide() {
59
+ if (timeout) {
60
+ clearTimeout(timeout);
61
+ timeout = null;
62
+ }
63
+ visible = false;
64
+ }
65
+ </script>
66
+
67
+ <div
68
+ class="tooltip-wrapper {className}"
69
+ onmouseenter={show}
70
+ onmouseleave={hide}
71
+ onfocus={show}
72
+ onblur={hide}
73
+ >
74
+ {#if children}
75
+ {@render children()}
76
+ {/if}
77
+
78
+ {#if visible && (text || content)}
79
+ <div class="tooltip tooltip-{position}" role="tooltip">
80
+ <div class="tooltip-content">
81
+ {#if content}
82
+ {@render content()}
83
+ {:else}
84
+ {text}
85
+ {/if}
86
+ </div>
87
+ <div class="tooltip-arrow"></div>
88
+ </div>
89
+ {/if}
90
+ </div>
91
+
92
+ <style>
93
+ .tooltip-wrapper {
94
+ position: relative;
95
+ display: inline-flex;
96
+ }
97
+
98
+ .tooltip {
99
+ position: absolute;
100
+ z-index: 50;
101
+ pointer-events: none;
102
+ animation: tooltip-enter 0.15s ease-out;
103
+ }
104
+
105
+ @keyframes tooltip-enter {
106
+ from {
107
+ opacity: 0;
108
+ transform: scale(0.95);
109
+ }
110
+ to {
111
+ opacity: 1;
112
+ transform: scale(1);
113
+ }
114
+ }
115
+
116
+ .tooltip-content {
117
+ padding: var(--space-2) var(--space-3);
118
+ font-size: var(--text-xs);
119
+ font-weight: 500;
120
+ color: var(--color-base06);
121
+ background: var(--color-base01);
122
+ border: 1px solid var(--color-base02);
123
+ border-radius: var(--radius-md);
124
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
125
+ white-space: nowrap;
126
+ max-width: 20rem;
127
+ }
128
+
129
+ .tooltip-arrow {
130
+ position: absolute;
131
+ width: 8px;
132
+ height: 8px;
133
+ background: var(--color-base01);
134
+ border: 1px solid var(--color-base02);
135
+ transform: rotate(45deg);
136
+ }
137
+
138
+ /* Position: Top */
139
+ .tooltip-top {
140
+ bottom: 100%;
141
+ left: 50%;
142
+ transform: translateX(-50%);
143
+ margin-bottom: var(--space-2);
144
+ }
145
+
146
+ .tooltip-top .tooltip-arrow {
147
+ bottom: -5px;
148
+ left: 50%;
149
+ transform: translateX(-50%) rotate(45deg);
150
+ border-top: none;
151
+ border-left: none;
152
+ }
153
+
154
+ /* Position: Bottom */
155
+ .tooltip-bottom {
156
+ top: 100%;
157
+ left: 50%;
158
+ transform: translateX(-50%);
159
+ margin-top: var(--space-2);
160
+ }
161
+
162
+ .tooltip-bottom .tooltip-arrow {
163
+ top: -5px;
164
+ left: 50%;
165
+ transform: translateX(-50%) rotate(45deg);
166
+ border-bottom: none;
167
+ border-right: none;
168
+ }
169
+
170
+ /* Position: Left */
171
+ .tooltip-left {
172
+ right: 100%;
173
+ top: 50%;
174
+ transform: translateY(-50%);
175
+ margin-right: var(--space-2);
176
+ }
177
+
178
+ .tooltip-left .tooltip-arrow {
179
+ right: -5px;
180
+ top: 50%;
181
+ transform: translateY(-50%) rotate(45deg);
182
+ border-left: none;
183
+ border-bottom: none;
184
+ }
185
+
186
+ /* Position: Right */
187
+ .tooltip-right {
188
+ left: 100%;
189
+ top: 50%;
190
+ transform: translateY(-50%);
191
+ margin-left: var(--space-2);
192
+ }
193
+
194
+ .tooltip-right .tooltip-arrow {
195
+ left: -5px;
196
+ top: 50%;
197
+ transform: translateY(-50%) rotate(45deg);
198
+ border-right: none;
199
+ border-top: none;
200
+ }
201
+ </style>
package/src/index.js CHANGED
@@ -35,6 +35,11 @@ export { default as Stat } from './components/primitives/Stat.svelte';
35
35
  export { default as Link } from './components/primitives/Link.svelte';
36
36
  export { default as LinkCard } from './components/primitives/LinkCard.svelte';
37
37
  export { default as LazyImage } from './components/primitives/LazyImage.svelte';
38
+ export { default as MetricCard } from './components/primitives/MetricCard.svelte';
39
+ export { default as FilterChip } from './components/primitives/FilterChip.svelte';
40
+ export { default as StatusLine } from './components/primitives/StatusLine.svelte';
41
+ export { default as Tooltip } from './components/primitives/Tooltip.svelte';
42
+ export { default as MemberCard } from './components/primitives/MemberCard.svelte';
38
43
 
39
44
  // ============================================
40
45
  // COMPONENTS - Forms
@@ -88,6 +93,8 @@ export { default as ConfirmDialog } from './components/overlays/ConfirmDialog.sv
88
93
  // ============================================
89
94
 
90
95
  export { default as Tabs } from './components/navigation/Tabs.svelte';
96
+ export { default as TabNav } from './components/navigation/TabNav.svelte';
97
+ export { default as ScrollNav } from './components/navigation/ScrollNav.svelte';
91
98
  export { default as Accordion } from './components/navigation/Accordion.svelte';
92
99
  export { default as AccordionItem } from './components/navigation/AccordionItem.svelte';
93
100
 
@@ -126,6 +133,13 @@ export { default as LeftBarToggle } from './components/navigation/LeftBarToggle.
126
133
  export { default as LeftBarPopover } from './components/navigation/LeftBarPopover.svelte';
127
134
  export { default as DropdownContainer } from './components/navigation/DropdownContainer.svelte';
128
135
 
136
+ // ============================================
137
+ // COMPONENTS - Layout
138
+ // ============================================
139
+
140
+ export { default as PageHeader } from './components/layout/PageHeader.svelte';
141
+ export { default as SettingCard } from './components/layout/SettingCard.svelte';
142
+
129
143
  // ============================================
130
144
  // COMPONENTS - Documentation
131
145
  // ============================================