Stepper de ejemplo integrado
This commit is contained in:
42
src/components/planes/App.css
Normal file
42
src/components/planes/App.css
Normal file
@@ -0,0 +1,42 @@
|
||||
#root {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
a:nth-of-type(2) .logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
163
src/components/planes/stepper.tsx
Normal file
163
src/components/planes/stepper.tsx
Normal file
@@ -0,0 +1,163 @@
|
||||
import { defineStepper } from '@stepperize/react'
|
||||
import * as React from 'react'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
// import './App.css'
|
||||
|
||||
const { useStepper, steps, utils } = defineStepper(
|
||||
{
|
||||
id: 'shipping',
|
||||
title: 'Shipping',
|
||||
description: 'Enter your shipping details',
|
||||
},
|
||||
{
|
||||
id: 'payment',
|
||||
title: 'Payment',
|
||||
description: 'Enter your payment details',
|
||||
},
|
||||
{ id: 'complete', title: 'Complete', description: 'Checkout complete' },
|
||||
)
|
||||
|
||||
function App() {
|
||||
const stepper = useStepper()
|
||||
|
||||
const currentIndex = utils.getIndex(stepper.current.id)
|
||||
return (
|
||||
<div className="w-[450px] space-y-6 rounded-lg border p-6">
|
||||
<div className="flex justify-between">
|
||||
<h2 className="text-lg font-medium">Checkout</h2>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-muted-foreground text-sm">
|
||||
Step {currentIndex + 1} of {steps.length}
|
||||
</span>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
<nav aria-label="Checkout Steps" className="group my-4">
|
||||
<ol
|
||||
className="flex items-center justify-between gap-2"
|
||||
aria-orientation="horizontal"
|
||||
>
|
||||
{stepper.all.map((step, index, array) => (
|
||||
<React.Fragment key={step.id}>
|
||||
<li className="flex flex-shrink-0 items-center gap-4">
|
||||
<Button
|
||||
type="button"
|
||||
role="tab"
|
||||
variant={index <= currentIndex ? 'default' : 'secondary'}
|
||||
aria-current={
|
||||
stepper.current.id === step.id ? 'step' : undefined
|
||||
}
|
||||
aria-posinset={index + 1}
|
||||
aria-setsize={steps.length}
|
||||
aria-selected={stepper.current.id === step.id}
|
||||
className="flex size-10 items-center justify-center rounded-full"
|
||||
onClick={() => stepper.goTo(step.id)}
|
||||
>
|
||||
{index + 1}
|
||||
</Button>
|
||||
<span className="text-sm font-medium">{step.title}</span>
|
||||
</li>
|
||||
{index < array.length - 1 && (
|
||||
<Separator
|
||||
className={`flex-1 ${
|
||||
index < currentIndex ? 'bg-primary' : 'bg-muted'
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</ol>
|
||||
</nav>
|
||||
<div className="space-y-4">
|
||||
{stepper.switch({
|
||||
shipping: () => <ShippingComponent />,
|
||||
payment: () => <PaymentComponent />,
|
||||
complete: () => <CompleteComponent />,
|
||||
})}
|
||||
{!stepper.isLast ? (
|
||||
<div className="flex justify-end gap-4">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={stepper.prev}
|
||||
disabled={stepper.isFirst}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
<Button onClick={stepper.next}>
|
||||
{stepper.isLast ? 'Complete' : 'Next'}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Button onClick={stepper.reset}>Reset</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ShippingComponent = () => {
|
||||
return (
|
||||
<div className="grid gap-4">
|
||||
<div className="grid gap-2">
|
||||
<label htmlFor="name" className="text-start text-sm font-medium">
|
||||
Name
|
||||
</label>
|
||||
<Input id="name" placeholder="John Doe" className="w-full" />
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<label htmlFor="address" className="text-start text-sm font-medium">
|
||||
Address
|
||||
</label>
|
||||
<Textarea
|
||||
id="address"
|
||||
placeholder="123 Main St, Anytown USA"
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const PaymentComponent = () => {
|
||||
return (
|
||||
<div className="grid gap-4">
|
||||
<div className="grid gap-2">
|
||||
<label htmlFor="card-number" className="text-start text-sm font-medium">
|
||||
Card Number
|
||||
</label>
|
||||
<Input
|
||||
id="card-number"
|
||||
placeholder="4111 1111 1111 1111"
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="grid gap-2">
|
||||
<label
|
||||
htmlFor="expiry-date"
|
||||
className="text-start text-sm font-medium"
|
||||
>
|
||||
Expiry Date
|
||||
</label>
|
||||
<Input id="expiry-date" placeholder="MM/YY" className="w-full" />
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<label htmlFor="cvc" className="text-start text-sm font-medium">
|
||||
CVC
|
||||
</label>
|
||||
<Input id="cvc" placeholder="123" className="w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const CompleteComponent = () => {
|
||||
return <h3 className="py-4 text-lg font-medium">Stepper complete 🔥</h3>
|
||||
}
|
||||
|
||||
export default App
|
||||
Reference in New Issue
Block a user