@miozu/jera 0.7.1 → 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,248 @@
1
+ <!--
2
+ @component TabNav
3
+
4
+ A flexible tab navigation component with multiple variants.
5
+ Supports icons, badges, counts, and different visual styles.
6
+
7
+ @example Basic tabs
8
+ <TabNav
9
+ tabs={[
10
+ {id: 'overview', label: 'Overview'},
11
+ {id: 'settings', label: 'Settings'}
12
+ ]}
13
+ bind:active={activeTab}
14
+ />
15
+
16
+ @example With icons and counts
17
+ <TabNav
18
+ tabs={[
19
+ {id: 'inbox', label: 'Inbox', icon: InboxIcon, count: 12},
20
+ {id: 'sent', label: 'Sent', icon: SendIcon}
21
+ ]}
22
+ bind:active={activeTab}
23
+ variant="pills"
24
+ />
25
+
26
+ @example Underline variant
27
+ <TabNav
28
+ tabs={[{id: 'all', label: 'All'}, {id: 'active', label: 'Active'}]}
29
+ bind:active={activeTab}
30
+ variant="underline"
31
+ />
32
+ -->
33
+ <script>
34
+ let {
35
+ tabs = [],
36
+ active = $bindable(''),
37
+ variant = 'default',
38
+ size = 'md',
39
+ class: className = '',
40
+ onchange
41
+ } = $props();
42
+
43
+ function handleTabClick(tab) {
44
+ if (tab.disabled) return;
45
+ active = tab.id;
46
+ onchange?.(tab);
47
+ }
48
+ </script>
49
+
50
+ <nav class="tab-nav tab-nav-{variant} tab-nav-{size} {className}" role="tablist">
51
+ {#each tabs as tab}
52
+ <button
53
+ type="button"
54
+ class="tab-item"
55
+ class:active={active === tab.id}
56
+ class:disabled={tab.disabled}
57
+ role="tab"
58
+ aria-selected={active === tab.id}
59
+ aria-disabled={tab.disabled}
60
+ onclick={() => handleTabClick(tab)}
61
+ >
62
+ {#if tab.icon}
63
+ <span class="tab-icon">
64
+ {@render tab.icon()}
65
+ </span>
66
+ {/if}
67
+ <span class="tab-label">{tab.label}</span>
68
+ {#if tab.count !== undefined}
69
+ <span class="tab-count">({tab.count})</span>
70
+ {/if}
71
+ {#if tab.badge}
72
+ <span class="tab-badge">{tab.badge}</span>
73
+ {/if}
74
+ </button>
75
+ {/each}
76
+ </nav>
77
+
78
+ <style>
79
+ .tab-nav {
80
+ display: flex;
81
+ align-items: center;
82
+ gap: var(--space-1);
83
+ }
84
+
85
+ /* Default variant - subtle background */
86
+ .tab-nav-default {
87
+ padding: var(--space-1);
88
+ background: color-mix(in srgb, var(--color-base01) 70%, transparent);
89
+ border-radius: var(--radius-lg);
90
+ }
91
+
92
+ .tab-nav-default .tab-item {
93
+ padding: var(--space-2) var(--space-3);
94
+ border-radius: var(--radius-md);
95
+ background: transparent;
96
+ border: none;
97
+ color: var(--color-base04);
98
+ font-size: var(--text-sm);
99
+ font-weight: 500;
100
+ cursor: pointer;
101
+ transition: all 0.15s ease;
102
+ }
103
+
104
+ .tab-nav-default .tab-item:hover:not(.disabled) {
105
+ color: var(--color-base05);
106
+ background: color-mix(in srgb, var(--color-base02) 50%, transparent);
107
+ }
108
+
109
+ .tab-nav-default .tab-item.active {
110
+ background: var(--color-base00);
111
+ color: var(--color-base06);
112
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
113
+ }
114
+
115
+ /* Pills variant */
116
+ .tab-nav-pills {
117
+ gap: var(--space-2);
118
+ }
119
+
120
+ .tab-nav-pills .tab-item {
121
+ padding: var(--space-2) var(--space-4);
122
+ border-radius: var(--radius-lg);
123
+ background: transparent;
124
+ border: 1px solid transparent;
125
+ color: var(--color-base04);
126
+ font-size: var(--text-sm);
127
+ font-weight: 500;
128
+ cursor: pointer;
129
+ transition: all 0.15s ease;
130
+ }
131
+
132
+ .tab-nav-pills .tab-item:hover:not(.disabled) {
133
+ background: var(--color-base01);
134
+ color: var(--color-base05);
135
+ }
136
+
137
+ .tab-nav-pills .tab-item.active {
138
+ background: color-mix(in srgb, var(--color-base0D) 10%, transparent);
139
+ border-color: color-mix(in srgb, var(--color-base0D) 30%, transparent);
140
+ color: var(--color-base0D);
141
+ }
142
+
143
+ /* Underline variant */
144
+ .tab-nav-underline {
145
+ gap: 0;
146
+ border-bottom: 1px solid var(--color-base02);
147
+ }
148
+
149
+ .tab-nav-underline .tab-item {
150
+ padding: var(--space-3) var(--space-4);
151
+ background: transparent;
152
+ border: none;
153
+ border-bottom: 2px solid transparent;
154
+ margin-bottom: -1px;
155
+ color: var(--color-base04);
156
+ font-size: var(--text-sm);
157
+ font-weight: 500;
158
+ cursor: pointer;
159
+ transition: all 0.15s ease;
160
+ }
161
+
162
+ .tab-nav-underline .tab-item:hover:not(.disabled) {
163
+ color: var(--color-base05);
164
+ border-bottom-color: var(--color-base03);
165
+ }
166
+
167
+ .tab-nav-underline .tab-item.active {
168
+ color: var(--color-base0D);
169
+ border-bottom-color: var(--color-base0D);
170
+ }
171
+
172
+ /* Sizes */
173
+ .tab-nav-sm .tab-item {
174
+ padding: var(--space-1) var(--space-2);
175
+ font-size: var(--text-xs);
176
+ }
177
+
178
+ .tab-nav-lg .tab-item {
179
+ padding: var(--space-3) var(--space-5);
180
+ font-size: var(--text-base);
181
+ }
182
+
183
+ /* Shared styles */
184
+ .tab-item {
185
+ display: inline-flex;
186
+ align-items: center;
187
+ gap: var(--space-2);
188
+ white-space: nowrap;
189
+ flex-shrink: 0;
190
+ }
191
+
192
+ .tab-item.disabled {
193
+ opacity: 0.5;
194
+ cursor: not-allowed;
195
+ }
196
+
197
+ .tab-item:focus-visible {
198
+ outline: 2px solid var(--color-base0D);
199
+ outline-offset: 2px;
200
+ }
201
+
202
+ .tab-icon {
203
+ display: flex;
204
+ align-items: center;
205
+ justify-content: center;
206
+ }
207
+
208
+ .tab-label {
209
+ line-height: 1;
210
+ }
211
+
212
+ .tab-count {
213
+ font-size: var(--text-xs);
214
+ color: var(--color-base04);
215
+ }
216
+
217
+ .tab-item.active .tab-count {
218
+ color: inherit;
219
+ opacity: 0.7;
220
+ }
221
+
222
+ .tab-badge {
223
+ display: inline-flex;
224
+ align-items: center;
225
+ justify-content: center;
226
+ min-width: 1.25rem;
227
+ height: 1.25rem;
228
+ padding: 0 var(--space-1);
229
+ font-size: var(--text-xs);
230
+ font-weight: 600;
231
+ background: var(--color-base09);
232
+ color: var(--color-base00);
233
+ border-radius: 9999px;
234
+ }
235
+
236
+ /* Scrollable container */
237
+ @media (max-width: 768px) {
238
+ .tab-nav {
239
+ overflow-x: auto;
240
+ scrollbar-width: none;
241
+ -ms-overflow-style: none;
242
+ }
243
+
244
+ .tab-nav::-webkit-scrollbar {
245
+ display: none;
246
+ }
247
+ }
248
+ </style>
@@ -0,0 +1,162 @@
1
+ <!--
2
+ @component FilterChip
3
+
4
+ A toggleable chip for filtering content.
5
+ Supports icons, counts, and multiple selection modes.
6
+
7
+ @example Basic filter chip
8
+ <FilterChip
9
+ label="Active"
10
+ active={filters.active}
11
+ onclick={() => toggleFilter('active')}
12
+ />
13
+
14
+ @example With icon and count
15
+ <FilterChip
16
+ label="Critical"
17
+ active={showCritical}
18
+ count={criticalCount}
19
+ variant="error"
20
+ >
21
+ {#snippet icon()}
22
+ <AlertIcon size={14} />
23
+ {/snippet}
24
+ </FilterChip>
25
+
26
+ @example Group of filters
27
+ <div class="filter-group">
28
+ {#each filters as filter}
29
+ <FilterChip
30
+ label={filter.label}
31
+ active={activeFilters.includes(filter.id)}
32
+ onclick={() => toggleFilter(filter.id)}
33
+ />
34
+ {/each}
35
+ </div>
36
+ -->
37
+ <script>
38
+ let {
39
+ label = '',
40
+ active = false,
41
+ count = null,
42
+ variant = 'default',
43
+ disabled = false,
44
+ class: className = '',
45
+ icon,
46
+ onclick
47
+ } = $props();
48
+ </script>
49
+
50
+ <button
51
+ type="button"
52
+ class="filter-chip filter-chip-{variant}"
53
+ class:active
54
+ class:disabled
55
+ {disabled}
56
+ {onclick}
57
+ aria-pressed={active}
58
+ >
59
+ {#if icon}
60
+ <span class="chip-icon">
61
+ {@render icon()}
62
+ </span>
63
+ {/if}
64
+ <span class="chip-label">{label}</span>
65
+ {#if count !== null}
66
+ <span class="chip-count">{count}</span>
67
+ {/if}
68
+ </button>
69
+
70
+ <style>
71
+ .filter-chip {
72
+ display: inline-flex;
73
+ align-items: center;
74
+ gap: var(--space-2);
75
+ padding: var(--space-1) var(--space-3);
76
+ font-size: var(--text-sm);
77
+ font-weight: 500;
78
+ background: transparent;
79
+ border: 1px solid var(--color-base02);
80
+ border-radius: var(--radius-lg);
81
+ color: var(--color-base04);
82
+ cursor: pointer;
83
+ transition: all 0.15s ease;
84
+ }
85
+
86
+ .filter-chip:hover:not(.disabled) {
87
+ border-color: var(--color-base03);
88
+ color: var(--color-base05);
89
+ background: var(--color-base01);
90
+ }
91
+
92
+ .filter-chip:focus-visible {
93
+ outline: 2px solid var(--color-base0D);
94
+ outline-offset: 2px;
95
+ }
96
+
97
+ .filter-chip.disabled {
98
+ opacity: 0.5;
99
+ cursor: not-allowed;
100
+ }
101
+
102
+ /* Active states by variant */
103
+ .filter-chip-default.active {
104
+ background: color-mix(in srgb, var(--color-base0D) 10%, transparent);
105
+ border-color: color-mix(in srgb, var(--color-base0D) 40%, transparent);
106
+ color: var(--color-base0D);
107
+ }
108
+
109
+ .filter-chip-error.active {
110
+ background: color-mix(in srgb, var(--color-base08) 10%, transparent);
111
+ border-color: color-mix(in srgb, var(--color-base08) 40%, transparent);
112
+ color: var(--color-base08);
113
+ }
114
+
115
+ .filter-chip-warning.active {
116
+ background: color-mix(in srgb, var(--color-base0A) 10%, transparent);
117
+ border-color: color-mix(in srgb, var(--color-base0A) 40%, transparent);
118
+ color: var(--color-base0A);
119
+ }
120
+
121
+ .filter-chip-success.active {
122
+ background: color-mix(in srgb, var(--color-base0B) 10%, transparent);
123
+ border-color: color-mix(in srgb, var(--color-base0B) 40%, transparent);
124
+ color: var(--color-base0B);
125
+ }
126
+
127
+ .filter-chip-info.active {
128
+ background: color-mix(in srgb, var(--color-base0C) 10%, transparent);
129
+ border-color: color-mix(in srgb, var(--color-base0C) 40%, transparent);
130
+ color: var(--color-base0C);
131
+ }
132
+
133
+ .chip-icon {
134
+ display: flex;
135
+ align-items: center;
136
+ justify-content: center;
137
+ }
138
+
139
+ .chip-label {
140
+ line-height: 1;
141
+ }
142
+
143
+ .chip-count {
144
+ display: inline-flex;
145
+ align-items: center;
146
+ justify-content: center;
147
+ min-width: 1.25rem;
148
+ height: 1.25rem;
149
+ padding: 0 var(--space-1);
150
+ font-size: var(--text-xs);
151
+ font-weight: 600;
152
+ background: var(--color-base02);
153
+ color: var(--color-base05);
154
+ border-radius: 9999px;
155
+ }
156
+
157
+ .filter-chip.active .chip-count {
158
+ background: currentColor;
159
+ color: var(--color-base00);
160
+ opacity: 0.8;
161
+ }
162
+ </style>
@@ -0,0 +1,94 @@
1
+ <!--
2
+ @component LinkCard
3
+
4
+ A clickable card for navigation, commonly used in dashboards for quick-nav sections.
5
+
6
+ @example Basic
7
+ <LinkCard href="/services" label="Ops Hub" />
8
+
9
+ @example With trailing icon
10
+ <LinkCard href="/pm" label="PM Board">
11
+ {#snippet trailing()}
12
+ <ArrowRight size={14} />
13
+ {/snippet}
14
+ </LinkCard>
15
+
16
+ @example Disabled
17
+ <LinkCard href="/admin" label="Admin Panel" disabled />
18
+ -->
19
+ <script>
20
+ let {
21
+ href,
22
+ label,
23
+ disabled = false,
24
+ class: className = '',
25
+ trailing,
26
+ ...rest
27
+ } = $props();
28
+ </script>
29
+
30
+ {#if disabled}
31
+ <div class="link-card link-card-disabled {className}" {...rest}>
32
+ <span class="link-card-label">{label}</span>
33
+ {#if trailing}
34
+ <span class="link-card-trailing">
35
+ {@render trailing()}
36
+ </span>
37
+ {/if}
38
+ </div>
39
+ {:else}
40
+ <a {href} class="link-card {className}" {...rest}>
41
+ <span class="link-card-label">{label}</span>
42
+ {#if trailing}
43
+ <span class="link-card-trailing">
44
+ {@render trailing()}
45
+ </span>
46
+ {/if}
47
+ </a>
48
+ {/if}
49
+
50
+ <style>
51
+ .link-card {
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: space-between;
55
+ width: 100%;
56
+ box-sizing: border-box;
57
+ padding: var(--space-3) var(--space-4);
58
+ border-radius: var(--radius-lg);
59
+ background: var(--color-base01);
60
+ border: 1px solid var(--color-base02);
61
+ text-decoration: none;
62
+ transition: border-color 0.2s ease, background-color 0.2s ease;
63
+ }
64
+
65
+ .link-card:hover {
66
+ border-color: var(--color-base03);
67
+ background: var(--color-base02);
68
+ }
69
+
70
+ .link-card:hover .link-card-trailing {
71
+ color: var(--color-base06);
72
+ }
73
+
74
+ .link-card-disabled {
75
+ opacity: 0.5;
76
+ cursor: not-allowed;
77
+ }
78
+
79
+ .link-card-disabled:hover {
80
+ border-color: var(--color-base02);
81
+ background: var(--color-base01);
82
+ }
83
+
84
+ .link-card-label {
85
+ font-size: var(--text-sm);
86
+ font-weight: 500;
87
+ color: var(--color-base05);
88
+ }
89
+
90
+ .link-card-trailing {
91
+ color: var(--color-base04);
92
+ transition: color 0.2s ease;
93
+ }
94
+ </style>