@djangocfg/ui-core 2.1.411 → 2.1.413

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/package.json +4 -4
  2. package/src/components/data/avatar-group/index.tsx +224 -0
  3. package/src/components/data/badge-overflow/index.tsx +259 -0
  4. package/src/components/data/circular-progress/index.tsx +358 -0
  5. package/src/components/data/relative-time-card/index.tsx +191 -0
  6. package/src/components/data/stat/index.tsx +140 -0
  7. package/src/components/data/status/index.tsx +80 -0
  8. package/src/components/effects/GlowBackground.tsx +9 -1
  9. package/src/components/effects/swap/index.tsx +289 -0
  10. package/src/components/feedback/banner/index.tsx +693 -0
  11. package/src/components/forms/checkbox-group/index.tsx +243 -0
  12. package/src/components/forms/editable/index.tsx +420 -0
  13. package/src/components/forms/input-otp/index.tsx +12 -3
  14. package/src/components/forms/mask-input/index.tsx +466 -0
  15. package/src/components/forms/otp/index.tsx +12 -8
  16. package/src/components/forms/segmented-input/index.tsx +319 -0
  17. package/src/components/forms/tags-input/index.tsx +896 -0
  18. package/src/components/forms/time-picker/index.tsx +285 -0
  19. package/src/components/index.ts +51 -0
  20. package/src/components/layout/key-value/index.tsx +884 -0
  21. package/src/components/layout/stack/index.tsx +349 -0
  22. package/src/components/navigation/context-menu/index.tsx +9 -6
  23. package/src/components/navigation/stepper/index.tsx +1307 -0
  24. package/src/components/select/multi-select-pro-async.tsx +11 -2
  25. package/src/components/select/multi-select-pro.tsx +11 -2
  26. package/src/components/select/select.tsx +13 -3
  27. package/src/components/specialized/presence/index.tsx +181 -0
  28. package/src/components/specialized/primitive/index.tsx +83 -0
  29. package/src/components/specialized/visually-hidden/index.tsx +19 -0
  30. package/src/components/specialized/visually-hidden-input/index.tsx +99 -0
  31. package/src/hooks/dom/index.ts +4 -0
  32. package/src/hooks/dom/useFormReset.ts +49 -0
  33. package/src/hooks/dom/useLayoutEffect.ts +16 -0
  34. package/src/hooks/dom/useSize.ts +57 -0
  35. package/src/hooks/state/index.ts +4 -0
  36. package/src/hooks/state/useCallbackRef.ts +25 -0
  37. package/src/hooks/state/usePrevious.ts +20 -0
  38. package/src/hooks/state/useStateMachine.ts +29 -0
  39. package/src/lib/compose-event-handlers.ts +22 -0
  40. package/src/lib/compose-refs.ts +65 -0
  41. package/src/lib/create-context.tsx +62 -0
  42. package/src/lib/get-element-ref.ts +33 -0
  43. package/src/lib/index.ts +5 -0
  44. package/src/lib/styles.ts +103 -0
  45. package/src/styles/README.md +43 -0
  46. package/src/styles/palette/utils.ts +15 -5
  47. package/src/styles/utilities/animations.css +135 -0
  48. package/src/styles/utilities/display.css +62 -0
  49. package/src/styles/utilities/glass.css +57 -0
  50. package/src/styles/utilities/marquee.css +69 -0
  51. package/src/styles/utilities/step.css +25 -0
  52. package/src/styles/utilities.css +6 -259
