From 26eaa394115c6b81b2fb1188fb43e7b215d4835d Mon Sep 17 00:00:00 2001 From: Hlars Date: Sat, 15 Mar 2025 07:44:02 +0100 Subject: [PATCH] more --- src/app/app.component.html | 3 +- .../progress-stepper.component.html | 100 +++++----- .../progress-stepper.component.scss | 179 +++++++++++++----- .../progress-stepper.component.ts | 55 +++++- 4 files changed, 243 insertions(+), 94 deletions(-) diff --git a/src/app/app.component.html b/src/app/app.component.html index b166914..80a3f6a 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,4 +1,5 @@ - +
Textbox: diff --git a/src/app/progress-stepper/progress-stepper.component.html b/src/app/progress-stepper/progress-stepper.component.html index f06f1f2..d8b7665 100644 --- a/src/app/progress-stepper/progress-stepper.component.html +++ b/src/app/progress-stepper/progress-stepper.component.html @@ -1,56 +1,64 @@
- @for (item of [{status: 'completed'}, {status: 'in progress'}, test]; track item; let i = - $index) { - -
- - - - @if (selectedIndex === i) { - - } - - @if (item.status == 'in progress') { - - } - - - -
-
-
-
- step {{ i + 1 }} - {{ item.status }} -
+ @for (item of steps; track item; let index = $index) { +
+
+
- +
+ + + + @if (index === status.progressIndex) { + + } + + + + + + +
+ +
+ {{ item }} + + {{ statusToLabel(indexStatus(index)) }} + +
+
}
- -
next
-
prev
+ +
- - - + + + + + +
\ No newline at end of file diff --git a/src/app/progress-stepper/progress-stepper.component.scss b/src/app/progress-stepper/progress-stepper.component.scss index 52f7ae7..e02b65b 100644 --- a/src/app/progress-stepper/progress-stepper.component.scss +++ b/src/app/progress-stepper/progress-stepper.component.scss @@ -1,3 +1,11 @@ +$checkboxWidth: 33px; +$checkboxHeight: 33px; + +$color-in-progress: #234dc2; +$color-completed: #23c274; +$color-canceled: #c23323; +$color-selected-step: rebeccapurple; + .step-progress { margin-bottom: 1rem; } @@ -9,12 +17,12 @@ width: 100%; padding: 30px; box-sizing: border-box; - position: relative; svg#checkbox { - width: 33px; - height: 33px; - stroke: #23c274; + position: relative; + width: $checkboxWidth; + height: $checkboxHeight; + stroke: $color-completed; stroke-width: 6; .circle { @@ -36,12 +44,16 @@ } .selection-circle { - // stroke-dasharray: 320; - // stroke-dashoffset: 320; - stroke: rebeccapurple; + stroke-dasharray: 320; + stroke-dashoffset: 320; + stroke: $color-selected-step; transition: stroke-dashoffset 0.5s, fill 0.5s 0.3s cubic-bezier(0.45, 0, 0.55, 1); + + &.selected { + stroke-dashoffset: 0; + } } .check { @@ -51,6 +63,13 @@ fill: none; transition: all 0.5s 0.5s cubic-bezier(0.45, 0, 0.55, 1); } + .fail { + stroke-dasharray: 70; + stroke-dashoffset: 70; + stroke: #fff; + fill: none; + transition: all 0.5s 0.5s cubic-bezier(0.45, 0, 0.55, 1); + } } } @@ -60,9 +79,48 @@ align-items: center; position: relative; + &.selectable { + .hover-box:hover, + .hover-box:has(+ .info:hover) { + &::before { + $hoverOffset: 7px; + + content: ""; + position: absolute; + background: #000; + top: calc($hoverOffset / -2); + left: calc($hoverOffset / -2); + width: calc($checkboxWidth + $hoverOffset); + height: calc($checkboxHeight + $hoverOffset); + + border-radius: calc($hoverOffset * 0.5); + z-index: 20; + opacity: 20%; + } + } + + .hover-box { + position: relative; + + &:hover { + cursor: pointer; + } + } + + .info:hover { + cursor: pointer; + } + } + + &.first { + flex-grow: 0.05; + } + .info { position: absolute; - bottom: -40px; + bottom: -30px; + right: 0; + text-align: right; display: grid; grid-template-columns: max-content; @@ -71,23 +129,38 @@ font-size: 11px; display: inline-block; margin-left: 5px; - padding: 2px 10px; transform: translateY(6px); font-weight: 500; color: currentColor; - background: #eee; - border-radius: 20px; - transition: background-color 0.1s ease-in-out; - } + transition: + opacity 0.5s, + fill 0.5s 0.3s cubic-bezier(0.45, 0, 0.55, 1); - span.progress-info.in-progress { - background-color: #234dc2; - color: #fff; - } + span { + padding: 2px 10px; + background: #eee; + border-radius: 20px; + transition: background-color 0.1s ease-in-out; + } - span.progress-info.completed { - background-color: #23c274; - color: #fff; + &.never-called { + opacity: 0; + } + + &.in-progress span { + background-color: $color-in-progress; + color: #fff; + } + + &.completed span { + background-color: $color-completed; + color: #fff; + } + + &.canceled span { + background-color: $color-canceled; + color: #fff; + } } span.step { @@ -102,7 +175,7 @@ } .progress-line { - width: 140px; + width: 10px; flex-grow: 1; margin: 0 10px; height: 4px; @@ -116,38 +189,58 @@ width: 0%; transition: all 0.5s 0.5s cubic-bezier(0.45, 0, 0.55, 1); } -} -.progress-line.active { - .progress-percent { - background: #234dc2; - width: 50%; + &.active .progress-percent { + background: $color-in-progress; + // width: 50%; + width: var(--progress); } -} -.progress-line.complete { - .progress-percent { + &.complete .progress-percent { width: 100%; - background: #23c274; + background: $color-completed; + } + + &.canceled .progress-percent { + width: 0; } } -.check-box.active { - svg#checkbox { - .circle { - fill: #234dc2; +.check-box { + &.active { + svg#checkbox { + .circle { + fill: $color-in-progress; + } } } -} -.check-box.complete { - svg#checkbox { - .circle { - stroke-dashoffset: 0; - fill: #23c274; - } - .check { - stroke-dashoffset: 0; + &.complete { + svg#checkbox { + .circle { + // stroke-dashoffset: 0; + fill: $color-completed; + } + + .check { + stroke-dashoffset: 0; + } + } + } + + &.canceled { + svg#checkbox { + .circle { + fill: $color-canceled; + } + + .inner-circle { + r: 0; + } + + .fail { + stroke-dashoffset: 0; + } } } } diff --git a/src/app/progress-stepper/progress-stepper.component.ts b/src/app/progress-stepper/progress-stepper.component.ts index 5dd7a62..db76da8 100644 --- a/src/app/progress-stepper/progress-stepper.component.ts +++ b/src/app/progress-stepper/progress-stepper.component.ts @@ -1,14 +1,61 @@ -import { NgClass } from '@angular/common'; -import { Component } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; + +export enum Status { + Open = 'open', + InProgress = 'in-progress', + Completed = 'completed', + Canceled = 'canceled' +} + +export interface ProgressStatus { + progressIndex: number, + progressStatus: Status.InProgress | Status.Canceled, + progressPercent: number, +} @Component({ selector: 'app-progress-stepper', - imports: [NgClass], + imports: [], templateUrl: './progress-stepper.component.html', styleUrl: './progress-stepper.component.scss' }) export class ProgressStepperComponent { selectedIndex = 0; + STATUS = Status; - test = { status: 'Offen' }; + @Input() steps: string[] = []; + @Input() status: ProgressStatus = { progressIndex: 0, progressStatus: Status.Canceled, progressPercent: 50 } + + @Output() selectionChange: EventEmitter = new EventEmitter(); + + selectStep(index: number) { + if (index <= this.status.progressIndex) + this.selectedIndex = index; + + this.selectionChange.emit(index) + } + + indexStatus(index: number): Status { + if (index < this.status.progressIndex) + return Status.Completed; + else if (index === this.status.progressIndex) + if (this.status.progressStatus === Status.InProgress) + return Status.InProgress; + else return Status.Canceled; + else + return Status.Open + } + + statusToLabel(status: Status): string { + switch (status) { + case Status.Open: return 'Offen'; + case Status.InProgress: return 'In Bearbeitung'; + case Status.Canceled: return 'Verweigert'; + case Status.Completed: return 'Fertig'; + } + } + + get getProgress(): string { + return `${Math.min(Math.max(this.status.progressPercent, 0), 100)}%` + } }