mirror of
https://github.com/bitwarden/browser
synced 2026-02-06 19:53:59 +00:00
* Copy Vertical stepper into CL * remove unused input * add docs around vertical step usage * use signal inputs * add vertical step story * enhance documentation * WIP * Rename to Stepper * adds horizontal stepper * updated view logic * add resizeobserver directive * add basic responsizeness to stepper * add comment about stateChanged method * update responsive logic * reformat with prettier * remove obsolete applyBorder input * fix step type mismatch * fix incorrect step import * fix borken disabled logic * fix class logic * move tabpanel out of tablist. correctly increment ids * make map private * use accordion attributes for vertical stepper * barrel export directive * fixing types * remove now obsolete step-content * reimplement constructors to fix storybook not rendering * move padding to different container * move map and observer into directive * remove useless test for now * add comment about constructor implementation * add template variable for disabled state * fix typo * simplify resize observer directive logic * add jsdoc description * use typography directive * use the variable for step disabled * Update libs/components/src/stepper/stepper.mdx Co-authored-by: Vicki League <vleague@bitwarden.com> --------- Co-authored-by: Will Martin <contact@willmartian.com> Co-authored-by: Vicki League <vleague@bitwarden.com>
127 lines
4.8 KiB
HTML
127 lines
4.8 KiB
HTML
<div resizeObserver (resize)="handleResize($event)">
|
|
@if (orientation === "horizontal") {
|
|
<div role="tablist">
|
|
<div class="tw-flex tw-gap-8 tw-justify-between">
|
|
@for (step of steps; track $index; let isLast = $last) {
|
|
@let isCurrentStepDisabled = isStepDisabled($index);
|
|
<button
|
|
type="button"
|
|
[disabled]="isCurrentStepDisabled"
|
|
(click)="selectStepByIndex($index)"
|
|
class="tw-flex tw-p-3 tw-items-center tw-border-none tw-bg-transparent tw-shrink-0"
|
|
[ngClass]="{
|
|
'hover:tw-bg-secondary-100': !isCurrentStepDisabled && step.editable,
|
|
}"
|
|
[attr.aria-selected]="selectedIndex === $index"
|
|
[attr.aria-controls]="contentId + $index"
|
|
role="tab"
|
|
>
|
|
@if (step.completed) {
|
|
<span
|
|
class="tw-me-3.5 tw-size-9 tw-rounded-full tw-bg-primary-600 tw-font-bold tw-leading-9 tw-text-contrast"
|
|
>
|
|
<i class="bwi bwi-fw bwi-check" aria-hidden="true"></i>
|
|
</span>
|
|
} @else {
|
|
<span
|
|
class="tw-me-3.5 tw-size-9 tw-rounded-full tw-font-bold tw-leading-9"
|
|
[ngClass]="{
|
|
'tw-bg-primary-600 tw-text-contrast': selectedIndex === $index,
|
|
'tw-bg-secondary-300 tw-text-main':
|
|
selectedIndex !== $index && !isCurrentStepDisabled && step.editable,
|
|
'tw-bg-transparent tw-text-muted': isCurrentStepDisabled,
|
|
}"
|
|
>
|
|
{{ $index + 1 }}
|
|
</span>
|
|
}
|
|
<div class="tw-leading-snug tw-text-left">
|
|
<p bitTypography="body1" class="tw-m-0">{{ step.label }}</p>
|
|
|
|
@if (step.subLabel()) {
|
|
<p bitTypography="body2" class="tw-m-0 tw-mt-1 tw-text-muted">
|
|
{{ step.subLabel() }}
|
|
</p>
|
|
}
|
|
</div>
|
|
</button>
|
|
@if (!isLast) {
|
|
<div
|
|
class="after:tw-left-0 after:tw-top-[50%] after:-tw-translate-y-[50%] after:tw-h-[2px] after:tw-w-full after:tw-absolute after:tw-bg-secondary-300 after:tw-content-[''] tw-relative tw-w-full"
|
|
></div>
|
|
}
|
|
}
|
|
</div>
|
|
</div>
|
|
@for (step of steps; track $index; let isLast = $last) {
|
|
<div role="tabpanel" [attr.id]="contentId + $index" [hidden]="!selected">
|
|
@if (selectedIndex === $index) {
|
|
<div [ngTemplateOutlet]="selected.content"></div>
|
|
}
|
|
</div>
|
|
}
|
|
} @else {
|
|
@for (step of steps; track $index; let isLast = $last) {
|
|
@let isCurrentStepDisabled = isStepDisabled($index);
|
|
<button
|
|
type="button"
|
|
[disabled]="isCurrentStepDisabled"
|
|
(click)="selectStepByIndex($index)"
|
|
class="tw-flex tw-p-3 tw-w-full tw-items-center tw-border-none tw-bg-transparent"
|
|
[ngClass]="{
|
|
'hover:tw-bg-secondary-100': !isCurrentStepDisabled && step.editable,
|
|
}"
|
|
[attr.id]="contentId + 'accordion' + $index"
|
|
[attr.aria-expanded]="selectedIndex === $index"
|
|
[attr.aria-controls]="contentId + $index"
|
|
>
|
|
@if (step.completed) {
|
|
<span
|
|
class="tw-me-3.5 tw-size-9 tw-rounded-full tw-bg-primary-600 tw-font-bold tw-leading-9 tw-text-contrast"
|
|
>
|
|
<i class="bwi bwi-fw bwi-check" aria-hidden="true"></i>
|
|
</span>
|
|
} @else {
|
|
<span
|
|
class="tw-me-3.5 tw-size-9 tw-rounded-full tw-font-bold tw-leading-9"
|
|
[ngClass]="{
|
|
'tw-bg-primary-600 tw-text-contrast': selectedIndex === $index,
|
|
'tw-bg-secondary-300 tw-text-main':
|
|
selectedIndex !== $index && !isCurrentStepDisabled && step.editable,
|
|
'tw-bg-transparent tw-text-muted': isCurrentStepDisabled,
|
|
}"
|
|
>
|
|
{{ $index + 1 }}
|
|
</span>
|
|
}
|
|
<div class="tw-leading-snug tw-text-left">
|
|
<p bitTypography="body1" class="tw-m-0">{{ step.label }}</p>
|
|
|
|
@if (step.subLabel()) {
|
|
<div bitTypography="body2" class="tw-m-0 tw-mt-1 tw-text-muted">
|
|
{{ step.subLabel() }}
|
|
</div>
|
|
}
|
|
</div>
|
|
</button>
|
|
<div
|
|
[attr.id]="contentId + $index"
|
|
[hidden]="!selected"
|
|
[attr.aria-labelledby]="contentId + 'accordion' + $index"
|
|
role="region"
|
|
>
|
|
<div
|
|
class="tw-ms-7 tw-border-solid tw-border-0 tw-border-s tw-border-secondary-300"
|
|
[ngClass]="{ 'tw-min-h-6': !isLast }"
|
|
>
|
|
@if (selectedIndex === $index) {
|
|
<div class="tw-ps-8 tw-py-2">
|
|
<div [ngTemplateOutlet]="selected.content"></div>
|
|
</div>
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
</div>
|