UNPKG

@szum-tech/design-system

Version:

Szum-Tech design system with tailwindcss support

1,053 lines (1,047 loc) 37 kB
import { Spinner } from './chunk-P5IUC7HJ.js'; import { useDirection, Direction } from './chunk-H5O5L6XT.js'; import { useLazyRef, useIsomorphicLayoutEffect } from './chunk-DFD2WUOU.js'; import { cn } from './chunk-ZD2QRAOX.js'; import * as React6 from 'react'; import { Slot } from '@radix-ui/react-slot'; import { jsx, jsxs } from 'react/jsx-runtime'; import { OctagonXIcon, TriangleAlertIcon, InfoIcon, CircleCheckIcon } from 'lucide-react'; import { Toaster as Toaster$1 } from 'sonner'; export { toast } from 'sonner'; import { cva } from 'class-variance-authority'; // src/components/stepper/stepper.constants.ts var STEPPER_ROOT_NAME = "Stepper"; var STEPPER_NAV_NAME = "StepperNav"; var STEPPER_ITEM_NAME = "StepperItem"; var STEPPER_TRIGGER_NAME = "StepperTrigger"; var STEPPER_INDICATOR_NAME = "StepperIndicator"; var STEPPER_TITLE_NAME = "StepperTitle"; var STEPPER_DESCRIPTION_NAME = "StepperDescription"; var STEPPER_CONTENT_NAME = "StepperContent"; var STEPPER_PREV_TRIGGER_NAME = "StepperPrevTrigger"; var STEPPER_NEXT_TRIGGER_NAME = "StepperNextTrigger"; var STEPPER_ENTRY_FOCUS = "stepperFocusGroup.onEntryFocus"; var STEPPER_EVENT_OPTIONS = { bubbles: false, cancelable: true }; var STEPPER_ARROW_KEYS = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"]; var STEPPER_MAP_KEY_TO_FOCUS_INTENT = { ArrowLeft: "prev", ArrowUp: "prev", ArrowRight: "next", ArrowDown: "next", PageUp: "first", Home: "first", PageDown: "last", End: "last" }; // src/components/stepper/stepper.context.tsx var StepperContext = React6.createContext(null); function useStepperContext(consumerName) { const context = React6.useContext(StepperContext); if (!context) { throw new Error(`\`${consumerName}\` must be used within \`${STEPPER_ROOT_NAME}\``); } return context; } function createStepperStore(listenersRef, stateRef, onValueChange, onValueComplete, onValueAdd, onValueRemove, onValidate) { const store = { subscribe: (cb) => { if (listenersRef.current) { listenersRef.current.add(cb); return () => listenersRef.current?.delete(cb); } return () => { }; }, getState: () => stateRef.current ?? { steps: /* @__PURE__ */ new Map(), value: void 0 }, setState: (key, value) => { const state = stateRef.current; if (!state || Object.is(state[key], value)) return; if (key === "value" && typeof value === "string") { state.value = value; onValueChange?.(value); } else { state[key] = value; } store.notify(); }, setStateWithValidation: async (value, direction) => { if (!onValidate) { store.setState("value", value); return true; } try { const isValid = await onValidate(value, direction); if (isValid) { store.setState("value", value); } return isValid; } catch { return false; } }, hasValidation: () => !!onValidate, addStep: (newStep) => { const state = stateRef.current; if (state) { state.steps = new Map(state.steps); state.steps.set(newStep.value, newStep); onValueAdd?.(newStep.value); store.notify(); } }, removeStep: (value) => { const state = stateRef.current; if (state) { state.steps = new Map(state.steps); state.steps.delete(value); onValueRemove?.(value); store.notify(); } }, setStep: ({ value, disabled, loading, completed }) => { const state = stateRef.current; if (state) { const step = state.steps.get(value); if (step) { const updatedStep = { ...step, completed, disabled, loading }; state.steps = new Map(state.steps); state.steps.set(value, updatedStep); if (completed !== step.completed) { onValueComplete?.(value, completed); } store.notify(); } } }, notify: () => { if (listenersRef.current) { for (const cb of listenersRef.current) { cb(); } } } }; return store; } var StepperStoreContext = React6.createContext(null); function useStepperStoreContext(consumerName) { const context = React6.useContext(StepperStoreContext); if (!context) { throw new Error(`\`${consumerName}\` must be used within \`${STEPPER_ROOT_NAME}\``); } return context; } function useStepperStore(selector) { const store = useStepperStoreContext("useStore"); const getSnapshot = React6.useCallback(() => selector(store.getState()), [selector, store]); return React6.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot); } function Stepper({ value, defaultValue, onValueChange, onValueComplete, onValueAdd, onValueRemove, onValidate, id: idProp, dir: dirProp, orientation = "horizontal", activationMode = "automatic", asChild, disabled = false, nonInteractive = false, loop = false, className, indicators = {}, ...rootProps }) { const id = React6.useId(); const rootId = idProp ?? id; const listenersRef = useLazyRef(() => /* @__PURE__ */ new Set()); const stateRef = useLazyRef(() => ({ steps: /* @__PURE__ */ new Map(), value: value ?? defaultValue })); const store = React6.useMemo( () => createStepperStore(listenersRef, stateRef, onValueChange, onValueComplete, onValueAdd, onValueRemove, onValidate), [listenersRef, stateRef, onValueChange, onValueComplete, onValueAdd, onValueRemove, onValidate] ); useIsomorphicLayoutEffect(() => { if (value !== void 0) { store.setState("value", value); } }, [value]); const dir = useDirection(dirProp); const contextValue = React6.useMemo( () => ({ id: rootId, dir, orientation, activationMode, disabled, nonInteractive, loop, indicators }), [rootId, dir, orientation, activationMode, disabled, nonInteractive, loop, indicators] ); const RootPrimitive = asChild ? Slot : "div"; return /* @__PURE__ */ jsx(StepperStoreContext.Provider, { value: store, children: /* @__PURE__ */ jsx(StepperContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx( RootPrimitive, { id: rootId, "data-disabled": disabled ? "" : void 0, "data-orientation": orientation, "data-slot": "stepper", dir, ...rootProps, className: cn("flex w-full flex-col gap-4 data-[orientation=vertical]:flex-row", className) } ) }) }); } // src/components/stepper/stepper.types.ts var StepperFocusIntent = { FIRST: "first", LAST: "last", PREV: "prev", NEXT: "next" }; var StepperOrientation = { HORIZONTAL: "horizontal", VERTICAL: "vertical" }; var StepperNavigationDirection = { NEXT: "next", PREV: "prev" }; var StepperActivationMode = { AUTOMATIC: "automatic", MANUAL: "manual" }; var StepperDataState = { INACTIVE: "inactive", ACTIVE: "active", COMPLETED: "completed" }; var StepperFocusContext = React6.createContext(null); function useStepperFocusContext(consumerName) { const context = React6.useContext(StepperFocusContext); if (!context) { throw new Error(`\`${consumerName}\` must be used within \`FocusProvider\``); } return context; } function setRef(ref, value) { if (typeof ref === "function") { return ref(value); } else if (ref !== null && ref !== void 0) { ref.current = value; } } function composeRefs(...refs) { return (node) => { let hasCleanup = false; const cleanups = refs.map((ref) => { const cleanup = setRef(ref, node); if (!hasCleanup && typeof cleanup == "function") { hasCleanup = true; } return cleanup; }); if (hasCleanup) { return () => { for (let i = 0; i < cleanups.length; i++) { const cleanup = cleanups[i]; if (typeof cleanup == "function") { cleanup(); } else { setRef(refs[i], null); } } }; } }; } function useComposedRefs(...refs) { return React6.useCallback(composeRefs(...refs), refs); } // src/components/stepper/stepper.utils.tsx function focusFirst(candidates, preventScroll = false) { const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement; for (const candidateRef of candidates) { const candidate = candidateRef.current; if (!candidate) continue; if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return; candidate.focus({ preventScroll }); if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return; } } function getDataState(value, itemValue, stepState, steps, variant = "item") { const stepKeys = Array.from(steps.keys()); const currentIndex = stepKeys.indexOf(itemValue); if (stepState?.completed) { return StepperDataState.COMPLETED; } if (value === itemValue) { return variant === "separator" ? StepperDataState.INACTIVE : StepperDataState.ACTIVE; } if (value) { const activeIndex = stepKeys.indexOf(value); if (activeIndex > currentIndex) { return StepperDataState.COMPLETED; } } return StepperDataState.INACTIVE; } function getId(id, variant, value) { return `${id}-${variant}-${value}`; } function wrapArray(array, startIndex) { return array.map((_, index) => array[(startIndex + index) % array.length]); } function getDirectionAwareKey(key, dir) { if (dir !== Direction.RTL) return key; return key === "ArrowLeft" ? "ArrowRight" : key === "ArrowRight" ? "ArrowLeft" : key; } function getFocusIntent(event, dir, orientation) { const key = getDirectionAwareKey(event.key, dir); if (orientation === "horizontal" && ["ArrowUp", "ArrowDown"].includes(key)) return void 0; if (orientation === "vertical" && ["ArrowLeft", "ArrowRight"].includes(key)) return void 0; return STEPPER_MAP_KEY_TO_FOCUS_INTENT[key]; } function StepperNav({ className, children, asChild, ref, ...listProps }) { const context = useStepperContext(STEPPER_NAV_NAME); const orientation = context.orientation; const currentValue = useStepperStore((state) => state.value); const [tabStopId, setTabStopId] = React6.useState(null); const [isTabbingBackOut, setIsTabbingBackOut] = React6.useState(false); const [focusableItemCount, setFocusableItemCount] = React6.useState(0); const isClickFocusRef = React6.useRef(false); const itemsRef = React6.useRef(/* @__PURE__ */ new Map()); const listRef = React6.useRef(null); const composedRef = useComposedRefs(ref, listRef); const onItemFocus = React6.useCallback((tabStopId2) => { setTabStopId(tabStopId2); }, []); const onItemShiftTab = React6.useCallback(() => { setIsTabbingBackOut(true); }, []); const onFocusableItemAdd = React6.useCallback(() => { setFocusableItemCount((prevCount) => prevCount + 1); }, []); const onFocusableItemRemove = React6.useCallback(() => { setFocusableItemCount((prevCount) => prevCount - 1); }, []); const onItemRegister = React6.useCallback((item) => { itemsRef.current.set(item.id, item); }, []); const onItemUnregister = React6.useCallback((id) => { itemsRef.current.delete(id); }, []); const getItems = React6.useCallback(() => { return Array.from(itemsRef.current.values()).filter((item) => item.ref.current).sort((a, b) => { const elementA = a.ref.current; const elementB = b.ref.current; if (!elementA || !elementB) return 0; const position = elementA.compareDocumentPosition(elementB); if (position & Node.DOCUMENT_POSITION_FOLLOWING) { return -1; } if (position & Node.DOCUMENT_POSITION_PRECEDING) { return 1; } return 0; }); }, []); const onBlur = React6.useCallback( (event) => { listProps.onBlur?.(event); if (event.defaultPrevented) return; setIsTabbingBackOut(false); }, [listProps.onBlur] ); const onFocus = React6.useCallback( (event) => { listProps.onFocus?.(event); if (event.defaultPrevented) return; const isKeyboardFocus = !isClickFocusRef.current; if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) { const entryFocusEvent = new CustomEvent(STEPPER_ENTRY_FOCUS, STEPPER_EVENT_OPTIONS); event.currentTarget.dispatchEvent(entryFocusEvent); if (!entryFocusEvent.defaultPrevented) { const items = Array.from(itemsRef.current.values()).filter((item) => !item.disabled); const selectedItem = currentValue ? items.find((item) => item.value === currentValue) : void 0; const activeItem = items.find((item) => item.active); const currentItem = items.find((item) => item.id === tabStopId); const candidateItems = [selectedItem, activeItem, currentItem, ...items].filter( Boolean ); const candidateRefs = candidateItems.map((item) => item.ref); focusFirst(candidateRefs, false); } } isClickFocusRef.current = false; }, // eslint-disable-next-line react-hooks/exhaustive-deps [listProps.onFocus, isTabbingBackOut, currentValue, tabStopId] ); const onMouseDown = React6.useCallback( (event) => { listProps.onMouseDown?.(event); if (event.defaultPrevented) return; isClickFocusRef.current = true; }, // eslint-disable-next-line react-hooks/exhaustive-deps [listProps.onMouseDown] ); const focusContextValue = React6.useMemo( () => ({ tabStopId, onItemFocus, onItemShiftTab, onFocusableItemAdd, onFocusableItemRemove, onItemRegister, onItemUnregister, getItems }), [ tabStopId, onItemFocus, onItemShiftTab, onFocusableItemAdd, onFocusableItemRemove, onItemRegister, onItemUnregister, getItems ] ); const StepperNavPrimitive = asChild ? Slot : "nav"; return /* @__PURE__ */ jsx(StepperFocusContext.Provider, { value: focusContextValue, children: /* @__PURE__ */ jsx( StepperNavPrimitive, { role: "tablist", "data-slot": "stepper-nav", "aria-orientation": orientation, "data-orientation": orientation, dir: context.dir, tabIndex: isTabbingBackOut || focusableItemCount === 0 ? -1 : 0, ref: composedRef, onBlur, onFocus, onMouseDown, className: cn( "group/stepper-nav inline-flex gap-4", "data-[orientation=horizontal]:w-full data-[orientation=horizontal]:flex-row data-[orientation=vertical]:flex-col", className ), ...listProps, children } ) }); } var StepperItemContext = React6.createContext(null); function useStepperItemContext(consumerName) { const context = React6.useContext(StepperItemContext); if (!context) { throw new Error(`\`${consumerName}\` must be used within \`${STEPPER_ITEM_NAME}\``); } return context; } function StepperItem({ value: itemValue, completed = false, disabled = false, loading = false, asChild, className, children, ...props }) { const { orientation, dir } = useStepperContext(STEPPER_ITEM_NAME); const store = useStepperStoreContext(STEPPER_ITEM_NAME); const value = useStepperStore((state) => state.value); useIsomorphicLayoutEffect(() => { store.addStep({ value: itemValue, completed, disabled, loading }); return () => { store.removeStep(itemValue); }; }, [itemValue, completed, disabled, loading]); useIsomorphicLayoutEffect(() => { store.setStep({ value: itemValue, completed, disabled, loading }); }, [itemValue, completed, disabled, loading]); const stepState = useStepperStore((state) => state.steps.get(itemValue)); const steps = useStepperStore((state) => state.steps); const dataState = getDataState(value, itemValue, stepState, steps); const itemContextValue = React6.useMemo( () => ({ value: itemValue, stepState }), [itemValue, stepState] ); const StepperItemPrimitive = asChild ? Slot : "div"; return /* @__PURE__ */ jsx(StepperItemContext.Provider, { value: itemContextValue, children: /* @__PURE__ */ jsxs( StepperItemPrimitive, { "data-disabled": stepState?.disabled ? "" : void 0, "data-orientation": orientation, "data-state": dataState, "data-slot": "stepper-item", dir, className: cn( "group/step flex flex-1 flex-col justify-end data-[disabled]:opacity-50", "", // "group-data-[orientation=horizontal]/stepper-nav:flex-row group-data-[orientation=vertical]/stepper-nav:flex-col", className ), ...props, children: [ children, /* @__PURE__ */ jsx( "div", { "data-state": dataState, className: cn( "bg-border h-1 w-full transition-colors duration-500", "data-[state=active]:bg-primary data-[state=completed]:bg-success" ) } ) ] } ) }); } function StepperTrigger({ asChild, disabled, className, ref, ...triggerProps }) { const context = useStepperContext(STEPPER_TRIGGER_NAME); const itemContext = useStepperItemContext(STEPPER_TRIGGER_NAME); const store = useStepperStoreContext(STEPPER_TRIGGER_NAME); const focusContext = useStepperFocusContext(STEPPER_TRIGGER_NAME); const value = useStepperStore((state) => state.value); const itemValue = itemContext.value; const stepState = useStepperStore((state) => state.steps.get(itemValue)); const activationMode = context.activationMode; const orientation = context.orientation; const loop = context.loop; const steps = useStepperStore((state) => state.steps); const stepIndex = Array.from(steps.keys()).indexOf(itemValue); const stepPosition = stepIndex + 1; const stepCount = steps.size; const triggerId = getId(context.id, "trigger", itemValue); const contentId = getId(context.id, "content", itemValue); const titleId = getId(context.id, "title", itemValue); const descriptionId = getId(context.id, "description", itemValue); const isDisabled = context.disabled || stepState?.disabled || disabled; const isActive = value === itemValue; const isTabStop = focusContext.tabStopId === triggerId; const dataState = getDataState(value, itemValue, stepState, steps); const triggerRef = React6.useRef(null); const composedRef = useComposedRefs(ref, triggerRef); const isArrowKeyPressedRef = React6.useRef(false); const isMouseClickRef = React6.useRef(false); React6.useEffect(() => { function onKeyDown2(event) { if (STEPPER_ARROW_KEYS.includes(event.key)) { isArrowKeyPressedRef.current = true; } } function onKeyUp() { isArrowKeyPressedRef.current = false; } document.addEventListener("keydown", onKeyDown2); document.addEventListener("keyup", onKeyUp); return () => { document.removeEventListener("keydown", onKeyDown2); document.removeEventListener("keyup", onKeyUp); }; }, []); useIsomorphicLayoutEffect(() => { focusContext.onItemRegister({ id: triggerId, ref: triggerRef, value: itemValue, active: isTabStop, disabled: !!isDisabled }); if (!isDisabled) { focusContext.onFocusableItemAdd(); } return () => { focusContext.onItemUnregister(triggerId); if (!isDisabled) { focusContext.onFocusableItemRemove(); } }; }, [focusContext, triggerId, itemValue, isTabStop, isDisabled]); const onClick = React6.useCallback( async (event) => { triggerProps.onClick?.(event); if (event.defaultPrevented) return; if (!isDisabled && !context.nonInteractive) { const currentStepIndex = Array.from(steps.keys()).indexOf(value ?? ""); const targetStepIndex = Array.from(steps.keys()).indexOf(itemValue); const direction = targetStepIndex > currentStepIndex ? "next" : "prev"; await store.setStateWithValidation(itemValue, direction); } }, // eslint-disable-next-line react-hooks/exhaustive-deps [isDisabled, context.nonInteractive, store, itemValue, value, steps, triggerProps.onClick] ); const onFocus = React6.useCallback( async (event) => { triggerProps.onFocus?.(event); if (event.defaultPrevented) return; focusContext.onItemFocus(triggerId); const isKeyboardFocus = !isMouseClickRef.current; if (!isActive && !isDisabled && activationMode !== "manual" && !context.nonInteractive && isKeyboardFocus) { const currentStepIndex = Array.from(steps.keys()).indexOf(value || ""); const targetStepIndex = Array.from(steps.keys()).indexOf(itemValue); const direction = targetStepIndex > currentStepIndex ? "next" : "prev"; await store.setStateWithValidation(itemValue, direction); } isMouseClickRef.current = false; }, [ triggerProps, focusContext, triggerId, isActive, isDisabled, activationMode, context.nonInteractive, steps, value, itemValue, store ] ); const onKeyDown = React6.useCallback( async (event) => { triggerProps.onKeyDown?.(event); if (event.defaultPrevented) return; if (event.key === "Enter" && context.nonInteractive) { event.preventDefault(); return; } if ((event.key === "Enter" || event.key === " ") && activationMode === "manual" && !context.nonInteractive) { event.preventDefault(); if (!isDisabled && triggerRef.current) { triggerRef.current.click(); } return; } if (event.key === "Tab" && event.shiftKey) { focusContext.onItemShiftTab(); return; } if (event.target !== event.currentTarget) return; const focusIntent = getFocusIntent(event, context.dir, orientation); if (focusIntent !== void 0) { if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return; event.preventDefault(); const items = focusContext.getItems().filter((item) => !item.disabled); let candidateRefs = items.map((item) => item.ref); if (focusIntent === "last") { candidateRefs.reverse(); } else if (focusIntent === "prev" || focusIntent === "next") { if (focusIntent === "prev") candidateRefs.reverse(); const currentIndex = candidateRefs.findIndex((ref2) => ref2.current === event.currentTarget); candidateRefs = loop ? wrapArray(candidateRefs, currentIndex + 1) : candidateRefs.slice(currentIndex + 1); } if (store.hasValidation() && candidateRefs.length > 0) { const nextRef = candidateRefs[0]; const nextElement = nextRef?.current; const nextItem = items.find((item) => item.ref.current === nextElement); if (nextItem && nextItem.value !== itemValue) { const currentStepIndex = Array.from(steps.keys()).indexOf(value || ""); const targetStepIndex = Array.from(steps.keys()).indexOf(nextItem.value); const direction = targetStepIndex > currentStepIndex ? "next" : "prev"; if (direction === "next") { const isValid = await store.setStateWithValidation(nextItem.value, direction); if (!isValid) return; } else { store.setState("value", nextItem.value); } queueMicrotask(() => nextElement?.focus()); return; } } queueMicrotask(() => focusFirst(candidateRefs)); } }, [ focusContext, context.nonInteractive, context.dir, activationMode, orientation, loop, isDisabled, triggerProps.onKeyDown, store, itemValue, value, steps ] ); const onMouseDown = React6.useCallback( (event) => { triggerProps.onMouseDown?.(event); if (event.defaultPrevented) { return; } isMouseClickRef.current = true; if (isDisabled) { event.preventDefault(); } else { focusContext.onItemFocus(triggerId); } }, // eslint-disable-next-line react-hooks/exhaustive-deps [focusContext, triggerId, isDisabled, triggerProps.onMouseDown] ); const TriggerPrimitive = asChild ? Slot : "button"; return /* @__PURE__ */ jsx( TriggerPrimitive, { id: triggerId, role: "tab", type: "button", disabled: isDisabled, tabIndex: isTabStop ? 0 : -1, ref: composedRef, "data-slot": "stepper-trigger", "data-disabled": isDisabled ? "" : void 0, "data-state": dataState, "aria-controls": contentId, "aria-current": isActive ? "step" : void 0, "aria-describedby": `${titleId} ${descriptionId}`, "aria-posinset": stepPosition, "aria-selected": isActive, "aria-setsize": stepCount, className: cn( "inline-flex w-full cursor-pointer items-center gap-3 rounded text-left transition-colors duration-500 outline-none", "disabled:pointer-events-none", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", className ), onClick, onFocus, onKeyDown, onMouseDown, ...triggerProps } ); } function StepperIndicator({ className, children, asChild, ref, ...props }) { const { indicators, dir } = useStepperContext(STEPPER_INDICATOR_NAME); const itemContext = useStepperItemContext(STEPPER_INDICATOR_NAME); const value = useStepperStore((state) => state.value); const itemValue = itemContext.value; const stepState = useStepperStore((state) => state.steps.get(itemValue)); const steps = useStepperStore((state) => state.steps); const stepPosition = Array.from(steps.keys()).indexOf(itemValue) + 1; const dataState = getDataState(value, itemValue, stepState, steps); const StepperIndicatorPrimitive = asChild ? Slot : "div"; return /* @__PURE__ */ jsx( StepperIndicatorPrimitive, { "data-slot": "stepper-indicator", "data-state": dataState, dir, ref, className: cn( "bg-muted border-border text-muted-foreground relative flex size-6 shrink-0 items-center justify-center self-end overflow-hidden rounded-t border-x border-t text-xs transition-colors duration-500", "data-[state=completed]:bg-success/50 data-[state=completed]:border-success data-[state=completed]:text-success-foreground", "data-[state=active]:bg-primary/50 data-[state=active]:border-primary data-[state=active]:text-primary-foreground", className ), ...props, children: /* @__PURE__ */ jsx("div", { className: "absolute", children: typeof children === "function" ? children(dataState) : indicators && (itemContext.stepState?.loading && indicators.loading || dataState === "completed" && indicators.completed || dataState === "active" && indicators.active || dataState === "inactive" && indicators.inactive) ? itemContext.stepState?.loading && indicators.loading || dataState === "completed" && indicators.completed || dataState === "active" && indicators.active || dataState === "inactive" && indicators.inactive : children ? children : stepPosition }) } ); } function StepperTitle({ className, asChild = false, ...props }) { const context = useStepperContext(STEPPER_TITLE_NAME); const itemContext = useStepperItemContext(STEPPER_TITLE_NAME); const titleId = getId(context.id, "title", itemContext.value); const TitlePrimitive = asChild ? Slot : "h3"; return /* @__PURE__ */ jsx( TitlePrimitive, { id: titleId, "data-slot": "stepper-title", dir: context.dir, className: cn("text-sm leading-none font-medium", className), ...props } ); } function StepperDescription({ className, asChild, ...props }) { const context = useStepperContext(STEPPER_DESCRIPTION_NAME); const itemContext = useStepperItemContext(STEPPER_DESCRIPTION_NAME); const descriptionId = getId(context.id, "description", itemContext.value); const StepperDescriptionPrimitive = asChild ? Slot : "span"; return /* @__PURE__ */ jsx( StepperDescriptionPrimitive, { id: descriptionId, "data-slot": "stepper-description", dir: context.dir, className: cn("text-muted-foreground text-sm", className), ...props } ); } function StepperPanel({ children, asChild = false, className, ...props }) { const currentValue = useStepperStore((state) => state.value); const StepperPanelPrimitive = asChild ? Slot : "div"; return /* @__PURE__ */ jsx( StepperPanelPrimitive, { "data-slot": "stepper-panel", "data-state": currentValue, className: cn("w-full", className), ...props, children } ); } function StepperContent({ value: valueProp, asChild = false, forceMount = false, className, ...props }) { const context = useStepperContext(STEPPER_CONTENT_NAME); const value = useStepperStore((state) => state.value); const contentId = getId(context.id, "content", valueProp); const triggerId = getId(context.id, "trigger", valueProp); const isActive = value === valueProp; if (!isActive && !forceMount) { return null; } const ContentPrimitive = asChild ? Slot : "div"; return /* @__PURE__ */ jsx( ContentPrimitive, { id: contentId, role: "tabpanel", "data-state": value, "aria-labelledby": triggerId, "data-slot": "stepper-content", dir: context.dir, className: cn("w-full", className, !isActive && forceMount && "hidden"), hidden: !isActive && forceMount, ...props } ); } function StepperNextTrigger({ asChild = false, onClick, disabled, ...props }) { const store = useStepperStoreContext(STEPPER_NEXT_TRIGGER_NAME); const value = useStepperStore((state) => state.value); const steps = useStepperStore((state) => state.steps); const stepKeys = Array.from(steps.keys()); const currentIndex = value ? stepKeys.indexOf(value) : -1; const isDisabled = disabled || currentIndex >= stepKeys.length - 1; const handleClick = React6.useCallback( async (event) => { onClick?.(event); if (event.defaultPrevented || isDisabled) { return; } const nextIndex = Math.min(currentIndex + 1, stepKeys.length - 1); const nextStepValue = stepKeys[nextIndex]; if (nextStepValue) { await store.setStateWithValidation(nextStepValue, "next"); } }, [onClick, isDisabled, currentIndex, stepKeys, store] ); const StepperNextTriggerPrimitive = asChild ? Slot : "button"; return /* @__PURE__ */ jsx( StepperNextTriggerPrimitive, { type: "button", "data-slot": "stepper-next-trigger", disabled: isDisabled, onClick: handleClick, ...props } ); } function StepperPrevTrigger({ asChild = false, disabled, onClick, ...props }) { const store = useStepperStoreContext(STEPPER_PREV_TRIGGER_NAME); const value = useStepperStore((state) => state.value); const steps = useStepperStore((state) => state.steps); const stepKeys = Array.from(steps.keys()); const currentIndex = value ? stepKeys.indexOf(value) : -1; const isDisabled = disabled || currentIndex <= 0; const handleClick = React6.useCallback( async (event) => { onClick?.(event); if (event.defaultPrevented || isDisabled) { return; } const prevIndex = Math.max(currentIndex - 1, 0); const prevStepValue = stepKeys[prevIndex]; if (prevStepValue) { store.setState("value", prevStepValue); } }, [currentIndex, isDisabled, onClick, stepKeys, store] ); const StepperPrevTriggerPrimitive = asChild ? Slot : "button"; return /* @__PURE__ */ jsx( StepperPrevTriggerPrimitive, { type: "button", "data-slot": "stepper-prev-trigger", disabled: isDisabled, onClick: handleClick, ...props } ); } var Toaster = ({ theme = "system", ...props }) => { return /* @__PURE__ */ jsx( Toaster$1, { className: "group", icons: { success: /* @__PURE__ */ jsx(CircleCheckIcon, { className: "size-4" }), info: /* @__PURE__ */ jsx(InfoIcon, { className: "size-4" }), warning: /* @__PURE__ */ jsx(TriangleAlertIcon, { className: "size-4" }), error: /* @__PURE__ */ jsx(OctagonXIcon, { className: "size-4" }), loading: /* @__PURE__ */ jsx(Spinner, {}) }, toastOptions: { classNames: { toast: "!bg-popover !text-popover-foreground !border !border-border !rounded", description: "!text-muted-foreground" } }, theme, ...props } ); }; var buttonVariants = cva( [ "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded text-sm font-medium outline-none transition-all", "disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50", "[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0", "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring", "aria-invalid:ring-error/20 dark:aria-invalid:ring-error/40 aria-invalid:border-error" ], { variants: { variant: { default: "bg-primary text-primary-foreground hover:bg-primary/90", error: "bg-error hover:bg-error/90 focus-visible:ring-error/20 dark:focus-visible:ring-error/40 dark:bg-error/60 text-white", outline: "bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", link: "text-primary underline-offset-4 hover:underline" }, size: { default: "h-9 px-4 py-2 has-[>svg]:px-3", sm: "h-8 gap-1.5 rounded px-3 has-[>svg]:px-2.5", lg: "h-10 rounded px-6 has-[>svg]:px-4", icon: "size-9", "icon-sm": "size-8", "icon-lg": "size-10" }, fullWidth: { true: "w-full" } }, defaultVariants: { variant: "default", size: "default", fullWidth: false } } ); function Button({ asChild = false, variant = "default", disabled = false, fullWidth = false, loadingPosition: loadingPositionProp = "start", children, type = "button", loading = false, size = "default", endIcon, startIcon, className, ...props }) { const Comp = asChild ? Slot : "button"; const isDisabled = disabled || loading; const loadingPosition = size?.startsWith("icon") ? "center" : loadingPositionProp; return /* @__PURE__ */ jsx( Comp, { "data-slot": "button", "aria-disabled": isDisabled || void 0, className: cn(buttonVariants({ fullWidth, size, variant }), className), "data-state": loading ? "loading" : void 0, disabled: isDisabled, role: Comp !== "button" ? "button" : void 0, tabIndex: isDisabled ? -1 : 0, type: Comp === "button" ? type : void 0, "data-size": size, "data-variant": variant, ...props, children: /* @__PURE__ */ jsx( ButtonContent, { loading, size, loadingPosition, startIcon, endIcon, asChild, children } ) } ); } function ButtonContent({ children, loading = false, loadingPosition = "start", startIcon, asChild, endIcon, ...props }) { const isStartLoading = loading && loadingPosition === "start"; const StartIcon = isStartLoading ? /* @__PURE__ */ jsx(Spinner, { "aria-label": "Loading" }) : startIcon || null; const isEndLoading = loading && loadingPosition === "end"; const EndIcon = isEndLoading ? /* @__PURE__ */ jsx(Spinner, { "aria-label": "Loading" }) : endIcon || null; const isCenterLoading = loading && loadingPosition === "center"; return asChild && React6.isValidElement(children) ? React6.cloneElement( children, props, /* @__PURE__ */ jsxs(React6.Fragment, { children: [ StartIcon, isCenterLoading ? /* @__PURE__ */ jsx(Spinner, { "aria-label": "Loading" }) : React6.isValidElement(children) ? children.props?.children : null, EndIcon ] }) ) : /* @__PURE__ */ jsxs(React6.Fragment, { children: [ StartIcon, isCenterLoading ? /* @__PURE__ */ jsx(Spinner, { "aria-label": "Loading" }) : children, EndIcon ] }); } export { Button, Stepper, StepperActivationMode, StepperContent, StepperDataState, StepperDescription, StepperFocusIntent, StepperIndicator, StepperItem, StepperNav, StepperNavigationDirection, StepperNextTrigger, StepperOrientation, StepperPanel, StepperPrevTrigger, StepperTitle, StepperTrigger, Toaster, useStepperContext, useStepperFocusContext, useStepperItemContext };