Repository navigation
Expand file tree
/
Copy pathwizard.tsx
More file actions
155 lines (149 loc) · 4.93 KB
/
Copy pathwizard.tsx
File metadata and controls
155 lines (149 loc) · 4.93 KB
1
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import { CheckIcon } from "lucide-react";
import type { ComponentProps, ReactNode } from "react";
import { cn } from "../../lib/utils";
import { AnimatedHeight } from "../AnimatedHeight";
import { DialogPopup, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "./dialog";
import { ScrollArea } from "./scroll-area";
export function WizardPopup({
children,
size = "default",
...props
}: Omit<ComponentProps<typeof DialogPopup>, "className" | "style"> & {
readonly size?: "default" | "wide";
}) {
return (
<DialogPopup {...props} className={size === "wide" ? "max-w-3xl" : "max-w-xl"}>
<ScrollArea className="*:data-[slot=scroll-area-scrollbar]:data-[orientation=vertical]:my-4">
<div className="flex min-h-0 flex-col">{children}</div>
</ScrollArea>
</DialogPopup>
);
}
export function WizardHeader({
title,
description,
identity,
children,
}: {
readonly title: ReactNode;
readonly description?: ReactNode;
readonly identity?: ReactNode;
readonly children?: ReactNode;
}) {
return (
<DialogHeader>
<DialogTitle className={identity ? "sr-only" : undefined}>{title}</DialogTitle>
{identity}
{description ? <DialogDescription>{description}</DialogDescription> : null}
{children}
</DialogHeader>
);
}
export function WizardFooter({
children,
leading,
}: {
readonly children: ReactNode;
readonly leading?: ReactNode;
}) {
return (
<DialogFooter variant="bare" className={leading ? "sm:justify-between" : undefined}>
{leading}
{leading ? (
<div className="flex flex-col-reverse gap-2 sm:flex-row">{children}</div>
) : (
children
)}
</DialogFooter>
);
}
export function WizardSteps({
steps,
currentStep,
summaries,
showSummaries = false,
onStepChange,
isStepDisabled,
}: {
readonly steps: readonly string[];
readonly currentStep: number;
readonly summaries?: readonly (string | null)[];
readonly showSummaries?: boolean;
readonly isStepDisabled?: (step: number) => boolean;
readonly onStepChange?: (step: number) => void;
}) {
const Step = onStepChange ? "button" : "div";
return (
<ol
className="grid auto-cols-fr grid-flow-col gap-1 rounded-xl bg-background p-1 ring-1 ring-black/5 dark:bg-white/4 dark:ring-white/5"
role="list"
aria-label="Setup progress"
>
{steps.map((step, index) => (
<li key={step} className="min-w-0">
<Step
{...(onStepChange
? { type: "button" as const, disabled: isStepDisabled?.(index) }
: {})}
className={cn(
"flex w-full min-w-0 items-center gap-2 rounded-lg px-2.5 py-2 text-left outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring max-sm:justify-center max-sm:px-2",
onStepChange &&
"cursor-pointer hover:bg-card disabled:cursor-default disabled:hover:bg-transparent",
index === currentStep &&
"bg-card text-foreground shadow-xs ring-1 ring-inset ring-black/5 hover:bg-card dark:shadow-none dark:ring-white/5",
)}
aria-current={index === currentStep ? "step" : undefined}
aria-label={`${step}, step ${index + 1}${index < currentStep && summaries?.[index] ? `, ${summaries?.[index]}` : ""}`}
onClick={onStepChange ? () => onStepChange(index) : undefined}
>
<span
className={cn(
"grid size-5 shrink-0 place-items-center rounded-full text-sm font-medium ring-1",
index < currentStep
? "bg-primary text-primary-foreground ring-primary"
: index === currentStep
? "bg-primary/10 text-primary ring-primary/30"
: "bg-card text-muted-foreground ring-black/10 dark:bg-white/5 dark:ring-white/10",
)}
aria-hidden
>
{index < currentStep ? <CheckIcon className="size-4 shrink-0" /> : index + 1}
</span>
<span
className={cn(
"min-w-0 truncate text-sm font-medium max-sm:hidden",
index === currentStep ? "text-foreground" : "text-muted-foreground",
)}
>
{step}
{showSummaries && index < currentStep && summaries?.[index]
? `: ${summaries[index]}`
: null}
</span>
</Step>
</li>
))}
</ol>
);
}
export function WizardPanel({
children,
holdHeight = false,
className,
}: {
readonly children: ReactNode;
readonly holdHeight?: boolean;
readonly className?: string;
}) {
return (
<div
data-slot="dialog-panel"
className={cn(
"min-w-0 space-y-4 bg-background/80 px-6 py-5 ring-1 ring-black/5 dark:bg-white/2 dark:ring-white/5",
className,
)}
>
<AnimatedHeight holdHeight={holdHeight}>{children}</AnimatedHeight>
</div>
);
}