@@ -0,0 +1,1307 @@
1
+ "use client"
2
+
3
+ import { Check } from "lucide-react"
4
+ import * as React from "react"
5
+ import { Slot } from "@radix-ui/react-slot"
6
+
7
+ import { cn } from "../../../lib/utils"
8
+
9
+ const ROOT_NAME = "Stepper"
10
+ const LIST_NAME = "StepperList"
11
+ const ITEM_NAME = "StepperItem"
12
+ const TRIGGER_NAME = "StepperTrigger"
13
+ const INDICATOR_NAME = "StepperIndicator"
14
+ const SEPARATOR_NAME = "StepperSeparator"
15
+ const TITLE_NAME = "StepperTitle"
16
+ const DESCRIPTION_NAME = "StepperDescription"
17
+ const CONTENT_NAME = "StepperContent"
18
+ const PREV_NAME = "StepperPrev"
19
+ const NEXT_NAME = "StepperNext"
20
+
21
+ const ENTRY_FOCUS = "stepperFocusGroup.onEntryFocus"
22
+ const EVENT_OPTIONS = { bubbles: false, cancelable: true }
23
+ const ARROW_KEYS = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"]
24
+
25
+ type Direction = "ltr" | "rtl"
26
+ type Orientation = "horizontal" | "vertical"
27
+ type NavigationDirection = "next" | "prev"
28
+ type ActivationMode = "automatic" | "manual"
29
+ type DataState = "inactive" | "active" | "completed"
30
+
31
+ interface DivProps extends React.ComponentProps<"div"> {
32
+ asChild?: boolean
33
+ }
34
+ interface ButtonProps extends React.ComponentProps<"button"> {
35
+ asChild?: boolean
36
+ }
37
+
38
+ type ListElement = HTMLDivElement
39
+ type TriggerElement = HTMLButtonElement
40
+
41
+ function getId(
42
+ id: string,
43
+ variant: "trigger" | "content" | "title" | "description",
44
+ value: string,
45
+ ) {
46
+ return `${id}-${variant}-${value}`
47
+ }
48
+
49
+ type FocusIntent = "first" | "last" | "prev" | "next"
50
+
51
+ const MAP_KEY_TO_FOCUS_INTENT: Record<string, FocusIntent> = {
52
+ ArrowLeft: "prev",
53
+ ArrowUp: "prev",
54
+ ArrowRight: "next",
55
+ ArrowDown: "next",
56
+ PageUp: "first",
57
+ Home: "first",
58
+ PageDown: "last",
59
+ End: "last",
60
+ }
61
+
62
+ function getDirectionAwareKey(key: string, dir?: Direction) {
63
+ if (dir !== "rtl") return key
64
+ return key === "ArrowLeft"
65
+ ? "ArrowRight"
66
+ : key === "ArrowRight"
67
+ ? "ArrowLeft"
68
+ : key
69
+ }
70
+
71
+ function getFocusIntent(
72
+ event: React.KeyboardEvent<TriggerElement>,
73
+ dir?: Direction,
74
+ orientation?: Orientation,
75
+ ) {
76
+ const key = getDirectionAwareKey(event.key, dir)
77
+ if (orientation === "horizontal" && ["ArrowUp", "ArrowDown"].includes(key))
78
+ return undefined
79
+ if (orientation === "vertical" && ["ArrowLeft", "ArrowRight"].includes(key))
80
+ return undefined
81
+ return MAP_KEY_TO_FOCUS_INTENT[key]
82
+ }
83
+
84
+ function focusFirst(
85
+ candidates: React.RefObject<TriggerElement | null>[],
86
+ preventScroll = false,
87
+ ) {
88
+ const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement
89
+ for (const candidateRef of candidates) {
90
+ const candidate = candidateRef.current
91
+ if (!candidate) continue
92
+ if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return
93
+ candidate.focus({ preventScroll })
94
+ if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return
95
+ }
96
+ }
97
+
98
+ function wrapArray<T>(array: T[], startIndex: number) {
99
+ return array.map<T>(
100
+ (_, index) => array[(startIndex + index) % array.length] as T,
101
+ )
102
+ }
103
+
104
+ function getDataState(
105
+ value: string | undefined,
106
+ itemValue: string,
107
+ stepState: StepState | undefined,
108
+ steps: Map<string, StepState>,
109
+ variant: "item" | "separator" = "item",
110
+ ): DataState {
111
+ const stepKeys = Array.from(steps.keys())
112
+ const currentIndex = stepKeys.indexOf(itemValue)
113
+
114
+ if (stepState?.completed) return "completed"
115
+
116
+ if (value === itemValue) {
117
+ return variant === "separator" ? "inactive" : "active"
118
+ }
119
+
120
+ if (value) {
121
+ const activeIndex = stepKeys.indexOf(value)
122
+
123
+ if (activeIndex > currentIndex) return "completed"
124
+ }
125
+
126
+ return "inactive"
127
+ }
128
+
129
+ interface StepState {
130
+ value: string
131
+ completed: boolean
132
+ disabled: boolean
133
+ }
134
+
135
+ interface StoreState {
136
+ steps: Map<string, StepState>
137
+ value: string
138
+ }
139
+
140
+ interface Store {
141
+ subscribe: (callback: () => void) => () => void
142
+ getState: () => StoreState
143
+ setState: <K extends keyof StoreState>(key: K, value: StoreState[K]) => void
144
+ setStateWithValidation: (
145
+ value: string,
146
+ direction: NavigationDirection,
147
+ ) => Promise<boolean>
148
+ hasValidation: () => boolean
149
+ notify: () => void
150
+ addStep: (value: string, completed: boolean, disabled: boolean) => void
151
+ removeStep: (value: string) => void
152
+ setStep: (value: string, completed: boolean, disabled: boolean) => void
153
+ }
154
+
155
+ const StoreContext = React.createContext<Store | null>(null)
156
+
157
+ function useStoreContext(consumerName: string) {
158
+ const context = React.useContext(StoreContext)
159
+ if (!context) {
160
+ throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``)
161
+ }
162
+ return context
163
+ }
164
+
165
+ function useStore<T>(selector: (state: StoreState) => T): T {
166
+ const store = useStoreContext("useStore")
167
+
168
+ const getSnapshot = React.useCallback(
169
+ () => selector(store.getState()),
170
+ [store, selector],
171
+ )
172
+
173
+ return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot)
174
+ }
175
+
176
+ interface ItemData {
177
+ id: string
178
+ ref: React.RefObject<TriggerElement | null>
179
+ value: string
180
+ active: boolean
181
+ disabled: boolean
182
+ }
183
+
184
+ interface StepperContextValue {
185
+ rootId: string
186
+ dir: Direction
187
+ orientation: Orientation
188
+ activationMode: ActivationMode
189
+ disabled: boolean
190
+ nonInteractive: boolean
191
+ loop: boolean
192
+ }
193
+
194
+ const StepperContext = React.createContext<StepperContextValue | null>(null)
195
+
196
+ function useStepperContext(consumerName: string) {
197
+ const context = React.useContext(StepperContext)
198
+ if (!context) {
199
+ throw new Error(`\`${consumerName}\` must be used within \`${ROOT_NAME}\``)
200
+ }
201
+ return context
202
+ }
203
+
204
+ interface StepperProps extends DivProps {
205
+ value?: string
206
+ defaultValue?: string
207
+ onValueChange?: (value: string) => void
208
+ onValueComplete?: (value: string, completed: boolean) => void
209
+ onValueAdd?: (value: string) => void
210
+ onValueRemove?: (value: string) => void
211
+ onValidate?: (
212
+ value: string,
213
+ direction: NavigationDirection,
214
+ ) => boolean | Promise<boolean>
215
+ activationMode?: ActivationMode
216
+ dir?: Direction
217
+ orientation?: Orientation
218
+ disabled?: boolean
219
+ loop?: boolean
220
+ nonInteractive?: boolean
221
+ }
222
+
223
+ function Stepper(props: StepperProps) {
224
+ const {
225
+ value,
226
+ defaultValue,
227
+ onValueChange,
228
+ onValueComplete,
229
+ onValueAdd,
230
+ onValueRemove,
231
+ onValidate,
232
+ dir: dirProp,
233
+ orientation = "horizontal",
234
+ activationMode = "automatic",
235
+ asChild,
236
+ disabled = false,
237
+ nonInteractive = false,
238
+ loop = false,
239
+ className,
240
+ id,
241
+ ...rootProps
242
+ } = props
243
+
244
+ const listenersRef = React.useRef<Set<() => void>>(new Set())
245
+ const stateRef = React.useRef<StoreState>({
246
+ steps: new Map(),
247
+ value: value ?? defaultValue ?? "",
248
+ })
249
+
250
+ const propsRef = React.useRef({
251
+ onValueChange,
252
+ onValueComplete,
253
+ onValueAdd,
254
+ onValueRemove,
255
+ onValidate,
256
+ })
257
+
258
+ React.useEffect(() => {
259
+ propsRef.current = {
260
+ onValueChange,
261
+ onValueComplete,
262
+ onValueAdd,
263
+ onValueRemove,
264
+ onValidate,
265
+ }
266
+ }, [onValueChange, onValueComplete, onValueAdd, onValueRemove, onValidate])
267
+
268
+ const store = React.useMemo<Store>(() => {
269
+ return {
270
+ subscribe: (cb) => {
271
+ listenersRef.current.add(cb)
272
+ return () => listenersRef.current.delete(cb)
273
+ },
274
+ getState: () => stateRef.current,
275
+ setState: (key, value) => {
276
+ if (Object.is(stateRef.current[key], value)) return
277
+
278
+ if (key === "value" && typeof value === "string") {
279
+ stateRef.current.value = value
280
+ propsRef.current.onValueChange?.(value)
281
+ } else {
282
+ stateRef.current[key] = value
283
+ }
284
+
285
+ store.notify()
286
+ },
287
+ setStateWithValidation: async (value, direction) => {
288
+ if (!propsRef.current.onValidate) {
289
+ store.setState("value", value)
290
+ return true
291
+ }
292
+
293
+ try {
294
+ const isValid = await propsRef.current.onValidate(value, direction)
295
+ if (isValid) {
296
+ store.setState("value", value)
297
+ }
298
+ return isValid
299
+ } catch {
300
+ return false
301
+ }
302
+ },
303
+ hasValidation: () => !!propsRef.current.onValidate,
304
+ addStep: (value, completed, disabled) => {
305
+ const newStep: StepState = { value, completed, disabled }
306
+ stateRef.current.steps.set(value, newStep)
307
+ propsRef.current.onValueAdd?.(value)
308
+ store.notify()
309
+ },
310
+ removeStep: (value) => {
311
+ stateRef.current.steps.delete(value)
312
+ propsRef.current.onValueRemove?.(value)
313
+ store.notify()
314
+ },
315
+ setStep: (value, completed, disabled) => {
316
+ const step = stateRef.current.steps.get(value)
317
+ if (step) {
318
+ const updatedStep: StepState = { ...step, completed, disabled }
319
+ stateRef.current.steps.set(value, updatedStep)
320
+
321
+ if (completed !== step.completed) {
322
+ propsRef.current.onValueComplete?.(value, completed)
323
+ }
324
+
325
+ store.notify()
326
+ }
327
+ },
328
+ notify: () => {
329
+ for (const cb of listenersRef.current) {
330
+ cb()
331
+ }
332
+ },
333
+ }
334
+ }, [])
335
+
336
+ React.useEffect(() => {
337
+ if (value !== undefined) {
338
+ store.setState("value", value)
339
+ }
340
+ }, [value, store])
341
+
342
+ const dir: Direction = dirProp ?? "ltr"
343
+
344
+ const instanceId = React.useId()
345
+ const rootId = id ?? instanceId
346
+
347
+ const contextValue = React.useMemo<StepperContextValue>(
348
+ () => ({
349
+ rootId,
350
+ dir,
351
+ orientation,
352
+ activationMode,
353
+ disabled,
354
+ nonInteractive,
355
+ loop,
356
+ }),
357
+ [rootId, dir, orientation, activationMode, disabled, nonInteractive, loop],
358
+ )
359
+
360
+ const RootPrimitive = asChild ? Slot : "div"
361
+
362
+ return (
363
+ <StoreContext.Provider value={store}>
364
+ <StepperContext.Provider value={contextValue}>
365
+ <RootPrimitive
366
+ id={rootId}
367
+ data-disabled={disabled ? "" : undefined}
368
+ data-orientation={orientation}
369
+ data-slot="stepper"
370
+ dir={dir}
371
+ {...rootProps}
372
+ className={cn(
373
+ "flex gap-6",
374
+ orientation === "horizontal" ? "w-full flex-col" : "flex-row",
375
+ className,
376
+ )}
377
+ />
378
+ </StepperContext.Provider>
379
+ </StoreContext.Provider>
380
+ )
381
+ }
382
+
383
+ interface FocusContextValue {
384
+ tabStopId: string | null
385
+ onItemFocus: (tabStopId: string) => void
386
+ onItemShiftTab: () => void
387
+ onFocusableItemAdd: () => void
388
+ onFocusableItemRemove: () => void
389
+ onItemRegister: (item: ItemData) => void
390
+ onItemUnregister: (id: string) => void
391
+ getItems: () => ItemData[]
392
+ }
393
+
394
+ const FocusContext = React.createContext<FocusContextValue | null>(null)
395
+
396
+ function useFocusContext(consumerName: string) {
397
+ const context = React.useContext(FocusContext)
398
+ if (!context) {
399
+ throw new Error(
400
+ `\`${consumerName}\` must be used within \`FocusProvider\``,
401
+ )
402
+ }
403
+ return context
404
+ }
405
+
406
+ function StepperList(props: DivProps) {
407
+ const {
408
+ asChild,
409
+ onBlur: onBlurProp,
410
+ onFocus: onFocusProp,
411
+ onMouseDown: onMouseDownProp,
412
+ className,
413
+ children,
414
+ ref,
415
+ ...listProps
416
+ } = props
417
+
418
+ const context = useStepperContext(LIST_NAME)
419
+ const orientation = context.orientation
420
+ const currentValue = useStore((state) => state.value)
421
+
422
+ const propsRef = React.useRef({
423
+ onBlur: onBlurProp,
424
+ onFocus: onFocusProp,
425
+ onMouseDown: onMouseDownProp,
426
+ })
427
+
428
+ React.useEffect(() => {
429
+ propsRef.current = { onBlur: onBlurProp, onFocus: onFocusProp, onMouseDown: onMouseDownProp }
430
+ }, [onBlurProp, onFocusProp, onMouseDownProp])
431
+
432
+ const [tabStopId, setTabStopId] = React.useState<string | null>(null)
433
+ const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false)
434
+ const [focusableItemCount, setFocusableItemCount] = React.useState(0)
435
+ const isClickFocusRef = React.useRef(false)
436
+ const itemsRef = React.useRef<Map<string, ItemData>>(new Map())
437
+ const listRef = React.useRef<ListElement>(null)
438
+
439
+ const composedRef = React.useCallback(
440
+ (node: ListElement | null) => {
441
+ listRef.current = node
442
+ if (typeof ref === "function") {
443
+ ref(node)
444
+ } else if (ref) {
445
+ ref.current = node
446
+ }
447
+ },
448
+ [ref],
449
+ )
450
+
451
+ const onItemFocus = React.useCallback((tabStopId: string) => {
452
+ setTabStopId(tabStopId)
453
+ }, [])
454
+
455
+ const onItemShiftTab = React.useCallback(() => {
456
+ setIsTabbingBackOut(true)
457
+ }, [])
458
+
459
+ const onFocusableItemAdd = React.useCallback(() => {
460
+ setFocusableItemCount((prevCount) => prevCount + 1)
461
+ }, [])
462
+
463
+ const onFocusableItemRemove = React.useCallback(() => {
464
+ setFocusableItemCount((prevCount) => prevCount - 1)
465
+ }, [])
466
+
467
+ const onItemRegister = React.useCallback((item: ItemData) => {
468
+ itemsRef.current.set(item.id, item)
469
+ }, [])
470
+
471
+ const onItemUnregister = React.useCallback((id: string) => {
472
+ itemsRef.current.delete(id)
473
+ }, [])
474
+
475
+ const getItems = React.useCallback(() => {
476
+ return Array.from(itemsRef.current.values())
477
+ .filter((item) => item.ref.current)
478
+ .sort((a, b) => {
479
+ const elementA = a.ref.current
480
+ const elementB = b.ref.current
481
+ if (!elementA || !elementB) return 0
482
+ const position = elementA.compareDocumentPosition(elementB)
483
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
484
+ return -1
485
+ }
486
+ if (position & Node.DOCUMENT_POSITION_PRECEDING) {
487
+ return 1
488
+ }
489
+ return 0
490
+ })
491
+ }, [])
492
+
493
+ const onBlur = React.useCallback(
494
+ (event: React.FocusEvent<ListElement>) => {
495
+ propsRef.current.onBlur?.(event)
496
+ if (event.defaultPrevented) return
497
+
498
+ setIsTabbingBackOut(false)
499
+ },
500
+ [],
501
+ )
502
+
503
+ const onFocus = React.useCallback(
504
+ (event: React.FocusEvent<ListElement>) => {
505
+ propsRef.current.onFocus?.(event)
506
+ if (event.defaultPrevented) return
507
+
508
+ const isKeyboardFocus = !isClickFocusRef.current
509
+ if (
510
+ event.target === event.currentTarget &&
511
+ isKeyboardFocus &&
512
+ !isTabbingBackOut
513
+ ) {
514
+ const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS)
515
+ event.currentTarget.dispatchEvent(entryFocusEvent)
516
+
517
+ if (!entryFocusEvent.defaultPrevented) {
518
+ const items = Array.from(itemsRef.current.values()).filter(
519
+ (item) => !item.disabled,
520
+ )
521
+ const selectedItem = currentValue
522
+ ? items.find((item) => item.value === currentValue)
523
+ : undefined
524
+ const activeItem = items.find((item) => item.active)
525
+ const currentItem = items.find((item) => item.id === tabStopId)
526
+
527
+ const candidateItems = [
528
+ selectedItem,
529
+ activeItem,
530
+ currentItem,
531
+ ...items,
532
+ ].filter(Boolean) as ItemData[]
533
+ const candidateRefs = candidateItems.map((item) => item.ref)
534
+ focusFirst(candidateRefs, false)
535
+ }
536
+ }
537
+ isClickFocusRef.current = false
538
+ },
539
+ [isTabbingBackOut, currentValue, tabStopId],
540
+ )
541
+
542
+ const onMouseDown = React.useCallback(
543
+ (event: React.MouseEvent<ListElement>) => {
544
+ propsRef.current.onMouseDown?.(event)
545
+
546
+ if (event.defaultPrevented) return
547
+
548
+ isClickFocusRef.current = true
549
+ },
550
+ [],
551
+ )
552
+
553
+ const focusContextValue = React.useMemo<FocusContextValue>(
554
+ () => ({
555
+ tabStopId,
556
+ onItemFocus,
557
+ onItemShiftTab,
558
+ onFocusableItemAdd,
559
+ onFocusableItemRemove,
560
+ onItemRegister,
561
+ onItemUnregister,
562
+ getItems,
563
+ }),
564
+ [
565
+ tabStopId,
566
+ onItemFocus,
567
+ onItemShiftTab,
568
+ onFocusableItemAdd,
569
+ onFocusableItemRemove,
570
+ onItemRegister,
571
+ onItemUnregister,
572
+ getItems,
573
+ ],
574
+ )
575
+
576
+ const ListPrimitive = asChild ? Slot : "div"
577
+
578
+ return (
579
+ <FocusContext.Provider value={focusContextValue}>
580
+ <ListPrimitive
581
+ role="tablist"
582
+ aria-orientation={orientation}
583
+ data-orientation={orientation}
584
+ data-slot="stepper-list"
585
+ dir={context.dir}
586
+ tabIndex={isTabbingBackOut || focusableItemCount === 0 ? -1 : 0}
587
+ {...listProps}
588
+ ref={composedRef}
589
+ className={cn(
590
+ "flex outline-none",
591
+ orientation === "horizontal"
592
+ ? "flex-row items-center"
593
+ : "flex-col items-start",
594
+ className,
595
+ )}
596
+ onBlur={onBlur}
597
+ onFocus={onFocus}
598
+ onMouseDown={onMouseDown}
599
+ >
600
+ {children}
601
+ </ListPrimitive>
602
+ </FocusContext.Provider>
603
+ )
604
+ }
605
+
606
+ interface StepperItemContextValue {
607
+ value: string
608
+ stepState: StepState | undefined
609
+ }
610
+
611
+ const StepperItemContext = React.createContext<StepperItemContextValue | null>(
612
+ null,
613
+ )
614
+
615
+ function useStepperItemContext(consumerName: string) {
616
+ const context = React.useContext(StepperItemContext)
617
+ if (!context) {
618
+ throw new Error(`\`${consumerName}\` must be used within \`${ITEM_NAME}\``)
619
+ }
620
+ return context
621
+ }
622
+
623
+ interface StepperItemProps extends DivProps {
624
+ value: string
625
+ completed?: boolean
626
+ disabled?: boolean
627
+ }
628
+
629
+ function StepperItem(props: StepperItemProps) {
630
+ const {
631
+ value: itemValue,
632
+ completed = false,
633
+ disabled = false,
634
+ asChild,
635
+ className,
636
+ children,
637
+ ref,
638
+ ...itemProps
639
+ } = props
640
+
641
+ const context = useStepperContext(ITEM_NAME)
642
+ const store = useStoreContext(ITEM_NAME)
643
+ const orientation = context.orientation
644
+ const value = useStore((state) => state.value)
645
+
646
+ React.useEffect(() => {
647
+ store.addStep(itemValue, completed, disabled)
648
+
649
+ return () => {
650
+ store.removeStep(itemValue)
651
+ }
652
+ }, [itemValue, completed, disabled, store])
653
+
654
+ React.useEffect(() => {
655
+ store.setStep(itemValue, completed, disabled)
656
+ }, [itemValue, completed, disabled, store])
657
+
658
+ const stepState = useStore((state) => state.steps.get(itemValue))
659
+ const steps = useStore((state) => state.steps)
660
+ const dataState = getDataState(value, itemValue, stepState, steps)
661
+
662
+ const itemContextValue = React.useMemo<StepperItemContextValue>(
663
+ () => ({
664
+ value: itemValue,
665
+ stepState,
666
+ }),
667
+ [itemValue, stepState],
668
+ )
669
+
670
+ const ItemPrimitive = asChild ? Slot : "div"
671
+
672
+ return (
673
+ <StepperItemContext.Provider value={itemContextValue}>
674
+ <ItemPrimitive
675
+ data-disabled={stepState?.disabled ? "" : undefined}
676
+ data-orientation={orientation}
677
+ data-state={dataState}
678
+ data-slot="stepper-item"
679
+ dir={context.dir}
680
+ {...itemProps}
681
+ ref={ref}
682
+ className={cn(
683
+ "relative flex not-last:flex-1 items-center",
684
+ orientation === "horizontal" ? "flex-row" : "flex-col",
685
+ className,
686
+ )}
687
+ >
688
+ {children}
689
+ </ItemPrimitive>
690
+ </StepperItemContext.Provider>
691
+ )
692
+ }
693
+
694
+ function StepperTrigger(props: ButtonProps) {
695
+ const {
696
+ asChild,
697
+ onClick: onClickProp,
698
+ onFocus: onFocusProp,
699
+ onKeyDown: onKeyDownProp,
700
+ onMouseDown: onMouseDownProp,
701
+ disabled,
702
+ className,
703
+ ref,
704
+ ...triggerProps
705
+ } = props
706
+
707
+ const context = useStepperContext(TRIGGER_NAME)
708
+ const itemContext = useStepperItemContext(TRIGGER_NAME)
709
+ const itemValue = itemContext.value
710
+
711
+ const store = useStoreContext(TRIGGER_NAME)
712
+ const focusContext = useFocusContext(TRIGGER_NAME)
713
+ const value = useStore((state) => state.value)
714
+ const steps = useStore((state) => state.steps)
715
+ const stepState = useStore((state) => state.steps.get(itemValue))
716
+
717
+ const propsRef = React.useRef({
718
+ onClick: onClickProp,
719
+ onFocus: onFocusProp,
720
+ onKeyDown: onKeyDownProp,
721
+ onMouseDown: onMouseDownProp,
722
+ })
723
+
724
+ React.useEffect(() => {
725
+ propsRef.current = {
726
+ onClick: onClickProp,
727
+ onFocus: onFocusProp,
728
+ onKeyDown: onKeyDownProp,
729
+ onMouseDown: onMouseDownProp,
730
+ }
731
+ }, [onClickProp, onFocusProp, onKeyDownProp, onMouseDownProp])
732
+
733
+ const activationMode = context.activationMode
734
+ const orientation = context.orientation
735
+ const loop = context.loop
736
+
737
+ const stepIndex = Array.from(steps.keys()).indexOf(itemValue)
738
+
739
+ const stepPosition = stepIndex + 1
740
+ const stepCount = steps.size
741
+
742
+ const triggerId = getId(context.rootId, "trigger", itemValue)
743
+ const contentId = getId(context.rootId, "content", itemValue)
744
+ const titleId = getId(context.rootId, "title", itemValue)
745
+ const descriptionId = getId(context.rootId, "description", itemValue)
746
+
747
+ const isDisabled = disabled || stepState?.disabled || context.disabled
748
+ const isActive = value === itemValue
749
+ const isTabStop = focusContext.tabStopId === triggerId
750
+ const dataState = getDataState(value, itemValue, stepState, steps)
751
+
752
+ const triggerRef = React.useRef<TriggerElement>(null)
753
+
754
+ const composedRef = React.useCallback(
755
+ (node: TriggerElement | null) => {
756
+ triggerRef.current = node
757
+ if (typeof ref === "function") {
758
+ ref(node)
759
+ } else if (ref) {
760
+ ref.current = node
761
+ }
762
+ },
763
+ [ref],
764
+ )
765
+
766
+ const isArrowKeyPressedRef = React.useRef(false)
767
+ const isMouseClickRef = React.useRef(false)
768
+
769
+ React.useEffect(() => {
770
+ function onKeyDown(event: KeyboardEvent) {
771
+ if (ARROW_KEYS.includes(event.key)) {
772
+ isArrowKeyPressedRef.current = true
773
+ }
774
+ }
775
+ function onKeyUp() {
776
+ isArrowKeyPressedRef.current = false
777
+ }
778
+ document.addEventListener("keydown", onKeyDown)
779
+ document.addEventListener("keyup", onKeyUp)
780
+ return () => {
781
+ document.removeEventListener("keydown", onKeyDown)
782
+ document.removeEventListener("keyup", onKeyUp)
783
+ }
784
+ }, [])
785
+
786
+ React.useEffect(() => {
787
+ focusContext.onItemRegister({
788
+ id: triggerId,
789
+ ref: triggerRef,
790
+ value: itemValue,
791
+ active: isTabStop,
792
+ disabled: !!isDisabled,
793
+ })
794
+
795
+ if (!isDisabled) {
796
+ focusContext.onFocusableItemAdd()
797
+ }
798
+
799
+ return () => {
800
+ focusContext.onItemUnregister(triggerId)
801
+ if (!isDisabled) {
802
+ focusContext.onFocusableItemRemove()
803
+ }
804
+ }
805
+ }, [focusContext, triggerId, itemValue, isTabStop, isDisabled])
806
+
807
+ const onClick = React.useCallback(
808
+ async (event: React.MouseEvent<TriggerElement>) => {
809
+ propsRef.current.onClick?.(event)
810
+ if (event.defaultPrevented) return
811
+
812
+ if (!isDisabled && !context.nonInteractive) {
813
+ const currentStepIndex = Array.from(steps.keys()).indexOf(value ?? "")
814
+ const targetStepIndex = Array.from(steps.keys()).indexOf(itemValue)
815
+ const direction = targetStepIndex > currentStepIndex ? "next" : "prev"
816
+
817
+ await store.setStateWithValidation(itemValue, direction)
818
+ }
819
+ },
820
+ [isDisabled, context.nonInteractive, store, itemValue, value, steps],
821
+ )
822
+
823
+ const onFocus = React.useCallback(
824
+ async (event: React.FocusEvent<TriggerElement>) => {
825
+ propsRef.current.onFocus?.(event)
826
+ if (event.defaultPrevented) return
827
+
828
+ focusContext.onItemFocus(triggerId)
829
+
830
+ const isKeyboardFocus = !isMouseClickRef.current
831
+
832
+ if (
833
+ !isActive &&
834
+ !isDisabled &&
835
+ activationMode !== "manual" &&
836
+ !context.nonInteractive &&
837
+ isKeyboardFocus
838
+ ) {
839
+ const currentStepIndex = Array.from(steps.keys()).indexOf(value || "")
840
+ const targetStepIndex = Array.from(steps.keys()).indexOf(itemValue)
841
+ const direction = targetStepIndex > currentStepIndex ? "next" : "prev"
842
+
843
+ await store.setStateWithValidation(itemValue, direction)
844
+ }
845
+
846
+ isMouseClickRef.current = false
847
+ },
848
+ [
849
+ focusContext,
850
+ triggerId,
851
+ activationMode,
852
+ isActive,
853
+ isDisabled,
854
+ context.nonInteractive,
855
+ store,
856
+ itemValue,
857
+ value,
858
+ steps,
859
+ ],
860
+ )
861
+
862
+ const onKeyDown = React.useCallback(
863
+ async (event: React.KeyboardEvent<TriggerElement>) => {
864
+ propsRef.current.onKeyDown?.(event)
865
+ if (event.defaultPrevented) return
866
+
867
+ if (event.key === "Enter" && context.nonInteractive) {
868
+ event.preventDefault()
869
+ return
870
+ }
871
+
872
+ if (
873
+ (event.key === "Enter" || event.key === " ") &&
874
+ activationMode === "manual" &&
875
+ !context.nonInteractive
876
+ ) {
877
+ event.preventDefault()
878
+ if (!isDisabled && triggerRef.current) {
879
+ triggerRef.current.click()
880
+ }
881
+ return
882
+ }
883
+
884
+ if (event.key === "Tab" && event.shiftKey) {
885
+ focusContext.onItemShiftTab()
886
+ return
887
+ }
888
+
889
+ if (event.target !== event.currentTarget) return
890
+
891
+ const focusIntent = getFocusIntent(event, context.dir, orientation)
892
+
893
+ if (focusIntent !== undefined) {
894
+ if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey)
895
+ return
896
+ event.preventDefault()
897
+
898
+ const items = focusContext.getItems().filter((item) => !item.disabled)
899
+ let candidateRefs = items.map((item) => item.ref)
900
+
901
+ if (focusIntent === "last") {
902
+ candidateRefs.reverse()
903
+ } else if (focusIntent === "prev" || focusIntent === "next") {
904
+ if (focusIntent === "prev") candidateRefs.reverse()
905
+ const currentIndex = candidateRefs.findIndex(
906
+ (ref) => ref.current === event.currentTarget,
907
+ )
908
+ candidateRefs = loop
909
+ ? wrapArray(candidateRefs, currentIndex + 1)
910
+ : candidateRefs.slice(currentIndex + 1)
911
+ }
912
+
913
+ if (store.hasValidation() && candidateRefs.length > 0) {
914
+ const nextRef = candidateRefs[0]
915
+ const nextElement = nextRef?.current
916
+ const nextItem = items.find(
917
+ (item) => item.ref.current === nextElement,
918
+ )
919
+
920
+ if (nextItem && nextItem.value !== itemValue) {
921
+ const currentStepIndex = Array.from(steps.keys()).indexOf(
922
+ value || "",
923
+ )
924
+ const targetStepIndex = Array.from(steps.keys()).indexOf(
925
+ nextItem.value,
926
+ )
927
+ const direction: NavigationDirection =
928
+ targetStepIndex > currentStepIndex ? "next" : "prev"
929
+
930
+ if (direction === "next") {
931
+ const isValid = await store.setStateWithValidation(
932
+ nextItem.value,
933
+ direction,
934
+ )
935
+ if (!isValid) return
936
+ } else {
937
+ store.setState("value", nextItem.value)
938
+ }
939
+
940
+ queueMicrotask(() => nextElement?.focus())
941
+ return
942
+ }
943
+ }
944
+
945
+ queueMicrotask(() => focusFirst(candidateRefs))
946
+ }
947
+ },
948
+ [
949
+ focusContext,
950
+ context.nonInteractive,
951
+ context.dir,
952
+ activationMode,
953
+ orientation,
954
+ loop,
955
+ isDisabled,
956
+ store,
957
+ itemValue,
958
+ value,
959
+ steps,
960
+ ],
961
+ )
962
+
963
+ const onMouseDown = React.useCallback(
964
+ (event: React.MouseEvent<TriggerElement>) => {
965
+ propsRef.current.onMouseDown?.(event)
966
+ if (event.defaultPrevented) return
967
+
968
+ isMouseClickRef.current = true
969
+
970
+ if (isDisabled) {
971
+ event.preventDefault()
972
+ } else {
973
+ focusContext.onItemFocus(triggerId)
974
+ }
975
+ },
976
+ [focusContext, triggerId, isDisabled],
977
+ )
978
+
979
+ const TriggerPrimitive = asChild ? Slot : "button"
980
+
981
+ return (
982
+ <TriggerPrimitive
983
+ id={triggerId}
984
+ role="tab"
985
+ type="button"
986
+ aria-controls={contentId}
987
+ aria-current={isActive ? "step" : undefined}
988
+ aria-describedby={`${titleId} ${descriptionId}`}
989
+ aria-posinset={stepPosition}
990
+ aria-selected={isActive}
991
+ aria-setsize={stepCount}
992
+ data-disabled={isDisabled ? "" : undefined}
993
+ data-state={dataState}
994
+ data-slot="stepper-trigger"
995
+ disabled={isDisabled}
996
+ tabIndex={isTabStop ? 0 : -1}
997
+ {...triggerProps}
998
+ ref={composedRef}
999
+ className={cn(
1000
+ "inline-flex items-center justify-center gap-3 rounded-md text-left outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
1001
+ "not-has-data-[slot=description]:rounded-full not-has-data-[slot=title]:rounded-full",
1002
+ className,
1003
+ )}
1004
+ onClick={onClick}
1005
+ onFocus={onFocus}
1006
+ onKeyDown={onKeyDown}
1007
+ onMouseDown={onMouseDown}
1008
+ />
1009
+ )
1010
+ }
1011
+
1012
+ interface StepperIndicatorProps extends Omit<DivProps, "children"> {
1013
+ children?: React.ReactNode | ((dataState: DataState) => React.ReactNode)
1014
+ }
1015
+
1016
+ function StepperIndicator(props: StepperIndicatorProps) {
1017
+ const { className, children, asChild, ref, ...indicatorProps } = props
1018
+
1019
+ const context = useStepperContext(INDICATOR_NAME)
1020
+ const itemContext = useStepperItemContext(INDICATOR_NAME)
1021
+
1022
+ const value = useStore((state) => state.value)
1023
+ const itemValue = itemContext.value
1024
+ const stepState = useStore((state) => state.steps.get(itemValue))
1025
+ const steps = useStore((state) => state.steps)
1026
+
1027
+ const stepPosition = Array.from(steps.keys()).indexOf(itemValue) + 1
1028
+
1029
+ const dataState = getDataState(value, itemValue, stepState, steps)
1030
+
1031
+ const IndicatorPrimitive = asChild ? Slot : "div"
1032
+
1033
+ return (
1034
+ <IndicatorPrimitive
1035
+ data-state={dataState}
1036
+ data-slot="stepper-indicator"
1037
+ dir={context.dir}
1038
+ {...indicatorProps}
1039
+ ref={ref}
1040
+ className={cn(
1041
+ "flex size-7 shrink-0 items-center justify-center rounded-full border-2 border-muted bg-background font-medium text-muted-foreground text-sm transition-colors data-[state=active]:border-primary data-[state=completed]:border-primary data-[state=active]:bg-primary data-[state=completed]:bg-primary data-[state=active]:text-primary-foreground data-[state=completed]:text-primary-foreground",
1042
+ className,
1043
+ )}
1044
+ >
1045
+ {typeof children === "function" ? (
1046
+ children(dataState)
1047
+ ) : children ? (
1048
+ children
1049
+ ) : dataState === "completed" ? (
1050
+ <Check className="size-4" />
1051
+ ) : (
1052
+ stepPosition
1053
+ )}
1054
+ </IndicatorPrimitive>
1055
+ )
1056
+ }
1057
+
1058
+ interface StepperSeparatorProps extends DivProps {
1059
+ forceMount?: boolean
1060
+ }
1061
+
1062
+ function StepperSeparator(props: StepperSeparatorProps) {
1063
+ const {
1064
+ className,
1065
+ asChild,
1066
+ forceMount = false,
1067
+ ref,
1068
+ ...separatorProps
1069
+ } = props
1070
+
1071
+ const context = useStepperContext(SEPARATOR_NAME)
1072
+ const itemContext = useStepperItemContext(SEPARATOR_NAME)
1073
+ const value = useStore((state) => state.value)
1074
+ const steps = useStore((state) => state.steps)
1075
+
1076
+ const orientation = context.orientation
1077
+
1078
+ const stepIndex = Array.from(steps.keys()).indexOf(itemContext.value)
1079
+
1080
+ const isLastStep = stepIndex === steps.size - 1
1081
+
1082
+ if (isLastStep && !forceMount) return null
1083
+
1084
+ const dataState = getDataState(
1085
+ value,
1086
+ itemContext.value,
1087
+ itemContext.stepState,
1088
+ steps,
1089
+ "separator",
1090
+ )
1091
+
1092
+ const SeparatorPrimitive = asChild ? Slot : "div"
1093
+
1094
+ return (
1095
+ <SeparatorPrimitive
1096
+ role="separator"
1097
+ aria-hidden="true"
1098
+ aria-orientation={orientation}
1099
+ data-orientation={orientation}
1100
+ data-state={dataState}
1101
+ data-slot="stepper-separator"
1102
+ dir={context.dir}
1103
+ {...separatorProps}
1104
+ ref={ref}
1105
+ className={cn(
1106
+ "bg-border transition-colors data-[state=active]:bg-primary data-[state=completed]:bg-primary",
1107
+ orientation === "horizontal" ? "h-px flex-1" : "h-10 w-px",
1108
+ className,
1109
+ )}
1110
+ />
1111
+ )
1112
+ }
1113
+
1114
+ interface StepperTitleProps extends React.ComponentProps<"span"> {
1115
+ asChild?: boolean
1116
+ }
1117
+
1118
+ function StepperTitle(props: StepperTitleProps) {
1119
+ const { className, asChild, ref, ...titleProps } = props
1120
+
1121
+ const context = useStepperContext(TITLE_NAME)
1122
+ const itemContext = useStepperItemContext(TITLE_NAME)
1123
+
1124
+ const titleId = getId(context.rootId, "title", itemContext.value)
1125
+
1126
+ const TitlePrimitive = asChild ? Slot : "span"
1127
+
1128
+ return (
1129
+ <TitlePrimitive
1130
+ id={titleId}
1131
+ data-slot="stepper-title"
1132
+ dir={context.dir}
1133
+ {...titleProps}
1134
+ ref={ref}
1135
+ className={cn("font-medium text-sm", className)}
1136
+ />
1137
+ )
1138
+ }
1139
+
1140
+ interface StepperDescriptionProps extends React.ComponentProps<"span"> {
1141
+ asChild?: boolean
1142
+ }
1143
+
1144
+ function StepperDescription(props: StepperDescriptionProps) {
1145
+ const { className, asChild, ref, ...descriptionProps } = props
1146
+
1147
+ const context = useStepperContext(DESCRIPTION_NAME)
1148
+ const itemContext = useStepperItemContext(DESCRIPTION_NAME)
1149
+
1150
+ const descriptionId = getId(context.rootId, "description", itemContext.value)
1151
+
1152
+ const DescriptionPrimitive = asChild ? Slot : "span"
1153
+
1154
+ return (
1155
+ <DescriptionPrimitive
1156
+ id={descriptionId}
1157
+ data-slot="stepper-description"
1158
+ dir={context.dir}
1159
+ {...descriptionProps}
1160
+ ref={ref}
1161
+ className={cn("text-muted-foreground text-xs", className)}
1162
+ />
1163
+ )
1164
+ }
1165
+
1166
+ interface StepperContentProps extends DivProps {
1167
+ value: string
1168
+ forceMount?: boolean
1169
+ }
1170
+
1171
+ function StepperContent(props: StepperContentProps) {
1172
+ const {
1173
+ value: valueProp,
1174
+ asChild,
1175
+ forceMount = false,
1176
+ ref,
1177
+ className,
1178
+ ...contentProps
1179
+ } = props
1180
+
1181
+ const context = useStepperContext(CONTENT_NAME)
1182
+ const value = useStore((state) => state.value)
1183
+
1184
+ const contentId = getId(context.rootId, "content", valueProp)
1185
+ const triggerId = getId(context.rootId, "trigger", valueProp)
1186
+
1187
+ if (valueProp !== value && !forceMount) return null
1188
+
1189
+ const ContentPrimitive = asChild ? Slot : "div"
1190
+
1191
+ return (
1192
+ <ContentPrimitive
1193
+ id={contentId}
1194
+ role="tabpanel"
1195
+ aria-labelledby={triggerId}
1196
+ data-slot="stepper-content"
1197
+ dir={context.dir}
1198
+ {...contentProps}
1199
+ ref={ref}
1200
+ className={cn("flex-1 outline-none", className)}
1201
+ />
1202
+ )
1203
+ }
1204
+
1205
+ function StepperPrev(props: ButtonProps) {
1206
+ const { asChild, onClick: onClickProp, disabled, ...prevProps } = props
1207
+
1208
+ const store = useStoreContext(PREV_NAME)
1209
+ const value = useStore((state) => state.value)
1210
+ const steps = useStore((state) => state.steps)
1211
+
1212
+ const propsRef = React.useRef({ onClick: onClickProp })
1213
+ React.useEffect(() => {
1214
+ propsRef.current = { onClick: onClickProp }
1215
+ }, [onClickProp])
1216
+
1217
+ const stepKeys = Array.from(steps.keys())
1218
+ const currentIndex = value ? stepKeys.indexOf(value) : -1
1219
+ const isDisabled = disabled || currentIndex <= 0
1220
+
1221
+ const onClick = React.useCallback(
1222
+ async (event: React.MouseEvent<HTMLButtonElement>) => {
1223
+ propsRef.current.onClick?.(event)
1224
+ if (event.defaultPrevented || isDisabled) return
1225
+
1226
+ const prevIndex = Math.max(currentIndex - 1, 0)
1227
+ const prevStepValue = stepKeys[prevIndex]
1228
+
1229
+ if (prevStepValue) {
1230
+ store.setState("value", prevStepValue)
1231
+ }
1232
+ },
1233
+ [isDisabled, currentIndex, stepKeys, store],
1234
+ )
1235
+
1236
+ const PrevPrimitive = asChild ? Slot : "button"
1237
+
1238
+ return (
1239
+ <PrevPrimitive
1240
+ type="button"
1241
+ data-slot="stepper-prev"
1242
+ disabled={isDisabled}
1243
+ {...prevProps}
1244
+ onClick={onClick}
1245
+ />
1246
+ )
1247
+ }
1248
+
1249
+ function StepperNext(props: ButtonProps) {
1250
+ const { asChild, onClick: onClickProp, disabled, ...nextProps } = props
1251
+
1252
+ const store = useStoreContext(NEXT_NAME)
1253
+ const value = useStore((state) => state.value)
1254
+ const steps = useStore((state) => state.steps)
1255
+
1256
+ const propsRef = React.useRef({ onClick: onClickProp })
1257
+ React.useEffect(() => {
1258
+ propsRef.current = { onClick: onClickProp }
1259
+ }, [onClickProp])
1260
+
1261
+ const stepKeys = Array.from(steps.keys())
1262
+ const currentIndex = value ? stepKeys.indexOf(value) : -1
1263
+ const isDisabled = disabled || currentIndex >= stepKeys.length - 1
1264
+
1265
+ const onClick = React.useCallback(
1266
+ async (event: React.MouseEvent<HTMLButtonElement>) => {
1267
+ propsRef.current.onClick?.(event)
1268
+ if (event.defaultPrevented || isDisabled) return
1269
+
1270
+ const nextIndex = Math.min(currentIndex + 1, stepKeys.length - 1)
1271
+ const nextStepValue = stepKeys[nextIndex]
1272
+
1273
+ if (nextStepValue) {
1274
+ await store.setStateWithValidation(nextStepValue, "next")
1275
+ }
1276
+ },
1277
+ [isDisabled, currentIndex, stepKeys, store],
1278
+ )
1279
+
1280
+ const NextPrimitive = asChild ? Slot : "button"
1281
+
1282
+ return (
1283
+ <NextPrimitive
1284
+ type="button"
1285
+ data-slot="stepper-next"
1286
+ disabled={isDisabled}
1287
+ {...nextProps}
1288
+ onClick={onClick}
1289
+ />
1290
+ )
1291
+ }
1292
+
1293
+ export {
1294
+ Stepper,
1295
+ StepperContent,
1296
+ StepperDescription,
1297
+ StepperIndicator,
1298
+ StepperItem,
1299
+ StepperList,
1300
+ StepperNext,
1301
+ StepperPrev,
1302
+ StepperSeparator,
1303
+ StepperTitle,
1304
+ StepperTrigger,
1305
+ useStore as useStepper,
1306
+ }
1307
+ export type { StepperProps }