@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.
- package/LICENSE +21 -0
- package/README.md +65 -0
- package/dist/index.d.ts +5909 -0
- package/dist/index.js +2 -0
- package/dist/style.css +1 -0
- package/package.json +114 -0
- package/src/components/accordion/Accordion.tsx +190 -0
- package/src/components/accordion/AccordionBody.tsx +46 -0
- package/src/components/accordion/AccordionHeader.tsx +21 -0
- package/src/components/accordion/AccordionItem.tsx +59 -0
- package/src/components/accordion/context.ts +8 -0
- package/src/components/accordion/index.ts +8 -0
- package/src/components/autocomplete/Autocomplete.tsx +464 -0
- package/src/components/autocomplete/AutocompleteGroup.tsx +20 -0
- package/src/components/autocomplete/AutocompleteItem.tsx +18 -0
- package/src/components/autocomplete/index.ts +6 -0
- package/src/components/avatar/Avatar.tsx +134 -0
- package/src/components/avatar/AvatarImage.tsx +19 -0
- package/src/components/avatar/AvatarStack.tsx +98 -0
- package/src/components/avatar/index.ts +6 -0
- package/src/components/badge/Badge.tsx +94 -0
- package/src/components/badge/index.ts +2 -0
- package/src/components/breadcrumb/Breadcrumb.tsx +59 -0
- package/src/components/breadcrumb/BreadcrumbItem.tsx +38 -0
- package/src/components/breadcrumb/index.ts +4 -0
- package/src/components/button/Button.tsx +229 -0
- package/src/components/button/index.ts +2 -0
- package/src/components/button-group/ButtonGroup.tsx +55 -0
- package/src/components/button-group/ButtonToolbar.tsx +47 -0
- package/src/components/button-group/index.ts +4 -0
- package/src/components/calendar/Calendar.scss +57 -0
- package/src/components/calendar/Calendar.tsx +324 -0
- package/src/components/calendar/CalendarMonthBlock.tsx +57 -0
- package/src/components/calendar/CalendarMonthGrid.tsx +92 -0
- package/src/components/calendar/CalendarMonthYearPicker.tsx +162 -0
- package/src/components/calendar/CalendarNavButton.tsx +24 -0
- package/src/components/calendar/CalendarWeekGrid.tsx +58 -0
- package/src/components/calendar/CalendarYearGrid.tsx +126 -0
- package/src/components/calendar/RangeCalendar.scss +95 -0
- package/src/components/calendar/RangeCalendar.tsx +361 -0
- package/src/components/calendar/index.ts +5 -0
- package/src/components/card/Card.tsx +173 -0
- package/src/components/card/CardBody.tsx +75 -0
- package/src/components/card/CardFooter.tsx +47 -0
- package/src/components/card/CardGroup.tsx +51 -0
- package/src/components/card/CardHeader.tsx +47 -0
- package/src/components/card/CardImage.tsx +70 -0
- package/src/components/card/CardImageOverlay.tsx +47 -0
- package/src/components/card/CardLink.tsx +40 -0
- package/src/components/card/CardSubtitle.tsx +47 -0
- package/src/components/card/CardText.tsx +47 -0
- package/src/components/card/CardTitle.tsx +47 -0
- package/src/components/card/index.ts +22 -0
- package/src/components/carousel/Carousel.tsx +742 -0
- package/src/components/carousel/CarouselControlButton.tsx +74 -0
- package/src/components/carousel/CarouselControlNext.tsx +22 -0
- package/src/components/carousel/CarouselControlPrev.tsx +22 -0
- package/src/components/carousel/CarouselIndicators.tsx +44 -0
- package/src/components/carousel/CarouselInner.tsx +56 -0
- package/src/components/carousel/CarouselItem.tsx +51 -0
- package/src/components/carousel/CarouselOverlay.tsx +51 -0
- package/src/components/carousel/CarouselPlayPause.tsx +61 -0
- package/src/components/carousel/carouselEngine.ts +170 -0
- package/src/components/carousel/context.ts +29 -0
- package/src/components/carousel/index.ts +21 -0
- package/src/components/checkbox/Checkbox.tsx +236 -0
- package/src/components/checkbox/CheckboxGroup.tsx +145 -0
- package/src/components/checkbox/context.ts +14 -0
- package/src/components/checkbox/index.ts +4 -0
- package/src/components/chip/Chip.tsx +183 -0
- package/src/components/chip/index.ts +2 -0
- package/src/components/chip-input/ChipInput.tsx +363 -0
- package/src/components/chip-input/ChipList.tsx +95 -0
- package/src/components/chip-input/index.ts +2 -0
- package/src/components/close-button/CloseButton.tsx +205 -0
- package/src/components/close-button/index.ts +2 -0
- package/src/components/collapse/Collapse.tsx +128 -0
- package/src/components/collapse/index.ts +2 -0
- package/src/components/color-input/ColorInput.tsx +106 -0
- package/src/components/color-input/index.ts +2 -0
- package/src/components/combobox/Combobox.tsx +320 -0
- package/src/components/combobox/ComboboxGroup.tsx +27 -0
- package/src/components/combobox/ComboboxItem.tsx +44 -0
- package/src/components/combobox/ComboboxListBox.tsx +98 -0
- package/src/components/combobox/index.ts +6 -0
- package/src/components/datepicker/CalendarToggleButton.tsx +58 -0
- package/src/components/datepicker/ClearButton.tsx +35 -0
- package/src/components/datepicker/DateField.tsx +54 -0
- package/src/components/datepicker/DatePicker.scss +51 -0
- package/src/components/datepicker/DatePicker.tsx +574 -0
- package/src/components/datepicker/DateRangePicker.scss +11 -0
- package/src/components/datepicker/DateRangePicker.tsx +343 -0
- package/src/components/datepicker/index.ts +4 -0
- package/src/components/datepicker/renderDatePickerShell.tsx +104 -0
- package/src/components/drawer/Drawer.tsx +211 -0
- package/src/components/drawer/DrawerBody.tsx +47 -0
- package/src/components/drawer/DrawerFooter.tsx +51 -0
- package/src/components/drawer/DrawerHeader.tsx +60 -0
- package/src/components/drawer/DrawerTitle.tsx +49 -0
- package/src/components/drawer/index.ts +10 -0
- package/src/components/file-input/FileInput.tsx +100 -0
- package/src/components/file-input/index.ts +2 -0
- package/src/components/flex/Flex.tsx +134 -0
- package/src/components/flex/index.ts +2 -0
- package/src/components/floating-input/FloatingInput.tsx +94 -0
- package/src/components/floating-input/index.ts +2 -0
- package/src/components/form/Form.tsx +26 -0
- package/src/components/form/FormFeedback.tsx +64 -0
- package/src/components/form/FormHelp.tsx +46 -0
- package/src/components/form/FormLabel.tsx +26 -0
- package/src/components/form/index.ts +8 -0
- package/src/components/form/renderFormCheck.tsx +86 -0
- package/src/components/form-field/FormField.tsx +84 -0
- package/src/components/form-field/index.ts +2 -0
- package/src/components/form-field/renderFormField.tsx +74 -0
- package/src/components/grid/Col.tsx +69 -0
- package/src/components/grid/Container.tsx +59 -0
- package/src/components/grid/Grid.tsx +86 -0
- package/src/components/grid/GridItem.tsx +123 -0
- package/src/components/grid/Row.tsx +71 -0
- package/src/components/grid/gap.ts +8 -0
- package/src/components/grid/index.ts +10 -0
- package/src/components/icon/Icon.tsx +74 -0
- package/src/components/icon/index.ts +2 -0
- package/src/components/input-adorn/InputAdorn.tsx +55 -0
- package/src/components/input-adorn/index.ts +2 -0
- package/src/components/input-group/InputGroup.tsx +50 -0
- package/src/components/input-group/InputGroupAddon.tsx +50 -0
- package/src/components/input-group/index.ts +4 -0
- package/src/components/link/Link.tsx +148 -0
- package/src/components/link/index.ts +2 -0
- package/src/components/list/List.tsx +193 -0
- package/src/components/list/ListItem.tsx +91 -0
- package/src/components/list/index.ts +4 -0
- package/src/components/menu/Menu.tsx +398 -0
- package/src/components/menu/MenuDivider.tsx +19 -0
- package/src/components/menu/MenuHeader.tsx +55 -0
- package/src/components/menu/MenuItem.tsx +85 -0
- package/src/components/menu/MenuItemDef.ts +83 -0
- package/src/components/menu/MenuList.tsx +142 -0
- package/src/components/menu/MenuSubmenu.tsx +284 -0
- package/src/components/menu/MenuSubmenuBack.tsx +39 -0
- package/src/components/menu/MenuText.tsx +47 -0
- package/src/components/menu/MenuToggle.tsx +148 -0
- package/src/components/menu/index.ts +19 -0
- package/src/components/menu/menuNavigation.ts +106 -0
- package/src/components/menu/submenuGroup.ts +43 -0
- package/src/components/modal/Modal.tsx +162 -0
- package/src/components/modal/ModalBody.tsx +47 -0
- package/src/components/modal/ModalFooter.tsx +51 -0
- package/src/components/modal/ModalHeader.tsx +60 -0
- package/src/components/modal/ModalTitle.tsx +49 -0
- package/src/components/modal/index.ts +10 -0
- package/src/components/nav/Nav.tsx +97 -0
- package/src/components/nav/NavItem.tsx +42 -0
- package/src/components/nav/NavLink.tsx +48 -0
- package/src/components/nav/NavTitle.tsx +22 -0
- package/src/components/nav/index.ts +7 -0
- package/src/components/navbar/Navbar.tsx +112 -0
- package/src/components/navbar/NavbarBrand.tsx +55 -0
- package/src/components/navbar/NavbarNav.tsx +47 -0
- package/src/components/navbar/NavbarText.tsx +47 -0
- package/src/components/navbar/NavbarToggler.tsx +34 -0
- package/src/components/navbar/index.ts +10 -0
- package/src/components/notification/Notification.scss +15 -0
- package/src/components/notification/Notification.tsx +187 -0
- package/src/components/notification/NotificationIcon.tsx +21 -0
- package/src/components/notification/NotificationStack.tsx +66 -0
- package/src/components/notification/NotificationText.tsx +47 -0
- package/src/components/notification/NotificationTitle.tsx +47 -0
- package/src/components/notification/context.ts +14 -0
- package/src/components/notification/index.ts +17 -0
- package/src/components/notification/notificationQueue.ts +48 -0
- package/src/components/otp-input/OtpBox.tsx +68 -0
- package/src/components/otp-input/OtpInput.tsx +341 -0
- package/src/components/otp-input/index.ts +2 -0
- package/src/components/pagination/Pagination.tsx +199 -0
- package/src/components/pagination/PaginationItem.tsx +133 -0
- package/src/components/pagination/index.ts +4 -0
- package/src/components/password-strength/PasswordStrength.tsx +155 -0
- package/src/components/password-strength/index.ts +2 -0
- package/src/components/password-strength/strengthScore.ts +79 -0
- package/src/components/placeholder/Placeholder.tsx +178 -0
- package/src/components/placeholder/index.ts +2 -0
- package/src/components/popover/Popover.tsx +243 -0
- package/src/components/popover/index.ts +2 -0
- package/src/components/progress/Progress.tsx +154 -0
- package/src/components/progress/ProgressBar.tsx +88 -0
- package/src/components/progress/index.ts +4 -0
- package/src/components/radio/Radio.tsx +88 -0
- package/src/components/radio/RadioGroup.tsx +142 -0
- package/src/components/radio/context.ts +14 -0
- package/src/components/radio/index.ts +4 -0
- package/src/components/range-input/RangeInput.tsx +109 -0
- package/src/components/range-input/index.ts +2 -0
- package/src/components/select/Select.tsx +277 -0
- package/src/components/select/index.ts +2 -0
- package/src/components/skeleton/Skeleton.tsx +89 -0
- package/src/components/skeleton/SkeletonLoader.tsx +91 -0
- package/src/components/skeleton/index.ts +4 -0
- package/src/components/spinner/Spinner.tsx +77 -0
- package/src/components/spinner/index.ts +2 -0
- package/src/components/stack/Stack.tsx +81 -0
- package/src/components/stack/index.ts +2 -0
- package/src/components/stepper/Stepper.tsx +172 -0
- package/src/components/stepper/StepperItem.tsx +66 -0
- package/src/components/stepper/index.ts +4 -0
- package/src/components/switch/Switch.tsx +246 -0
- package/src/components/switch/index.ts +2 -0
- package/src/components/table/Table.css +39 -0
- package/src/components/table/Table.tsx +390 -0
- package/src/components/table/TableBody.tsx +25 -0
- package/src/components/table/TableCell.tsx +27 -0
- package/src/components/table/TableColumn.tsx +31 -0
- package/src/components/table/TableHeader.tsx +26 -0
- package/src/components/table/TableRow.tsx +24 -0
- package/src/components/table/index.ts +12 -0
- package/src/components/tabs/Tab.tsx +36 -0
- package/src/components/tabs/TabList.tsx +76 -0
- package/src/components/tabs/TabPanel.tsx +68 -0
- package/src/components/tabs/Tabs.tsx +136 -0
- package/src/components/tabs/context.ts +24 -0
- package/src/components/tabs/index.ts +8 -0
- package/src/components/text-input/TextInput.tsx +188 -0
- package/src/components/text-input/index.ts +2 -0
- package/src/components/textarea/Textarea.tsx +145 -0
- package/src/components/textarea/index.ts +2 -0
- package/src/components/toast/Toast.tsx +219 -0
- package/src/components/toast/ToastBody.tsx +64 -0
- package/src/components/toast/ToastFooter.tsx +47 -0
- package/src/components/toast/ToastHeader.tsx +104 -0
- package/src/components/toast/ToastIcon.tsx +21 -0
- package/src/components/toast/Toaster.tsx +87 -0
- package/src/components/toast/context.ts +14 -0
- package/src/components/toast/index.ts +18 -0
- package/src/components/toast/toastQueue.ts +56 -0
- package/src/components/tooltip/Tooltip.tsx +155 -0
- package/src/components/tooltip/index.ts +2 -0
- package/src/global.d.ts +2 -0
- package/src/hooks/index.ts +57 -0
- package/src/hooks/useAutoDismiss.ts +88 -0
- package/src/hooks/useButtonSemantics.ts +33 -0
- package/src/hooks/useControllableState.ts +24 -0
- package/src/hooks/useDialogElement.ts +216 -0
- package/src/hooks/useDisabledAnchorGuard.ts +22 -0
- package/src/hooks/useDismissibleTransition.ts +126 -0
- package/src/hooks/useDrawer.ts +9 -0
- package/src/hooks/useFloatingOverlay.ts +115 -0
- package/src/hooks/useForkedRef.ts +50 -0
- package/src/hooks/useFormField.ts +89 -0
- package/src/hooks/useIsomorphicLayoutEffect.ts +9 -0
- package/src/hooks/useModal.ts +9 -0
- package/src/hooks/useNotification.ts +10 -0
- package/src/hooks/useOverlayPlacement.ts +93 -0
- package/src/hooks/usePagination.ts +85 -0
- package/src/hooks/useToast.ts +9 -0
- package/src/hooks/useToastRegionQueue.ts +27 -0
- package/src/index.ts +276 -0
- package/src/types.ts +133 -0
- package/src/utils/breakpoints.ts +43 -0
- package/src/utils/comboboxCollection.tsx +152 -0
- package/src/utils/dateRangePresets.ts +6 -0
- package/src/utils/dialogTransition.ts +39 -0
- package/src/utils/focusRedirect.ts +29 -0
- package/src/utils/isWholeUnitDisabled.ts +17 -0
- package/src/utils/mergeIsDateUnavailable.ts +15 -0
- package/src/utils/overlayPlacement.ts +93 -0
- package/src/utils/pointerInteraction.ts +21 -0
- package/src/utils/polymorphic.ts +36 -0
- package/src/utils/renderMenuItemContent.tsx +62 -0
- package/src/utils/setVisibleRangeStart.ts +64 -0
- package/src/utils/spacingClassName.ts +14 -0
- package/src/utils/suppressFocusRingGlobally.ts +69 -0
- package/src/utils/validationClassName.ts +8 -0
- 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,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,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,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,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
|
+
)
|