import * as React from 'react' import { FieldLabel } from '../shared/FieldLabel' import type { SAILLabelPosition, SAILMarginSize, SAILColorInput } from '../../types/sail' import { mergeClasses } from '../../utils/classNames' import { isPaletteColor, resolveColorClass } from '../../utils/colorResolver' import { marginAboveMap, marginBelowMap } from '../../utils/sailMaps' type Orientation = "HORIZONTAL" | "VERTICAL" type StepStyle = "LINE" | "CHEVRON" | "DOT" type Color = "ACCENT" | "POSITIVE" | "NEGATIVE" | "WARN" | SAILColorInput export interface MilestoneFieldProps { /** Text to display as the field label */ label?: string /** Supplemental text about this field */ instructions?: string /** Array of labels describing the sequence of steps */ steps: string[] /** Array of links to apply to the steps */ links?: any[] /** Index of the current step. When null, all steps are future. When -1, all steps are completed */ active?: number | null /** Determines where the label appears */ labelPosition?: SAILLabelPosition /** Displays a help icon with tooltip text */ helpTooltip?: string /** Determines whether the component is displayed */ showWhen?: boolean /** Determines the layout of the milestone steps */ orientation?: Orientation /** Additional text for screen readers */ accessibilityText?: string /** Determines the fill color */ color?: Color /** Determines how much space is added above the layout */ marginAbove?: SAILMarginSize /** Determines how much space is added below the layout */ marginBelow?: SAILMarginSize /** Determines the style of the milestone steps */ stepStyle?: StepStyle /** Additional Tailwind classes for prototype-specific styling (not part of SAIL API) */ className?: string } /** * Displays the completed, current, and future steps of a process or sequence * */ export const MilestoneField: React.FC = ({ label, instructions, steps, links = [], active = null, labelPosition = "ABOVE", helpTooltip, showWhen = true, orientation = "HORIZONTAL", accessibilityText, color = "ACCENT", marginAbove = "NONE", marginBelow = "STANDARD", stepStyle = "LINE", className: classNameProp }) => { // Visibility control if (!showWhen) return null const fieldId = `milestone-${Math.random().toString(36).slice(2, 11)}` // Map semantic colors to Tailwind classes const getColorClasses = (colorValue: Color) => { const semanticColorMap: Record = { ACCENT: { bg: 'bg-blue-50', text: 'text-blue-500', border: 'border-blue-200', chevronL: 'border-l-blue-50', chevronT: 'border-t-blue-50' }, POSITIVE: { bg: 'bg-green-50', text: 'text-green-700', border: 'border-green-200', chevronL: 'border-l-green-50', chevronT: 'border-t-green-50' }, NEGATIVE: { bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200', chevronL: 'border-l-red-50', chevronT: 'border-t-red-50' }, WARN: { bg: 'bg-yellow-50', text: 'text-yellow-800', border: 'border-yellow-300', chevronL: 'border-l-yellow-50', chevronT: 'border-t-yellow-50' } } if (semanticColorMap[colorValue]) { return { ...semanticColorMap[colorValue], style: undefined } } // Handle palette colors if (isPaletteColor(colorValue)) { return { bg: resolveColorClass(colorValue, 'bg'), text: resolveColorClass(colorValue, 'text'), border: resolveColorClass(colorValue, 'border'), chevronL: '', chevronT: '', style: undefined } } // Handle hex colors return { bg: '', text: '', border: '', chevronL: '', chevronT: '', style: { backgroundColor: colorValue, borderColor: colorValue, color: colorValue } } } const colorClasses = getColorClasses(color) // Determine step states const getStepState = (index: number): 'completed' | 'current' | 'future' => { if (active === null) return 'future' if (active === -1) return 'completed' if (index < active) return 'completed' if (index === active) return 'current' return 'future' } // Render individual step const renderStep = (step: string, index: number) => { const state = getStepState(index) const link = links[index] const stepNumber = index + 1 const stepContent = (
{/* Step indicator */}
{stepStyle === "DOT" && ( <>
{/* Vertical connector line */} {orientation === "VERTICAL" && index < steps.length - 1 && (
)} )} {stepStyle === "LINE" && (
{stepNumber}
)} {stepStyle === "CHEVRON" && (
{step}
{/* Chevron arrow point */} {orientation === "HORIZONTAL" && index < steps.length - 1 && (
)} {/* Chevron arrow for vertical */} {orientation === "VERTICAL" && index < steps.length - 1 && (
)}
)}
{/* Step label (for DOT and LINE styles) */} {stepStyle !== "CHEVRON" && ( {step} )}
) // Wrap with link if provided if (link) { return ( ) } return (
{stepContent}
) } const sailContainerClasses = [ marginAboveMap[marginAbove], marginBelowMap[marginBelow], ].filter(Boolean).join(' ') const containerClasses = mergeClasses(sailContainerClasses, classNameProp) return (
= 0 && active < steps.length ? steps[active] : undefined} > {/* Continuous progress bar for horizontal LINE style */} {orientation === "HORIZONTAL" && stepStyle === "LINE" && steps.length > 1 && (
)} {/* Steps container */}
{steps.map((step, index) => renderStep(step, index))}
{/* Instructions */} {instructions && (

{instructions}

)}
) }