@chassis-ui/react 0.0.0-20260831090525

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 (275) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +65 -0
  3. package/dist/index.d.ts +5909 -0
  4. package/dist/index.js +2 -0
  5. package/dist/style.css +1 -0
  6. package/package.json +114 -0
  7. package/src/components/accordion/Accordion.tsx +190 -0
  8. package/src/components/accordion/AccordionBody.tsx +46 -0
  9. package/src/components/accordion/AccordionHeader.tsx +21 -0
  10. package/src/components/accordion/AccordionItem.tsx +59 -0
  11. package/src/components/accordion/context.ts +8 -0
  12. package/src/components/accordion/index.ts +8 -0
  13. package/src/components/autocomplete/Autocomplete.tsx +464 -0
  14. package/src/components/autocomplete/AutocompleteGroup.tsx +20 -0
  15. package/src/components/autocomplete/AutocompleteItem.tsx +18 -0
  16. package/src/components/autocomplete/index.ts +6 -0
  17. package/src/components/avatar/Avatar.tsx +134 -0
  18. package/src/components/avatar/AvatarImage.tsx +19 -0
  19. package/src/components/avatar/AvatarStack.tsx +98 -0
  20. package/src/components/avatar/index.ts +6 -0
  21. package/src/components/badge/Badge.tsx +94 -0
  22. package/src/components/badge/index.ts +2 -0
  23. package/src/components/breadcrumb/Breadcrumb.tsx +59 -0
  24. package/src/components/breadcrumb/BreadcrumbItem.tsx +38 -0
  25. package/src/components/breadcrumb/index.ts +4 -0
  26. package/src/components/button/Button.tsx +229 -0
  27. package/src/components/button/index.ts +2 -0
  28. package/src/components/button-group/ButtonGroup.tsx +55 -0
  29. package/src/components/button-group/ButtonToolbar.tsx +47 -0
  30. package/src/components/button-group/index.ts +4 -0
  31. package/src/components/calendar/Calendar.scss +57 -0
  32. package/src/components/calendar/Calendar.tsx +324 -0
  33. package/src/components/calendar/CalendarMonthBlock.tsx +57 -0
  34. package/src/components/calendar/CalendarMonthGrid.tsx +92 -0
  35. package/src/components/calendar/CalendarMonthYearPicker.tsx +162 -0
  36. package/src/components/calendar/CalendarNavButton.tsx +24 -0
  37. package/src/components/calendar/CalendarWeekGrid.tsx +58 -0
  38. package/src/components/calendar/CalendarYearGrid.tsx +126 -0
  39. package/src/components/calendar/RangeCalendar.scss +95 -0
  40. package/src/components/calendar/RangeCalendar.tsx +361 -0
  41. package/src/components/calendar/index.ts +5 -0
  42. package/src/components/card/Card.tsx +173 -0
  43. package/src/components/card/CardBody.tsx +75 -0
  44. package/src/components/card/CardFooter.tsx +47 -0
  45. package/src/components/card/CardGroup.tsx +51 -0
  46. package/src/components/card/CardHeader.tsx +47 -0
  47. package/src/components/card/CardImage.tsx +70 -0
  48. package/src/components/card/CardImageOverlay.tsx +47 -0
  49. package/src/components/card/CardLink.tsx +40 -0
  50. package/src/components/card/CardSubtitle.tsx +47 -0
  51. package/src/components/card/CardText.tsx +47 -0
  52. package/src/components/card/CardTitle.tsx +47 -0
  53. package/src/components/card/index.ts +22 -0
  54. package/src/components/carousel/Carousel.tsx +742 -0
  55. package/src/components/carousel/CarouselControlButton.tsx +74 -0
  56. package/src/components/carousel/CarouselControlNext.tsx +22 -0
  57. package/src/components/carousel/CarouselControlPrev.tsx +22 -0
  58. package/src/components/carousel/CarouselIndicators.tsx +44 -0
  59. package/src/components/carousel/CarouselInner.tsx +56 -0
  60. package/src/components/carousel/CarouselItem.tsx +51 -0
  61. package/src/components/carousel/CarouselOverlay.tsx +51 -0
  62. package/src/components/carousel/CarouselPlayPause.tsx +61 -0
  63. package/src/components/carousel/carouselEngine.ts +170 -0
  64. package/src/components/carousel/context.ts +29 -0
  65. package/src/components/carousel/index.ts +21 -0
  66. package/src/components/checkbox/Checkbox.tsx +236 -0
  67. package/src/components/checkbox/CheckboxGroup.tsx +145 -0
  68. package/src/components/checkbox/context.ts +14 -0
  69. package/src/components/checkbox/index.ts +4 -0
  70. package/src/components/chip/Chip.tsx +183 -0
  71. package/src/components/chip/index.ts +2 -0
  72. package/src/components/chip-input/ChipInput.tsx +363 -0
  73. package/src/components/chip-input/ChipList.tsx +95 -0
  74. package/src/components/chip-input/index.ts +2 -0
  75. package/src/components/close-button/CloseButton.tsx +205 -0
  76. package/src/components/close-button/index.ts +2 -0
  77. package/src/components/collapse/Collapse.tsx +128 -0
  78. package/src/components/collapse/index.ts +2 -0
  79. package/src/components/color-input/ColorInput.tsx +106 -0
  80. package/src/components/color-input/index.ts +2 -0
  81. package/src/components/combobox/Combobox.tsx +320 -0
  82. package/src/components/combobox/ComboboxGroup.tsx +27 -0
  83. package/src/components/combobox/ComboboxItem.tsx +44 -0
  84. package/src/components/combobox/ComboboxListBox.tsx +98 -0
  85. package/src/components/combobox/index.ts +6 -0
  86. package/src/components/datepicker/CalendarToggleButton.tsx +58 -0
  87. package/src/components/datepicker/ClearButton.tsx +35 -0
  88. package/src/components/datepicker/DateField.tsx +54 -0
  89. package/src/components/datepicker/DatePicker.scss +51 -0
  90. package/src/components/datepicker/DatePicker.tsx +574 -0
  91. package/src/components/datepicker/DateRangePicker.scss +11 -0
  92. package/src/components/datepicker/DateRangePicker.tsx +343 -0
  93. package/src/components/datepicker/index.ts +4 -0
  94. package/src/components/datepicker/renderDatePickerShell.tsx +104 -0
  95. package/src/components/drawer/Drawer.tsx +211 -0
  96. package/src/components/drawer/DrawerBody.tsx +47 -0
  97. package/src/components/drawer/DrawerFooter.tsx +51 -0
  98. package/src/components/drawer/DrawerHeader.tsx +60 -0
  99. package/src/components/drawer/DrawerTitle.tsx +49 -0
  100. package/src/components/drawer/index.ts +10 -0
  101. package/src/components/file-input/FileInput.tsx +100 -0
  102. package/src/components/file-input/index.ts +2 -0
  103. package/src/components/flex/Flex.tsx +134 -0
  104. package/src/components/flex/index.ts +2 -0
  105. package/src/components/floating-input/FloatingInput.tsx +94 -0
  106. package/src/components/floating-input/index.ts +2 -0
  107. package/src/components/form/Form.tsx +26 -0
  108. package/src/components/form/FormFeedback.tsx +64 -0
  109. package/src/components/form/FormHelp.tsx +46 -0
  110. package/src/components/form/FormLabel.tsx +26 -0
  111. package/src/components/form/index.ts +8 -0
  112. package/src/components/form/renderFormCheck.tsx +86 -0
  113. package/src/components/form-field/FormField.tsx +84 -0
  114. package/src/components/form-field/index.ts +2 -0
  115. package/src/components/form-field/renderFormField.tsx +74 -0
  116. package/src/components/grid/Col.tsx +69 -0
  117. package/src/components/grid/Container.tsx +59 -0
  118. package/src/components/grid/Grid.tsx +86 -0
  119. package/src/components/grid/GridItem.tsx +123 -0
  120. package/src/components/grid/Row.tsx +71 -0
  121. package/src/components/grid/gap.ts +8 -0
  122. package/src/components/grid/index.ts +10 -0
  123. package/src/components/icon/Icon.tsx +74 -0
  124. package/src/components/icon/index.ts +2 -0
  125. package/src/components/input-adorn/InputAdorn.tsx +55 -0
  126. package/src/components/input-adorn/index.ts +2 -0
  127. package/src/components/input-group/InputGroup.tsx +50 -0
  128. package/src/components/input-group/InputGroupAddon.tsx +50 -0
  129. package/src/components/input-group/index.ts +4 -0
  130. package/src/components/link/Link.tsx +148 -0
  131. package/src/components/link/index.ts +2 -0
  132. package/src/components/list/List.tsx +193 -0
  133. package/src/components/list/ListItem.tsx +91 -0
  134. package/src/components/list/index.ts +4 -0
  135. package/src/components/menu/Menu.tsx +398 -0
  136. package/src/components/menu/MenuDivider.tsx +19 -0
  137. package/src/components/menu/MenuHeader.tsx +55 -0
  138. package/src/components/menu/MenuItem.tsx +85 -0
  139. package/src/components/menu/MenuItemDef.ts +83 -0
  140. package/src/components/menu/MenuList.tsx +142 -0
  141. package/src/components/menu/MenuSubmenu.tsx +284 -0
  142. package/src/components/menu/MenuSubmenuBack.tsx +39 -0
  143. package/src/components/menu/MenuText.tsx +47 -0
  144. package/src/components/menu/MenuToggle.tsx +148 -0
  145. package/src/components/menu/index.ts +19 -0
  146. package/src/components/menu/menuNavigation.ts +106 -0
  147. package/src/components/menu/submenuGroup.ts +43 -0
  148. package/src/components/modal/Modal.tsx +162 -0
  149. package/src/components/modal/ModalBody.tsx +47 -0
  150. package/src/components/modal/ModalFooter.tsx +51 -0
  151. package/src/components/modal/ModalHeader.tsx +60 -0
  152. package/src/components/modal/ModalTitle.tsx +49 -0
  153. package/src/components/modal/index.ts +10 -0
  154. package/src/components/nav/Nav.tsx +97 -0
  155. package/src/components/nav/NavItem.tsx +42 -0
  156. package/src/components/nav/NavLink.tsx +48 -0
  157. package/src/components/nav/NavTitle.tsx +22 -0
  158. package/src/components/nav/index.ts +7 -0
  159. package/src/components/navbar/Navbar.tsx +112 -0
  160. package/src/components/navbar/NavbarBrand.tsx +55 -0
  161. package/src/components/navbar/NavbarNav.tsx +47 -0
  162. package/src/components/navbar/NavbarText.tsx +47 -0
  163. package/src/components/navbar/NavbarToggler.tsx +34 -0
  164. package/src/components/navbar/index.ts +10 -0
  165. package/src/components/notification/Notification.scss +15 -0
  166. package/src/components/notification/Notification.tsx +187 -0
  167. package/src/components/notification/NotificationIcon.tsx +21 -0
  168. package/src/components/notification/NotificationStack.tsx +66 -0
  169. package/src/components/notification/NotificationText.tsx +47 -0
  170. package/src/components/notification/NotificationTitle.tsx +47 -0
  171. package/src/components/notification/context.ts +14 -0
  172. package/src/components/notification/index.ts +17 -0
  173. package/src/components/notification/notificationQueue.ts +48 -0
  174. package/src/components/otp-input/OtpBox.tsx +68 -0
  175. package/src/components/otp-input/OtpInput.tsx +341 -0
  176. package/src/components/otp-input/index.ts +2 -0
  177. package/src/components/pagination/Pagination.tsx +199 -0
  178. package/src/components/pagination/PaginationItem.tsx +133 -0
  179. package/src/components/pagination/index.ts +4 -0
  180. package/src/components/password-strength/PasswordStrength.tsx +155 -0
  181. package/src/components/password-strength/index.ts +2 -0
  182. package/src/components/password-strength/strengthScore.ts +79 -0
  183. package/src/components/placeholder/Placeholder.tsx +178 -0
  184. package/src/components/placeholder/index.ts +2 -0
  185. package/src/components/popover/Popover.tsx +243 -0
  186. package/src/components/popover/index.ts +2 -0
  187. package/src/components/progress/Progress.tsx +154 -0
  188. package/src/components/progress/ProgressBar.tsx +88 -0
  189. package/src/components/progress/index.ts +4 -0
  190. package/src/components/radio/Radio.tsx +88 -0
  191. package/src/components/radio/RadioGroup.tsx +142 -0
  192. package/src/components/radio/context.ts +14 -0
  193. package/src/components/radio/index.ts +4 -0
  194. package/src/components/range-input/RangeInput.tsx +109 -0
  195. package/src/components/range-input/index.ts +2 -0
  196. package/src/components/select/Select.tsx +277 -0
  197. package/src/components/select/index.ts +2 -0
  198. package/src/components/skeleton/Skeleton.tsx +89 -0
  199. package/src/components/skeleton/SkeletonLoader.tsx +91 -0
  200. package/src/components/skeleton/index.ts +4 -0
  201. package/src/components/spinner/Spinner.tsx +77 -0
  202. package/src/components/spinner/index.ts +2 -0
  203. package/src/components/stack/Stack.tsx +81 -0
  204. package/src/components/stack/index.ts +2 -0
  205. package/src/components/stepper/Stepper.tsx +172 -0
  206. package/src/components/stepper/StepperItem.tsx +66 -0
  207. package/src/components/stepper/index.ts +4 -0
  208. package/src/components/switch/Switch.tsx +246 -0
  209. package/src/components/switch/index.ts +2 -0
  210. package/src/components/table/Table.css +39 -0
  211. package/src/components/table/Table.tsx +390 -0
  212. package/src/components/table/TableBody.tsx +25 -0
  213. package/src/components/table/TableCell.tsx +27 -0
  214. package/src/components/table/TableColumn.tsx +31 -0
  215. package/src/components/table/TableHeader.tsx +26 -0
  216. package/src/components/table/TableRow.tsx +24 -0
  217. package/src/components/table/index.ts +12 -0
  218. package/src/components/tabs/Tab.tsx +36 -0
  219. package/src/components/tabs/TabList.tsx +76 -0
  220. package/src/components/tabs/TabPanel.tsx +68 -0
  221. package/src/components/tabs/Tabs.tsx +136 -0
  222. package/src/components/tabs/context.ts +24 -0
  223. package/src/components/tabs/index.ts +8 -0
  224. package/src/components/text-input/TextInput.tsx +188 -0
  225. package/src/components/text-input/index.ts +2 -0
  226. package/src/components/textarea/Textarea.tsx +145 -0
  227. package/src/components/textarea/index.ts +2 -0
  228. package/src/components/toast/Toast.tsx +219 -0
  229. package/src/components/toast/ToastBody.tsx +64 -0
  230. package/src/components/toast/ToastFooter.tsx +47 -0
  231. package/src/components/toast/ToastHeader.tsx +104 -0
  232. package/src/components/toast/ToastIcon.tsx +21 -0
  233. package/src/components/toast/Toaster.tsx +87 -0
  234. package/src/components/toast/context.ts +14 -0
  235. package/src/components/toast/index.ts +18 -0
  236. package/src/components/toast/toastQueue.ts +56 -0
  237. package/src/components/tooltip/Tooltip.tsx +155 -0
  238. package/src/components/tooltip/index.ts +2 -0
  239. package/src/global.d.ts +2 -0
  240. package/src/hooks/index.ts +57 -0
  241. package/src/hooks/useAutoDismiss.ts +88 -0
  242. package/src/hooks/useButtonSemantics.ts +33 -0
  243. package/src/hooks/useControllableState.ts +24 -0
  244. package/src/hooks/useDialogElement.ts +216 -0
  245. package/src/hooks/useDisabledAnchorGuard.ts +22 -0
  246. package/src/hooks/useDismissibleTransition.ts +126 -0
  247. package/src/hooks/useDrawer.ts +9 -0
  248. package/src/hooks/useFloatingOverlay.ts +115 -0
  249. package/src/hooks/useForkedRef.ts +50 -0
  250. package/src/hooks/useFormField.ts +89 -0
  251. package/src/hooks/useIsomorphicLayoutEffect.ts +9 -0
  252. package/src/hooks/useModal.ts +9 -0
  253. package/src/hooks/useNotification.ts +10 -0
  254. package/src/hooks/useOverlayPlacement.ts +93 -0
  255. package/src/hooks/usePagination.ts +85 -0
  256. package/src/hooks/useToast.ts +9 -0
  257. package/src/hooks/useToastRegionQueue.ts +27 -0
  258. package/src/index.ts +276 -0
  259. package/src/types.ts +133 -0
  260. package/src/utils/breakpoints.ts +43 -0
  261. package/src/utils/comboboxCollection.tsx +152 -0
  262. package/src/utils/dateRangePresets.ts +6 -0
  263. package/src/utils/dialogTransition.ts +39 -0
  264. package/src/utils/focusRedirect.ts +29 -0
  265. package/src/utils/isWholeUnitDisabled.ts +17 -0
  266. package/src/utils/mergeIsDateUnavailable.ts +15 -0
  267. package/src/utils/overlayPlacement.ts +93 -0
  268. package/src/utils/pointerInteraction.ts +21 -0
  269. package/src/utils/polymorphic.ts +36 -0
  270. package/src/utils/renderMenuItemContent.tsx +62 -0
  271. package/src/utils/setVisibleRangeStart.ts +64 -0
  272. package/src/utils/spacingClassName.ts +14 -0
  273. package/src/utils/suppressFocusRingGlobally.ts +69 -0
  274. package/src/utils/validationClassName.ts +8 -0
  275. package/src/utils/virtualFocusStyle.ts +16 -0
