@primitiv-ui/react 0.1.0 → 0.1.2

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 (207) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +2 -1
  3. package/src/AccessibleIcon/AccessibleIcon.tsx +6 -2
  4. package/src/AccessibleIcon/__tests__/AccessibleIcon.test.tsx +1 -1
  5. package/src/AccessibleIcon/types.ts +4 -0
  6. package/src/Accordion/Accordion.tsx +34 -12
  7. package/src/Accordion/AccordionContext.ts +1 -1
  8. package/src/Accordion/__tests__/Accordion.reading-direction.test.tsx +1 -1
  9. package/src/Accordion/hooks/useAccordionItem.ts +1 -1
  10. package/src/Accordion/hooks/useAccordionRoot.ts +1 -1
  11. package/src/Accordion/hooks/useAccordionTrigger.ts +2 -2
  12. package/src/Accordion/index.ts +2 -1
  13. package/src/Accordion/types.ts +55 -13
  14. package/src/Alert/Alert.tsx +9 -2
  15. package/src/Alert/__tests__/Alert.test.tsx +1 -1
  16. package/src/Alert/types.ts +1 -0
  17. package/src/Avatar/Avatar.tsx +20 -7
  18. package/src/Avatar/AvatarContext.ts +12 -6
  19. package/src/Breadcrumb/Breadcrumb.tsx +32 -10
  20. package/src/Button/Button.tsx +5 -2
  21. package/src/Button/types.ts +4 -0
  22. package/src/Carousel/Carousel.tsx +30 -14
  23. package/src/Carousel/CarouselContext.ts +7 -3
  24. package/src/Carousel/__tests__/Carousel.asChild.test.tsx +1 -1
  25. package/src/Carousel/__tests__/Carousel.auto-play.test.tsx +1 -1
  26. package/src/Carousel/__tests__/Carousel.basic-rendering.test.tsx +1 -1
  27. package/src/Carousel/__tests__/Carousel.controlled-state.test.tsx +1 -1
  28. package/src/Carousel/__tests__/Carousel.error-handling.test.tsx +1 -1
  29. package/src/Carousel/__tests__/Carousel.ids.test.tsx +1 -1
  30. package/src/Carousel/__tests__/Carousel.imperative-api.test.tsx +2 -2
  31. package/src/Carousel/__tests__/Carousel.indicators.test.tsx +1 -1
  32. package/src/Carousel/__tests__/Carousel.intersection-observer.test.tsx +2 -2
  33. package/src/Carousel/__tests__/Carousel.keyboard-navigation.test.tsx +1 -1
  34. package/src/Carousel/__tests__/Carousel.play-pause.test.tsx +1 -1
  35. package/src/Carousel/__tests__/Carousel.prev-next.test.tsx +1 -1
  36. package/src/Carousel/__tests__/Carousel.reduced-motion.test.tsx +1 -1
  37. package/src/Carousel/__tests__/Carousel.refresh-progress.test.tsx +2 -2
  38. package/src/Carousel/__tests__/Carousel.scroll-snap-change.test.tsx +1 -1
  39. package/src/Carousel/__tests__/Carousel.scroll-sync.test.tsx +1 -1
  40. package/src/Carousel/__tests__/Carousel.slides-per-move.test.tsx +1 -1
  41. package/src/Carousel/__tests__/Carousel.slides-per-page.test.tsx +1 -1
  42. package/src/Carousel/__tests__/Carousel.touch-interaction.test.tsx +1 -1
  43. package/src/Carousel/__tests__/Carousel.transition-modes.test.tsx +1 -1
  44. package/src/Carousel/__tests__/Carousel.translations.test.tsx +1 -1
  45. package/src/Carousel/__tests__/Carousel.uncontrolled-state.test.tsx +1 -1
  46. package/src/Carousel/types.ts +8 -0
  47. package/src/Checkbox/Checkbox.tsx +11 -6
  48. package/src/Checkbox/CheckboxContext.ts +1 -1
  49. package/src/Checkbox/hooks/useCheckboxRoot.ts +1 -1
  50. package/src/Checkbox/index.ts +1 -0
  51. package/src/Checkbox/types.ts +30 -3
  52. package/src/CheckboxCard/CheckboxCard.tsx +13 -11
  53. package/src/CheckboxCard/CheckboxCardContext.ts +19 -6
  54. package/src/CheckboxCard/hooks/useCheckboxCardRoot.ts +2 -2
  55. package/src/CheckboxCard/types.ts +21 -5
  56. package/src/Collapsible/Collapsible.tsx +37 -21
  57. package/src/Collapsible/CollapsibleContext.ts +1 -1
  58. package/src/Collapsible/hooks/useCollapsibleRoot.ts +1 -1
  59. package/src/Collapsible/hooks/useCollapsibleTrigger.ts +1 -1
  60. package/src/Collapsible/index.ts +1 -0
  61. package/src/Collapsible/types.ts +45 -12
  62. package/src/ContextMenu/ContextMenu.tsx +60 -34
  63. package/src/ContextMenu/ContextMenuContext.ts +2 -2
  64. package/src/ContextMenu/ContextMenuSubContext.ts +1 -1
  65. package/src/ContextMenu/__tests__/ContextMenu.reading-direction.test.tsx +1 -1
  66. package/src/ContextMenu/index.ts +2 -1
  67. package/src/ContextMenu/types.ts +160 -17
  68. package/src/DirectionProvider/DirectionProvider.tsx +7 -1
  69. package/src/DirectionProvider/__tests__/DirectionProvider.test.tsx +1 -1
  70. package/src/DirectionProvider/types.ts +1 -0
  71. package/src/Divider/Divider.tsx +4 -1
  72. package/src/Divider/__tests__/Divider.test.tsx +1 -1
  73. package/src/Divider/index.ts +2 -1
  74. package/src/Divider/types.ts +5 -0
  75. package/src/Dropdown/Dropdown.tsx +60 -34
  76. package/src/Dropdown/DropdownContext.ts +2 -2
  77. package/src/Dropdown/DropdownSubContext.ts +1 -1
  78. package/src/Dropdown/__tests__/Dropdown.reading-direction.test.tsx +1 -1
  79. package/src/Dropdown/hooks/useDropdownContent.ts +1 -1
  80. package/src/Dropdown/hooks/useDropdownItem.ts +1 -1
  81. package/src/Dropdown/hooks/useDropdownRoot.ts +2 -2
  82. package/src/Dropdown/hooks/useDropdownTrigger.ts +1 -1
  83. package/src/Dropdown/index.ts +2 -1
  84. package/src/Dropdown/types.ts +153 -25
  85. package/src/EmptyState/EmptyState.tsx +34 -20
  86. package/src/EmptyState/__tests__/EmptyState.Actions.test.tsx +1 -1
  87. package/src/EmptyState/__tests__/EmptyState.Description.test.tsx +1 -1
  88. package/src/EmptyState/__tests__/EmptyState.Media.test.tsx +1 -1
  89. package/src/EmptyState/__tests__/EmptyState.Root.test.tsx +1 -1
  90. package/src/EmptyState/__tests__/EmptyState.Title.test.tsx +1 -1
  91. package/src/EmptyState/types.ts +2 -1
  92. package/src/Field/Field.tsx +24 -10
  93. package/src/Field/FieldContext.ts +1 -1
  94. package/src/Field/types.ts +4 -0
  95. package/src/Fieldset/Fieldset.tsx +26 -10
  96. package/src/Fieldset/types.ts +2 -0
  97. package/src/Input/Input.tsx +6 -3
  98. package/src/Input/__tests__/Input.field-integration.test.tsx +1 -1
  99. package/src/Input/types.ts +4 -0
  100. package/src/InputGroup/InputGroup.tsx +15 -8
  101. package/src/InputGroup/types.ts +9 -0
  102. package/src/MillerColumns/MillerColumns.tsx +28 -8
  103. package/src/MillerColumns/MillerColumnsContext.ts +1 -1
  104. package/src/MillerColumns/hooks/useMillerColumnsItem.ts +2 -2
  105. package/src/MillerColumns/hooks/useMillerColumnsRoot.ts +0 -0
  106. package/src/MillerColumns/index.ts +1 -1
  107. package/src/MillerColumns/types.ts +67 -14
  108. package/src/MillerColumns/useMillerColumnsSelection.ts +1 -1
  109. package/src/Modal/Modal.tsx +25 -11
  110. package/src/Modal/ModalContext.ts +14 -7
  111. package/src/Modal/hooks/useModalRoot.ts +1 -1
  112. package/src/Modal/hooks/useModalTrigger.ts +2 -2
  113. package/src/Modal/types.ts +51 -2
  114. package/src/Portal/Portal.tsx +3 -1
  115. package/src/Portal/types.ts +4 -0
  116. package/src/Progress/Progress.tsx +12 -7
  117. package/src/Progress/ProgressContext.ts +18 -6
  118. package/src/RadioCard/RadioCard.tsx +17 -11
  119. package/src/RadioCard/RadioCardContext.ts +17 -5
  120. package/src/RadioCard/RadioCardItemContext.ts +18 -5
  121. package/src/RadioCard/__tests__/RadioCard.reading-direction.test.tsx +1 -1
  122. package/src/RadioCard/hooks/useRadioCardRoot.ts +1 -1
  123. package/src/RadioCard/types.ts +24 -3
  124. package/src/RadioGroup/RadioGroup.tsx +17 -11
  125. package/src/RadioGroup/RadioGroupContext.ts +1 -1
  126. package/src/RadioGroup/RadioGroupItemContext.ts +1 -1
  127. package/src/RadioGroup/__tests__/RadioGroup.reading-direction.test.tsx +1 -1
  128. package/src/RadioGroup/hooks/useRadioGroupRoot.ts +1 -1
  129. package/src/RadioGroup/index.ts +1 -0
  130. package/src/RadioGroup/types.ts +34 -3
  131. package/src/Select/Select.tsx +23 -8
  132. package/src/Select/__tests__/Select.field-integration.test.tsx +1 -1
  133. package/src/Select/index.ts +1 -1
  134. package/src/Select/types.ts +18 -3
  135. package/src/SkipNav/SkipNav.tsx +7 -2
  136. package/src/SkipNav/__tests__/SkipNav.ids.test.tsx +1 -1
  137. package/src/Slider/Slider.tsx +26 -11
  138. package/src/Slider/SliderContext.ts +13 -6
  139. package/src/Slider/__tests__/Slider.reading-direction.test.tsx +1 -1
  140. package/src/Slider/hooks/useSliderRoot.ts +1 -1
  141. package/src/Slider/types.ts +12 -3
  142. package/src/Status/Status.tsx +9 -2
  143. package/src/Status/__tests__/Status.test.tsx +1 -1
  144. package/src/Status/types.ts +4 -0
  145. package/src/Switch/Switch.tsx +16 -6
  146. package/src/Switch/SwitchContext.ts +13 -5
  147. package/src/Switch/hooks/useSwitchRoot.ts +1 -1
  148. package/src/Switch/types.ts +24 -3
  149. package/src/Table/Table.tsx +51 -25
  150. package/src/Table/__tests__/Table.Body.test.tsx +1 -1
  151. package/src/Table/__tests__/Table.Caption.test.tsx +1 -1
  152. package/src/Table/__tests__/Table.Cell.test.tsx +1 -1
  153. package/src/Table/__tests__/Table.Footer.test.tsx +1 -1
  154. package/src/Table/__tests__/Table.Head.test.tsx +1 -1
  155. package/src/Table/__tests__/Table.Header.test.tsx +1 -1
  156. package/src/Table/__tests__/Table.Root.test.tsx +1 -1
  157. package/src/Table/__tests__/Table.Row.test.tsx +1 -1
  158. package/src/Table/__tests__/Table.ScrollArea.test.tsx +1 -1
  159. package/src/Table/index.ts +2 -1
  160. package/src/Tabs/Tabs.tsx +30 -10
  161. package/src/Tabs/TabsContext.ts +15 -7
  162. package/src/Tabs/__tests__/Tabs.asChild.test.tsx +1 -1
  163. package/src/Tabs/__tests__/Tabs.basic-rendering.test.tsx +1 -1
  164. package/src/Tabs/__tests__/Tabs.change-event-callbacks.test.tsx +1 -1
  165. package/src/Tabs/__tests__/Tabs.controlled-state.test.tsx +1 -1
  166. package/src/Tabs/__tests__/Tabs.error-handling.test.tsx +1 -1
  167. package/src/Tabs/__tests__/Tabs.imperative-api.test.tsx +1 -1
  168. package/src/Tabs/__tests__/Tabs.keyboard-interaction.test.tsx +1 -1
  169. package/src/Tabs/__tests__/Tabs.lazy-mount.test.tsx +1 -1
  170. package/src/Tabs/__tests__/Tabs.mouse-interaction.test.tsx +1 -1
  171. package/src/Tabs/__tests__/Tabs.reading-direction.test.tsx +1 -1
  172. package/src/Tabs/__tests__/Tabs.uncontrolled-state.test.tsx +1 -1
  173. package/src/Tabs/hooks/useTabsContent.ts +1 -1
  174. package/src/Tabs/hooks/useTabsRoot.ts +1 -1
  175. package/src/Tabs/hooks/useTabsTrigger.ts +1 -1
  176. package/src/Tabs/types.ts +35 -1
  177. package/src/Tabs/utils.ts +1 -1
  178. package/src/Textarea/Textarea.tsx +6 -3
  179. package/src/Textarea/__tests__/Textarea.field-integration.test.tsx +1 -1
  180. package/src/Textarea/types.ts +4 -0
  181. package/src/Toggle/Toggle.tsx +11 -4
  182. package/src/Toggle/types.ts +7 -3
  183. package/src/ToggleGroup/ToggleGroup.tsx +23 -13
  184. package/src/ToggleGroup/ToggleGroupContext.ts +1 -1
  185. package/src/ToggleGroup/__tests__/ToggleGroup.reading-direction.test.tsx +1 -1
  186. package/src/ToggleGroup/hooks/useToggleGroupRoot.ts +1 -1
  187. package/src/ToggleGroup/types.ts +45 -5
  188. package/src/Tooltip/Tooltip.tsx +46 -15
  189. package/src/Tooltip/TooltipContext.ts +1 -1
  190. package/src/Tooltip/hooks/useTooltipContent.ts +1 -1
  191. package/src/Tooltip/hooks/useTooltipRoot.ts +1 -1
  192. package/src/Tooltip/hooks/useTooltipTrigger.ts +1 -1
  193. package/src/Tooltip/index.ts +1 -0
  194. package/src/Tooltip/types.ts +50 -2
  195. package/src/Tree/Tree.tsx +58 -12
  196. package/src/Tree/TreeContext.ts +1 -1
  197. package/src/Tree/__tests__/Tree.selection-path.test.tsx +2 -2
  198. package/src/Tree/hooks/useTreeItemKeyboard.ts +1 -1
  199. package/src/Tree/hooks/useTreeRoot.ts +1 -1
  200. package/src/Tree/index.ts +1 -1
  201. package/src/Tree/types.ts +39 -7
  202. package/src/VisuallyHidden/VisuallyHidden.tsx +4 -2
  203. package/src/VisuallyHidden/__tests__/VisuallyHidden.test.tsx +1 -1
  204. package/src/VisuallyHidden/types.ts +4 -0
  205. package/src/index.ts +39 -38
  206. package/src/types.ts +1 -0
  207. package/src/utils/createStrictContext.ts +9 -5
