svelte-comp 1.0.7 → 1.1.2
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 +8 -6
- package/dist/App.svelte +555 -0
- package/dist/App.svelte.d.ts +3 -0
- package/dist/app.css +3 -0
- package/dist/lib/Carousel.svelte +6 -3
- package/dist/lib/CheckBox.svelte +1 -1
- package/dist/lib/CheckBox.svelte.d.ts +1 -1
- package/dist/lib/CodeView.svelte +26 -8
- package/dist/lib/FilePicker.svelte +1 -1
- package/dist/lib/Form.svelte +2 -2
- package/dist/lib/Hamburger.svelte +2 -2
- package/dist/lib/Hamburger.svelte.d.ts +1 -1
- package/dist/lib/Menu.svelte +59 -17
- package/dist/lib/PaginatedCard.svelte +1 -1
- package/dist/lib/Pagination.svelte +1 -1
- package/dist/lib/PrimaryColorSelect.svelte +99 -0
- package/dist/lib/PrimaryColorSelect.svelte.d.ts +9 -0
- package/dist/lib/ProgressCircle.svelte +191 -0
- package/dist/lib/ProgressCircle.svelte.d.ts +39 -0
- package/dist/lib/Select.svelte +28 -6
- package/dist/lib/Splitter.svelte +1 -1
- package/dist/lib/Table.svelte +1 -1
- package/dist/lib/Tabs.svelte +12 -8
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.js +2 -0
- package/dist/lib/lang.d.ts +9 -0
- package/dist/lib/lang.js +3 -0
- package/dist/lib/types/index.d.ts +7 -0
- package/dist/main.d.ts +4 -0
- package/dist/main.js +8 -0
- package/dist/styles.css +64 -1
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -45,13 +45,13 @@ Add to `src/app.css`:
|
|
|
45
45
|
|
|
46
46
|
## 📁 Components included
|
|
47
47
|
|
|
48
|
-
Accordion • Button • Card • Carousel • CheckBox • CodeView • ColorPicker
|
|
49
|
-
DatePicker • Dialog • Field • FilePicker • Form • Hamburger • Menu
|
|
50
|
-
PaginatedCard • Pagination •
|
|
51
|
-
Splitter • Switch • Tabs • Table • ThemeToggle •
|
|
52
|
-
Toast • Tooltip
|
|
48
|
+
Accordion • Button • Card • Carousel • CheckBox • CodeView • ColorPicker •
|
|
49
|
+
DatePicker • Dialog • Field • FilePicker • Form • Hamburger • Menu •
|
|
50
|
+
PaginatedCard • Pagination • PrimaryColorSelect • ProgressBar • ProgressCircle •
|
|
51
|
+
Radio • Select • Slider • Splitter • Switch • Tabs • Table • ThemeToggle •
|
|
52
|
+
TimePicker • Toast • Tooltip
|
|
53
53
|
|
|
54
|
-
Full component list in repository
|
|
54
|
+
Full component list in repository
|
|
55
55
|
|
|
56
56
|
---
|
|
57
57
|
|
|
@@ -88,3 +88,5 @@ MIT License
|
|
|
88
88
|
## 🔗 Links
|
|
89
89
|
|
|
90
90
|
GitHub: [https://github.com/MaestroFusion360/svelte-comp](https://github.com/MaestroFusion360/svelte-comp)
|
|
91
|
+
|
|
92
|
+
Demo: [https://maestrofusion360.github.io/svelte-comp/](https://maestrofusion360.github.io/svelte-comp/)
|
package/dist/App.svelte
ADDED
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Accordion from "./lib/Accordion.svelte";
|
|
3
|
+
import Button from "./lib/Button.svelte";
|
|
4
|
+
import Card from "./lib/Card.svelte";
|
|
5
|
+
import Carousel from "./lib/Carousel.svelte";
|
|
6
|
+
import ColorPicker from "./lib/ColorPicker.svelte";
|
|
7
|
+
import DatePicker from "./lib/DatePicker.svelte";
|
|
8
|
+
import Field from "./lib/Field.svelte";
|
|
9
|
+
import PrimaryColorSelect from "./lib/PrimaryColorSelect.svelte";
|
|
10
|
+
import ProgressBar from "./lib/ProgressBar.svelte";
|
|
11
|
+
import ProgressCircle from "./lib/ProgressCircle.svelte";
|
|
12
|
+
import Select from "./lib/Select.svelte";
|
|
13
|
+
import Slider from "./lib/Slider.svelte";
|
|
14
|
+
import Switch from "./lib/Switch.svelte";
|
|
15
|
+
import Table from "./lib/Table.svelte";
|
|
16
|
+
import Tabs from "./lib/Tabs.svelte";
|
|
17
|
+
import ThemeToggle from "./lib/ThemeToggle.svelte";
|
|
18
|
+
import TimePicker from "./lib/TimePicker.svelte";
|
|
19
|
+
import Toast from "./lib/Toast.svelte";
|
|
20
|
+
import Tooltip from "./lib/Tooltip.svelte";
|
|
21
|
+
import type { ToastVariant } from "./lib/types";
|
|
22
|
+
|
|
23
|
+
const tabs = [
|
|
24
|
+
{ id: "overview", label: "Overview" },
|
|
25
|
+
{ id: "team", label: "Team" },
|
|
26
|
+
{ id: "table", label: "Data" },
|
|
27
|
+
];
|
|
28
|
+
let activeTab = $state(tabs[0].id);
|
|
29
|
+
|
|
30
|
+
let sliderValue = $state(68);
|
|
31
|
+
let autoplay = $state(true);
|
|
32
|
+
|
|
33
|
+
const planOptions = [
|
|
34
|
+
{ label: "Starter", value: "starter" },
|
|
35
|
+
{ label: "Pro", value: "pro" },
|
|
36
|
+
{ label: "Enterprise", value: "enterprise" },
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
let selectedPlan = $state(planOptions[1].value);
|
|
40
|
+
let featureName = $state("Dashboard 2.0");
|
|
41
|
+
let contactEmail = $state("team@studio.dev");
|
|
42
|
+
let dateValue = $state<string | null>(null);
|
|
43
|
+
let timeValue = $state<string | null>(null);
|
|
44
|
+
let accentColor = $state<string | null>("#7c3aed");
|
|
45
|
+
|
|
46
|
+
const accordionItems = [
|
|
47
|
+
{
|
|
48
|
+
title: "Composition",
|
|
49
|
+
content:
|
|
50
|
+
"Card, Tabs, Table and Carousel let you assemble complex screens without extra layout work.",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
title: "Forms",
|
|
54
|
+
content:
|
|
55
|
+
"Field, Select, DatePicker, TimePicker and ColorPicker share spacing, tokens and state handling.",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
title: "Feedback",
|
|
59
|
+
content:
|
|
60
|
+
"ProgressBar, ProgressCircle, Toast and Dialog keep users informed without noise.",
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const carouselItems = [
|
|
65
|
+
{
|
|
66
|
+
title: "Smooth forms",
|
|
67
|
+
content:
|
|
68
|
+
"Clean fields, tight spacing and built-in hints help ship surveys in minutes.",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
title: "Smart navigation",
|
|
72
|
+
content:
|
|
73
|
+
"Tabs and Accordion keep content nearby while Carousel saves horizontal space.",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
title: "Status signals",
|
|
77
|
+
content:
|
|
78
|
+
"Toast and progress indicators deliver context quickly without blocking flows.",
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const tableColumns = [
|
|
83
|
+
{ key: "name", label: "Feature", width: "42%" },
|
|
84
|
+
{ key: "owner", label: "Owner" },
|
|
85
|
+
{ key: "status", label: "Status" },
|
|
86
|
+
{ key: "eta", label: "ETA", align: "end" },
|
|
87
|
+
] as const;
|
|
88
|
+
|
|
89
|
+
const tableRows = [
|
|
90
|
+
{ id: 1, name: "Onboarding", owner: "Anna", status: "Ready", eta: "Today" },
|
|
91
|
+
{ id: 2, name: "Theming", owner: "Mark", status: "In progress", eta: "Fri" },
|
|
92
|
+
{ id: 3, name: "Notifications", owner: "Oleg", status: "Testing", eta: "Thu" },
|
|
93
|
+
{ id: 4, name: "Data tables", owner: "Ira", status: "In progress", eta: "Next week" },
|
|
94
|
+
{ id: 5, name: "Carousel", owner: "Anton", status: "Design", eta: "In two weeks" },
|
|
95
|
+
{ id: 6, name: "Accessibility", owner: "Sasha", status: "Review", eta: "Today" },
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const pageSize = 4;
|
|
99
|
+
let tablePage = $state(1);
|
|
100
|
+
const totalPages = $derived(
|
|
101
|
+
Math.max(1, Math.ceil(tableRows.length / pageSize))
|
|
102
|
+
);
|
|
103
|
+
const pagedRows = $derived(
|
|
104
|
+
tableRows.slice((tablePage - 1) * pageSize, tablePage * pageSize)
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
$effect(() => {
|
|
108
|
+
if (tablePage > totalPages) tablePage = totalPages;
|
|
109
|
+
if (tablePage < 1) tablePage = 1;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const quality = [
|
|
113
|
+
{ label: "UI polish", value: 86 },
|
|
114
|
+
{ label: "Accessibility", value: 72 },
|
|
115
|
+
{ label: "Performance", value: 64 },
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
const team = [
|
|
119
|
+
{ name: "Anna", role: "Product", focus: "UX flows" },
|
|
120
|
+
{ name: "Mark", role: "Frontend", focus: "Components" },
|
|
121
|
+
{ name: "Oleg", role: "QA", focus: "Automation" },
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
type ToastItem = { id: number; title?: string; message: string; variant: ToastVariant };
|
|
125
|
+
let toasts = $state<ToastItem[]>([]);
|
|
126
|
+
let toastId = 0;
|
|
127
|
+
|
|
128
|
+
function pushToast(variant: ToastVariant) {
|
|
129
|
+
const messageMap: Record<ToastVariant, string> = {
|
|
130
|
+
success: "Changes saved and ready to roll out.",
|
|
131
|
+
info: "Components share tokens, typography, and behavior.",
|
|
132
|
+
warning: "Double-check disabled, focus, and hover states.",
|
|
133
|
+
danger: "Tests caught a blocking issue. Investigate.",
|
|
134
|
+
};
|
|
135
|
+
const titleMap: Record<ToastVariant, string> = {
|
|
136
|
+
success: "Success",
|
|
137
|
+
info: "Heads up",
|
|
138
|
+
warning: "Warning",
|
|
139
|
+
danger: "Error",
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const id = ++toastId;
|
|
143
|
+
toasts = [
|
|
144
|
+
...toasts,
|
|
145
|
+
{ id, variant, title: titleMap[variant], message: messageMap[variant] },
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function removeToast(id: number) {
|
|
150
|
+
toasts = toasts.filter((t) => t.id !== id);
|
|
151
|
+
}
|
|
152
|
+
</script>
|
|
153
|
+
|
|
154
|
+
<main class="min-h-screen bg-[var(--color-bg-page)] text-[var(--color-text-default)]">
|
|
155
|
+
<div class="pointer-events-none absolute inset-0 overflow-hidden">
|
|
156
|
+
<div class="absolute -left-10 top-10 h-64 w-64 rounded-full bg-[var(--color-bg-primary)]/10 blur-3xl"></div>
|
|
157
|
+
<div class="absolute right-0 bottom-0 h-72 w-72 rounded-full bg-[var(--color-bg-warning)]/20 blur-3xl"></div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<div class="relative mx-auto max-w-6xl space-y-8 px-6 py-10">
|
|
161
|
+
<section
|
|
162
|
+
class="relative overflow-hidden rounded-[28px] border border-[var(--border-color-default)] bg-gradient-to-br from-[var(--color-bg-surface)] via-white/70 to-[var(--color-bg-muted)] shadow-[0_20px_60px_-25px_var(--shadow-color)] dark:from-[var(--color-bg-surface)] dark:via-slate-900/70 dark:to-slate-900/50"
|
|
163
|
+
>
|
|
164
|
+
<div
|
|
165
|
+
class="absolute inset-0 bg-[radial-gradient(circle_at_20%_20%,rgba(99,102,241,0.18),transparent_35%),radial-gradient(circle_at_80%_0%,rgba(16,185,129,0.14),transparent_25%)]"
|
|
166
|
+
></div>
|
|
167
|
+
|
|
168
|
+
<div class="relative grid gap-8 p-8 md:p-10 lg:grid-cols-[1.1fr_0.9fr]">
|
|
169
|
+
<div class="space-y-4">
|
|
170
|
+
<p class="text-xs uppercase tracking-[0.25em] text-[var(--color-text-muted)]">
|
|
171
|
+
svelte-comp kit
|
|
172
|
+
</p>
|
|
173
|
+
<h1 class="text-3xl font-bold leading-tight md:text-4xl">
|
|
174
|
+
Component showcase
|
|
175
|
+
</h1>
|
|
176
|
+
<p class="text-[var(--color-text-muted)] md:w-3/4">
|
|
177
|
+
Toggle theme, tweak the primary color, and try the main lib components in a cohesive layout.
|
|
178
|
+
</p>
|
|
179
|
+
|
|
180
|
+
<div class="flex flex-wrap gap-3">
|
|
181
|
+
<Button variant="primary" onClick={() => pushToast("info")} sz="lg">
|
|
182
|
+
Launch interactive
|
|
183
|
+
</Button>
|
|
184
|
+
<Button variant="ghost" onClick={() => pushToast("warning")} sz="lg">
|
|
185
|
+
Remind to review
|
|
186
|
+
</Button>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<div class="flex flex-wrap gap-3 text-sm text-[var(--color-text-muted)]">
|
|
190
|
+
<span
|
|
191
|
+
class="inline-flex items-center gap-2 rounded-full border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] px-3 py-2"
|
|
192
|
+
>
|
|
193
|
+
<span class="h-2 w-2 animate-pulse rounded-full bg-[var(--color-bg-primary)]"></span>
|
|
194
|
+
Live preview is on
|
|
195
|
+
</span>
|
|
196
|
+
<span
|
|
197
|
+
class="inline-flex items-center gap-2 rounded-full border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] px-3 py-2"
|
|
198
|
+
>
|
|
199
|
+
<span class="h-2 w-2 rounded-full bg-[var(--color-bg-secondary)]"></span>
|
|
200
|
+
{tabs.length} sections below
|
|
201
|
+
</span>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
<div
|
|
206
|
+
class="space-y-4 rounded-2xl border border-[var(--border-color-default)] bg-white/70 p-5 shadow-lg backdrop-blur dark:bg-slate-900/60"
|
|
207
|
+
>
|
|
208
|
+
<div class="flex items-start justify-between gap-3">
|
|
209
|
+
<div>
|
|
210
|
+
<p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
|
|
211
|
+
Theme
|
|
212
|
+
</p>
|
|
213
|
+
<p class="text-lg font-semibold">Mode and accent</p>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<Tooltip text="Toggles document-level theme" position="bottom">
|
|
217
|
+
<ThemeToggle class="relative shadow-sm" sz="sm" />
|
|
218
|
+
</Tooltip>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<PrimaryColorSelect class="w-full" />
|
|
222
|
+
|
|
223
|
+
<div
|
|
224
|
+
class="flex items-center justify-between rounded-xl border border-[var(--border-color-default)] bg-[var(--color-bg-muted)]/70 px-4 py-3"
|
|
225
|
+
>
|
|
226
|
+
<div>
|
|
227
|
+
<p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
|
|
228
|
+
Autoplay
|
|
229
|
+
</p>
|
|
230
|
+
<p class="text-sm font-medium">Carousel</p>
|
|
231
|
+
</div>
|
|
232
|
+
<Switch
|
|
233
|
+
checked={autoplay}
|
|
234
|
+
onChange={(v) => (autoplay = v)}
|
|
235
|
+
sz="sm"
|
|
236
|
+
rightLabel={autoplay ? "On" : "Off"}
|
|
237
|
+
/>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
<div class="grid gap-3 md:grid-cols-2">
|
|
241
|
+
<ProgressCircle value={sliderValue} label="Readiness" />
|
|
242
|
+
<ProgressBar value={sliderValue} label="Sprint focus" />
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
</section>
|
|
247
|
+
|
|
248
|
+
{#snippet controlsHeader()}
|
|
249
|
+
<div class="flex items-center justify-between gap-2">
|
|
250
|
+
<div>
|
|
251
|
+
<p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
|
|
252
|
+
Actions
|
|
253
|
+
</p>
|
|
254
|
+
<h2 class="text-lg font-semibold leading-tight">Quick triggers</h2>
|
|
255
|
+
</div>
|
|
256
|
+
<Tooltip text="Buttons use the full variant set" position="left">
|
|
257
|
+
<span
|
|
258
|
+
class="inline-flex h-9 w-9 items-center justify-center rounded-full bg-[var(--color-bg-muted)] text-sm font-semibold text-[var(--color-text-default)]"
|
|
259
|
+
>
|
|
260
|
+
?
|
|
261
|
+
</span>
|
|
262
|
+
</Tooltip>
|
|
263
|
+
</div>
|
|
264
|
+
{/snippet}
|
|
265
|
+
|
|
266
|
+
{#snippet formHeader()}
|
|
267
|
+
<div class="flex items-center justify-between">
|
|
268
|
+
<div>
|
|
269
|
+
<p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
|
|
270
|
+
Form
|
|
271
|
+
</p>
|
|
272
|
+
<h2 class="text-lg font-semibold leading-tight">Mini brief</h2>
|
|
273
|
+
</div>
|
|
274
|
+
<Tooltip text="Field, Select, DatePicker, TimePicker and ColorPicker" position="left">
|
|
275
|
+
<span
|
|
276
|
+
class="inline-flex h-8 w-8 items-center justify-center rounded-full bg-[var(--color-bg-muted)] text-sm font-semibold text-[var(--color-text-default)]"
|
|
277
|
+
>
|
|
278
|
+
i
|
|
279
|
+
</span>
|
|
280
|
+
</Tooltip>
|
|
281
|
+
</div>
|
|
282
|
+
{/snippet}
|
|
283
|
+
|
|
284
|
+
<div class="grid gap-6 lg:grid-cols-2">
|
|
285
|
+
<Card header={controlsHeader}>
|
|
286
|
+
<div class="space-y-4">
|
|
287
|
+
<div class="flex flex-wrap gap-2">
|
|
288
|
+
<Button variant="primary" onClick={() => pushToast("success")}>Primary</Button>
|
|
289
|
+
<Button variant="secondary">Secondary</Button>
|
|
290
|
+
<Button variant="ghost">Ghost</Button>
|
|
291
|
+
<Button variant="pill">Pill</Button>
|
|
292
|
+
<Button variant="danger" onClick={() => pushToast("danger")}>Danger</Button>
|
|
293
|
+
</div>
|
|
294
|
+
|
|
295
|
+
<div
|
|
296
|
+
class="flex items-center justify-between gap-3 rounded-xl border border-[var(--border-color-default)] px-4 py-3"
|
|
297
|
+
>
|
|
298
|
+
<div class="space-y-1">
|
|
299
|
+
<p class="text-sm font-medium">Live mode</p>
|
|
300
|
+
<p class="text-xs text-[var(--color-text-muted)]">
|
|
301
|
+
Keep autoplay and progress linked across the page
|
|
302
|
+
</p>
|
|
303
|
+
</div>
|
|
304
|
+
<Switch checked={autoplay} onChange={(v) => (autoplay = v)} sz="sm" />
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<div class="space-y-2">
|
|
308
|
+
<div class="flex items-center justify-between text-sm">
|
|
309
|
+
<span class="text-[var(--color-text-muted)]">Drag to refresh progress</span>
|
|
310
|
+
<span class="font-mono text-[var(--color-text-default)]">{sliderValue}%</span>
|
|
311
|
+
</div>
|
|
312
|
+
<Slider
|
|
313
|
+
value={sliderValue}
|
|
314
|
+
min={0}
|
|
315
|
+
max={100}
|
|
316
|
+
onInput={(v) => (sliderValue = v)}
|
|
317
|
+
showValue={false}
|
|
318
|
+
/>
|
|
319
|
+
</div>
|
|
320
|
+
|
|
321
|
+
<div class="grid gap-3 md:grid-cols-2">
|
|
322
|
+
<ProgressBar value={sliderValue} label="Iteration" />
|
|
323
|
+
<ProgressCircle value={sliderValue} label="Readiness" />
|
|
324
|
+
</div>
|
|
325
|
+
|
|
326
|
+
<div class="flex flex-wrap gap-2">
|
|
327
|
+
<Button variant="success" onClick={() => pushToast("success")} sz="sm">
|
|
328
|
+
Success toast
|
|
329
|
+
</Button>
|
|
330
|
+
<Button variant="warning" onClick={() => pushToast("warning")} sz="sm">
|
|
331
|
+
Warning toast
|
|
332
|
+
</Button>
|
|
333
|
+
<Button variant="info" onClick={() => pushToast("info")} sz="sm">
|
|
334
|
+
Info toast
|
|
335
|
+
</Button>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
</Card>
|
|
339
|
+
|
|
340
|
+
<Card header={formHeader}>
|
|
341
|
+
<div class="space-y-4">
|
|
342
|
+
<div class="grid gap-3 md:grid-cols-2">
|
|
343
|
+
<Field
|
|
344
|
+
label="Name"
|
|
345
|
+
value={featureName}
|
|
346
|
+
onChange={(v) => (featureName = String(v))}
|
|
347
|
+
/>
|
|
348
|
+
<Field
|
|
349
|
+
label="Contact email"
|
|
350
|
+
type="email"
|
|
351
|
+
value={contactEmail}
|
|
352
|
+
onChange={(v) => (contactEmail = String(v))}
|
|
353
|
+
/>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<Select
|
|
357
|
+
label="Plan"
|
|
358
|
+
options={planOptions}
|
|
359
|
+
value={selectedPlan}
|
|
360
|
+
placeholder="Choose a plan"
|
|
361
|
+
onChange={(v) => (selectedPlan = v)}
|
|
362
|
+
/>
|
|
363
|
+
|
|
364
|
+
<div class="grid gap-3 md:grid-cols-3">
|
|
365
|
+
<DatePicker
|
|
366
|
+
label="Launch date"
|
|
367
|
+
value={dateValue}
|
|
368
|
+
onChange={(v) => (dateValue = v)}
|
|
369
|
+
/>
|
|
370
|
+
<TimePicker
|
|
371
|
+
label="Release time"
|
|
372
|
+
value={timeValue}
|
|
373
|
+
onChange={(v) => (timeValue = v)}
|
|
374
|
+
/>
|
|
375
|
+
<ColorPicker
|
|
376
|
+
label="Accent"
|
|
377
|
+
value={accentColor}
|
|
378
|
+
onChange={(v) => (accentColor = v)}
|
|
379
|
+
/>
|
|
380
|
+
</div>
|
|
381
|
+
|
|
382
|
+
<div class="flex justify-end gap-3">
|
|
383
|
+
<Button
|
|
384
|
+
variant="ghost"
|
|
385
|
+
onClick={() => {
|
|
386
|
+
featureName = "Dashboard 2.0";
|
|
387
|
+
selectedPlan = planOptions[1].value;
|
|
388
|
+
contactEmail = "team@studio.dev";
|
|
389
|
+
dateValue = null;
|
|
390
|
+
timeValue = null;
|
|
391
|
+
accentColor = "#7c3aed";
|
|
392
|
+
}}
|
|
393
|
+
>
|
|
394
|
+
Reset
|
|
395
|
+
</Button>
|
|
396
|
+
<Button variant="primary" onClick={() => pushToast("success")}>
|
|
397
|
+
Save
|
|
398
|
+
</Button>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
</Card>
|
|
402
|
+
</div>
|
|
403
|
+
|
|
404
|
+
{#snippet dataHeader()}
|
|
405
|
+
<div class="flex items-center justify-between gap-3">
|
|
406
|
+
<div>
|
|
407
|
+
<p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
|
|
408
|
+
Data
|
|
409
|
+
</p>
|
|
410
|
+
<h2 class="text-lg font-semibold leading-tight">Sprint snapshot</h2>
|
|
411
|
+
</div>
|
|
412
|
+
<Button variant="secondary" sz="sm" onClick={() => pushToast("info")}>
|
|
413
|
+
Refresh
|
|
414
|
+
</Button>
|
|
415
|
+
</div>
|
|
416
|
+
{/snippet}
|
|
417
|
+
|
|
418
|
+
{#snippet accordionHeader()}
|
|
419
|
+
<div class="flex items-center justify-between gap-3">
|
|
420
|
+
<div>
|
|
421
|
+
<p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
|
|
422
|
+
Details
|
|
423
|
+
</p>
|
|
424
|
+
<h3 class="text-lg font-semibold leading-tight">Sections</h3>
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
{/snippet}
|
|
428
|
+
|
|
429
|
+
{#snippet carouselHeader()}
|
|
430
|
+
<div class="flex items-center justify-between gap-3">
|
|
431
|
+
<div>
|
|
432
|
+
<p class="text-xs uppercase tracking-[0.15em] text-[var(--color-text-muted)]">
|
|
433
|
+
Carousel
|
|
434
|
+
</p>
|
|
435
|
+
<h3 class="text-lg font-semibold leading-tight">Stories</h3>
|
|
436
|
+
</div>
|
|
437
|
+
<span class="text-xs text-[var(--color-text-muted)]">
|
|
438
|
+
Autoplay {autoplay ? "on" : "off"}
|
|
439
|
+
</span>
|
|
440
|
+
</div>
|
|
441
|
+
{/snippet}
|
|
442
|
+
|
|
443
|
+
<div class="grid gap-6 lg:grid-cols-[1.2fr_0.8fr]">
|
|
444
|
+
<Card header={dataHeader} class="h-full">
|
|
445
|
+
<Tabs
|
|
446
|
+
tabs={tabs}
|
|
447
|
+
activeTab={activeTab}
|
|
448
|
+
onChange={(id) => (activeTab = id)}
|
|
449
|
+
variant="underline"
|
|
450
|
+
fitted={true}
|
|
451
|
+
>
|
|
452
|
+
{#if activeTab === "overview"}
|
|
453
|
+
<div class="grid gap-4 md:grid-cols-3">
|
|
454
|
+
<div
|
|
455
|
+
class="rounded-xl border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] p-4"
|
|
456
|
+
>
|
|
457
|
+
<p class="text-xs uppercase tracking-[0.1em] text-[var(--color-text-muted)]">
|
|
458
|
+
Readiness
|
|
459
|
+
</p>
|
|
460
|
+
<p class="text-2xl font-bold">{sliderValue}%</p>
|
|
461
|
+
<p class="text-sm text-[var(--color-text-muted)]">
|
|
462
|
+
Synced with the slider above
|
|
463
|
+
</p>
|
|
464
|
+
</div>
|
|
465
|
+
|
|
466
|
+
<div
|
|
467
|
+
class="rounded-xl border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] p-4"
|
|
468
|
+
>
|
|
469
|
+
<p class="text-xs uppercase tracking-[0.1em] text-[var(--color-text-muted)]">
|
|
470
|
+
Plan
|
|
471
|
+
</p>
|
|
472
|
+
<p class="text-lg font-semibold">{selectedPlan}</p>
|
|
473
|
+
<p class="text-sm text-[var(--color-text-muted)]">
|
|
474
|
+
Contact: {contactEmail}
|
|
475
|
+
</p>
|
|
476
|
+
</div>
|
|
477
|
+
|
|
478
|
+
<div
|
|
479
|
+
class="rounded-xl border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] p-4"
|
|
480
|
+
>
|
|
481
|
+
<p class="text-xs uppercase tracking-[0.1em] text-[var(--color-text-muted)]">
|
|
482
|
+
Release window
|
|
483
|
+
</p>
|
|
484
|
+
<p class="text-lg font-semibold">
|
|
485
|
+
{dateValue ?? "Not selected yet"}
|
|
486
|
+
</p>
|
|
487
|
+
<p class="text-sm text-[var(--color-text-muted)]">
|
|
488
|
+
{timeValue ?? "Time is being planned"}
|
|
489
|
+
</p>
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
|
|
493
|
+
<div class="mt-4 grid gap-3 md:grid-cols-3">
|
|
494
|
+
{#each quality as metric (metric.label)}
|
|
495
|
+
<ProgressBar value={metric.value} label={metric.label} sz="sm" />
|
|
496
|
+
{/each}
|
|
497
|
+
</div>
|
|
498
|
+
{:else if activeTab === "team"}
|
|
499
|
+
<div class="space-y-3">
|
|
500
|
+
{#each team as person (person.name)}
|
|
501
|
+
<div
|
|
502
|
+
class="flex items-center justify-between rounded-xl border border-[var(--border-color-default)] bg-[var(--color-bg-surface)] px-4 py-3"
|
|
503
|
+
>
|
|
504
|
+
<div>
|
|
505
|
+
<p class="font-semibold">{person.name}</p>
|
|
506
|
+
<p class="text-sm text-[var(--color-text-muted)]">{person.role}</p>
|
|
507
|
+
</div>
|
|
508
|
+
<span
|
|
509
|
+
class="rounded-full bg-[var(--color-bg-muted)] px-3 py-1 text-xs text-[var(--color-text-default)]"
|
|
510
|
+
>
|
|
511
|
+
{person.focus}
|
|
512
|
+
</span>
|
|
513
|
+
</div>
|
|
514
|
+
{/each}
|
|
515
|
+
</div>
|
|
516
|
+
{:else}
|
|
517
|
+
<Table
|
|
518
|
+
columns={tableColumns}
|
|
519
|
+
rows={pagedRows}
|
|
520
|
+
variant="zebra"
|
|
521
|
+
pagination={{ currentPage: tablePage, totalPages, onPageChange: (p) => (tablePage = p) }}
|
|
522
|
+
/>
|
|
523
|
+
{/if}
|
|
524
|
+
</Tabs>
|
|
525
|
+
</Card>
|
|
526
|
+
|
|
527
|
+
<div class="grid gap-6">
|
|
528
|
+
<Card header={accordionHeader}>
|
|
529
|
+
<Accordion items={accordionItems} multiple={true} defaultOpen={[0]} />
|
|
530
|
+
</Card>
|
|
531
|
+
|
|
532
|
+
<Card header={carouselHeader}>
|
|
533
|
+
<Carousel
|
|
534
|
+
items={carouselItems}
|
|
535
|
+
autoplay={autoplay}
|
|
536
|
+
interval={4200}
|
|
537
|
+
showDots={true}
|
|
538
|
+
showArrows={true}
|
|
539
|
+
sz="sm"
|
|
540
|
+
/>
|
|
541
|
+
</Card>
|
|
542
|
+
</div>
|
|
543
|
+
</div>
|
|
544
|
+
</div>
|
|
545
|
+
|
|
546
|
+
{#each toasts as toast (toast.id)}
|
|
547
|
+
<Toast
|
|
548
|
+
title={toast.title}
|
|
549
|
+
message={toast.message}
|
|
550
|
+
variant={toast.variant}
|
|
551
|
+
onClose={() => removeToast(toast.id)}
|
|
552
|
+
timeout={3500}
|
|
553
|
+
/>
|
|
554
|
+
{/each}
|
|
555
|
+
</main>
|
package/dist/app.css
ADDED
package/dist/lib/Carousel.svelte
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* @note Supports touch gestures (swipe left/right).
|
|
30
30
|
* @note Autoplay pauses automatically when unmounted.
|
|
31
31
|
* @note Uses `Card.svelte` internally for slide structure.
|
|
32
|
-
* @note Navigation dots and arrows appear only if there
|
|
32
|
+
* @note Navigation dots and arrows appear only if there’s more than one item.
|
|
33
33
|
* @note Accessible via `aria-label`, `aria-current`, and keyboard focus on controls.
|
|
34
34
|
*/
|
|
35
35
|
import Card from "./Card.svelte";
|
|
@@ -123,12 +123,15 @@
|
|
|
123
123
|
const arrowClass = $derived(
|
|
124
124
|
cx(
|
|
125
125
|
arrowSize[sz],
|
|
126
|
-
"rounded-full bg-[var(--color-bg-surface)] shadow-lg flex items-center justify-center [color:var(--color-text-default)] hover:bg-[var(--color-bg-hover)] transition-colors"
|
|
126
|
+
"rounded-full bg-[var(--color-bg-surface)] shadow-lg flex items-center justify-center [color:var(--color-text-default)] hover:bg-[var(--color-bg-hover)] transition-colors focus-visible:ring-2 focus-visible:ring-[var(--border-color-focus)] focus:outline-none"
|
|
127
127
|
)
|
|
128
128
|
);
|
|
129
129
|
|
|
130
130
|
const dotClass = $derived(
|
|
131
|
-
cx(
|
|
131
|
+
cx(
|
|
132
|
+
dotSize[sz],
|
|
133
|
+
"rounded-full transition-all duration-200 focus-visible:ring-2 focus-visible:ring-[var(--border-color-focus)] focus:outline-none"
|
|
134
|
+
)
|
|
132
135
|
);
|
|
133
136
|
|
|
134
137
|
$effect(() => {
|
package/dist/lib/CheckBox.svelte
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* @note Clicking the custom box while `indeterminate` clears it and sets `checked=true`.
|
|
36
36
|
* @note `invalid` maps to `aria-invalid`; `describedBy` maps to `aria-describedby`.
|
|
37
37
|
* @note SVG check and dash are inline; colors adapt per `variant` (`neutral` uses border color).
|
|
38
|
-
* @note Sizes scale the control box (`xs
|
|
38
|
+
* @note Sizes scale the control box (`xs → xl`).
|
|
39
39
|
*/
|
|
40
40
|
import type { HTMLInputAttributes } from "svelte/elements";
|
|
41
41
|
import { type SizeKey, type ComponentVariant, TEXT } from "./types";
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* @note Clicking the custom box while `indeterminate` clears it and sets `checked=true`.
|
|
34
34
|
* @note `invalid` maps to `aria-invalid`; `describedBy` maps to `aria-describedby`.
|
|
35
35
|
* @note SVG check and dash are inline; colors adapt per `variant` (`neutral` uses border color).
|
|
36
|
-
* @note Sizes scale the control box (`xs
|
|
36
|
+
* @note Sizes scale the control box (`xs → xl`).
|
|
37
37
|
*/
|
|
38
38
|
import type { HTMLInputAttributes } from "svelte/elements";
|
|
39
39
|
import { type SizeKey, type ComponentVariant } from "./types";
|