@@ -0,0 +1,134 @@
1
+ import React, { ElementType, ForwardRefRenderFunction, ReactElement } from 'react'
2
+ import classNames from 'classnames'
3
+
4
+ import { ContextColor, ExtendedSizing } from '../../types'
5
+ import {
6
+ createPolymorphicComponent,
7
+ PolymorphicComponentProps,
8
+ PolymorphicRef
9
+ } from '../../utils/polymorphic'
10
+ import { Link } from '../link/Link'
11
+ import { Badge } from '../badge'
12
+ import { AvatarImage } from './AvatarImage'
13
+
14
+ type AvatarOwnProps<C extends ElementType> = {
15
+ /**
16
+ * A string of all className you want applied to the component.
17
+ */
18
+ className?: string
19
+ /**
20
+ * Sets the color of the component to one of Chassis context colors.
21
+ */
22
+ color?: ContextColor
23
+ /**
24
+ * Toggle the disabled state for the component. Only applies when `component` is `a` or `button`.
25
+ */
26
+ disabled?: boolean
27
+ /**
28
+ * Renders the smooth (tinted background) context variant instead of the solid default.
29
+ */
30
+ smooth?: boolean
31
+ /**
32
+ * Sets the size of the component to one of Chassis component sizes.
33
+ */
34
+ size?: ExtendedSizing
35
+ /**
36
+ * Component used for the root node. Either a string to use a HTML element or a component.
37
+ * Defaults to `span`, or `a` when `href` is set. Set explicitly to `button` (or another
38
+ * interactive element/component) to make a non-link avatar focusable and clickable.
39
+ */
40
+ component?: C
41
+ /**
42
+ * Image source. When set, renders an `AvatarImage` in place of `children`.
43
+ */
44
+ src?: string
45
+ /**
46
+ * Alt text for the image rendered when `src` is set.
47
+ */
48
+ alt?: string
49
+ /**
50
+ * Renders the avatar as a link to this URL. Defaults `component` to `a`. Passed through to a
51
+ * custom `component` too, regardless of whether it resolves to a native `a`/`button`.
52
+ */
53
+ href?: string
54
+ /**
55
+ * Renders a status badge in the bottom-end corner, in one of the Chassis themed colors.
56
+ */
57
+ status?: ContextColor
58
+ /**
59
+ * Accessible label for the status badge, exposed to assistive tech as visually hidden text.
60
+ * Falls back to the `status` value itself.
61
+ */
62
+ statusLabel?: string
63
+ }
64
+
65
+ export type AvatarProps<C extends ElementType = 'span'> = PolymorphicComponentProps<
66
+ C,
67
+ AvatarOwnProps<C>
68
+ >
69
+
70
+ type AvatarComponent = (<C extends ElementType = 'span'>(
71
+ props: AvatarProps<C> & { ref?: PolymorphicRef<C> }
72
+ ) => ReactElement | null) & { displayName?: string }
73
+
74
+ function AvatarRender<C extends ElementType = 'span'>(
75
+ {
76
+ children,
77
+ className,
78
+ color,
79
+ disabled,
80
+ smooth,
81
+ size,
82
+ component,
83
+ src,
84
+ alt = 'Profile picture',
85
+ href,
86
+ status,
87
+ statusLabel,
88
+ ...rest
89
+ }: AvatarProps<C>,
90
+ ref: PolymorphicRef<C>
91
+ ) {
92
+ const tag = component ?? (href ? 'a' : 'span')
93
+ const isInteractive = tag === 'a' || tag === 'button'
94
+
95
+ const _className = classNames(
96
+ 'avatar',
97
+ color,
98
+ size,
99
+ { smooth },
100
+ !isInteractive && { disabled },
101
+ className
102
+ )
103
+
104
+ const Component = (isInteractive ? Link : tag) as ElementType
105
+
106
+ return (
107
+ <Component
108
+ className={_className}
109
+ {...(isInteractive && { component: tag })}
110
+ // `disabled`/`href` used to only reach `Component` when `tag` was the native `'a'`/`'button'`
111
+ // string (routed through `Link`, which already forwards them itself) — a custom `component`
112
+ // (e.g. a router `Link`) fell through this `isInteractive` check and silently never received
113
+ // either prop. Passing them unconditionally fixes that; they're harmless no-ops on a plain,
114
+ // non-interactive `'span'`.
115
+ disabled={disabled}
116
+ href={href}
117
+ {...(tag === 'button' && { type: 'button' })}
118
+ {...rest}
119
+ ref={ref}
120
+ >
121
+ {src ? <AvatarImage src={src} alt={alt} /> : children}
122
+ {status && (
123
+ <Badge color={status} circle>
124
+ <span className="visually-hidden">{statusLabel ?? status}</span>
125
+ </Badge>
126
+ )}
127
+ </Component>
128
+ )
129
+ }
130
+
131
+ export const Avatar = createPolymorphicComponent<AvatarComponent>(
132
+ AvatarRender as ForwardRefRenderFunction<Element, AvatarProps<ElementType>>,
133
+ 'Avatar'
134
+ )
@@ -0,0 +1,19 @@
1
+ import React, { forwardRef, ImgHTMLAttributes } from 'react'
2
+ import classNames from 'classnames'
3
+
4
+ export interface AvatarImageProps extends ImgHTMLAttributes<HTMLImageElement> {
5
+ /**
6
+ * A string of all className you want applied to the component.
7
+ */
8
+ className?: string
9
+ }
10
+
11
+ export const AvatarImage = forwardRef<HTMLImageElement, AvatarImageProps>(
12
+ ({ className, ...rest }, ref) => {
13
+ const _className = classNames('avatar-image', className)
14
+
15
+ return <img className={_className} {...rest} ref={ref} />
16
+ }
17
+ )
18
+
19
+ AvatarImage.displayName = 'AvatarImage'
@@ -0,0 +1,98 @@
1
+ import React, { ElementType, ForwardRefRenderFunction, ReactElement, ReactNode } from 'react'
2
+ import classNames from 'classnames'
3
+
4
+ import { ContextColor, ExtendedSizing } from '../../types'
5
+ import {
6
+ createPolymorphicComponent,
7
+ PolymorphicComponentProps,
8
+ PolymorphicRef
9
+ } from '../../utils/polymorphic'
10
+ import { Avatar } from './Avatar'
11
+
12
+ export interface AvatarStackItemDef {
13
+ /**
14
+ * React key for the rendered `Avatar`. Falls back to the item's index in `data`.
15
+ */
16
+ key?: string | number
17
+ /**
18
+ * Image source, rendered via `AvatarImage`.
19
+ */
20
+ src?: string
21
+ /**
22
+ * Alt text for the image rendered when `src` is set.
23
+ */
24
+ alt?: string
25
+ /**
26
+ * Renders the avatar as a link to this URL.
27
+ */
28
+ href?: string
29
+ /**
30
+ * Renders a status badge in one of the Chassis themed colors.
31
+ */
32
+ status?: ContextColor
33
+ /**
34
+ * Accessible label for the status badge.
35
+ */
36
+ statusLabel?: string
37
+ /**
38
+ * Content to render when `src` isn't set, e.g. initials or a "+5" overflow count.
39
+ */
40
+ content?: ReactNode
41
+ /**
42
+ * Component used for the root node. Either a string to use a HTML element or a component.
43
+ */
44
+ component?: ElementType
45
+ }
46
+
47
+ type AvatarStackOwnProps<C extends ElementType> = {
48
+ /**
49
+ * A string of all className you want applied to the component.
50
+ */
51
+ className?: string
52
+ /**
53
+ * Component used for the root node. Either a string to use a HTML element or a component.
54
+ */
55
+ component?: C
56
+ /**
57
+ * Sets the size of every `Avatar` in the stack to one of Chassis component sizes.
58
+ */
59
+ size?: ExtendedSizing
60
+ /**
61
+ * Renders a `Avatar` for each item, ahead of any JSX `children` (handy for a trailing "+N"
62
+ * overflow avatar).
63
+ */
64
+ items?: AvatarStackItemDef[]
65
+ }
66
+
67
+ export type AvatarStackProps<C extends ElementType = 'div'> = PolymorphicComponentProps<
68
+ C,
69
+ AvatarStackOwnProps<C>
70
+ >
71
+
72
+ type AvatarStackComponent = (<C extends ElementType = 'div'>(
73
+ props: AvatarStackProps<C> & { ref?: PolymorphicRef<C> }
74
+ ) => ReactElement | null) & { displayName?: string }
75
+
76
+ function AvatarStackRender<C extends ElementType = 'div'>(
77
+ { children, className, component, items, size, ...rest }: AvatarStackProps<C>,
78
+ ref: PolymorphicRef<C>
79
+ ) {
80
+ const Component = (component ?? 'div') as ElementType
81
+ const _className = classNames('avatar-stack', size, className)
82
+
83
+ return (
84
+ <Component className={_className} {...rest} ref={ref}>
85
+ {items?.map(({ key, content, ...item }, index) => (
86
+ <Avatar key={key ?? index} {...item}>
87
+ {content}
88
+ </Avatar>
89
+ ))}
90
+ {children}
91
+ </Component>
92
+ )
93
+ }
94
+
95
+ export const AvatarStack = createPolymorphicComponent<AvatarStackComponent>(
96
+ AvatarStackRender as ForwardRefRenderFunction<Element, AvatarStackProps<ElementType>>,
97
+ 'AvatarStack'
98
+ )
@@ -0,0 +1,6 @@
1
+ export { Avatar } from './Avatar'
2
+ export type { AvatarProps } from './Avatar'
3
+ export { AvatarImage } from './AvatarImage'
4
+ export type { AvatarImageProps } from './AvatarImage'
5
+ export { AvatarStack } from './AvatarStack'
6
+ export type { AvatarStackProps, AvatarStackItemDef } from './AvatarStack'
@@ -0,0 +1,94 @@
1
+ import React, { ElementType, ForwardRefRenderFunction, ReactElement } from 'react'
2
+ import classNames from 'classnames'
3
+
4
+ import { ContextColor, ContextStyle, Sizing } from '../../types'
5
+ import {
6
+ createPolymorphicComponent,
7
+ PolymorphicComponentProps,
8
+ PolymorphicRef
9
+ } from '../../utils/polymorphic'
10
+
11
+ type BadgeOwnProps<C extends ElementType> = {
12
+ /**
13
+ * A string of all className you want applied to the component.
14
+ */
15
+ className?: string
16
+ /**
17
+ * Sets the color of the component to one of Chassis context colors.
18
+ */
19
+ color?: ContextColor
20
+ /**
21
+ * Sets the context style of the component. `solid`/`basic` render the default look with no extra class.
22
+ */
23
+ variant?: ContextStyle
24
+ /**
25
+ * Component used for the root node. Either a string to use a HTML element or a component.
26
+ */
27
+ component?: C
28
+ /**
29
+ * Position badge in one of the corners of a link or button.
30
+ */
31
+ position?: 'top-start' | 'top-end' | 'bottom-end' | 'bottom-start'
32
+ /**
33
+ * Select the shape of the component.
34
+ */
35
+ circle?: boolean
36
+ /**
37
+ * Sets the size of the component to one of Chassis component sizes.
38
+ */
39
+ size?: Sizing
40
+ }
41
+
42
+ export type BadgeProps<C extends ElementType = 'span'> = PolymorphicComponentProps<
43
+ C,
44
+ BadgeOwnProps<C>
45
+ >
46
+
47
+ type BadgeComponent = (<C extends ElementType = 'span'>(
48
+ props: BadgeProps<C> & { ref?: PolymorphicRef<C> }
49
+ ) => ReactElement | null) & { displayName?: string }
50
+
51
+ function BadgeRender<C extends ElementType = 'span'>(
52
+ {
53
+ children,
54
+ className,
55
+ color,
56
+ variant,
57
+ component,
58
+ position,
59
+ circle,
60
+ size,
61
+ ...rest
62
+ }: BadgeProps<C>,
63
+ ref: PolymorphicRef<C>
64
+ ) {
65
+ const Component = component ?? 'span'
66
+
67
+ const _className = classNames(
68
+ 'badge',
69
+ color,
70
+ size,
71
+ {
72
+ outline: variant === 'outline',
73
+ smooth: variant === 'smooth',
74
+ 'position-absolute translate-middle': position,
75
+ 'top-0': position?.includes('top'),
76
+ 'top-100': position?.includes('bottom'),
77
+ 'start-100': position?.includes('end'),
78
+ 'start-0': position?.includes('start')
79
+ },
80
+ { circle },
81
+ className
82
+ )
83
+
84
+ return (
85
+ <Component className={_className} {...rest} ref={ref}>
86
+ {children}
87
+ </Component>
88
+ )
89
+ }
90
+
91
+ export const Badge = createPolymorphicComponent<BadgeComponent>(
92
+ BadgeRender as ForwardRefRenderFunction<Element, BadgeProps<ElementType>>,
93
+ 'Badge'
94
+ )
@@ -0,0 +1,2 @@
1
+ export { Badge } from './Badge'
2
+ export type { BadgeProps } from './Badge'
@@ -0,0 +1,59 @@
1
+ import React, { forwardRef, HTMLAttributes } from 'react'
2
+ import classNames from 'classnames'
3
+
4
+ import { BreadcrumbItem } from './BreadcrumbItem'
5
+
6
+ export interface BreadcrumbItemDef {
7
+ /**
8
+ * Label for the breadcrumb item.
9
+ */
10
+ label: React.ReactNode
11
+ /**
12
+ * URL for the breadcrumb link. The last item in the array is always rendered as active (no link needed).
13
+ */
14
+ href?: string
15
+ }
16
+
17
+ export interface BreadcrumbProps extends HTMLAttributes<HTMLOListElement> {
18
+ /**
19
+ * A string of all className you want applied to the component.
20
+ */
21
+ className?: string
22
+ /**
23
+ * Array of breadcrumb items for data-driven rendering. The last item is automatically marked active.
24
+ * When provided, children are ignored.
25
+ */
26
+ items?: BreadcrumbItemDef[]
27
+ }
28
+
29
+ export const Breadcrumb = forwardRef<HTMLOListElement, BreadcrumbProps>(
30
+ ({ children, className, items, ...rest }, ref) => {
31
+ const _className = classNames('breadcrumb', className)
32
+
33
+ const autoContent = items
34
+ ? items.map((item, idx) => {
35
+ const isLast = idx === items.length - 1
36
+ return (
37
+ <BreadcrumbItem
38
+ // eslint-disable-next-line react/no-array-index-key
39
+ key={idx}
40
+ active={isLast}
41
+ href={isLast ? undefined : item.href}
42
+ >
43
+ {item.label}
44
+ </BreadcrumbItem>
45
+ )
46
+ })
47
+ : null
48
+
49
+ return (
50
+ <nav aria-label="breadcrumb">
51
+ <ol className={_className} {...rest} ref={ref}>
52
+ {autoContent ?? children}
53
+ </ol>
54
+ </nav>
55
+ )
56
+ }
57
+ )
58
+
59
+ Breadcrumb.displayName = 'Breadcrumb'
@@ -0,0 +1,38 @@
1
+ import React, { forwardRef, HTMLAttributes } from 'react'
2
+ import classNames from 'classnames'
3
+
4
+ import { Link } from '../link/Link'
5
+
6
+ export interface BreadcrumbItemProps extends HTMLAttributes<HTMLLIElement> {
7
+ /**
8
+ * Toggle the active state for the component.
9
+ */
10
+ active?: boolean
11
+ /**
12
+ * A string of all className you want applied to the base component.
13
+ */
14
+ className?: string
15
+ /**
16
+ * The `href` attribute for the inner `<Link>` component.
17
+ */
18
+ href?: string
19
+ }
20
+
21
+ export const BreadcrumbItem = forwardRef<HTMLLIElement, BreadcrumbItemProps>(
22
+ ({ children, active, className, href, ...rest }, ref) => {
23
+ const _className = classNames(
24
+ 'breadcrumb-item',
25
+ {
26
+ active: active
27
+ },
28
+ className
29
+ )
30
+ return (
31
+ <li className={_className} {...(active && { 'aria-current': 'page' })} {...rest} ref={ref}>
32
+ {href ? <Link href={href}>{children}</Link> : children}
33
+ </li>
34
+ )
35
+ }
36
+ )
37
+
38
+ BreadcrumbItem.displayName = 'BreadcrumbItem'
@@ -0,0 +1,4 @@
1
+ export { Breadcrumb } from './Breadcrumb'
2
+ export type { BreadcrumbProps, BreadcrumbItemDef } from './Breadcrumb'
3
+ export { BreadcrumbItem } from './BreadcrumbItem'
4
+ export type { BreadcrumbItemProps } from './BreadcrumbItem'
@@ -0,0 +1,229 @@
1
+ import React, {
2
+ ElementType,
3
+ ForwardRefRenderFunction,
4
+ MouseEventHandler,
5
+ ReactElement,
6
+ Ref
7
+ } from 'react'
8
+ import classNames from 'classnames'
9
+ import { mergeProps } from 'react-aria'
10
+
11
+ import { ContextColor, ContextStyle, Shapes } from '../../types'
12
+ import { useButtonSemantics, useDisabledAnchorGuard } from '../../hooks'
13
+ import {
14
+ createPolymorphicComponent,
15
+ PolymorphicComponentProps,
16
+ PolymorphicRef
17
+ } from '../../utils/polymorphic'
18
+
19
+ type ButtonOwnProps<C extends ElementType> = {
20
+ /**
21
+ * A string of all className you want applied to the base component.
22
+ */
23
+ className?: string
24
+ /**
25
+ * Sets the color of the component to one of Chassis context colors.
26
+ */
27
+ color?: ContextColor
28
+ /**
29
+ * Component used for the root node. Either a string to use a HTML element or a component.
30
+ */
31
+ component?: C
32
+ /**
33
+ * Toggle the disabled state for the component.
34
+ */
35
+ disabled?: boolean
36
+ /**
37
+ * The href attribute specifies the URL of the page the link goes to.
38
+ */
39
+ href?: string
40
+ /**
41
+ * Fires on click. Typed for every element `component` can actually render (`button`, `a`,
42
+ * `input`, or a custom component), rather than narrowed to `HTMLButtonElement` alone.
43
+ */
44
+ onClick?: MouseEventHandler<HTMLButtonElement | HTMLAnchorElement | HTMLInputElement>
45
+ /**
46
+ * Marks the button as pressed for toggle-style usage (e.g. a formatting toolbar button).
47
+ * Applies the `.active` class and sets `aria-pressed` so assistive technology announces
48
+ * "button, pressed" rather than treating the button as a navigation link.
49
+ */
50
+ pressed?: boolean
51
+ /**
52
+ * Select the shape of the component.
53
+ */
54
+ shape?: Shapes
55
+ /**
56
+ * Size the component small or large.
57
+ */
58
+ size?: 'small' | 'large'
59
+ /**
60
+ * Specifies the type of button. Always specify the type attribute for the `<button>` element.
61
+ * Different browsers may use different default types for the `<button>` element.
62
+ */
63
+ type?: 'button' | 'submit' | 'reset'
64
+ /**
65
+ * Set the button style variant. Same as `ContextStyle`, but `solid` (the unmodified default
66
+ * look) doesn't apply as a class, and `link` — button-specific, not a context color — makes
67
+ * the button look and behave like a hyperlink while keeping its `color`.
68
+ */
69
+ variant?: Exclude<ContextStyle, 'solid'> | 'link'
70
+ }
71
+
72
+ export type ButtonProps<C extends ElementType = 'button'> = PolymorphicComponentProps<
73
+ C,
74
+ ButtonOwnProps<C>
75
+ >
76
+
77
+ type ButtonComponent = (<C extends ElementType = 'button'>(
78
+ props: ButtonProps<C> & { ref?: PolymorphicRef<C> }
79
+ ) => ReactElement | null) & { displayName?: string }
80
+
81
+ function ButtonRender<C extends ElementType = 'button'>(
82
+ {
83
+ children,
84
+ className,
85
+ color = 'primary',
86
+ component,
87
+ disabled,
88
+ href,
89
+ onClick,
90
+ pressed,
91
+ shape,
92
+ size,
93
+ type = 'button',
94
+ variant,
95
+ ...rest
96
+ }: ButtonProps<C>,
97
+ ref: PolymorphicRef<C>
98
+ ) {
99
+ // Only defaults to `a` when `component` wasn't explicitly passed — an explicit `component`
100
+ // (even alongside `href`, e.g. a router Link component that accepts `href` itself) always wins.
101
+ const Component = (component ?? (href ? 'a' : 'button')) as ElementType
102
+ const isAnchor = Component === 'a'
103
+
104
+ // A component reference (e.g. a router `Link`) has its own visual identity and is trusted to
105
+ // handle its own keyboard/role semantics, same as `CloseButton`'s `isComponentReference` escape
106
+ // hatch — synthesizing `role="button"` and Enter/Space handling on top of it would stamp the
107
+ // wrong ARIA role onto whatever it actually renders (e.g. an `<a>`) and can double-fire a click
108
+ // (native anchor Enter→click, plus the synthesized keydown→click). Only a bare, non-native HTML
109
+ // tag string (e.g. `'div'`) still needs `useButtonSemantics`'s synthesis.
110
+ const isComponentReference = typeof Component !== 'string'
111
+
112
+ const _className = classNames(
113
+ 'button',
114
+ color,
115
+ {
116
+ outline: variant === 'outline',
117
+ smooth: variant === 'smooth',
118
+ link: variant === 'link',
119
+ active: pressed,
120
+ disabled: Component !== 'button' && disabled
121
+ },
122
+ size,
123
+ shape,
124
+ className
125
+ )
126
+
127
+ // `<a>` has no real `disabled` attribute, so a disabled link button still fires click
128
+ // (and still navigates) unless it's blocked here. Native `button`/`input` already stop
129
+ // clicks on their own once the `disabled` attribute below is set.
130
+ const handleClick = useDisabledAnchorGuard<
131
+ HTMLButtonElement | HTMLAnchorElement | HTMLInputElement
132
+ >(isAnchor, disabled, onClick)
133
+
134
+ // Native `button`/`a`/`input` elements get keyboard activation, focus and disabled
135
+ // handling for free from the browser. A custom `component` doesn't, so useButtonSemantics
136
+ // fills in role, tabIndex and Enter/Space activation for it, matching native button behavior.
137
+ // `ref` is cast for the same reason as the branches below — it's declared for whatever `C`
138
+ // was passed, but this path (a non-native `component`) only ever populates it with a
139
+ // button/anchor-shaped instance.
140
+ const { buttonProps, forkedRef } = useButtonSemantics<HTMLButtonElement | HTMLAnchorElement>(
141
+ ref as Ref<HTMLButtonElement | HTMLAnchorElement>,
142
+ { disabled, onClick }
143
+ )
144
+
145
+ // Rendered as three separate, literal JSX tags (rather than one `<Component>` tag driven
146
+ // by a `'button' | 'a' | 'input'`-typed variable) because a union-typed tag makes JSX
147
+ // intersect all three elements' prop types — `onClick`/`ref` would then need to satisfy
148
+ // `button`, `a` *and* `input` simultaneously, which `handleClick`/`ref`'s real, narrower
149
+ // types can't. `ref` is cast per branch since it's declared for the generic `C`, not the
150
+ // specific element each branch actually renders (true by construction — each branch's ref
151
+ // only ever populates with a matching instance). The props common to all three
152
+ // (className/aria-pressed/onClick) are factored out here so a future addition only needs to
153
+ // change one place, not three.
154
+ const sharedProps = {
155
+ className: _className,
156
+ 'aria-pressed': pressed,
157
+ onClick: handleClick
158
+ }
159
+
160
+ if (Component === 'button') {
161
+ return (
162
+ <button
163
+ {...(rest as Record<string, unknown>)}
164
+ {...sharedProps}
165
+ type={type}
166
+ disabled={disabled}
167
+ ref={ref as Ref<HTMLButtonElement>}
168
+ >
169
+ {children}
170
+ </button>
171
+ )
172
+ }
173
+
174
+ if (Component === 'input') {
175
+ return (
176
+ // `rest` is cast because it's typed generically, whose event handlers don't structurally
177
+ // match `<input>`'s own `onChange` (value-change semantics) — `component="input"` is a
178
+ // fixed set of documented attributes (`type`, `value`, `disabled`, ...), not a
179
+ // general-purpose input, so this is a safe, deliberate escape.
180
+ <input
181
+ {...(rest as Record<string, unknown>)}
182
+ {...sharedProps}
183
+ type={type}
184
+ disabled={disabled}
185
+ ref={ref as Ref<HTMLInputElement>}
186
+ />
187
+ )
188
+ }
189
+
190
+ if (isAnchor) {
191
+ return (
192
+ // `rest` is cast for the same reason as the `input` branch above — it's typed generically,
193
+ // whose DOM event handlers are parameterized for whatever `C` was passed, which don't
194
+ // structurally match `<a>`'s own `HTMLAnchorElement` ones (e.g. `onCopy`), even though both
195
+ // accept a real DOM event at runtime.
196
+ <a
197
+ {...(rest as Record<string, unknown>)}
198
+ {...sharedProps}
199
+ href={href}
200
+ {...(disabled && { 'aria-disabled': true, tabIndex: -1 })}
201
+ ref={ref as Ref<HTMLAnchorElement>}
202
+ >
203
+ {children}
204
+ </a>
205
+ )
206
+ }
207
+
208
+ return (
209
+ <Component
210
+ {...(isComponentReference
211
+ ? ({ disabled, onClick, ...rest } as Record<string, unknown>)
212
+ : (mergeProps(rest, buttonProps) as Record<string, unknown>))}
213
+ className={_className}
214
+ aria-pressed={pressed}
215
+ // `href` was only ever forwarded when it also forced `Component` to `'a'` — now that an
216
+ // explicit `component` wins over that default (see above), it needs to keep reaching a
217
+ // custom `component`/HTML tag directly, same as `Avatar`'s equivalent fix.
218
+ {...(href && { href })}
219
+ ref={isComponentReference ? ref : forkedRef}
220
+ >
221
+ {children}
222
+ </Component>
223
+ )
224
+ }
225
+
226
+ export const Button = createPolymorphicComponent<ButtonComponent>(
227
+ ButtonRender as ForwardRefRenderFunction<Element, ButtonProps<ElementType>>,
228
+ 'Button'
229
+ )
@@ -0,0 +1,2 @@
1
+ export { Button } from './Button'
2
+ export type { ButtonProps } from './Button'