1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-20 10:13:31 +00:00

[CL-194] add vertical stepper to CL (#14528)

* 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>
This commit is contained in:
Bryan Cunningham
2025-06-06 14:04:01 -04:00
committed by GitHub
parent 703715aea5
commit 3e4c37b8b3
12 changed files with 411 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
import { Directionality } from "@angular/cdk/bidi";
import { CdkStepper, StepperOrientation } from "@angular/cdk/stepper";
import { CommonModule } from "@angular/common";
import { ChangeDetectorRef, Component, ElementRef, Input, QueryList } from "@angular/core";
import { ResizeObserverDirective } from "../resize-observer";
import { TypographyModule } from "../typography";
import { StepComponent } from "./step.component";
/**
* The `<bit-stepper>` component extends the
* [Angular CdkStepper](https://material.angular.io/cdk/stepper/api#CdkStepper) component
*/
@Component({
selector: "bit-stepper",
templateUrl: "stepper.component.html",
providers: [{ provide: CdkStepper, useExisting: StepperComponent }],
imports: [CommonModule, ResizeObserverDirective, TypographyModule],
standalone: true,
})
export class StepperComponent extends CdkStepper {
// Need to reimplement the constructor to fix an invalidFactoryDep error in Storybook
// @see https://github.com/storybookjs/storybook/issues/23534#issuecomment-2042888436
constructor(
_dir: Directionality,
_changeDetectorRef: ChangeDetectorRef,
_elementRef: ElementRef<HTMLElement>,
) {
super(_dir, _changeDetectorRef, _elementRef);
}
private resizeWidthsMap = new Map([
[2, 600],
[3, 768],
[4, 900],
]);
override readonly steps!: QueryList<StepComponent>;
private internalOrientation: StepperOrientation | undefined = undefined;
private initialOrientation: StepperOrientation | undefined = undefined;
// overriding CdkStepper orientation input so we can default to vertical
@Input()
override get orientation() {
return this.internalOrientation || "vertical";
}
override set orientation(value: StepperOrientation) {
if (!this.internalOrientation) {
// tracking the first value of orientation. We want to handle resize events if it's 'horizontal'.
// If it's 'vertical' don't change the orientation to 'horizontal' when resizing
this.initialOrientation = value;
}
this.internalOrientation = value;
}
handleResize(entry: ResizeObserverEntry) {
if (this.initialOrientation === "horizontal") {
const stepperContainerWidth = entry.contentRect.width;
const numberOfSteps = this.steps.length;
const breakpoint = this.resizeWidthsMap.get(numberOfSteps) || 450;
this.orientation = stepperContainerWidth < breakpoint ? "vertical" : "horizontal";
// This is a method of CdkStepper. Their docs define it as: 'Marks the component to be change detected'
this._stateChanged();
}
}
isStepDisabled(index: number) {
if (this.selectedIndex !== index) {
return this.selectedIndex === index - 1
? !this.steps.find((_, i) => i == index - 1)?.completed
: true;
}
return false;
}
selectStepByIndex(index: number): void {
this.selectedIndex = index;
}
/**
* UID for `[attr.aria-controls]`
*/
protected contentId = Math.random().toString(36).substring(2);
}