@@ -1,13 +1,20 @@
1
- import { createStrictContext } from "../utils";
1
+ import type { Context, Provider } from "react";
2
+ import { createStrictContext } from "../utils/index.ts";
2
3
 
3
4
  import { ModalContextValue } from "./types";
4
5
 
5
- export const [ModalContext, useModalContext] =
6
- createStrictContext<ModalContextValue>(
7
- "Component must be rendered as a child of Modal.Root",
8
- "ModalContext",
9
- );
6
+ const modalContextPair = createStrictContext<ModalContextValue>(
7
+ "Component must be rendered as a child of Modal.Root",
8
+ "ModalContext",
9
+ );
10
10
 
11
- const ModalProvider = ModalContext.Provider;
11
+ /** React context carrying the shared {@link ModalContextValue} for a modal. */
12
+ export const ModalContext: Context<ModalContextValue | null> =
13
+ modalContextPair[0];
14
+ /** Hook returning the nearest {@link ModalContextValue}; throws outside `Modal.Root`. */
15
+ export const useModalContext: () => ModalContextValue = modalContextPair[1];
16
+
17
+ /** Context provider for {@link ModalContext}, rendered internally by `Modal.Root`. */
18
+ const ModalProvider: Provider<ModalContextValue | null> = ModalContext.Provider;
12
19
 
