Files
nebula/components/stepper.templ

141 lines
3.4 KiB
Plaintext

package components
import "strconv"
// StepInfo represents a single step in the stepper
type StepInfo struct {
Number int
Label string
}
// OnboardingStepper renders a 3-step progress indicator
// currentStep: 1-indexed current step number
// steps: slice of step labels (e.g., []string{"Welcome", "Learn", "Get Started"})
templ OnboardingStepper(currentStep int, steps []string) {
@stepperStyles()
<div class="onboarding-stepper">
for i, label := range steps {
@stepperItem(i+1, label, currentStep)
if i < len(steps)-1 {
@stepperLine(i+1 < currentStep)
}
}
</div>
}
// stepperItem renders a single step circle with label
templ stepperItem(stepNum int, label string, currentStep int) {
<div class={ "stepper-item", templ.KV("active", stepNum == currentStep), templ.KV("completed", stepNum < currentStep) } data-step={ strconv.Itoa(stepNum) }>
<div class="stepper-number">
if stepNum < currentStep {
<wa-icon name="check" style="font-size: 14px;"></wa-icon>
} else {
{ strconv.Itoa(stepNum) }
}
</div>
<span class="stepper-label">{ label }</span>
</div>
}
// stepperLine renders the connecting line between steps
templ stepperLine(completed bool) {
<div class={ "stepper-line", templ.KV("completed", completed) }></div>
}
// ProgressDots renders a compact dot-based progress indicator
templ ProgressDots(current int, total int) {
<div class="progress-dots">
for i := 1; i <= total; i++ {
<div class={ "progress-dot", templ.KV("active", i == current), templ.KV("completed", i < current) }></div>
}
</div>
}
templ stepperStyles() {
<style>
.onboarding-stepper {
display: flex;
justify-content: center;
align-items: center;
gap: var(--wa-space-s);
padding: var(--wa-space-m) 0;
}
.stepper-item {
display: flex;
align-items: center;
gap: var(--wa-space-xs);
}
.stepper-number {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: var(--wa-font-size-s);
font-weight: 600;
background: var(--wa-color-neutral-200);
color: var(--wa-color-neutral-600);
transition: all 0.3s;
}
.stepper-item.active .stepper-number {
background: var(--wa-color-primary);
color: white;
box-shadow: 0 0 0 4px var(--wa-color-primary-subtle);
}
.stepper-item.completed .stepper-number {
background: var(--wa-color-success);
color: white;
}
.stepper-label {
font-size: var(--wa-font-size-xs);
color: var(--wa-color-neutral-500);
display: none;
}
@media (min-width: 480px) {
.stepper-label {
display: block;
}
}
.stepper-item.active .stepper-label {
color: var(--wa-color-primary);
font-weight: 500;
}
.stepper-line {
width: 40px;
height: 2px;
background: var(--wa-color-neutral-200);
transition: background 0.3s;
}
.stepper-line.completed {
background: var(--wa-color-success);
}
/* Progress dots */
.progress-dots {
display: flex;
justify-content: center;
gap: var(--wa-space-xs);
}
.progress-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--wa-color-neutral-200);
transition: all 0.3s;
}
.progress-dot.active {
background: var(--wa-color-primary);
transform: scale(1.25);
}
.progress-dot.completed {
background: var(--wa-color-success);
}
/* Responsive adjustments */
@media (max-height: 600px) {
.onboarding-stepper {
padding: var(--wa-space-s) 0;
}
}
</style>
}