@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,246 @@
|
|
|
1
|
+
import React, { forwardRef, InputHTMLAttributes, ReactNode, useRef } from 'react'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
import { AriaSwitchProps, useSwitch } from 'react-aria'
|
|
4
|
+
import { useToggleState } from 'react-stately'
|
|
5
|
+
|
|
6
|
+
import { useForkedRef } from '../../hooks'
|
|
7
|
+
import { ContextColor } from '../../types'
|
|
8
|
+
import { validationClassName } from '../../utils/validationClassName'
|
|
9
|
+
|
|
10
|
+
import { FormLabel } from '../form/FormLabel'
|
|
11
|
+
|
|
12
|
+
export interface SwitchProps extends Omit<
|
|
13
|
+
InputHTMLAttributes<HTMLInputElement>,
|
|
14
|
+
'checked' | 'defaultChecked' | 'onChange' | 'size'
|
|
15
|
+
> {
|
|
16
|
+
/**
|
|
17
|
+
* A string of all className you want applied to the component.
|
|
18
|
+
*/
|
|
19
|
+
className?: string
|
|
20
|
+
/**
|
|
21
|
+
* Sets the color of the switch indicator to one of Chassis context colors.
|
|
22
|
+
*/
|
|
23
|
+
color?: ContextColor
|
|
24
|
+
/**
|
|
25
|
+
* Whether the switch is selected, uncontrolled.
|
|
26
|
+
*/
|
|
27
|
+
defaultSelected?: boolean
|
|
28
|
+
/**
|
|
29
|
+
* The id global attribute defines an identifier (ID) that must be unique in the whole document.
|
|
30
|
+
*/
|
|
31
|
+
id?: string
|
|
32
|
+
/**
|
|
33
|
+
* Set component validation state to invalid.
|
|
34
|
+
*/
|
|
35
|
+
invalid?: boolean
|
|
36
|
+
/**
|
|
37
|
+
* Whether the switch is selected, controlled.
|
|
38
|
+
*/
|
|
39
|
+
isSelected?: boolean
|
|
40
|
+
/**
|
|
41
|
+
* The element represents a caption for a component.
|
|
42
|
+
*/
|
|
43
|
+
label?: ReactNode
|
|
44
|
+
/**
|
|
45
|
+
* Callback fired when the selected state changes.
|
|
46
|
+
*/
|
|
47
|
+
onChange?: (isSelected: boolean) => void
|
|
48
|
+
/**
|
|
49
|
+
* Size the component small or large.
|
|
50
|
+
*/
|
|
51
|
+
size?: 'small' | 'large'
|
|
52
|
+
/**
|
|
53
|
+
* Specifies the type of component.
|
|
54
|
+
*/
|
|
55
|
+
type?: 'checkbox' | 'radio'
|
|
56
|
+
/**
|
|
57
|
+
* Set component validation state to valid.
|
|
58
|
+
*/
|
|
59
|
+
valid?: boolean
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type SwitchVariantProps = Omit<SwitchProps, 'type'>
|
|
63
|
+
|
|
64
|
+
interface RenderSwitchOptions {
|
|
65
|
+
checkInputClassName: string
|
|
66
|
+
className: string
|
|
67
|
+
input: ReactNode
|
|
68
|
+
label?: ReactNode
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Mirrors renderFormCheck's nested-label/bare-span shape — Switch inlines its own markup (see
|
|
72
|
+
// FORMS.md) rather than sharing that helper directly, since `role="switch"` placement didn't fit
|
|
73
|
+
// it cleanly. Keep this in sync with renderFormCheck by eye if either changes.
|
|
74
|
+
const renderSwitch = ({ checkInputClassName, className, input, label }: RenderSwitchOptions) => {
|
|
75
|
+
if (!label) {
|
|
76
|
+
return <span className={checkInputClassName}>{input}</span>
|
|
77
|
+
}
|
|
78
|
+
return (
|
|
79
|
+
<FormLabel customClassName={className}>
|
|
80
|
+
<span className={checkInputClassName}>{input}</span>
|
|
81
|
+
{label}
|
|
82
|
+
</FormLabel>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface RenderSwitchInputOptions {
|
|
87
|
+
className?: string
|
|
88
|
+
color?: ContextColor
|
|
89
|
+
forkedRef: React.Ref<HTMLInputElement>
|
|
90
|
+
id?: string
|
|
91
|
+
inputProps: InputHTMLAttributes<HTMLInputElement>
|
|
92
|
+
invalid?: boolean
|
|
93
|
+
label?: ReactNode
|
|
94
|
+
size?: 'small' | 'large'
|
|
95
|
+
valid?: boolean
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Shared className-building/markup for `SwitchCheckbox`/`SwitchRadio` — the two variants only
|
|
99
|
+
// differ in how `inputProps` gets built (a react-aria hook vs. a hand-wired radio), everything
|
|
100
|
+
// after that is identical.
|
|
101
|
+
const renderSwitchInput = ({
|
|
102
|
+
className,
|
|
103
|
+
color,
|
|
104
|
+
forkedRef,
|
|
105
|
+
id,
|
|
106
|
+
inputProps,
|
|
107
|
+
invalid,
|
|
108
|
+
label,
|
|
109
|
+
size,
|
|
110
|
+
valid
|
|
111
|
+
}: RenderSwitchInputOptions) => {
|
|
112
|
+
const inputClassName = classNames(validationClassName(invalid, valid))
|
|
113
|
+
const checkInputClassName = classNames('check-input', color, validationClassName(invalid, valid))
|
|
114
|
+
const _className = classNames(
|
|
115
|
+
'form-check form-switch',
|
|
116
|
+
size,
|
|
117
|
+
validationClassName(invalid, valid),
|
|
118
|
+
className
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return renderSwitch({
|
|
122
|
+
checkInputClassName,
|
|
123
|
+
className: _className,
|
|
124
|
+
input: <input {...inputProps} className={inputClassName} id={id} ref={forkedRef} />,
|
|
125
|
+
label
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Checkbox-backed switch: a real toggle, so it goes through react-aria's useSwitch/useToggleState
|
|
130
|
+
// like Checkbox does. Split into its own component (rather than branching inside one Switch on
|
|
131
|
+
// `type`) so SwitchRadio below never has to call these hooks just to discard their result —
|
|
132
|
+
// calling them unconditionally and ignoring the output risked react-aria's own dev-mode warnings
|
|
133
|
+
// (e.g. its missing-accessible-name check) firing for state that was never actually rendered.
|
|
134
|
+
const SwitchCheckbox = forwardRef<HTMLInputElement, SwitchVariantProps>(
|
|
135
|
+
(
|
|
136
|
+
{
|
|
137
|
+
className,
|
|
138
|
+
color,
|
|
139
|
+
defaultSelected,
|
|
140
|
+
disabled,
|
|
141
|
+
id,
|
|
142
|
+
invalid,
|
|
143
|
+
isSelected,
|
|
144
|
+
label,
|
|
145
|
+
onChange,
|
|
146
|
+
size,
|
|
147
|
+
valid,
|
|
148
|
+
...rest
|
|
149
|
+
},
|
|
150
|
+
ref
|
|
151
|
+
) => {
|
|
152
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
153
|
+
const forkedRef = useForkedRef(ref, inputRef)
|
|
154
|
+
|
|
155
|
+
const toggleState = useToggleState({
|
|
156
|
+
defaultSelected,
|
|
157
|
+
isDisabled: disabled,
|
|
158
|
+
isSelected,
|
|
159
|
+
onChange
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const { inputProps: switchProps } = useSwitch(
|
|
163
|
+
{
|
|
164
|
+
...rest,
|
|
165
|
+
children: label,
|
|
166
|
+
isDisabled: disabled,
|
|
167
|
+
isInvalid: invalid,
|
|
168
|
+
value: rest.value as string | undefined
|
|
169
|
+
} as AriaSwitchProps,
|
|
170
|
+
toggleState,
|
|
171
|
+
inputRef
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return renderSwitchInput({
|
|
175
|
+
className,
|
|
176
|
+
color,
|
|
177
|
+
forkedRef,
|
|
178
|
+
id,
|
|
179
|
+
inputProps: switchProps,
|
|
180
|
+
invalid,
|
|
181
|
+
label,
|
|
182
|
+
size,
|
|
183
|
+
valid
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
SwitchCheckbox.displayName = 'SwitchCheckbox'
|
|
188
|
+
|
|
189
|
+
// Radio-backed switch: react-aria has no ungrouped radio hook (see Checkbox's own split for the
|
|
190
|
+
// same reason), so this stays a native, hand-wired `<input type="radio" role="switch">` — no
|
|
191
|
+
// react-aria hook to call here at all.
|
|
192
|
+
const SwitchRadio = forwardRef<HTMLInputElement, SwitchVariantProps>(
|
|
193
|
+
(
|
|
194
|
+
{
|
|
195
|
+
className,
|
|
196
|
+
color,
|
|
197
|
+
defaultSelected,
|
|
198
|
+
disabled,
|
|
199
|
+
id,
|
|
200
|
+
invalid,
|
|
201
|
+
isSelected,
|
|
202
|
+
label,
|
|
203
|
+
onChange,
|
|
204
|
+
size,
|
|
205
|
+
valid,
|
|
206
|
+
...rest
|
|
207
|
+
},
|
|
208
|
+
ref
|
|
209
|
+
) => {
|
|
210
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
211
|
+
const forkedRef = useForkedRef(ref, inputRef)
|
|
212
|
+
|
|
213
|
+
const radioProps = {
|
|
214
|
+
...rest,
|
|
215
|
+
'aria-invalid': invalid,
|
|
216
|
+
checked: isSelected,
|
|
217
|
+
defaultChecked: defaultSelected,
|
|
218
|
+
disabled,
|
|
219
|
+
onChange: onChange
|
|
220
|
+
? (event: React.ChangeEvent<HTMLInputElement>) => onChange(event.target.checked)
|
|
221
|
+
: undefined,
|
|
222
|
+
role: 'switch' as const,
|
|
223
|
+
type: 'radio' as const
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return renderSwitchInput({
|
|
227
|
+
className,
|
|
228
|
+
color,
|
|
229
|
+
forkedRef,
|
|
230
|
+
id,
|
|
231
|
+
inputProps: radioProps,
|
|
232
|
+
invalid,
|
|
233
|
+
label,
|
|
234
|
+
size,
|
|
235
|
+
valid
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
SwitchRadio.displayName = 'SwitchRadio'
|
|
240
|
+
|
|
241
|
+
export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
242
|
+
({ type = 'checkbox', ...rest }, ref) =>
|
|
243
|
+
type === 'radio' ? <SwitchRadio {...rest} ref={ref} /> : <SwitchCheckbox {...rest} ref={ref} />
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
Switch.displayName = 'Switch'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Table's `<table>`/`<thead>`/`<tbody>`/`<tr>`/`<th>`/`<td>` chrome reuses chassis-css's
|
|
3
|
+
* existing `.table` class as-is (colors, borders, striping, hover, the `.active` row highlight —
|
|
4
|
+
* now driven by real selection state instead of a manual prop). Only the pieces with no
|
|
5
|
+
* chassis-css equivalent are new here: the sort indicator and the selection-checkbox column,
|
|
6
|
+
* both built on `@chassis-ui/tokens`' public `--cx-*` tokens for the same reason as
|
|
7
|
+
* DatePicker's calendar grid.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
.table-sort-icon {
|
|
11
|
+
display: inline-block;
|
|
12
|
+
margin-inline-start: var(--cx-space-2xsmall);
|
|
13
|
+
font-size: .75em;
|
|
14
|
+
opacity: .6;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
[aria-sort="ascending"] .table-sort-icon,
|
|
18
|
+
[aria-sort="descending"] .table-sort-icon {
|
|
19
|
+
opacity: 1;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
[role="columnheader"][aria-sort] {
|
|
23
|
+
cursor: pointer;
|
|
24
|
+
user-select: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.table-selection-cell {
|
|
28
|
+
width: 1%;
|
|
29
|
+
padding-inline-end: 0;
|
|
30
|
+
text-align: center;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.table [role="row"]:focus-visible,
|
|
34
|
+
.table [role="gridcell"]:focus-visible,
|
|
35
|
+
.table [role="columnheader"]:focus-visible,
|
|
36
|
+
.table [role="rowheader"]:focus-visible {
|
|
37
|
+
outline: var(--cx-border-width-xlarge) solid var(--cx-primary);
|
|
38
|
+
outline-offset: -2px;
|
|
39
|
+
}
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import React, { ReactElement, ReactNode, Ref, forwardRef, useRef } from 'react'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
import {
|
|
4
|
+
AriaCheckboxProps,
|
|
5
|
+
Key,
|
|
6
|
+
useCheckbox,
|
|
7
|
+
useTable,
|
|
8
|
+
useTableCell,
|
|
9
|
+
useTableColumnHeader,
|
|
10
|
+
useTableHeaderRow,
|
|
11
|
+
useTableRow,
|
|
12
|
+
useTableRowGroup,
|
|
13
|
+
useTableSelectAllCheckbox,
|
|
14
|
+
useTableSelectionCheckbox
|
|
15
|
+
} from 'react-aria'
|
|
16
|
+
import {
|
|
17
|
+
Selection,
|
|
18
|
+
SortDescriptor,
|
|
19
|
+
TableBodyProps,
|
|
20
|
+
TableHeaderProps,
|
|
21
|
+
TableState,
|
|
22
|
+
useTableState,
|
|
23
|
+
useToggleState
|
|
24
|
+
} from 'react-stately'
|
|
25
|
+
|
|
26
|
+
import { useForkedRef } from '../../hooks/useForkedRef'
|
|
27
|
+
import { ContextColor } from '../../types'
|
|
28
|
+
import './Table.css'
|
|
29
|
+
|
|
30
|
+
// `GridNode<T>` (from `@react-types/shared`) isn't re-exported by either package's public
|
|
31
|
+
// surface — derived from the hooks' own parameter types instead of adding an undeclared
|
|
32
|
+
// dependency on react-aria/react-stately's shared internals package.
|
|
33
|
+
type ColumnNode<T> = Parameters<typeof useTableColumnHeader<T>>[0]['node']
|
|
34
|
+
type RowNode<T> = Parameters<typeof useTableRow<T>>[0]['node']
|
|
35
|
+
type CellNode<T> = Parameters<typeof useTableCell<T>>[0]['node']
|
|
36
|
+
|
|
37
|
+
export interface TableProps<T extends object> {
|
|
38
|
+
/**
|
|
39
|
+
* An accessible label for the table, used when there's no visible heading.
|
|
40
|
+
*/
|
|
41
|
+
'aria-label'?: string
|
|
42
|
+
/**
|
|
43
|
+
* Identifies a visible heading element for the table.
|
|
44
|
+
*/
|
|
45
|
+
'aria-labelledby'?: string
|
|
46
|
+
/**
|
|
47
|
+
* Set the vertical alignment of cell content.
|
|
48
|
+
*/
|
|
49
|
+
align?: 'bottom' | 'middle' | 'top'
|
|
50
|
+
/**
|
|
51
|
+
* Add borders on all sides of the table and cells.
|
|
52
|
+
*/
|
|
53
|
+
bordered?: boolean
|
|
54
|
+
/**
|
|
55
|
+
* Remove borders on all sides of the table and cells.
|
|
56
|
+
*/
|
|
57
|
+
borderless?: boolean
|
|
58
|
+
/**
|
|
59
|
+
* Content shown above the table, functioning as a heading for it.
|
|
60
|
+
*/
|
|
61
|
+
caption?: ReactNode
|
|
62
|
+
/**
|
|
63
|
+
* A `TableHeader` and a `TableBody`, each built from `TableColumn`/`TableRow`/
|
|
64
|
+
* `TableCell` — read as data to build the table's collection. Not rendered directly.
|
|
65
|
+
*/
|
|
66
|
+
children: [
|
|
67
|
+
ReactElement<TableHeaderProps<T> & { className?: string }>,
|
|
68
|
+
ReactElement<TableBodyProps<T> & { className?: string }>
|
|
69
|
+
]
|
|
70
|
+
/**
|
|
71
|
+
* A string of all className you want applied to the component.
|
|
72
|
+
*/
|
|
73
|
+
className?: string
|
|
74
|
+
/**
|
|
75
|
+
* Sets the color of the component.
|
|
76
|
+
*/
|
|
77
|
+
color?: ContextColor
|
|
78
|
+
/**
|
|
79
|
+
* A list of row keys to disable. Disabled rows cannot be selected, focused, or interacted with.
|
|
80
|
+
*/
|
|
81
|
+
disabledKeys?: Iterable<Key>
|
|
82
|
+
/**
|
|
83
|
+
* Content shown below the table in a `<tfoot>`, e.g. a totals row. Rendered as plain markup —
|
|
84
|
+
* not part of the keyboard-navigable grid.
|
|
85
|
+
*/
|
|
86
|
+
footer?: ReactNode
|
|
87
|
+
/**
|
|
88
|
+
* Enable a hover state on table rows.
|
|
89
|
+
*/
|
|
90
|
+
hover?: boolean
|
|
91
|
+
/**
|
|
92
|
+
* `id` forwarded to the table element.
|
|
93
|
+
*/
|
|
94
|
+
id?: string
|
|
95
|
+
/**
|
|
96
|
+
* Handler that is called when the selection changes.
|
|
97
|
+
*/
|
|
98
|
+
onSelectionChange?: (keys: Selection) => void
|
|
99
|
+
/**
|
|
100
|
+
* Handler that is called when a column is sorted.
|
|
101
|
+
*/
|
|
102
|
+
onSortChange?: (descriptor: SortDescriptor) => void
|
|
103
|
+
/**
|
|
104
|
+
* Make any table responsive across all viewports or pick a maximum breakpoint.
|
|
105
|
+
*/
|
|
106
|
+
responsive?: boolean | 'small' | 'medium' | 'large' | 'xlarge' | '2xlarge'
|
|
107
|
+
/**
|
|
108
|
+
* The currently selected row keys (controlled).
|
|
109
|
+
*/
|
|
110
|
+
selectedKeys?: Selection
|
|
111
|
+
/**
|
|
112
|
+
* The type of selection that is allowed.
|
|
113
|
+
*/
|
|
114
|
+
selectionMode?: 'none' | 'single' | 'multiple'
|
|
115
|
+
/**
|
|
116
|
+
* Make table more compact by cutting all cell padding.
|
|
117
|
+
*/
|
|
118
|
+
small?: boolean
|
|
119
|
+
/**
|
|
120
|
+
* The current sort column and direction.
|
|
121
|
+
*/
|
|
122
|
+
sortDescriptor?: SortDescriptor
|
|
123
|
+
/**
|
|
124
|
+
* Convert rows into stacked label/value blocks below a container width, for tables with too
|
|
125
|
+
* many columns to read comfortably even with horizontal scrolling. `true` always stacks; a
|
|
126
|
+
* breakpoint name stacks only below it. Implies `responsive` when `responsive` isn't set
|
|
127
|
+
* separately, since stacking needs the same `.table-responsive` container-query ancestor.
|
|
128
|
+
* Labels come from each `TableColumn`'s text (or its `textValue`, for non-text headers).
|
|
129
|
+
*/
|
|
130
|
+
stacked?: boolean | 'small' | 'medium' | 'large' | 'xlarge' | '2xlarge'
|
|
131
|
+
/**
|
|
132
|
+
* Add zebra-striping to table rows.
|
|
133
|
+
*/
|
|
134
|
+
striped?: boolean
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const TableInner = <T extends object>(
|
|
138
|
+
{
|
|
139
|
+
align,
|
|
140
|
+
bordered,
|
|
141
|
+
borderless,
|
|
142
|
+
caption,
|
|
143
|
+
children,
|
|
144
|
+
className,
|
|
145
|
+
color,
|
|
146
|
+
disabledKeys,
|
|
147
|
+
footer,
|
|
148
|
+
hover,
|
|
149
|
+
id,
|
|
150
|
+
onSelectionChange,
|
|
151
|
+
onSortChange,
|
|
152
|
+
responsive,
|
|
153
|
+
selectedKeys,
|
|
154
|
+
selectionMode = 'none',
|
|
155
|
+
small,
|
|
156
|
+
sortDescriptor,
|
|
157
|
+
stacked,
|
|
158
|
+
striped,
|
|
159
|
+
...rest
|
|
160
|
+
}: TableProps<T>,
|
|
161
|
+
forwardedRef: Ref<HTMLTableElement>
|
|
162
|
+
) => {
|
|
163
|
+
const state = useTableState<T>({
|
|
164
|
+
children,
|
|
165
|
+
disabledKeys,
|
|
166
|
+
onSelectionChange,
|
|
167
|
+
onSortChange,
|
|
168
|
+
selectedKeys,
|
|
169
|
+
selectionMode,
|
|
170
|
+
showSelectionCheckboxes: selectionMode === 'multiple',
|
|
171
|
+
sortDescriptor
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
const internalRef = useRef<HTMLTableElement>(null)
|
|
175
|
+
const ref = useForkedRef(internalRef, forwardedRef)
|
|
176
|
+
const { gridProps } = useTable(
|
|
177
|
+
{ 'aria-label': rest['aria-label'], 'aria-labelledby': rest['aria-labelledby'], id },
|
|
178
|
+
state,
|
|
179
|
+
internalRef
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
const _className = classNames(
|
|
183
|
+
'table',
|
|
184
|
+
color,
|
|
185
|
+
{
|
|
186
|
+
[`align-${align}`]: align,
|
|
187
|
+
bordered,
|
|
188
|
+
borderless,
|
|
189
|
+
hoverable: hover,
|
|
190
|
+
small,
|
|
191
|
+
stacked: stacked === true,
|
|
192
|
+
striped
|
|
193
|
+
},
|
|
194
|
+
typeof stacked === 'string' ? `max-${stacked}:stacked` : undefined,
|
|
195
|
+
className
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
const tableEl = (
|
|
199
|
+
<table {...gridProps} className={_className || undefined} ref={ref}>
|
|
200
|
+
{caption && <caption>{caption}</caption>}
|
|
201
|
+
<TableRowGroup className={children[0].props.className} type="thead">
|
|
202
|
+
{state.collection.headerRows.map((headerRow) => (
|
|
203
|
+
<TableHeaderRow key={headerRow.key} item={headerRow} state={state}>
|
|
204
|
+
{[...(state.collection.getChildren?.(headerRow.key) ?? [])].map((column) =>
|
|
205
|
+
column.props?.isSelectionCell ? (
|
|
206
|
+
<TableSelectAllCell key={column.key} column={column} state={state} />
|
|
207
|
+
) : (
|
|
208
|
+
<TableColumnHeader key={column.key} column={column} state={state} />
|
|
209
|
+
)
|
|
210
|
+
)}
|
|
211
|
+
</TableHeaderRow>
|
|
212
|
+
))}
|
|
213
|
+
</TableRowGroup>
|
|
214
|
+
<TableRowGroup className={children[1].props.className} type="tbody">
|
|
215
|
+
{[...state.collection.body.childNodes].map((row) => (
|
|
216
|
+
<TableRow key={row.key} item={row} state={state}>
|
|
217
|
+
{[...(state.collection.getChildren?.(row.key) ?? [])].map((cell) =>
|
|
218
|
+
cell.props?.isSelectionCell ? (
|
|
219
|
+
<TableSelectionCell key={cell.key} cell={cell} state={state} />
|
|
220
|
+
) : (
|
|
221
|
+
<TableCell key={cell.key} cell={cell} stacked={!!stacked} state={state} />
|
|
222
|
+
)
|
|
223
|
+
)}
|
|
224
|
+
</TableRow>
|
|
225
|
+
))}
|
|
226
|
+
</TableRowGroup>
|
|
227
|
+
{footer && <tfoot>{footer}</tfoot>}
|
|
228
|
+
</table>
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
if (!responsive && !stacked) return tableEl
|
|
232
|
+
|
|
233
|
+
const wrapperClassName = responsive
|
|
234
|
+
? typeof responsive === 'boolean'
|
|
235
|
+
? 'table-responsive'
|
|
236
|
+
: `max-${responsive}:table-responsive`
|
|
237
|
+
: 'table-responsive'
|
|
238
|
+
|
|
239
|
+
return <div className={wrapperClassName}>{tableEl}</div>
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// `forwardRef` erases type parameters, so the generic component is cast back to a generic
|
|
243
|
+
// signature for callers — same reasoning as `TableHeader`/`TableBody`'s casts above.
|
|
244
|
+
export const Table = forwardRef(TableInner) as <T extends object>(
|
|
245
|
+
props: TableProps<T> & { ref?: Ref<HTMLTableElement> }
|
|
246
|
+
) => ReactElement
|
|
247
|
+
|
|
248
|
+
;(Table as { displayName?: string }).displayName = 'Table'
|
|
249
|
+
|
|
250
|
+
const TableRowGroup = ({
|
|
251
|
+
className,
|
|
252
|
+
type: Element,
|
|
253
|
+
children
|
|
254
|
+
}: {
|
|
255
|
+
children: ReactNode
|
|
256
|
+
className?: string
|
|
257
|
+
type: 'thead' | 'tbody'
|
|
258
|
+
}) => {
|
|
259
|
+
const { rowGroupProps } = useTableRowGroup()
|
|
260
|
+
return (
|
|
261
|
+
<Element {...rowGroupProps} className={className || undefined}>
|
|
262
|
+
{children}
|
|
263
|
+
</Element>
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
interface TableHeaderRowProps<T> {
|
|
268
|
+
children: ReactNode
|
|
269
|
+
item: RowNode<T>
|
|
270
|
+
state: TableState<T>
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const TableHeaderRow = <T extends object>({ children, item, state }: TableHeaderRowProps<T>) => {
|
|
274
|
+
const ref = useRef<HTMLTableRowElement>(null)
|
|
275
|
+
const { rowProps } = useTableHeaderRow({ node: item }, state, ref)
|
|
276
|
+
return (
|
|
277
|
+
<tr {...rowProps} ref={ref}>
|
|
278
|
+
{children}
|
|
279
|
+
</tr>
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
interface TableColumnHeaderProps<T> {
|
|
284
|
+
column: ColumnNode<T>
|
|
285
|
+
state: TableState<T>
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const TableColumnHeader = <T extends object>({ column, state }: TableColumnHeaderProps<T>) => {
|
|
289
|
+
const ref = useRef<HTMLTableCellElement>(null)
|
|
290
|
+
const { columnHeaderProps } = useTableColumnHeader({ node: column }, state, ref)
|
|
291
|
+
const sortActive = state.sortDescriptor?.column === column.key
|
|
292
|
+
const sortIcon = state.sortDescriptor?.direction === 'ascending' ? '▲' : '▼'
|
|
293
|
+
|
|
294
|
+
return (
|
|
295
|
+
<th
|
|
296
|
+
{...columnHeaderProps}
|
|
297
|
+
className={column.props?.className || undefined}
|
|
298
|
+
colSpan={column.colSpan ?? undefined}
|
|
299
|
+
ref={ref}
|
|
300
|
+
>
|
|
301
|
+
{column.rendered}
|
|
302
|
+
{column.props?.allowsSorting && (
|
|
303
|
+
<span aria-hidden="true" className="table-sort-icon">
|
|
304
|
+
{sortActive ? sortIcon : '⇅'}
|
|
305
|
+
</span>
|
|
306
|
+
)}
|
|
307
|
+
</th>
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
interface TableSelectAllCellProps<T> {
|
|
312
|
+
column: ColumnNode<T>
|
|
313
|
+
state: TableState<T>
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const TableSelectAllCell = <T extends object>({ column, state }: TableSelectAllCellProps<T>) => {
|
|
317
|
+
const ref = useRef<HTMLTableCellElement>(null)
|
|
318
|
+
const { columnHeaderProps } = useTableColumnHeader({ node: column }, state, ref)
|
|
319
|
+
const { checkboxProps } = useTableSelectAllCheckbox(state)
|
|
320
|
+
|
|
321
|
+
return (
|
|
322
|
+
<th {...columnHeaderProps} className="table-selection-cell" ref={ref}>
|
|
323
|
+
<TableCheckbox checkboxProps={checkboxProps} />
|
|
324
|
+
</th>
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
interface TableRowProps<T> {
|
|
329
|
+
children: ReactNode
|
|
330
|
+
item: RowNode<T>
|
|
331
|
+
state: TableState<T>
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const TableRow = <T extends object>({ children, item, state }: TableRowProps<T>) => {
|
|
335
|
+
const ref = useRef<HTMLTableRowElement>(null)
|
|
336
|
+
const { rowProps } = useTableRow({ node: item }, state, ref)
|
|
337
|
+
const isSelected = state.selectionManager.isSelected(item.key)
|
|
338
|
+
|
|
339
|
+
return (
|
|
340
|
+
<tr
|
|
341
|
+
{...rowProps}
|
|
342
|
+
className={classNames({ active: isSelected }, item.props?.className)}
|
|
343
|
+
ref={ref}
|
|
344
|
+
>
|
|
345
|
+
{children}
|
|
346
|
+
</tr>
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
interface TableCellProps<T> {
|
|
351
|
+
cell: CellNode<T>
|
|
352
|
+
stacked?: boolean
|
|
353
|
+
state: TableState<T>
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const TableCell = <T extends object>({ cell, stacked, state }: TableCellProps<T>) => {
|
|
357
|
+
const ref = useRef<HTMLTableCellElement>(null)
|
|
358
|
+
const { gridCellProps } = useTableCell({ node: cell }, state, ref)
|
|
359
|
+
|
|
360
|
+
return (
|
|
361
|
+
<td
|
|
362
|
+
{...gridCellProps}
|
|
363
|
+
className={cell.props?.className || undefined}
|
|
364
|
+
data-cell={stacked ? cell.column?.textValue || undefined : undefined}
|
|
365
|
+
ref={ref}
|
|
366
|
+
>
|
|
367
|
+
{cell.rendered}
|
|
368
|
+
</td>
|
|
369
|
+
)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const TableSelectionCell = <T extends object>({ cell, state }: TableCellProps<T>) => {
|
|
373
|
+
const ref = useRef<HTMLTableCellElement>(null)
|
|
374
|
+
const { gridCellProps } = useTableCell({ node: cell }, state, ref)
|
|
375
|
+
const { checkboxProps } = useTableSelectionCheckbox({ key: cell.parentKey! }, state)
|
|
376
|
+
|
|
377
|
+
return (
|
|
378
|
+
<td {...gridCellProps} className="table-selection-cell" ref={ref}>
|
|
379
|
+
<TableCheckbox checkboxProps={checkboxProps} />
|
|
380
|
+
</td>
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const TableCheckbox = ({ checkboxProps }: { checkboxProps: AriaCheckboxProps }) => {
|
|
385
|
+
const toggleState = useToggleState(checkboxProps)
|
|
386
|
+
const ref = useRef<HTMLInputElement>(null)
|
|
387
|
+
const { inputProps } = useCheckbox(checkboxProps, toggleState, ref)
|
|
388
|
+
|
|
389
|
+
return <input {...inputProps} className="check-input" ref={ref} />
|
|
390
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ReactElement } from 'react'
|
|
2
|
+
import { TableBody as StatelyTableBody } from 'react-stately'
|
|
3
|
+
|
|
4
|
+
export interface TableBodyProps<T> {
|
|
5
|
+
/**
|
|
6
|
+
* `TableRow` elements, or a render function paired with `items` for dynamic row generation.
|
|
7
|
+
*/
|
|
8
|
+
children: ReactElement | ReactElement[] | ((item: T) => ReactElement)
|
|
9
|
+
/**
|
|
10
|
+
* A string of all className you want applied to the `<tbody>` element.
|
|
11
|
+
*/
|
|
12
|
+
className?: string
|
|
13
|
+
/**
|
|
14
|
+
* A list of row data objects, rendered via the function form of `children`.
|
|
15
|
+
*/
|
|
16
|
+
items?: Iterable<T>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Collection node, data-only — read by `Table` to build the table's row collection. Never
|
|
21
|
+
* rendered directly.
|
|
22
|
+
*/
|
|
23
|
+
export const TableBody = StatelyTableBody as unknown as <T>(
|
|
24
|
+
props: TableBodyProps<T>
|
|
25
|
+
) => ReactElement
|