13
20
  export { ModalProvider };
@@ -8,7 +8,7 @@ import {
8
8
  useState,
9
9
  } from "react";
10
10
 
11
- import { useControllableState } from "../../hooks";
11
+ import { useControllableState } from "../../hooks/index.ts";
12
12
 
13
13
  import {
14
14
  ModalContentCallbacks,
@@ -1,6 +1,6 @@
1
1
  import { MouseEventHandler } from "react";
2
- import { useModalContext } from ".";
3
- import { composeEventHandlers } from "../../Slot";
2
+ import { useModalContext } from "./index.ts";
3
+ import { composeEventHandlers } from "../../Slot/index.ts";
4
4
  import type { ModalTriggerProps } from "../types";
5
5
 
6
6
  export function useModalTrigger(
@@ -6,71 +6,120 @@ import {
6
6
  RefObject,
7
7
  } from "react";
8
8
 
9
+ /**
10
+ * Imperative handle exposed on `Modal.Root`'s `ref`, letting callers open
11
+ * and close the modal programmatically.
12
+ */
9
13
  export type ModalImperativeApi = {
14
+ /** Opens the modal. */
10
15
  open: () => void;
16
+ /** Closes the modal. */
11
17
  close: () => void;
12
18
  };
13
19
 
20
+ /**
21
+ * Optional escape-hatch callbacks accepted by `Modal.Content` for
22
+ * intercepting dismissal gestures.
23
+ */
14
24
  export type ModalContentCallbacks = {
25
+ /** Fires when the user presses Escape; call `preventDefault` to keep the modal open. */
15
26
  onEscapeKeyDown?: (event: Event) => void;
27
+ /** Fires on a pointer-down outside the content; call `preventDefault` to keep the modal open. */
16
28
  onPointerDownOutside?: (event: PointerEvent) => void;
17
29
  };
18
30
 
31
+ /**
32
+ * Shared context value published by `Modal.Root` and consumed by every
33
+ * sub-component to coordinate open state, ids, and ARIA wiring.
34
+ */
19
35
  export type ModalContextValue = {
36
+ /** Whether the modal is currently open. */
20
37
  open: boolean;
38
+ /** Sets the open state. */
21
39
  setOpen: (open: boolean) => void;
40
+ /** Generated id of the dialog content element. */
22
41
  contentId: string;
42
+ /** Mutable ref holding the current content callbacks. */
23
43
  contentCallbacksRef: RefObject<ModalContentCallbacks>;
44
+ /** Id of the registered title, or `undefined` when none is rendered. */
24
45
  titleId: string | undefined;
46
+ /** Id of the registered description, or `undefined` when none is rendered. */
25
47
  descriptionId: string | undefined;
48
+ /** Registers (or clears) the title id for `aria-labelledby`. */
26
49
  registerTitle: (id: string | undefined) => void;
50
+ /** Registers (or clears) the description id for `aria-describedby`. */
27
51
  registerDescription: (id: string | undefined) => void;
28
52
  };
29
53
 
54
+ /** Props for `Modal.Content` — native `<dialog>` props plus dismissal callbacks. */
30
55
  export type ModalContentProps = ComponentProps<"dialog"> &
31
56
  ModalContentCallbacks;
32
57
 
33
- type UncontrolledModalRootProps = {
58
+ /** `Modal.Root` props for the uncontrolled (self-managed) open state. */
59
+ export type UncontrolledModalRootProps = {
60
+ /** Initial open state when uncontrolled. */
34
61
  defaultOpen?: boolean;
35
62
  open?: never;
63
+ /** Called whenever the open state changes. */
36
64
  onOpenChange?: (open: boolean) => void;
37
65
  };
38
66
 
39
- type ControlledModalRootProps = {
67
+ /** `Modal.Root` props for the controlled open state. */
68
+ export type ControlledModalRootProps = {
69
+ /** Controlled open state. */
40
70
  open: boolean;
71
+ /** Called whenever the modal requests an open-state change. */
41
72
  onOpenChange: (open: boolean) => void;
42
73
  defaultOpen?: never;
43
74
  };
44
75
 
76
+ /** Props for `Modal.Root`, in either the controlled or uncontrolled mode. */
45
77
  export type ModalRootProps = {
78
+ /** Modal sub-components. */
46
79
  children?: ReactNode;
47
80
  } & (UncontrolledModalRootProps | ControlledModalRootProps) & {
81
+ /** Ref receiving the imperative open/close handle. */
48
82
  ref?: Ref<ModalImperativeApi>;
49
83
  };
50
84
 
85
+ /** Props for `Modal.Trigger` — a `<button>` that opens the modal. */
51
86
  export type ModalTriggerProps = ComponentProps<"button"> & {
87
+ /** Render into the consumer's own element instead of a `<button>`. */
52
88
  asChild?: boolean;
53
89
  };
54
90
 
91
+ /** Props for `Modal.Close` — a `<button>` that closes the modal. */
55
92
  export type ModalCloseProps = ComponentProps<"button"> & {
93
+ /** Render into the consumer's own element instead of a `<button>`. */
56
94
  asChild?: boolean;
57
95
  };
58
96
 
97
+ /** Props for `Modal.Portal` — renders its children into `container`. */
59
98
  export type ModalPortalProps = {
99
+ /** Content to portal. */
60
100
  children?: ReactNode;
101
+ /** Target element to portal into. Defaults to `document.body`. */
61
102
  container?: HTMLElement;
103
+ /** Keep the portal mounted even while the modal is closed. */
62
104
  forceMount?: boolean;
63
105
  };
64
106
 
107
+ /** Props for `Modal.Overlay` — the click-outside backdrop. */
65
108
  export type ModalOverlayProps = ComponentProps<"div"> & {
109
+ /** Render into the consumer's own element instead of a `<div>`. */
66
110
  asChild?: boolean;
111
+ /** Keep the overlay mounted even while the modal is closed. */
67
112
  forceMount?: boolean;
68
113
  };
69
114
 
115
+ /** Props for `Modal.Title` — supplies the dialog's accessible name. */
70
116
  export type ModalTitleProps = HTMLAttributes<HTMLElement> & {
117
+ /** Render into the consumer's own element instead of an `<h2>`. */
71
118
  asChild?: boolean;
72
119
  };
73
120
 
121
+ /** Props for `Modal.Description` — supplies the dialog's accessible description. */
74
122
  export type ModalDescriptionProps = HTMLAttributes<HTMLElement> & {
123
+ /** Render into the consumer's own element instead of a `<p>`. */
75
124
  asChild?: boolean;
76
125
  };
@@ -1,3 +1,4 @@
1
+ import type { ReactPortal } from "react";
1
2
  import { createPortal } from "react-dom";
2
3
 
3
4
  import type { PortalProps } from "./types";
@@ -19,10 +20,11 @@ import type { PortalProps } from "./types";
19
20
  * <div role="dialog">…</div>
20
21
  * </Portal>
21
22
  */
22
- function Portal({ children, container }: PortalProps) {
23
+ function Portal({ children, container }: PortalProps): ReactPortal {
23
24
  return createPortal(children, container ?? document.body);
24
25
  }
25
26
 
27
+ /** @internal */
26
28
  Portal.displayName = "Portal";
27
29
 
28
30
  export { Portal };
@@ -1,5 +1,9 @@
1
1
  import { ReactNode } from "react";
2
2
 
3
+ /**
4
+ * Props for {@link Portal} — the `children` to teleport and an optional
5
+ * `container` to render into (defaults to `document.body`).
6
+ */
3
7
  export type PortalProps = {
4
8
  children?: ReactNode;
5
9
  container?: HTMLElement;
@@ -1,9 +1,10 @@
1
1
  import { useMemo } from "react";
2
+ import type { ReactElement } from "react";
2
3
 
3
- import { Slot } from "../Slot";
4
+ import { Slot } from "../Slot/index.ts";
4
5
 
5
6
  import { ProgressContext } from "./ProgressContext";
6
- import { useProgressContext, useProgressRoot } from "./hooks";
7
+ import { useProgressContext, useProgressRoot } from "./hooks/index.ts";
7
8
  import { ProgressIndicatorProps, ProgressRootProps } from "./types";
8
9
 
9
10
  function defaultGetValueLabel(value: number, max: number): string {
@@ -46,14 +47,14 @@ function defaultGetValueLabel(value: number, max: number): string {
46
47
  * </Progress.Root>
47
48
  * ```
48
49
  */
49
- function ProgressRoot({
50
+ export function ProgressRoot({
50
51
  value,
51
52
  max,
52
53
  getValueLabel = defaultGetValueLabel,
53
54
  asChild = false,
54
55
  children,
55
56
  ...rest
56
- }: ProgressRootProps) {
57
+ }: ProgressRootProps): ReactElement {
57
58
  const resolved = useProgressRoot({ value, max });
58
59
  const { state } = resolved;
59
60
 
@@ -92,6 +93,7 @@ function ProgressRoot({
92
93
  );
93
94
  }
94
95
 
96
+ /** @internal */
95
97
  ProgressRoot.displayName = "ProgressRoot";
96
98
 
97
99
  /**
@@ -116,11 +118,11 @@ ProgressRoot.displayName = "ProgressRoot";
116
118
  *
117
119
  * @throws if rendered outside a `Progress.Root`.
118
120
  */
119
- function ProgressIndicator({
121
+ export function ProgressIndicator({
120
122
  asChild = false,
121
123
  children,
122
124
  ...rest
123
- }: ProgressIndicatorProps) {
125
+ }: ProgressIndicatorProps): ReactElement {
124
126
  const { value, max, state } = useProgressContext();
125
127
  const indicatorProps = {
126
128
  ...rest,
@@ -134,9 +136,11 @@ function ProgressIndicator({
134
136
  return <div {...indicatorProps}>{children}</div>;
135
137
  }
136
138
 
139
+ /** @internal */
137
140
  ProgressIndicator.displayName = "ProgressIndicator";
138
141
 
139
- type TProgressCompound = typeof ProgressRoot & {
142
+ /** Type of the {@link Progress} compound: the root callable plus its attached sub-components. */
143
+ export type TProgressCompound = typeof ProgressRoot & {
140
144
  Root: typeof ProgressRoot;
141
145
  Indicator: typeof ProgressIndicator;
142
146
  };
@@ -173,6 +177,7 @@ const ProgressCompound: TProgressCompound = Object.assign(ProgressRoot, {
173
177
  Indicator: ProgressIndicator,
174
178
  });
175
179
 
180
+ /** @internal */
176
181
  ProgressCompound.displayName = "Progress";
177
182
 
178
183
  export { ProgressCompound as Progress };
@@ -1,15 +1,27 @@
1
- import { createStrictContext } from "../utils";
1
+ import type { Context } from "react";
2
+ import { createStrictContext } from "../utils/index.ts";
2
3
 
3
4
  import { ProgressState } from "./types";
4
5
 
6
+ /**
7
+ * Context value published by {@link Progress.Root} and consumed by
8
+ * {@link Progress.Indicator} — the current `value` (or `null` while
9
+ * indeterminate), the `max`, and the derived loading `state`.
10
+ */
5
11
  export type ProgressContextValue = {
6
12
  value: number | null;
7
13
  max: number;
8
14
  state: ProgressState;
9
15
  };
10
16
 
11
- export const [ProgressContext, useProgressContext] =
12
- createStrictContext<ProgressContextValue>(
13
- "Progress.Indicator must be rendered inside a <Progress.Root>.",
14
- "ProgressContext",
15
- );
17
+ const progressContextPair = createStrictContext<ProgressContextValue>(
18
+ "Progress.Indicator must be rendered inside a <Progress.Root>.",
19
+ "ProgressContext",
20
+ );
21
+
22
+ /** React context carrying the {@link ProgressContextValue}; `null` when no provider is present. */
23
+ export const ProgressContext: Context<ProgressContextValue | null> =
24
+ progressContextPair[0];
25
+ /** Reads the {@link ProgressContextValue}; throws if used outside a {@link Progress.Root}. */
26
+ export const useProgressContext: () => ProgressContextValue =
27
+ progressContextPair[1];
@@ -1,8 +1,9 @@
1
1
  import { useEffect, useMemo, useRef } from "react";
2
+ import type { ReactElement } from "react";
2
3
 
3
- import { useDirection } from "../DirectionProvider";
4
- import { useRovingTabindex } from "../hooks";
5
- import { Slot, composeEventHandlers, composeRefs } from "../Slot";
4
+ import { useDirection } from "../DirectionProvider/index.ts";
5
+ import { useRovingTabindex } from "../hooks/index.ts";
6
+ import { Slot, composeEventHandlers, composeRefs } from "../Slot/index.ts";
6
7
 
7
8
  import { RadioCardContext } from "./RadioCardContext";
8
9
  import { RadioCardItemContext } from "./RadioCardItemContext";
@@ -10,7 +11,7 @@ import {
10
11
  useRadioCardContext,
11
12
  useRadioCardItemContext,
12
13
  useRadioCardRoot,
13
- } from "./hooks";
14
+ } from "./hooks/index.ts";
14
15
  import {
15
16
  RadioCardIndicatorProps,
16
17
  RadioCardItemProps,
@@ -72,7 +73,7 @@ import {
72
73
  * </RadioCard.Root>
73
74
  * ```
74
75
  */
75
- function RadioCardRoot({
76
+ export function RadioCardRoot({
76
77
  defaultValue,
77
78
  value: controlledValue,
78
79
  onValueChange,
@@ -81,7 +82,7 @@ function RadioCardRoot({
81
82
  asChild = false,
82
83
  children,
83
84
  ...rest
84
- }: RadioCardRootProps) {
85
+ }: RadioCardRootProps): ReactElement {
85
86
  const resolvedDir = dir ?? useDirection();
86
87
  const { value, select, registerItem, itemValues, disabledValues, focusItem } =
87
88
  useRadioCardRoot({
@@ -128,6 +129,7 @@ function RadioCardRoot({
128
129
  );
129
130
  }
130
131
 
132
+ /** @internal */
131
133
  RadioCardRoot.displayName = "RadioCardRoot";
132
134
 
133
135
  /**
@@ -157,7 +159,7 @@ RadioCardRoot.displayName = "RadioCardRoot";
157
159
  *
158
160
  * @throws if rendered outside a `RadioCard.Root`.
159
161
  */
160
- function RadioCardItem({
162
+ export function RadioCardItem({
161
163
  value,
162
164
  children,
163
165
  onClick,
@@ -166,7 +168,7 @@ function RadioCardItem({
166
168
  asChild = false,
167
169
  ref,
168
170
  ...rest
169
- }: RadioCardItemProps) {
171
+ }: RadioCardItemProps): ReactElement {
170
172
  const {
171
173
  value: selectedValue,
172
174
  select,
@@ -231,6 +233,7 @@ function RadioCardItem({
231
233
  );
232
234
  }
233
235
 
236
+ /** @internal */
234
237
  RadioCardItem.displayName = "RadioCardItem";
235
238
 
236
239
  /**
@@ -259,12 +262,12 @@ RadioCardItem.displayName = "RadioCardItem";
259
262
  *
260
263
  * @throws if rendered outside a `RadioCard.Item`.
261
264
  */
262
- function RadioCardIndicator({
265
+ export function RadioCardIndicator({
263
266
  children,
264
267
  forceMount,
265
268
  asChild = false,
266
269
  ...rest
267
- }: RadioCardIndicatorProps) {
270
+ }: RadioCardIndicatorProps): ReactElement | null {
268
271
  const { checked } = useRadioCardItemContext();
269
272
  if (!checked && !forceMount) return null;
270
273
  const indicatorProps = {
@@ -278,9 +281,12 @@ function RadioCardIndicator({
278
281
  return <span {...indicatorProps}>{children}</span>;
279
282
  }
280
283
 
284
+ /** @internal */
281
285
  RadioCardIndicator.displayName = "RadioCardIndicator";
282
286
 
283
- type TRadioCardCompound = typeof RadioCardRoot & {
287
+ /** Type of the {@link RadioCard} compound: the callable `RadioCard.Root`
288
+ * component augmented with its sub-components as static properties. */
289
+ export type TRadioCardCompound = typeof RadioCardRoot & {
284
290
  Root: typeof RadioCardRoot;
285
291
  Item: typeof RadioCardItem;
286
292
  Indicator: typeof RadioCardIndicator;
@@ -1,7 +1,11 @@
1
- import { createStrictContext } from "../utils";
1
+ import type { Context } from "react";
2
+ import { createStrictContext } from "../utils/index.ts";
2
3
 
3
4
  import { RadioCardOrientation, RadioCardReadingDirection } from "./types";
4
5
 
6
+ /** The value shared by `RadioCard.Root` with its items through context: the
7
+ * current selection and setter, the item registry used for roving focus and
8
+ * keyboard navigation, and the resolved orientation/direction. */
5
9
  export type RadioCardContextValue = {
6
10
  value: string | undefined;
7
11
  select: (value: string) => void;
@@ -17,7 +21,15 @@ export type RadioCardContextValue = {
17
21
  dir: RadioCardReadingDirection;
18
22
  };
19
23
 
20
- export const [RadioCardContext, useRadioCardContext] =
21
- createStrictContext<RadioCardContextValue>(
22
- "RadioCard sub-components must be rendered inside a <RadioCard.Root>.",
23
- );
24
+ const radioCardContextPair = createStrictContext<RadioCardContextValue>(
25
+ "RadioCard sub-components must be rendered inside a <RadioCard.Root>.",
26
+ );
27
+
28
+ /** Strict React context carrying the {@link RadioCardContextValue} from
29
+ * `RadioCard.Root` to its items. `null` when read outside a `RadioCard.Root`. */
30
+ export const RadioCardContext: Context<RadioCardContextValue | null> =
31
+ radioCardContextPair[0];
32
+ /** Reads the {@link RadioCardContextValue}; throws when used outside a
33
+ * `RadioCard.Root`. */
34
+ export const useRadioCardContext: () => RadioCardContextValue =
35
+ radioCardContextPair[1];
@@ -1,10 +1,23 @@
1
- import { createStrictContext } from "../utils";
1
+ import type { Context } from "react";
2
+ import { createStrictContext } from "../utils/index.ts";
2
3
 
4
+ /** The value shared by `RadioCard.Item` with its `RadioCard.Indicator`,
5
+ * exposing whether the item is currently selected. */
3
6
  export type RadioCardItemContextValue = {
7
+ /** Whether the enclosing item is the selected card. */
4
8
  checked: boolean;
5
9
  };
6
10
 
7
- export const [RadioCardItemContext, useRadioCardItemContext] =
8
- createStrictContext<RadioCardItemContextValue>(
9
- "RadioCard.Indicator must be rendered inside a <RadioCard.Item>.",
10
- );
11
+ const radioCardItemContextPair = createStrictContext<RadioCardItemContextValue>(
12
+ "RadioCard.Indicator must be rendered inside a <RadioCard.Item>.",
13
+ );
14
+
15
+ /** Strict React context carrying the {@link RadioCardItemContextValue} from
16
+ * `RadioCard.Item` to its indicator. `null` when read outside a
17
+ * `RadioCard.Item`. */
18
+ export const RadioCardItemContext: Context<RadioCardItemContextValue | null> =
19
+ radioCardItemContextPair[0];
20
+ /** Reads the {@link RadioCardItemContextValue}; throws when used outside a
21
+ * `RadioCard.Item`. */
22
+ export const useRadioCardItemContext: () => RadioCardItemContextValue =
23
+ radioCardItemContextPair[1];
@@ -1,7 +1,7 @@
1
1
  import { render, screen } from "@testing-library/react";
2
2
  import userEvent from "@testing-library/user-event";
3
3
 
4
- import { DirectionProvider } from "../../DirectionProvider";
4
+ import { DirectionProvider } from "../../DirectionProvider/index.ts";
5
5
  import { RadioCard } from "../RadioCard";
6
6
 
7
7
  describe("RadioCard reading direction", () => {
@@ -1,6 +1,6 @@
1
1
  import { useCallback, useMemo } from "react";
2
2
 
3
- import { useCollection, useControllableState } from "../../hooks";
3
+ import { useCollection, useControllableState } from "../../hooks/index.ts";
4
4
 
5
5
  type UseRadioCardRootArgs = {
6
6
  defaultValue?: string;
@@ -10,30 +10,48 @@ export type RadioCardOrientation = "horizontal" | "vertical" | "both";
10
10
  /** Reading direction — swaps the horizontal arrow pair when `"rtl"`. */
11
11
  export type RadioCardReadingDirection = "ltr" | "rtl";
12
12
 
13
- type RadioCardRootBaseProps = Omit<ComponentProps<"div">, "role"> & {
13
+ /** Props shared by both controlled and uncontrolled `RadioCard.Root` usage.
14
+ * Extends the native `<div>` props (minus `role`). */
15
+ export type RadioCardRootBaseProps = Omit<ComponentProps<"div">, "role"> & {
14
16
  children?: ReactNode;
17
+ /** Ref to the rendered root element. */
15
18
  ref?: Ref<HTMLDivElement>;
19
+ /** Render the child element instead of the default `<div>`. */
16
20
  asChild?: boolean;
21
+ /** Which arrow keys navigate the group. */
17
22
  orientation?: RadioCardOrientation;
23
+ /** Reading direction for horizontal arrow navigation. */
18
24
  dir?: RadioCardReadingDirection;
19
25
  };
20
26
 
21
- type RadioCardRootUncontrolledProps = RadioCardRootBaseProps & {
27
+ /** Uncontrolled `RadioCard.Root` props: the component owns the selection,
28
+ * seeded by an optional `defaultValue`. */
29
+ export type RadioCardRootUncontrolledProps = RadioCardRootBaseProps & {
30
+ /** Value selected on first render. */
22
31
  defaultValue?: string;
23
32
  value?: never;
33
+ /** Called with the value when the selection changes. */
24
34
  onValueChange?: (value: string) => void;
25
35
  };
26
36
 
27
- type RadioCardRootControlledProps = RadioCardRootBaseProps & {
37
+ /** Controlled `RadioCard.Root` props: the caller owns the selection via
38
+ * `value` and is notified through the required `onValueChange`. */
39
+ export type RadioCardRootControlledProps = RadioCardRootBaseProps & {
28
40
  defaultValue?: never;
41
+ /** Value of the currently selected card. */
29
42
  value: string;
43
+ /** Called with the value when the user selects a card. */
30
44
  onValueChange: (value: string) => void;
31
45
  };
32
46
 
47
+ /** Props for `RadioCard.Root` — resolves to either the controlled or
48
+ * uncontrolled prop shape. */
33
49
  export type RadioCardRootProps =
34
50
  | RadioCardRootUncontrolledProps
35
51
  | RadioCardRootControlledProps;
36
52
 
53
+ /** Props for `RadioCard.Item` — a selectable card rendered as a
54
+ * `role="radio"` button, identified by its required `value`. */
37
55
  export type RadioCardItemProps = Omit<
38
56
  ComponentProps<"button">,
39
57
  "type" | "role" | "aria-checked" | "value"
@@ -44,6 +62,9 @@ export type RadioCardItemProps = Omit<
44
62
  asChild?: boolean;
45
63
  };
46
64
 
65
+ /** Props for `RadioCard.Indicator` — the visual marker rendered inside the
66
+ * selected item. By default it renders only when its item is checked; set
67
+ * `forceMount` to keep it mounted for exit transitions. */
47
68
  export type RadioCardIndicatorProps = ComponentProps<"span"> & {
48
69
  children?: ReactNode;
49
70
  forceMount?: boolean;
@@ -1,8 +1,9 @@
1
1
  import { useEffect, useMemo, useRef } from "react";
2
+ import type { ReactElement } from "react";
2
3
 
3
- import { useDirection } from "../DirectionProvider";
4
- import { useRovingTabindex } from "../hooks";
5
- import { Slot, composeEventHandlers, composeRefs } from "../Slot";
4
+ import { useDirection } from "../DirectionProvider/index.ts";
5
+ import { useRovingTabindex } from "../hooks/index.ts";
6
+ import { Slot, composeEventHandlers, composeRefs } from "../Slot/index.ts";
6
7
 
7
8
  import { RadioGroupContext } from "./RadioGroupContext";
8
9
  import { RadioGroupItemContext } from "./RadioGroupItemContext";
@@ -10,7 +11,7 @@ import {
10
11
  useRadioGroupContext,
11
12
  useRadioGroupItemContext,
12
13
  useRadioGroupRoot,
13
- } from "./hooks";
14
+ } from "./hooks/index.ts";
14
15
  import {
15
16
  RadioGroupIndicatorProps,
16
17
  RadioGroupItemProps,
@@ -73,7 +74,7 @@ import {
73
74
  * </RadioGroup.Root>
74
75
  * ```
75
76
  */
76
- function RadioGroupRoot({
77
+ export function RadioGroupRoot({
77
78
  defaultValue,
78
79
  value: controlledValue,
79
80
  onValueChange,
@@ -82,7 +83,7 @@ function RadioGroupRoot({
82
83
  asChild = false,
83
84
  children,
84
85
  ...rest
85
- }: RadioGroupRootProps) {
86
+ }: RadioGroupRootProps): ReactElement {
86
87
  const resolvedDir = dir ?? useDirection();
87
88
  const { value, select, registerItem, itemValues, disabledValues, focusItem } =
88
89
  useRadioGroupRoot({
@@ -129,6 +130,7 @@ function RadioGroupRoot({
129
130
  );
130
131
  }
131
132
 
133
+ /** @internal */
132
134
  RadioGroupRoot.displayName = "RadioGroupRoot";
133
135
 
134
136
  /**
@@ -163,7 +165,7 @@ RadioGroupRoot.displayName = "RadioGroupRoot";
163
165
  *
164
166
  * @throws if rendered outside a `RadioGroup.Root`.
165
167
  */
166
- function RadioGroupItem({
168
+ export function RadioGroupItem({
167
169
  value,
168
170
  children,
169
171
  onClick,
@@ -172,7 +174,7 @@ function RadioGroupItem({
172
174
  asChild = false,
173
175
  ref,
174
176
  ...rest
175
- }: RadioGroupItemProps) {
177
+ }: RadioGroupItemProps): ReactElement {
176
178
  const {
177
179
  value: selectedValue,
178
180
  select,
@@ -237,6 +239,7 @@ function RadioGroupItem({
237
239
  );
238
240
  }
239
241
 
242
+ /** @internal */
240
243
  RadioGroupItem.displayName = "RadioGroupItem";
241
244
 
242
245
  /**
@@ -277,12 +280,12 @@ RadioGroupItem.displayName = "RadioGroupItem";
277
280
  *
278
281
  * @throws if rendered outside a `RadioGroup.Item`.
279
282
  */
280
- function RadioGroupIndicator({
283
+ export function RadioGroupIndicator({
281
284
  children,
282
285
  forceMount,
283
286
  asChild = false,
284
287
  ...rest
285
- }: RadioGroupIndicatorProps) {
288
+ }: RadioGroupIndicatorProps): ReactElement | null {
286
289
  const { checked } = useRadioGroupItemContext();
287
290
  if (!checked && !forceMount) return null;
288
291
  const indicatorProps = {
@@ -296,9 +299,11 @@ function RadioGroupIndicator({
296
299
  return <span {...indicatorProps}>{children}</span>;
297
300
  }
298
301
 
302
+ /** @internal */
299
303
  RadioGroupIndicator.displayName = "RadioGroupIndicator";
300
304
 
301
- type TRadioGroupCompound = typeof RadioGroupRoot & {
305
+ /** Type of the {@link RadioGroup} compound: the root callable plus its attached sub-components. */
306
+ export type TRadioGroupCompound = typeof RadioGroupRoot & {
302
307
  Root: typeof RadioGroupRoot;
303
308
  Item: typeof RadioGroupItem;
304
309
  Indicator: typeof RadioGroupIndicator;
@@ -348,6 +353,7 @@ const RadioGroupCompound: TRadioGroupCompound = Object.assign(RadioGroupRoot, {
348
353
  Indicator: RadioGroupIndicator,
349
354
  });
350
355
 
356
+ /** @internal */
351
357
  RadioGroupCompound.displayName = "RadioGroup";
352
358
 
353
359
  export { RadioGroupCompound as RadioGroup };
@@ -1,4 +1,4 @@
1
- import { createStrictContext } from "../utils";
1
+ import { createStrictContext } from "../utils/index.ts";
2
2
 
3
3
  import { RadioGroupOrientation, RadioGroupReadingDirection } from "./types";
4
4
 
@@ -1,4 +1,4 @@
1
- import { createStrictContext } from "../utils";
1
+ import { createStrictContext } from "../utils/index.ts";
2
2
 
3
3
  export type RadioGroupItemContextValue = {
4
4
  checked: boolean;