Start organizational migration

This commit is contained in:
adilallo
2026-02-05 18:21:56 -07:00
parent 69074b23f3
commit db3c0274f6
161 changed files with 145 additions and 145 deletions
@@ -0,0 +1,23 @@
"use client";
import { memo } from "react";
import { ProgressView } from "./Progress.view";
import type { ProgressProps } from "./Progress.types";
const ProgressContainer = memo<ProgressProps>(
({ progress = "3-2", className = "" }) => {
const barClasses = `h-[8px] relative w-full`;
return (
<ProgressView
progress={progress}
className={className}
barClasses={barClasses}
/>
);
},
);
ProgressContainer.displayName = "Progress";
export default ProgressContainer;
@@ -0,0 +1,24 @@
export type ProgressBarState =
| "1-0"
| "1-1"
| "1-2"
| "1-3"
| "1-4"
| "1-5"
| "2-0"
| "2-1"
| "2-2"
| "3-0"
| "3-1"
| "3-2";
export interface ProgressProps {
progress?: ProgressBarState;
className?: string;
}
export interface ProgressViewProps {
progress: ProgressBarState;
className: string;
barClasses: string;
}
@@ -0,0 +1,80 @@
import type { ProgressViewProps } from "./Progress.types";
export function ProgressView({
progress,
className,
barClasses,
}: ProgressViewProps) {
// Progress bar type
const [fullSegments, partialSegment] = progress.split("-").map(Number);
// Calculate total progress:
// - For 1-X: first section is (X+1)/6 filled
// - For 2-X: first section full, second section X/3 filled
// - For 3-X: first two sections full, third section X/3 filled
// Max is 3 full segments = 9 units
let totalProgress = 0;
if (fullSegments === 1) {
totalProgress = (partialSegment + 1) / 6; // 1/6 to 6/6 of first section
} else if (fullSegments === 2) {
totalProgress = 1 + partialSegment / 3; // 1 full + 0/3 to 2/3 of second
} else if (fullSegments === 3) {
totalProgress = 2 + partialSegment / 3; // 2 full + 0/3 to 2/3 of third
}
const maxProgress = 3;
const progressPercentage = Math.round((totalProgress / maxProgress) * 100);
return (
<div
className={`${barClasses} ${className}`}
role="progressbar"
aria-valuenow={totalProgress}
aria-valuemin={0}
aria-valuemax={3}
aria-label={`Progress: ${progressPercentage}%`}
>
{/* Background layer - 3 segments */}
<div className="absolute inset-0 flex gap-[var(--spacing-scale-008)] px-[4px]">
<div className="flex-1 h-full bg-[var(--color-surface-default-secondary)] rounded-l-[var(--radius-full)]" />
<div className="flex-1 h-full bg-[var(--color-surface-default-secondary)]" />
<div className="flex-1 h-full bg-[var(--color-surface-default-secondary)] rounded-r-[var(--radius-full)]" />
</div>
{/* Fill layer - always show 3 sections, fill amount varies */}
<div className="absolute inset-0 flex gap-[var(--spacing-scale-008)] px-[4px] overflow-hidden">
{/* First section - for 1-X: (X+1)/6 filled, for 2-X and 3-X: fully filled */}
<div className="flex-1 h-full relative">
{fullSegments === 1 ? (
<div
className="absolute inset-y-0 left-0 bg-[var(--color-content-default-brand-primary)] rounded-l-[var(--radius-full)]"
style={{ width: `${((partialSegment + 1) / 6) * 100}%` }}
/>
) : fullSegments >= 2 ? (
<div className="absolute inset-0 bg-[var(--color-content-default-brand-primary)] rounded-l-[var(--radius-full)]" />
) : null}
</div>
{/* Second section - for 2-X: X/3 filled, for 3-X: fully filled, otherwise empty */}
<div className="flex-1 h-full relative">
{fullSegments === 2 ? (
partialSegment > 0 ? (
<div
className="absolute inset-y-0 left-0 bg-[var(--color-content-default-brand-primary)]"
style={{ width: `${(partialSegment / 3) * 100}%` }}
/>
) : null
) : fullSegments >= 3 ? (
<div className="absolute inset-0 bg-[var(--color-content-default-brand-primary)]" />
) : null}
</div>
{/* Third section - for 3-X: X/3 filled, otherwise empty */}
<div className="flex-1 h-full relative">
{fullSegments === 3 && partialSegment > 0 ? (
<div
className="absolute inset-y-0 left-0 bg-[var(--color-content-default-brand-primary)]"
style={{ width: `${(partialSegment / 3) * 100}%` }}
/>
) : null}
</div>
</div>
</div>
);
}
@@ -0,0 +1,24 @@
"use client";
import { memo } from "react";
import { StepperView } from "./Stepper.view";
import type { StepperProps } from "./Stepper.types";
const StepperContainer = memo<StepperProps>(
({ active = 1, totalSteps = 5, className = "" }) => {
const stepperClasses = `flex gap-[var(--spacing-scale-012)] items-center relative`;
return (
<StepperView
active={active}
totalSteps={totalSteps}
className={className}
stepperClasses={stepperClasses}
/>
);
},
);
StepperContainer.displayName = "Stepper";
export default StepperContainer;
@@ -0,0 +1,14 @@
export type StepperActive = number;
export interface StepperProps {
active?: StepperActive;
totalSteps?: number;
className?: string;
}
export interface StepperViewProps {
active: StepperActive;
totalSteps: number;
className: string;
stepperClasses: string;
}
@@ -0,0 +1,50 @@
import type { StepperViewProps } from "./Stepper.types";
export function StepperView({
active,
totalSteps,
className,
stepperClasses,
}: StepperViewProps) {
return (
<div
className={`${stepperClasses} ${className}`}
role="progressbar"
aria-valuenow={active}
aria-valuemin={1}
aria-valuemax={totalSteps}
aria-label={`Step ${active} of ${totalSteps}`}
>
{Array.from({ length: totalSteps }, (_, index) => {
const stepNumber = index + 1;
const isActive = stepNumber === active;
return (
<div
key={stepNumber}
className="shrink-0 w-[12px] h-[12px] flex items-center justify-center"
>
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
className={isActive ? "opacity-100" : "opacity-30"}
>
<circle
cx="6"
cy="6"
r="5.5"
fill="var(--color-surface-inverse-secondary)"
stroke="var(--color-surface-inverse-secondary)"
strokeWidth="1"
/>
</svg>
</div>
);
})}
</div>
);
}
@@ -0,0 +1 @@
export { default } from "./Stepper.container";
@@ -0,0 +1 @@
export { default } from "./Progress.container";