@szum-tech/design-system
Version:
Szum-Tech design system with tailwindcss support
1,053 lines (1,047 loc) • 37 kB
JavaScript
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 };