move stepper logic to own component

This commit is contained in:
Igor Hrenowitsch Propisnov 2024-08-22 13:18:31 +02:00
parent 2805e57e60
commit 70f413f453
4 changed files with 154 additions and 38 deletions

View File

@ -1,23 +1,18 @@
<div class="flex flex-col h-full">
<div class="w-full bg-base-100 max-w-full sticky top-0 z-10 pt-4">
<ul class="steps steps-horizontal w-full py-2">
@for (step of steps; track $index) {
<li
[class.step-primary]="$index === currentStep()"
[class.step-success]="$index < currentStep() && isStepValid($index)"
[class.step-warning]="$index < currentStep() && !isStepValid($index)"
[attr.data-content]="getStepContent($index)"
class="step text-sm md:text-base"
(click)="goToStep($index)"
[class.cursor-pointer]="
$index <= currentStep() || canAdvanceToStep($index)
">
<span>{{ step }}</span>
</li>
}
</ul>
<div
class="w-full bg-base-100 max-w-full sticky top-0 z-10 pt-4 px-4 sm:px-8">
<div class="w-full max-w-4xl mx-auto">
<app-stepper-indicator
[steps]="steps"
[currentStep]="currentStep()"
[isStepValid]="isStepValid.bind(this)"
[canAdvanceToStep]="canAdvanceToStep.bind(this)"
(stepChange)="goToStep($event)"></app-stepper-indicator>
</div>
</div>
<!-- Rest of the component remains the same -->
<div class="flex-grow overflow-y-auto px-4 sm:px-8 py-8">
<div class="w-full max-w-4xl mx-auto">
@if (currentStep() === 0) {
@ -33,29 +28,24 @@
class="w-full bg-base-100 max-w-full sticky bottom-0 z-10 px-4 sm:px-8 py-4">
<div class="flex justify-between max-w-4xl mx-auto">
<div>
@if (currentStep() > 0) {
<button
type="button"
class="btn btn-primary btn-outline"
(click)="prevStep()"
[disabled]="currentStep() === 0"
[class.opacity-50]="currentStep() === 0">
<span>
Back to
<span class="font-bold">{{ steps[currentStep() - 1] }}</span>
</span>
</button>
}
<button
type="button"
class="btn btn-primary btn-outline"
(click)="prevStep()"
[disabled]="currentStep() === 0"
[attr.aria-label]="getBackButtonAriaLabel()"
[class.opacity-50]="currentStep() === 0">
<span [innerHTML]="getBackButtonText()"></span>
</button>
</div>
<div>
@if (currentStep() < steps.length - 1) {
<button
type="button"
[class]="getNextButtonClass()"
(click)="nextStep()">
<span>
{{ getNextButtonText() }}
</span>
(click)="nextStep()"
[attr.aria-label]="getNextButtonAriaLabel()">
<span [innerHTML]="getNextButtonText()"></span>
</button>
}
</div>

View File

@ -9,6 +9,8 @@ import {
} from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { StepperIndicatorComponent } from '../../../shared/components/stepper-indicator/stepper-indicator.component';
import { BasicStepComponent } from './steps/basic-step.component';
import { TicketsStepComponent } from './steps/tickets-step.component';
@ -16,7 +18,12 @@ import { TicketsStepComponent } from './steps/tickets-step.component';
selector: 'app-create-event',
standalone: true,
providers: [],
imports: [CommonModule, BasicStepComponent, TicketsStepComponent],
imports: [
CommonModule,
BasicStepComponent,
TicketsStepComponent,
StepperIndicatorComponent,
],
templateUrl: './create-event.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
@ -114,9 +121,35 @@ export class CreateEventComponent {
}
public getNextButtonText(): string {
return this.hasAttemptedNextStep() && !this.isCurrentStepValid()
? 'Required Fields Need Attention'
: `Next to ${this.steps[this.currentStep() + 1]}`;
if (this.hasAttemptedNextStep() && !this.isCurrentStepValid()) {
return 'Required Fields Need Attention';
} else {
return `Next to <span class="font-bold">${this.steps[this.currentStep() + 1]}</span>`;
}
}
public getNextButtonAriaLabel(): string {
if (this.hasAttemptedNextStep() && !this.isCurrentStepValid()) {
return 'Required Fields Need Attention';
} else {
return `Next to ${this.steps[this.currentStep() + 1]}`;
}
}
public getBackButtonText(): string {
if (this.currentStep() === 0) {
return 'Back';
} else {
return `Back to <span class="font-bold">${this.steps[this.currentStep() - 1]}</span>`;
}
}
public getBackButtonAriaLabel(): string {
if (this.currentStep() === 0) {
return 'Back (disabled)';
} else {
return `Back to ${this.steps[this.currentStep() - 1]}`;
}
}
private markCurrentStepAsTouched(): void {

View File

@ -0,0 +1,69 @@
<div class="w-full max-w-4xl mx-auto">
<div class="relative flex justify-between w-full mb-4">
<!-- Connecting lines -->
<div class="absolute top-5 left-0 right-0 h-1 flex">
@for (step of steps; track $index) {
@if ($index < steps.length - 1) {
<div
class="flex-1 mx-2 transition-all duration-300"
[class.bg-primary]="$index < currentStep"
[class.bg-success]="
$index < currentStep - 1 &&
isStepValid($index) &&
isStepValid($index + 1)
"
[class.bg-warning]="
$index < currentStep - 1 &&
(!isStepValid($index) || !isStepValid($index + 1))
"
[class.bg-base-300]="$index >= currentStep"></div>
}
}
</div>
<!-- Steps -->
@for (step of steps; track $index) {
<div class="flex flex-col items-center z-10">
<button
[class.bg-primary]="$index <= currentStep"
[class.bg-success]="$index < currentStep && isStepValid($index)"
[class.bg-warning]="$index < currentStep && !isStepValid($index)"
[class.bg-base-300]="$index > currentStep"
class="w-10 h-10 rounded-full flex items-center justify-center text-sm font-bold text-base-100 transition-all duration-300 mb-2 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"
[class.cursor-pointer]="
$index <= currentStep || canAdvanceToStep($index)
"
[attr.aria-label]="'Go to step ' + ($index + 1)"
[attr.tabindex]="
$index <= currentStep || canAdvanceToStep($index) ? 0 : -1
"
(click)="goToStep($index)"
(keydown.enter)="goToStep($index)"
(keydown.space)="goToStep($index); $event.preventDefault()">
@if ($index < currentStep && isStepValid($index)) {
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 13l4 4L19 7" />
</svg>
} @else {
{{ $index + 1 }}
}
</button>
<span
class="text-center text-sm"
[class.font-bold]="$index === currentStep">
{{ step }}
</span>
</div>
}
</div>
</div>

View File

@ -0,0 +1,24 @@
import { CommonModule } from '@angular/common';
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-stepper-indicator',
standalone: true,
imports: [CommonModule],
templateUrl: './stepper-indicator.component.html',
styles: [],
})
export class StepperIndicatorComponent {
@Output() public stepChange: EventEmitter<number> =
new EventEmitter<number>();
@Input() public steps: string[] = [];
@Input() public currentStep: number = 0;
@Input() public isStepValid: (index: number) => boolean = () => true;
@Input() public canAdvanceToStep: (index: number) => boolean = () => true;
public goToStep(index: number): void {
if (index <= this.currentStep || this.canAdvanceToStep(index)) {
this.stepChange.emit(index);
}
}
}