@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,74 @@
1
+ import React, { forwardRef, HTMLAttributes, SVGAttributes } from 'react'
2
+ import classNames from 'classnames'
3
+
4
+ interface IconOwnProps {
5
+ /**
6
+ * Icon name, e.g. `folder-tree`. Matches a `cx-{name}` font glyph class or an id in the SVG sprite.
7
+ */
8
+ name: string
9
+ /**
10
+ * A string of all className you want applied to the component.
11
+ */
12
+ className?: string
13
+ /**
14
+ * Width/height (in px) applied to the SVG. Ignored in `font` mode.
15
+ */
16
+ size?: number
17
+ /**
18
+ * Accessible name. When set, the icon is exposed to assistive tech instead of hidden.
19
+ */
20
+ title?: string
21
+ /**
22
+ * Path to the SVG sprite file. Ignored in `font` mode.
23
+ */
24
+ sprite?: string
25
+ }
26
+
27
+ export type IconProps =
28
+ | (IconOwnProps & { font: true } & Omit<HTMLAttributes<HTMLSpanElement>, keyof IconOwnProps>)
29
+ | (IconOwnProps & { font?: false } & Omit<SVGAttributes<SVGSVGElement>, keyof IconOwnProps>)
30
+
31
+ export const Icon = forwardRef<HTMLSpanElement | SVGSVGElement, IconProps>(
32
+ (
33
+ { name, className, size = 24, title, sprite = '/static/icons/chassis-icons.svg', ...props },
34
+ ref
35
+ ) => {
36
+ const _className = classNames('icon', { [`cx-${name}`]: props.font }, className)
37
+
38
+ if (props.font) {
39
+ const { font: _, ...rest } = props
40
+ return (
41
+ <span
42
+ className={_className}
43
+ // A plain `<span>`'s implicit "generic" role doesn't support naming, so `aria-label`
44
+ // alone is dropped by browsers/assistive tech - `role="img"` (matching the SVG path's
45
+ // own implicit role) is what actually makes the accessible name stick.
46
+ role={title ? 'img' : undefined}
47
+ aria-hidden={title ? undefined : true}
48
+ aria-label={title}
49
+ ref={ref as React.Ref<HTMLSpanElement>}
50
+ {...rest}
51
+ />
52
+ )
53
+ }
54
+
55
+ const { font: _, ...rest } = props
56
+
57
+ return (
58
+ <svg
59
+ className={_className}
60
+ width={size}
61
+ height={size}
62
+ role={title ? 'img' : undefined}
63
+ aria-hidden={title ? undefined : true}
64
+ ref={ref as React.Ref<SVGSVGElement>}
65
+ {...rest}
66
+ >
67
+ {title && <title>{title}</title>}
68
+ <use href={`${sprite}#${name}`} />
69
+ </svg>
70
+ )
71
+ }
72
+ )
73
+
74
+ Icon.displayName = 'Icon'
@@ -0,0 +1,2 @@
1
+ export { Icon } from './Icon'
2
+ export type { IconProps } from './Icon'
@@ -0,0 +1,55 @@
1
+ import React, { ElementType, ForwardRefRenderFunction, ReactElement } from 'react'
2
+ import classNames from 'classnames'
3
+
4
+ import {
5
+ createPolymorphicComponent,
6
+ PolymorphicComponentProps,
7
+ PolymorphicRef
8
+ } from '../../utils/polymorphic'
9
+
10
+ type InputAdornOwnProps<C extends ElementType> = {
11
+ /**
12
+ * A string of all className you want applied to the component.
13
+ */
14
+ className?: string
15
+ /**
16
+ * Component used for the root node. Either a string to use a HTML element or a component. Use
17
+ * `"button"` or `"a"` for an actionable adorn, e.g. a password reveal toggle or a clear button.
18
+ */
19
+ component?: C
20
+ }
21
+
22
+ export type InputAdornProps<C extends ElementType = 'span'> = PolymorphicComponentProps<
23
+ C,
24
+ InputAdornOwnProps<C>
25
+ >
26
+
27
+ type InputAdornComponent = (<C extends ElementType = 'span'>(
28
+ props: InputAdornProps<C> & { ref?: PolymorphicRef<C> }
29
+ ) => ReactElement | null) & { displayName?: string }
30
+
31
+ function InputAdornRender<C extends ElementType = 'span'>(
32
+ { children, className, component, onMouseDown, ...rest }: InputAdornProps<C>,
33
+ ref: PolymorphicRef<C>
34
+ ) {
35
+ const Component = component ?? 'span'
36
+ const _className = classNames('input-adorn', className)
37
+ const handleMouseDown: React.MouseEventHandler<Element> = (event) => {
38
+ onMouseDown?.(event)
39
+ // Keep focus on the associated input (e.g. a password toggle) instead of
40
+ // letting the browser shift it to this button/anchor on mousedown.
41
+ if (!event.defaultPrevented) {
42
+ event.preventDefault()
43
+ }
44
+ }
45
+ return (
46
+ <Component className={_className} onMouseDown={handleMouseDown} {...rest} ref={ref}>
47
+ {children}
48
+ </Component>
49
+ )
50
+ }
51
+
52
+ export const InputAdorn = createPolymorphicComponent<InputAdornComponent>(
53
+ InputAdornRender as ForwardRefRenderFunction<Element, InputAdornProps<ElementType>>,
54
+ 'InputAdorn'
55
+ )
@@ -0,0 +1,2 @@
1
+ export { InputAdorn } from './InputAdorn'
2
+ export type { InputAdornProps } from './InputAdorn'
@@ -0,0 +1,50 @@
1
+ import React, { ElementType, ForwardRefRenderFunction, ReactElement } from 'react'
2
+ import classNames from 'classnames'
3
+
4
+ import {
5
+ createPolymorphicComponent,
6
+ PolymorphicComponentProps,
7
+ PolymorphicRef
8
+ } from '../../utils/polymorphic'
9
+
10
+ type InputGroupOwnProps<C extends ElementType> = {
11
+ /**
12
+ * A string of all className you want applied to the component.
13
+ */
14
+ className?: string
15
+ /**
16
+ * Component used for the root node. Either a string to use a HTML element or a component.
17
+ */
18
+ component?: C
19
+ /**
20
+ * Size the component small or large.
21
+ */
22
+ size?: 'small' | 'large'
23
+ }
24
+
25
+ export type InputGroupProps<C extends ElementType = 'div'> = PolymorphicComponentProps<
26
+ C,
27
+ InputGroupOwnProps<C>
28
+ >
29
+
30
+ type InputGroupComponent = (<C extends ElementType = 'div'>(
31
+ props: InputGroupProps<C> & { ref?: PolymorphicRef<C> }
32
+ ) => ReactElement | null) & { displayName?: string }
33
+
34
+ function InputGroupRender<C extends ElementType = 'div'>(
35
+ { children, className, component, size, ...rest }: InputGroupProps<C>,
36
+ ref: PolymorphicRef<C>
37
+ ) {
38
+ const Component = component ?? 'div'
39
+ const _className = classNames('input-group', size, className)
40
+ return (
41
+ <Component className={_className} {...rest} ref={ref}>
42
+ {children}
43
+ </Component>
44
+ )
45
+ }
46
+
47
+ export const InputGroup = createPolymorphicComponent<InputGroupComponent>(
48
+ InputGroupRender as ForwardRefRenderFunction<Element, InputGroupProps<ElementType>>,
49
+ 'InputGroup'
50
+ )
@@ -0,0 +1,50 @@
1
+ import React, { ElementType, ForwardRefRenderFunction, ReactElement } from 'react'
2
+ import classNames from 'classnames'
3
+
4
+ import {
5
+ createPolymorphicComponent,
6
+ PolymorphicComponentProps,
7
+ PolymorphicRef
8
+ } from '../../utils/polymorphic'
9
+
10
+ type InputGroupAddonOwnProps<C extends ElementType> = {
11
+ /**
12
+ * A string of all className you want applied to the component.
13
+ */
14
+ className?: string
15
+ /**
16
+ * Component used for the root node. Either a string to use a HTML element or a component.
17
+ */
18
+ component?: C
19
+ /**
20
+ * The id of the form control this addon labels, when rendered as `component="label"`.
21
+ */
22
+ htmlFor?: string
23
+ }
24
+
25
+ export type InputGroupAddonProps<C extends ElementType = 'span'> = PolymorphicComponentProps<
26
+ C,
27
+ InputGroupAddonOwnProps<C>
28
+ >
29
+
30
+ type InputGroupAddonComponent = (<C extends ElementType = 'span'>(
31
+ props: InputGroupAddonProps<C> & { ref?: PolymorphicRef<C> }
32
+ ) => ReactElement | null) & { displayName?: string }
33
+
34
+ function InputGroupAddonRender<C extends ElementType = 'span'>(
35
+ { children, className, component, ...rest }: InputGroupAddonProps<C>,
36
+ ref: PolymorphicRef<C>
37
+ ) {
38
+ const Component = component ?? 'span'
39
+ const _className = classNames('input-addon', className)
40
+ return (
41
+ <Component className={_className} {...rest} ref={ref}>
42
+ {children}
43
+ </Component>
44
+ )
45
+ }
46
+
47
+ export const InputGroupAddon = createPolymorphicComponent<InputGroupAddonComponent>(
48
+ InputGroupAddonRender as ForwardRefRenderFunction<Element, InputGroupAddonProps<ElementType>>,
49
+ 'InputGroupAddon'
50
+ )
@@ -0,0 +1,4 @@
1
+ export { InputGroup } from './InputGroup'
2
+ export type { InputGroupProps } from './InputGroup'
3
+ export { InputGroupAddon } from './InputGroupAddon'
4
+ export type { InputGroupAddonProps } from './InputGroupAddon'
@@ -0,0 +1,148 @@
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 } from '../../types'
12
+ import { useButtonSemantics, useDisabledAnchorGuard } from '../../hooks'
13
+ import {
14
+ createPolymorphicComponent,
15
+ PolymorphicComponentProps,
16
+ PolymorphicRef
17
+ } from '../../utils/polymorphic'
18
+
19
+ type LinkOwnProps<C extends ElementType> = {
20
+ /**
21
+ * Toggle the active state for the component.
22
+ */
23
+ active?: boolean
24
+ /**
25
+ * A string of all className you want applied to the component.
26
+ */
27
+ className?: string
28
+ /**
29
+ * Sets the link color to one of Chassis context colors, including its interactive
30
+ * (`:hover`/`:focus`/`:active`/`:visited`) states.
31
+ */
32
+ color?: ContextColor
33
+ /**
34
+ * Component used for the root node. Either a string to use a HTML element or a component.
35
+ */
36
+ component?: C
37
+ /**
38
+ * Toggle the disabled state for the component.
39
+ */
40
+ disabled?: boolean
41
+ /**
42
+ * The href attribute specifies the URL of the page the link goes to. Only meaningful when
43
+ * `component` resolves to (or accepts) `'a'`. Declared explicitly here — rather than left to
44
+ * flow through generically from whatever `C` is — so consumers that wrap `Link` (`MenuItem`,
45
+ * `ListItem`, `NavLink`) can read it with a concrete type regardless of `component`.
46
+ */
47
+ href?: string
48
+ /**
49
+ * Fires on click.
50
+ */
51
+ onClick?: MouseEventHandler<HTMLElement>
52
+ /**
53
+ * Aligns a leading or trailing icon with the link text using flexbox, with a gap between
54
+ * them and an offset underline. Icons need to be passed as `children` alongside the text.
55
+ * Named `iconLink` rather than `icon` to avoid colliding with components (e.g. `MenuItem`)
56
+ * that already have their own, differently-typed `icon` prop for the icon content itself.
57
+ */
58
+ iconLink?: boolean
59
+ /**
60
+ * Removes the foreground color override, so the link inherits its color from the nearest
61
+ * ancestor instead of the default link color.
62
+ */
63
+ reset?: boolean
64
+ /**
65
+ * Expands the link's click target to fill its positioned ancestor (the nearest ancestor with
66
+ * a `position` other than `static`).
67
+ */
68
+ stretched?: boolean
69
+ /**
70
+ * Specifies the type of button. Only applies when `component="button"`. Different browsers may
71
+ * use different default types for the `<button>` element, so always specify it explicitly.
72
+ */
73
+ type?: 'button' | 'submit' | 'reset'
74
+ }
75
+
76
+ export type LinkProps<C extends ElementType = 'a'> = PolymorphicComponentProps<C, LinkOwnProps<C>>
77
+
78
+ type LinkComponent = (<C extends ElementType = 'a'>(
79
+ props: LinkProps<C> & { ref?: PolymorphicRef<C> }
80
+ ) => ReactElement | null) & { displayName?: string }
81
+
82
+ function LinkRender<C extends ElementType = 'a'>(
83
+ {
84
+ children,
85
+ active,
86
+ className,
87
+ color,
88
+ component,
89
+ disabled,
90
+ iconLink,
91
+ onClick,
92
+ reset,
93
+ stretched,
94
+ type = 'button',
95
+ ...rest
96
+ }: LinkProps<C>,
97
+ ref: PolymorphicRef<C>
98
+ ) {
99
+ const Component = (component ?? 'a') as ElementType
100
+
101
+ const _className = classNames(
102
+ color && `link-${color}`,
103
+ { 'icon-link': iconLink, 'fg-reset': reset, 'stretched-link': stretched },
104
+ { active, disabled },
105
+ className
106
+ )
107
+
108
+ const isInteractive = Component === 'a' || Component === 'button'
109
+ // A component reference (e.g. a router `Link`) has its own visual identity and is trusted to
110
+ // handle its own keyboard/role semantics, same as `CloseButton`'s `isComponentReference` escape
111
+ // hatch — synthesizing `role="button"` and Enter/Space handling on top of it would stamp the
112
+ // wrong ARIA role onto whatever it actually renders (e.g. an `<a>`) and can double-fire a click
113
+ // (native anchor Enter→click, plus the synthesized keydown→click).
114
+ const isComponentReference = typeof Component !== 'string'
115
+ // `<a>` has no real `disabled` attribute, so a disabled anchor link still fires click (and
116
+ // still navigates) unless it's blocked here, same guard `Button`/`CloseButton` apply. A real
117
+ // `<button>` already stops clicks on its own once the `disabled` attribute below is set.
118
+ const handleClick = useDisabledAnchorGuard<HTMLElement>(Component === 'a', disabled, onClick)
119
+
120
+ // A `component` that isn't a native interactive element gets a raw onClick with no
121
+ // keyboard semantics otherwise — mouse-only, unlike `Button`/`CloseButton`, which
122
+ // synthesize this via `useButtonSemantics` for exactly this "arbitrary component" case. A
123
+ // component reference is excluded (see `isComponentReference` above).
124
+ const needsButtonSemantics = !isInteractive && !isComponentReference && !!onClick
125
+ const { buttonProps, forkedRef } = useButtonSemantics<HTMLElement>(ref as Ref<HTMLElement>, {
126
+ disabled,
127
+ onClick
128
+ })
129
+
130
+ return (
131
+ <Component
132
+ {...(mergeProps(rest, needsButtonSemantics ? buttonProps : {}) as Record<string, unknown>)}
133
+ className={_className}
134
+ {...(active && { 'aria-current': 'page' })}
135
+ {...(Component === 'a' && disabled && { 'aria-disabled': true, tabIndex: -1 })}
136
+ {...(!needsButtonSemantics && { onClick: handleClick })}
137
+ {...(Component === 'button' && { disabled, type })}
138
+ ref={needsButtonSemantics ? forkedRef : ref}
139
+ >
140
+ {children}
141
+ </Component>
142
+ )
143
+ }
144
+
145
+ export const Link = createPolymorphicComponent<LinkComponent>(
146
+ LinkRender as ForwardRefRenderFunction<Element, LinkProps<ElementType>>,
147
+ 'Link'
148
+ )
@@ -0,0 +1,2 @@
1
+ export { Link } from './Link'
2
+ export type { LinkProps } from './Link'
@@ -0,0 +1,193 @@
1
+ import React, {
2
+ Children,
3
+ ElementType,
4
+ ForwardRefRenderFunction,
5
+ isValidElement,
6
+ ReactElement
7
+ } from 'react'
8
+ import classNames from 'classnames'
9
+
10
+ import { ContextColor, ContextStyle } from '../../types'
11
+ import {
12
+ createPolymorphicComponent,
13
+ PolymorphicComponentProps,
14
+ PolymorphicRef
15
+ } from '../../utils/polymorphic'
16
+ import { ListItem } from './ListItem'
17
+
18
+ export interface ListItemDef {
19
+ /**
20
+ * Stable key for the rendered item. Falls back to the item's index in `items`.
21
+ */
22
+ id?: number | string
23
+ /**
24
+ * Item label content.
25
+ */
26
+ label: React.ReactNode
27
+ /**
28
+ * Makes the item a link.
29
+ */
30
+ href?: string
31
+ /**
32
+ * Sets the color of the item.
33
+ */
34
+ color?: ContextColor
35
+ /**
36
+ * Marks the item as active.
37
+ */
38
+ active?: boolean
39
+ /**
40
+ * Marks the item as disabled.
41
+ */
42
+ disabled?: boolean
43
+ }
44
+
45
+ type ListOwnProps<C extends ElementType> = {
46
+ /**
47
+ * A string of all className you want applied to the component.
48
+ */
49
+ className?: string
50
+ /**
51
+ * Component used for the root node. Either a string to use a HTML element or a component.
52
+ * Defaults to `'ul'`, unless an item (a data-driven item with `href`, or a `<ListItem
53
+ * component="a">`/`<ListItem component="button">` child) is interactive — a bare `<a>`/
54
+ * `<button>` isn't a valid direct child of `<ul>`/`<ol>`, so the default switches to `'div'`
55
+ * instead. Pass `component` explicitly to opt out of this.
56
+ */
57
+ component?: C
58
+ /**
59
+ * Sets the color of the component to one of Chassis context colors.
60
+ */
61
+ color?: ContextColor
62
+ /**
63
+ * Remove outer borders and rounded corners to render list items edge-to-edge in a parent component (e.g., `<Card>`).
64
+ */
65
+ flush?: boolean
66
+ /**
67
+ * Array of item definitions for data-driven rendering. When provided, children are ignored.
68
+ */
69
+ items?: ListItemDef[]
70
+ /**
71
+ * Specify a layout type.
72
+ */
73
+ layout?:
74
+ | 'horizontal'
75
+ | 'small:horizontal'
76
+ | 'medium:horizontal'
77
+ | 'large:horizontal'
78
+ | 'xlarge:horizontal'
79
+ | '2xlarge:horizontal'
80
+ /**
81
+ * Number list items sequentially using CSS counters. Pair with `component="ol"` for semantic correctness.
82
+ */
83
+ numbered?: boolean
84
+ /**
85
+ * Remove outer borders, rounded corners, and horizontal padding for a minimal, edge-to-edge appearance.
86
+ */
87
+ plain?: boolean
88
+ /**
89
+ * Applies a `.solid`, `.outline`, or `.smooth` context style. Only meaningful together with `color`.
90
+ */
91
+ variant?: ContextStyle
92
+ }
93
+
94
+ export type ListProps<C extends ElementType = 'ul'> = PolymorphicComponentProps<C, ListOwnProps<C>>
95
+
96
+ type ListComponent = (<C extends ElementType = 'ul'>(
97
+ props: ListProps<C> & { ref?: PolymorphicRef<C> }
98
+ ) => ReactElement | null) & { displayName?: string }
99
+
100
+ function ListRender<C extends ElementType = 'ul'>(
101
+ {
102
+ children,
103
+ className,
104
+ component,
105
+ color,
106
+ variant,
107
+ flush,
108
+ items,
109
+ layout,
110
+ numbered,
111
+ plain,
112
+ ...rest
113
+ }: ListProps<C>,
114
+ ref: PolymorphicRef<C>
115
+ ) {
116
+ // A linked item (data-driven `href`, or a `<ListItem component="a"|"button">` child) can't
117
+ // render as a bare `<a>`/`<button>` inside the default `<ul>`/`<ol>` root — only `<li>`/
118
+ // `script`/`template` are valid children there. Default to `<div>` instead when that's the
119
+ // case (unless the caller already chose their own `component`), matching the same
120
+ // `component="div"` pattern already documented for `Stepper`'s composed interactive usage.
121
+ const hasInteractiveItem = items
122
+ ? items.some((item) => !!item.href)
123
+ : Children.toArray(children).some(
124
+ (child) =>
125
+ isValidElement<{ component?: string | ElementType }>(child) &&
126
+ child.type === ListItem &&
127
+ (child.props.component === 'a' || child.props.component === 'button')
128
+ )
129
+ const Component = (component ?? (hasInteractiveItem ? 'div' : 'ul')) as ElementType
130
+ const isListSemantic = Component === 'ul' || Component === 'ol'
131
+
132
+ const _className = classNames(
133
+ 'list',
134
+ color && 'context',
135
+ color,
136
+ variant,
137
+ layout,
138
+ {
139
+ flush,
140
+ plain,
141
+ numbered
142
+ },
143
+ className
144
+ )
145
+
146
+ const autoContent = items
147
+ ? items.map((item, idx) => {
148
+ const itemClass = classNames('list-item', item.color && 'context', item.color, {
149
+ 'list-action': !!item.href,
150
+ active: item.active,
151
+ disabled: item.disabled
152
+ })
153
+ const Tag = item.href ? 'a' : isListSemantic ? 'li' : 'div'
154
+ return (
155
+ <Tag
156
+ key={item.id ?? idx}
157
+ className={itemClass}
158
+ {...(item.href ? { href: item.href } : {})}
159
+ {...(item.active ? { 'aria-current': 'page' } : {})}
160
+ {...(item.disabled ? { 'aria-disabled': true } : {})}
161
+ >
162
+ {item.label}
163
+ </Tag>
164
+ )
165
+ })
166
+ : null
167
+
168
+ // When the root switched to `div` because of an interactive child, every plain `<ListItem>`
169
+ // sibling defaulting to `<li>` would be just as invalid (only valid inside `<ul>`/`<ol>`/
170
+ // `<menu>`) — so give each `ListItem` child the same `div` treatment the auto-generated items
171
+ // above already get, unless it set its own `component`. Other child types are left untouched
172
+ // — this is only meaningful for `List`'s own list items.
173
+ const renderedChildren =
174
+ autoContent ??
175
+ (isListSemantic
176
+ ? children
177
+ : Children.map(children, (child) =>
178
+ isValidElement<{ component?: string | ElementType }>(child) && child.type === ListItem
179
+ ? React.cloneElement(child, { component: child.props.component ?? 'div' })
180
+ : child
181
+ ))
182
+
183
+ return (
184
+ <Component className={_className} {...rest} ref={ref}>
185
+ {renderedChildren}
186
+ </Component>
187
+ )
188
+ }
189
+
190
+ export const List = createPolymorphicComponent<ListComponent>(
191
+ ListRender as ForwardRefRenderFunction<Element, ListProps<ElementType>>,
192
+ 'List'
193
+ )
@@ -0,0 +1,91 @@
1
+ import React, { ElementType, ForwardRefRenderFunction, ReactElement } from 'react'
2
+ import classNames from 'classnames'
3
+
4
+ import { ContextColor } from '../../types'
5
+ import {
6
+ createPolymorphicComponent,
7
+ PolymorphicComponentProps,
8
+ PolymorphicRef
9
+ } from '../../utils/polymorphic'
10
+ import { Link } from '../link/Link'
11
+
12
+ type ListItemOwnProps<C extends ElementType> = {
13
+ /**
14
+ * Toggle the active state for the component.
15
+ */
16
+ active?: boolean
17
+ /**
18
+ * A string of all className you want applied to the component.
19
+ */
20
+ className?: string
21
+ /**
22
+ * Sets the color of the component to one of Chassis context colors.
23
+ */
24
+ color?: ContextColor
25
+ /**
26
+ * Toggle the disabled state for the component.
27
+ */
28
+ disabled?: boolean
29
+ /**
30
+ * Component used for the root node. Either a string to use a HTML element or a component.
31
+ */
32
+ component?: C
33
+ /**
34
+ * The href attribute specifies the URL of the page the link goes to. Only applicable when
35
+ * `component` is `"a"`.
36
+ */
37
+ href?: string
38
+ }
39
+
40
+ export type ListItemProps<C extends ElementType = 'li'> = PolymorphicComponentProps<
41
+ C,
42
+ ListItemOwnProps<C>
43
+ >
44
+
45
+ type ListItemComponent = (<C extends ElementType = 'li'>(
46
+ props: ListItemProps<C> & { ref?: PolymorphicRef<C> }
47
+ ) => ReactElement | null) & { displayName?: string }
48
+
49
+ function ListItemRender<C extends ElementType = 'li'>(
50
+ { children, active, className, disabled, color, component, ...rest }: ListItemProps<C>,
51
+ ref: PolymorphicRef<C>
52
+ ) {
53
+ const tag = component ?? 'li'
54
+ const isInteractive = tag === 'a' || tag === 'button'
55
+
56
+ const _className = classNames(
57
+ 'list-item',
58
+ color && 'context',
59
+ color,
60
+ {
61
+ 'list-action': isInteractive,
62
+ active,
63
+ disabled
64
+ },
65
+ className
66
+ )
67
+
68
+ const Component = (isInteractive ? Link : tag) as ElementType
69
+
70
+ const finalRest = {
71
+ ...(isInteractive && {
72
+ active,
73
+ disabled,
74
+ component: tag
75
+ }),
76
+ ...(active && { 'aria-current': 'page' }),
77
+ ...(disabled && { 'aria-disabled': true }),
78
+ ...rest
79
+ }
80
+
81
+ return (
82
+ <Component className={_className} {...finalRest} ref={ref}>
83
+ {children}
84
+ </Component>
85
+ )
86
+ }
87
+
88
+ export const ListItem = createPolymorphicComponent<ListItemComponent>(
89
+ ListItemRender as ForwardRefRenderFunction<Element, ListItemProps<ElementType>>,
90
+ 'ListItem'
91
+ )
@@ -0,0 +1,4 @@
1
+ export { List } from './List'
2
+ export type { ListProps, ListItemDef } from './List'
3
+ export { ListItem } from './ListItem'
4
+ export type { ListItemProps } from './ListItem'