@alpic-ai/ui 0.0.0-dev.fffc79a → 0.0.0-dev.g01173d7
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/dist/components/accordion-card.d.mts +4 -5
- package/dist/components/accordion.d.mts +4 -5
- package/dist/components/alert.d.mts +6 -8
- package/dist/components/attachment-tile.d.mts +1 -3
- package/dist/components/avatar.d.mts +5 -7
- package/dist/components/badge.d.mts +2 -4
- package/dist/components/breadcrumb.d.mts +8 -9
- package/dist/components/button.d.mts +2 -4
- package/dist/components/card.d.mts +7 -8
- package/dist/components/checkbox.d.mts +1 -2
- package/dist/components/collapsible.d.mts +3 -4
- package/dist/components/combobox.d.mts +12 -11
- package/dist/components/combobox.mjs +7 -4
- package/dist/components/command.d.mts +8 -9
- package/dist/components/copyable.d.mts +2 -3
- package/dist/components/description-list.d.mts +4 -5
- package/dist/components/dialog.d.mts +12 -14
- package/dist/components/dropdown-menu.d.mts +14 -16
- package/dist/components/form.d.mts +68 -16
- package/dist/components/form.mjs +114 -4
- package/dist/components/github-button.d.mts +1 -2
- package/dist/components/input-group.d.mts +4 -6
- package/dist/components/input.d.mts +1 -2
- package/dist/components/label.d.mts +1 -2
- package/dist/components/page-loader.d.mts +1 -3
- package/dist/components/pagination.d.mts +1 -2
- package/dist/components/popover.d.mts +4 -5
- package/dist/components/radio-group.d.mts +2 -3
- package/dist/components/scroll-area.d.mts +2 -3
- package/dist/components/select-trigger-variants.d.mts +1 -3
- package/dist/components/select.d.mts +8 -9
- package/dist/components/separator.d.mts +1 -2
- package/dist/components/sheet.d.mts +9 -10
- package/dist/components/shimmer-text.d.mts +10 -0
- package/dist/components/shimmer-text.mjs +22 -0
- package/dist/components/sidebar.d.mts +23 -25
- package/dist/components/skeleton.d.mts +2 -4
- package/dist/components/sonner.d.mts +2 -3
- package/dist/components/spinner.d.mts +3 -5
- package/dist/components/status-dot.d.mts +2 -4
- package/dist/components/switch.d.mts +1 -2
- package/dist/components/table.d.mts +8 -9
- package/dist/components/table.mjs +3 -3
- package/dist/components/tabs.d.mts +9 -11
- package/dist/components/tabs.mjs +4 -4
- package/dist/components/tag.d.mts +3 -5
- package/dist/components/task-progress.d.mts +25 -0
- package/dist/components/task-progress.mjs +66 -0
- package/dist/components/textarea.d.mts +1 -2
- package/dist/components/toggle-group.d.mts +3 -5
- package/dist/components/tooltip-icon-button.d.mts +1 -2
- package/dist/components/tooltip.d.mts +4 -5
- package/dist/components/tooltip.mjs +1 -1
- package/dist/components/typography.d.mts +4 -5
- package/dist/components/wizard.d.mts +33 -0
- package/dist/components/wizard.mjs +46 -0
- package/package.json +13 -13
- package/src/components/combobox.tsx +9 -2
- package/src/components/form.tsx +164 -3
- package/src/components/shimmer-text.tsx +23 -0
- package/src/components/table.tsx +2 -3
- package/src/components/tabs.tsx +4 -4
- package/src/components/task-progress.tsx +107 -0
- package/src/components/tooltip.tsx +1 -1
- package/src/components/wizard.tsx +69 -0
- package/src/stories/form.stories.tsx +64 -2
- package/src/stories/tabs.stories.tsx +4 -2
- package/src/stories/task-progress.stories.tsx +81 -0
- package/src/stories/wizard.stories.tsx +64 -0
- package/src/styles/tokens.css +11 -4
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* TaskProgress — multi-step async progress primitive.
|
|
5
|
+
*
|
|
6
|
+
* Dumb component: the consumer owns orchestration (timer, websocket, polling) and
|
|
7
|
+
* passes `steps` with each step's status. Renders a percentage bar at the top
|
|
8
|
+
* and one row per step with done/running/pending state badges.
|
|
9
|
+
*
|
|
10
|
+
* `trailingLabel` is for "still working on something not in the step list" —
|
|
11
|
+
* shows after the last step as a running row that isn't counted in the percent.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { CheckIcon } from "lucide-react";
|
|
15
|
+
|
|
16
|
+
import { cn } from "../lib/cn";
|
|
17
|
+
import { Separator } from "./separator";
|
|
18
|
+
|
|
19
|
+
type TaskProgressStatus = "pending" | "running" | "done";
|
|
20
|
+
|
|
21
|
+
interface TaskProgressStep {
|
|
22
|
+
id: string;
|
|
23
|
+
label: string;
|
|
24
|
+
status: TaskProgressStatus;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface TaskProgressProps extends Omit<React.ComponentProps<"div">, "children"> {
|
|
28
|
+
steps: readonly TaskProgressStep[];
|
|
29
|
+
trailingLabel?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Optional mount-time stagger: each row fades + slides in with `animation-delay = index × stepRevealDelayMs`.
|
|
32
|
+
* Pure CSS, runs once on mount. Omit for no animation.
|
|
33
|
+
*/
|
|
34
|
+
stepRevealDelayMs?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const REVEAL_CLASSES = "animate-in fade-in slide-in-from-bottom-2 duration-300";
|
|
38
|
+
|
|
39
|
+
function TaskProgress({ steps, trailingLabel, stepRevealDelayMs, className, ...props }: TaskProgressProps) {
|
|
40
|
+
const total = steps.length;
|
|
41
|
+
const doneCount = steps.filter((step) => step.status === "done").length;
|
|
42
|
+
const percent = total > 0 ? Math.round((doneCount / total) * 100) : 0;
|
|
43
|
+
const stagger = stepRevealDelayMs != null;
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className={cn("flex flex-col gap-4", className)} {...props}>
|
|
47
|
+
<div className="bg-muted h-2 w-full overflow-hidden rounded-full">
|
|
48
|
+
<div className="h-full rounded-full bg-success transition-all duration-500" style={{ width: `${percent}%` }} />
|
|
49
|
+
</div>
|
|
50
|
+
<div>
|
|
51
|
+
{steps.map((step, idx) => (
|
|
52
|
+
<div
|
|
53
|
+
key={step.id}
|
|
54
|
+
className={stagger ? REVEAL_CLASSES : undefined}
|
|
55
|
+
style={
|
|
56
|
+
stagger ? { animationDelay: `${idx * (stepRevealDelayMs ?? 0)}ms`, animationFillMode: "both" } : undefined
|
|
57
|
+
}
|
|
58
|
+
>
|
|
59
|
+
<TaskProgressRow label={step.label} status={step.status} />
|
|
60
|
+
{idx < steps.length - 1 && <Separator />}
|
|
61
|
+
</div>
|
|
62
|
+
))}
|
|
63
|
+
{trailingLabel && (
|
|
64
|
+
<div className={stagger ? REVEAL_CLASSES : undefined}>
|
|
65
|
+
<Separator />
|
|
66
|
+
<TaskProgressRow label={trailingLabel} status="running" muted />
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface TaskProgressRowProps {
|
|
75
|
+
label: string;
|
|
76
|
+
status: TaskProgressStatus;
|
|
77
|
+
muted?: boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function TaskProgressRow({ label, status, muted }: TaskProgressRowProps) {
|
|
81
|
+
return (
|
|
82
|
+
<div className="flex items-center justify-between py-2">
|
|
83
|
+
<span className={cn("type-text-sm", muted && "text-muted-foreground")}>{label}</span>
|
|
84
|
+
{status === "done" && (
|
|
85
|
+
<span className="flex items-center gap-1 type-text-sm text-success">
|
|
86
|
+
<CheckIcon className="size-3.5" />
|
|
87
|
+
<span>done</span>
|
|
88
|
+
</span>
|
|
89
|
+
)}
|
|
90
|
+
{status === "running" && (
|
|
91
|
+
<span className="flex items-center gap-1.5 type-text-sm text-warning">
|
|
92
|
+
<span className="size-2 rounded-full bg-warning" />
|
|
93
|
+
<span>running…</span>
|
|
94
|
+
</span>
|
|
95
|
+
)}
|
|
96
|
+
{status === "pending" && (
|
|
97
|
+
<span className="flex items-center gap-1.5 type-text-sm text-muted-foreground">
|
|
98
|
+
<span className="size-2 rounded-full border border-muted-foreground" />
|
|
99
|
+
<span>pending</span>
|
|
100
|
+
</span>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type { TaskProgressStatus, TaskProgressStep };
|
|
107
|
+
export { TaskProgress };
|
|
@@ -35,7 +35,7 @@ function TooltipContent({
|
|
|
35
35
|
className={cn(
|
|
36
36
|
"bg-inverted text-inverted-foreground dark:bg-subtle dark:text-foreground",
|
|
37
37
|
"z-50 w-fit rounded-md px-3 py-2 shadow-lg dark:shadow-none dark:drop-shadow-[0_0_0.5px_var(--color-border)]",
|
|
38
|
-
"type-text-xs font-semibold text-balance text-center",
|
|
38
|
+
"type-text-xs font-semibold text-balance text-center whitespace-pre-line",
|
|
39
39
|
"animate-in fade-in-0 zoom-in-95",
|
|
40
40
|
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
41
41
|
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Wizard family — primitives for multi-step flows.
|
|
5
|
+
*
|
|
6
|
+
* - WizardSteps — vertical step rail (controlled by activeIdx + onSelect)
|
|
7
|
+
* - WizardProgress — step counter + progress bar
|
|
8
|
+
*
|
|
9
|
+
* Consumers compose these inside whatever container they need (sticky aside, modal, etc.).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type * as React from "react";
|
|
13
|
+
|
|
14
|
+
import { cn } from "../lib/cn";
|
|
15
|
+
import { TabsNav, TabsNavList, TabsNavTrigger } from "./tabs";
|
|
16
|
+
|
|
17
|
+
interface WizardStep {
|
|
18
|
+
id: string;
|
|
19
|
+
label: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface WizardStepsProps {
|
|
23
|
+
steps: readonly WizardStep[];
|
|
24
|
+
activeIdx: number;
|
|
25
|
+
onSelect: (idx: number) => void;
|
|
26
|
+
ariaLabel?: string;
|
|
27
|
+
className?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function WizardSteps({ steps, activeIdx, onSelect, ariaLabel = "Wizard steps", className }: WizardStepsProps) {
|
|
31
|
+
return (
|
|
32
|
+
<TabsNav orientation="vertical" aria-label={ariaLabel} className={className}>
|
|
33
|
+
<TabsNavList>
|
|
34
|
+
{steps.map((step, idx) => (
|
|
35
|
+
<TabsNavTrigger key={step.id} active={idx === activeIdx} asChild>
|
|
36
|
+
<button type="button" onClick={() => onSelect(idx)} className="w-full justify-start text-left">
|
|
37
|
+
{step.label}
|
|
38
|
+
</button>
|
|
39
|
+
</TabsNavTrigger>
|
|
40
|
+
))}
|
|
41
|
+
</TabsNavList>
|
|
42
|
+
</TabsNav>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface WizardProgressProps extends React.ComponentProps<"div"> {
|
|
47
|
+
current: number;
|
|
48
|
+
total: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function WizardProgress({ current, total, className, ...props }: WizardProgressProps) {
|
|
52
|
+
const percent = total > 0 ? Math.round((current / total) * 100) : 0;
|
|
53
|
+
return (
|
|
54
|
+
<div className={cn("flex flex-col gap-1.5 px-2", className)} {...props}>
|
|
55
|
+
<div className="text-muted-foreground flex items-center justify-between text-xs">
|
|
56
|
+
<span>
|
|
57
|
+
Step {current}/{total}
|
|
58
|
+
</span>
|
|
59
|
+
<span>{percent}%</span>
|
|
60
|
+
</div>
|
|
61
|
+
<div className="bg-muted h-1.5 overflow-hidden rounded-full">
|
|
62
|
+
<div className="bg-primary h-full transition-all" style={{ width: `${percent}%` }} />
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type { WizardStep };
|
|
69
|
+
export { WizardProgress, WizardSteps };
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import type { Story } from "@ladle/react";
|
|
2
2
|
import { useForm } from "react-hook-form";
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
CheckboxField,
|
|
6
|
+
ChecklistField,
|
|
7
|
+
Form,
|
|
8
|
+
FormFields,
|
|
9
|
+
FormHeader,
|
|
10
|
+
InputField,
|
|
11
|
+
RadioField,
|
|
12
|
+
SelectField,
|
|
13
|
+
TextareaField,
|
|
14
|
+
} from "../components/form";
|
|
4
15
|
|
|
5
16
|
/* ── Types ───────────────────────────────────────────────────────────────── */
|
|
6
17
|
|
|
@@ -8,6 +19,9 @@ interface CreateProjectForm {
|
|
|
8
19
|
email: string;
|
|
9
20
|
role: string;
|
|
10
21
|
description: string;
|
|
22
|
+
visibility: string;
|
|
23
|
+
capabilities: string[];
|
|
24
|
+
acceptTerms: boolean;
|
|
11
25
|
}
|
|
12
26
|
|
|
13
27
|
const roles = [
|
|
@@ -16,11 +30,31 @@ const roles = [
|
|
|
16
30
|
{ value: "viewer", label: "Viewer" },
|
|
17
31
|
];
|
|
18
32
|
|
|
33
|
+
const visibilities = [
|
|
34
|
+
{ value: "public", label: "Public" },
|
|
35
|
+
{ value: "private", label: "Private" },
|
|
36
|
+
{ value: "internal", label: "Internal" },
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const capabilities = [
|
|
40
|
+
{ value: "read", label: "Read" },
|
|
41
|
+
{ value: "write", label: "Write" },
|
|
42
|
+
{ value: "deploy", label: "Deploy" },
|
|
43
|
+
{ value: "admin", label: "Admin" },
|
|
44
|
+
];
|
|
45
|
+
|
|
19
46
|
/* ── Composed Form ───────────────────────────────────────────────────────── */
|
|
20
47
|
|
|
21
48
|
function ComposedForm() {
|
|
22
49
|
const form = useForm<CreateProjectForm>({
|
|
23
|
-
defaultValues: {
|
|
50
|
+
defaultValues: {
|
|
51
|
+
email: "",
|
|
52
|
+
role: "",
|
|
53
|
+
description: "",
|
|
54
|
+
visibility: "",
|
|
55
|
+
capabilities: [],
|
|
56
|
+
acceptTerms: false,
|
|
57
|
+
},
|
|
24
58
|
});
|
|
25
59
|
|
|
26
60
|
const onSubmit = (data: CreateProjectForm) => {
|
|
@@ -64,6 +98,34 @@ function ComposedForm() {
|
|
|
64
98
|
placeholder="A short description of your project..."
|
|
65
99
|
required
|
|
66
100
|
/>
|
|
101
|
+
|
|
102
|
+
<RadioField
|
|
103
|
+
control={form.control}
|
|
104
|
+
name="visibility"
|
|
105
|
+
rules={{ required: "Pick a visibility." }}
|
|
106
|
+
label="Visibility"
|
|
107
|
+
description="Who can see and access this project."
|
|
108
|
+
options={visibilities}
|
|
109
|
+
required
|
|
110
|
+
/>
|
|
111
|
+
|
|
112
|
+
<ChecklistField
|
|
113
|
+
control={form.control}
|
|
114
|
+
name="capabilities"
|
|
115
|
+
rules={{ validate: (value) => (value?.length ? true : "Pick at least one capability.") }}
|
|
116
|
+
label="Capabilities"
|
|
117
|
+
description="Select all that apply."
|
|
118
|
+
options={capabilities}
|
|
119
|
+
required
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
<CheckboxField
|
|
123
|
+
control={form.control}
|
|
124
|
+
name="acceptTerms"
|
|
125
|
+
rules={{ validate: (value) => (value ? true : "You must accept the terms.") }}
|
|
126
|
+
label="I accept the terms and conditions"
|
|
127
|
+
description="Required to create the project."
|
|
128
|
+
/>
|
|
67
129
|
</FormFields>
|
|
68
130
|
|
|
69
131
|
<button
|
|
@@ -18,9 +18,11 @@ const navTabs = [
|
|
|
18
18
|
|
|
19
19
|
const sideNavTabs = [
|
|
20
20
|
{ id: "general", label: "General" },
|
|
21
|
+
{ id: "build-settings", label: "Build settings" },
|
|
22
|
+
{ id: "environment-variables", label: "Environment variables" },
|
|
23
|
+
{ id: "domains", label: "Domains" },
|
|
21
24
|
{ id: "authentication", label: "Authentication" },
|
|
22
|
-
{ id: "
|
|
23
|
-
{ id: "billing", label: "Billing" },
|
|
25
|
+
{ id: "marketplace", label: "Marketplace" },
|
|
24
26
|
];
|
|
25
27
|
|
|
26
28
|
function useHashRoute(fallback: string) {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Story } from "@ladle/react";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
import { TaskProgress, type TaskProgressStep } from "../components/task-progress";
|
|
5
|
+
|
|
6
|
+
const SECTION_HEADER = "type-text-xs font-medium text-subtle-foreground uppercase tracking-wide pt-4";
|
|
7
|
+
|
|
8
|
+
const beaconSteps: TaskProgressStep[] = [
|
|
9
|
+
{ id: "init", label: "Initialize MCP connection", status: "done" },
|
|
10
|
+
{ id: "fetch", label: "Fetch tools and resources", status: "done" },
|
|
11
|
+
{ id: "compat", label: "Check ChatGPT & Claude.ai compatibility", status: "done" },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
const partialSteps: TaskProgressStep[] = [
|
|
15
|
+
{ id: "a", label: "Reading project metadata", status: "done" },
|
|
16
|
+
{ id: "b", label: "Fetching MCP server manifest", status: "running" },
|
|
17
|
+
{ id: "c", label: "Preparing your submission", status: "pending" },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const allDoneSteps: TaskProgressStep[] = [
|
|
21
|
+
{ id: "a", label: "Reading project metadata", status: "done" },
|
|
22
|
+
{ id: "b", label: "Fetching MCP server manifest", status: "done" },
|
|
23
|
+
{ id: "c", label: "Preparing your submission", status: "done" },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const STEP_INTERVAL_MS = 1200;
|
|
27
|
+
|
|
28
|
+
function AnimatedExample() {
|
|
29
|
+
const [activeIdx, setActiveIdx] = useState(0);
|
|
30
|
+
|
|
31
|
+
const labels = ["Reading project metadata", "Fetching MCP server manifest", "Preparing your submission"];
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (activeIdx >= labels.length) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const timeoutId = setTimeout(() => setActiveIdx((prev) => prev + 1), STEP_INTERVAL_MS);
|
|
38
|
+
return () => clearTimeout(timeoutId);
|
|
39
|
+
}, [activeIdx, labels.length]);
|
|
40
|
+
|
|
41
|
+
const steps: TaskProgressStep[] = labels.map((label, idx) => ({
|
|
42
|
+
id: String(idx),
|
|
43
|
+
label,
|
|
44
|
+
status: idx < activeIdx ? "done" : idx === activeIdx ? "running" : "pending",
|
|
45
|
+
}));
|
|
46
|
+
const allDone = activeIdx >= labels.length;
|
|
47
|
+
|
|
48
|
+
return <TaskProgress steps={steps} trailingLabel={allDone ? "Almost there…" : undefined} />;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const AllVariants: Story = () => (
|
|
52
|
+
<div className="flex flex-col gap-8 p-8 max-w-[640px]">
|
|
53
|
+
<div>
|
|
54
|
+
<p className={SECTION_HEADER}>In-progress (running step in the middle)</p>
|
|
55
|
+
<div className="mt-4">
|
|
56
|
+
<TaskProgress steps={partialSteps} />
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div>
|
|
61
|
+
<p className={SECTION_HEADER}>All done</p>
|
|
62
|
+
<div className="mt-4">
|
|
63
|
+
<TaskProgress steps={allDoneSteps} />
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div>
|
|
68
|
+
<p className={SECTION_HEADER}>All done + trailing "Running checks…" row</p>
|
|
69
|
+
<div className="mt-4">
|
|
70
|
+
<TaskProgress steps={beaconSteps} trailingLabel="Running checks…" />
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<div>
|
|
75
|
+
<p className={SECTION_HEADER}>Animated (timer-driven, mirrors the submission-prefill flow)</p>
|
|
76
|
+
<div className="mt-4">
|
|
77
|
+
<AnimatedExample />
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Story } from "@ladle/react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
|
|
4
|
+
import { WizardProgress, type WizardStep, WizardSteps } from "../components/wizard";
|
|
5
|
+
|
|
6
|
+
const SECTION_HEADER = "type-text-xs font-medium text-muted-foreground uppercase tracking-wide pt-4";
|
|
7
|
+
|
|
8
|
+
const steps: WizardStep[] = [
|
|
9
|
+
{ id: "overview", label: "Overview" },
|
|
10
|
+
{ id: "branding", label: "Branding & metadata" },
|
|
11
|
+
{ id: "auth", label: "Authentication" },
|
|
12
|
+
{ id: "tools", label: "Tools & test cases" },
|
|
13
|
+
{ id: "review", label: "Review & submit" },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export const AllVariants: Story = () => {
|
|
17
|
+
const [activeIdx, setActiveIdx] = useState(1);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className="flex flex-col gap-10 p-8">
|
|
21
|
+
{/* Full rail (steps + progress) */}
|
|
22
|
+
<div>
|
|
23
|
+
<p className={SECTION_HEADER}>Full rail — steps + progress</p>
|
|
24
|
+
<div className="mt-4 flex gap-6">
|
|
25
|
+
<aside className="basis-56 shrink-0 flex flex-col gap-4 self-start">
|
|
26
|
+
<WizardSteps steps={steps} activeIdx={activeIdx} onSelect={setActiveIdx} ariaLabel="Submission steps" />
|
|
27
|
+
<WizardProgress current={activeIdx + 1} total={steps.length} />
|
|
28
|
+
</aside>
|
|
29
|
+
<div className="flex-1 rounded-md border p-4">
|
|
30
|
+
<p className="type-text-sm text-muted-foreground">Content for step "{steps[activeIdx]?.label}"</p>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
{/* Steps only */}
|
|
36
|
+
<div>
|
|
37
|
+
<p className={SECTION_HEADER}>Steps only</p>
|
|
38
|
+
<div className="mt-4 max-w-56">
|
|
39
|
+
<WizardSteps steps={steps} activeIdx={activeIdx} onSelect={setActiveIdx} />
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
{/* Progress only — various positions */}
|
|
44
|
+
<div>
|
|
45
|
+
<p className={SECTION_HEADER}>Progress — first step</p>
|
|
46
|
+
<div className="mt-4 max-w-56">
|
|
47
|
+
<WizardProgress current={1} total={5} />
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
<div>
|
|
51
|
+
<p className={SECTION_HEADER}>Progress — mid-flow</p>
|
|
52
|
+
<div className="mt-4 max-w-56">
|
|
53
|
+
<WizardProgress current={3} total={5} />
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div>
|
|
57
|
+
<p className={SECTION_HEADER}>Progress — complete</p>
|
|
58
|
+
<div className="mt-4 max-w-56">
|
|
59
|
+
<WizardProgress current={5} total={5} />
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
};
|
package/src/styles/tokens.css
CHANGED
|
@@ -113,6 +113,15 @@
|
|
|
113
113
|
--animate-beacon-ring-pulse: beacon-ring-pulse 2.2s ease-in-out infinite;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
@keyframes shimmer-text {
|
|
117
|
+
from {
|
|
118
|
+
background-position: 100% center;
|
|
119
|
+
}
|
|
120
|
+
to {
|
|
121
|
+
background-position: 0% center;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
116
125
|
@keyframes alpic-ride {
|
|
117
126
|
0% {
|
|
118
127
|
transform: translate(0px, 0px);
|
|
@@ -155,14 +164,12 @@
|
|
|
155
164
|
@keyframes beacon-ring-pulse {
|
|
156
165
|
0%,
|
|
157
166
|
100% {
|
|
158
|
-
opacity: 0.
|
|
167
|
+
opacity: 0.55;
|
|
159
168
|
transform: scale(1);
|
|
160
|
-
filter: drop-shadow(0 0 0 rgba(59, 130, 246, 0));
|
|
161
169
|
}
|
|
162
170
|
50% {
|
|
163
|
-
opacity:
|
|
171
|
+
opacity: 0.9;
|
|
164
172
|
transform: scale(1.02);
|
|
165
|
-
filter: drop-shadow(0 0 8px rgba(59, 130, 246, 0.24));
|
|
166
173
|
}
|
|
167
174
|
}
|
|
168
175
|
|