@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,145 @@
|
|
|
1
|
+
import React, { forwardRef, ReactNode, TextareaHTMLAttributes, useRef } from 'react'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
import { AriaTextFieldOptions, useTextField } from 'react-aria'
|
|
4
|
+
|
|
5
|
+
import { useForkedRef, useFormField } from '../../hooks'
|
|
6
|
+
import { validationClassName } from '../../utils/validationClassName'
|
|
7
|
+
import { renderFormField } from '../form-field/renderFormField'
|
|
8
|
+
|
|
9
|
+
export interface TextareaProps extends Omit<
|
|
10
|
+
TextareaHTMLAttributes<HTMLTextAreaElement>,
|
|
11
|
+
'defaultValue' | 'onChange' | 'value'
|
|
12
|
+
> {
|
|
13
|
+
/**
|
|
14
|
+
* A string of all className you want applied to the component.
|
|
15
|
+
*/
|
|
16
|
+
className?: string
|
|
17
|
+
/**
|
|
18
|
+
* The value of the textarea, uncontrolled.
|
|
19
|
+
*/
|
|
20
|
+
defaultValue?: string
|
|
21
|
+
/**
|
|
22
|
+
* Toggle the disabled state for the component.
|
|
23
|
+
*/
|
|
24
|
+
disabled?: boolean
|
|
25
|
+
/**
|
|
26
|
+
* A description for the field, rendered below the textarea.
|
|
27
|
+
*/
|
|
28
|
+
help?: ReactNode
|
|
29
|
+
/**
|
|
30
|
+
* Set component validation state to invalid.
|
|
31
|
+
*/
|
|
32
|
+
invalid?: boolean
|
|
33
|
+
/**
|
|
34
|
+
* An error message for the field, rendered below the textarea when `invalid` is set.
|
|
35
|
+
*/
|
|
36
|
+
invalidFeedback?: ReactNode
|
|
37
|
+
/**
|
|
38
|
+
* The field's caption, rendered as a `FormLabel` associated with this textarea.
|
|
39
|
+
*/
|
|
40
|
+
label?: ReactNode
|
|
41
|
+
/**
|
|
42
|
+
* Handler that is called when the value changes.
|
|
43
|
+
*/
|
|
44
|
+
onChange?: (value: string) => void
|
|
45
|
+
/**
|
|
46
|
+
* Render the component styled as plain text. Removes the default form field styling and preserve the correct margin and padding. Recommend to use only along side `readonly`.
|
|
47
|
+
*/
|
|
48
|
+
plainText?: boolean
|
|
49
|
+
/**
|
|
50
|
+
* Toggle the readonly state for the component.
|
|
51
|
+
*/
|
|
52
|
+
readOnly?: boolean
|
|
53
|
+
/**
|
|
54
|
+
* The number of visible text lines for the control.
|
|
55
|
+
*/
|
|
56
|
+
rows?: number
|
|
57
|
+
/**
|
|
58
|
+
* Size the component small or large.
|
|
59
|
+
*/
|
|
60
|
+
size?: 'small' | 'large'
|
|
61
|
+
/**
|
|
62
|
+
* Set component validation state to valid.
|
|
63
|
+
*/
|
|
64
|
+
valid?: boolean
|
|
65
|
+
/**
|
|
66
|
+
* A success message for the field, rendered below the textarea when `valid` is set.
|
|
67
|
+
*/
|
|
68
|
+
validFeedback?: ReactNode
|
|
69
|
+
/**
|
|
70
|
+
* The value of the textarea, controlled.
|
|
71
|
+
*/
|
|
72
|
+
value?: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
76
|
+
(
|
|
77
|
+
{
|
|
78
|
+
className,
|
|
79
|
+
disabled,
|
|
80
|
+
help,
|
|
81
|
+
id,
|
|
82
|
+
invalid,
|
|
83
|
+
invalidFeedback,
|
|
84
|
+
label,
|
|
85
|
+
plainText,
|
|
86
|
+
readOnly,
|
|
87
|
+
rows,
|
|
88
|
+
size,
|
|
89
|
+
valid,
|
|
90
|
+
validFeedback,
|
|
91
|
+
...rest
|
|
92
|
+
},
|
|
93
|
+
ref
|
|
94
|
+
) => {
|
|
95
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
|
96
|
+
const forkedRef = useForkedRef(ref, textareaRef)
|
|
97
|
+
|
|
98
|
+
const { describedBy, feedbackId, helpId, inputId, labelId, labelledBy } = useFormField({
|
|
99
|
+
ariaDescribedBy: rest['aria-describedby'],
|
|
100
|
+
ariaLabelledBy: rest['aria-labelledby'],
|
|
101
|
+
help,
|
|
102
|
+
id,
|
|
103
|
+
invalid,
|
|
104
|
+
invalidFeedback,
|
|
105
|
+
label,
|
|
106
|
+
valid,
|
|
107
|
+
validFeedback
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const { inputProps } = useTextField<'textarea'>(
|
|
111
|
+
{
|
|
112
|
+
...rest,
|
|
113
|
+
'aria-describedby': describedBy,
|
|
114
|
+
'aria-labelledby': labelledBy,
|
|
115
|
+
id: inputId,
|
|
116
|
+
inputElementType: 'textarea',
|
|
117
|
+
isDisabled: disabled,
|
|
118
|
+
isInvalid: invalid,
|
|
119
|
+
isReadOnly: readOnly
|
|
120
|
+
} as AriaTextFieldOptions<'textarea'>,
|
|
121
|
+
textareaRef
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
const _className = classNames(
|
|
125
|
+
'form-input',
|
|
126
|
+
plainText && 'plaintext',
|
|
127
|
+
size,
|
|
128
|
+
validationClassName(invalid, valid),
|
|
129
|
+
className
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
return renderFormField({
|
|
133
|
+
children: <textarea {...inputProps} rows={rows} className={_className} ref={forkedRef} />,
|
|
134
|
+
help,
|
|
135
|
+
ids: { feedback: feedbackId, help: helpId, input: inputId, label: labelId },
|
|
136
|
+
invalid,
|
|
137
|
+
invalidFeedback,
|
|
138
|
+
label,
|
|
139
|
+
valid,
|
|
140
|
+
validFeedback
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
Textarea.displayName = 'Textarea'
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import React, { forwardRef, HTMLAttributes, ReactNode } from 'react'
|
|
2
|
+
import { mergeProps } from 'react-aria'
|
|
3
|
+
import { Transition } from 'react-transition-group'
|
|
4
|
+
import classNames from 'classnames'
|
|
5
|
+
|
|
6
|
+
import { ContextColor } from '../../types'
|
|
7
|
+
import { useAutoDismiss, useDismissibleTransition } from '../../hooks'
|
|
8
|
+
import { ToastContext } from './context'
|
|
9
|
+
import { ToastBody } from './ToastBody'
|
|
10
|
+
import { ToastFooter } from './ToastFooter'
|
|
11
|
+
import { ToastHeader } from './ToastHeader'
|
|
12
|
+
|
|
13
|
+
export interface ToastProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
14
|
+
/**
|
|
15
|
+
* Apply a CSS fade transition to the toast.
|
|
16
|
+
*/
|
|
17
|
+
animation?: boolean
|
|
18
|
+
/**
|
|
19
|
+
* Auto hide the toast. The timer starts once the show transition completes and pauses
|
|
20
|
+
* while the pointer is over the toast or focus is within it.
|
|
21
|
+
*/
|
|
22
|
+
autohide?: boolean
|
|
23
|
+
/**
|
|
24
|
+
* A string of all className you want applied to the base component.
|
|
25
|
+
*/
|
|
26
|
+
className?: string
|
|
27
|
+
/**
|
|
28
|
+
* Adds a close button — shorthand for `ToastHeader`'s/`ToastBody`'s `closeButton` prop.
|
|
29
|
+
* Placed in the header when `icon`/`title`/`time` is set; otherwise placed in the body
|
|
30
|
+
* alongside `message`, or in a header containing only the close button if `message` is
|
|
31
|
+
* also unset.
|
|
32
|
+
*/
|
|
33
|
+
closeButton?: boolean
|
|
34
|
+
/**
|
|
35
|
+
* Overrides the close button's accessible name (defaults to `'Close'`). Set this for
|
|
36
|
+
* non-English UIs. Shorthand for `ToastHeader`'s `closeLabel` prop.
|
|
37
|
+
*/
|
|
38
|
+
closeLabel?: string
|
|
39
|
+
/**
|
|
40
|
+
* Sets the color of the component to one of Chassis context colors.
|
|
41
|
+
*/
|
|
42
|
+
color?: ContextColor
|
|
43
|
+
/**
|
|
44
|
+
* Delay hiding the toast (ms).
|
|
45
|
+
*/
|
|
46
|
+
delay?: number
|
|
47
|
+
/**
|
|
48
|
+
* Trailing content — typically a row of `Button`s — rendered via a single `ToastFooter`,
|
|
49
|
+
* after `message`/`children`. Pass a function to receive `close` directly, instead of
|
|
50
|
+
* calling `useToast()` from a child component to wire up a "Close" action.
|
|
51
|
+
*/
|
|
52
|
+
footer?: ReactNode | ((close: () => void) => ReactNode)
|
|
53
|
+
/**
|
|
54
|
+
* Leading icon for the header. A string is rendered as `<ToastIcon name={icon} />`; pass
|
|
55
|
+
* any other node for a fully custom icon (typically a logo or avatar). Shorthand for
|
|
56
|
+
* `ToastHeader`'s `icon` prop; hidden from assistive technology by default, since it
|
|
57
|
+
* duplicates `title` visually.
|
|
58
|
+
*/
|
|
59
|
+
icon?: string | ReactNode
|
|
60
|
+
/**
|
|
61
|
+
* Message body, rendered via a single `ToastBody`. For multi-block content, compose
|
|
62
|
+
* `children` manually instead — `message` wraps everything in one element.
|
|
63
|
+
*/
|
|
64
|
+
message?: ReactNode
|
|
65
|
+
/**
|
|
66
|
+
* Header timestamp, rendered after `title`. Shorthand for `ToastHeader`'s `time` prop.
|
|
67
|
+
*/
|
|
68
|
+
time?: ReactNode
|
|
69
|
+
/**
|
|
70
|
+
* Header heading, rendered before `time`. Shorthand for `ToastHeader`'s children. When set
|
|
71
|
+
* alongside `message`, wires the toast's `aria-labelledby`/`aria-describedby` to them
|
|
72
|
+
* automatically.
|
|
73
|
+
*/
|
|
74
|
+
title?: ReactNode
|
|
75
|
+
/**
|
|
76
|
+
* Callback fired when the component requests to be closed.
|
|
77
|
+
*/
|
|
78
|
+
onClose?: () => void
|
|
79
|
+
/**
|
|
80
|
+
* Callback fired when the component requests to be shown.
|
|
81
|
+
*/
|
|
82
|
+
onShow?: () => void
|
|
83
|
+
/**
|
|
84
|
+
* ARIA live-region role. Use `status` (the default) for confirmation, progress, and
|
|
85
|
+
* informational messages, which announce politely. Use `alert` for messages that need
|
|
86
|
+
* immediate attention — validation errors, failed operations — which interrupt speech.
|
|
87
|
+
*/
|
|
88
|
+
role?: 'status' | 'alert'
|
|
89
|
+
/**
|
|
90
|
+
* Apply a full-color background with inverted text. Only meaningful alongside `color`.
|
|
91
|
+
*/
|
|
92
|
+
solid?: boolean
|
|
93
|
+
/**
|
|
94
|
+
* Apply a semi-transparent background.
|
|
95
|
+
*/
|
|
96
|
+
translucent?: boolean
|
|
97
|
+
/**
|
|
98
|
+
* Toggle the visibility of component.
|
|
99
|
+
*/
|
|
100
|
+
visible?: boolean
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const Toast = forwardRef<HTMLDivElement, ToastProps>(
|
|
104
|
+
(
|
|
105
|
+
{
|
|
106
|
+
children,
|
|
107
|
+
animation = true,
|
|
108
|
+
autohide = true,
|
|
109
|
+
className,
|
|
110
|
+
closeButton,
|
|
111
|
+
closeLabel,
|
|
112
|
+
color,
|
|
113
|
+
delay = 5000,
|
|
114
|
+
footer,
|
|
115
|
+
icon,
|
|
116
|
+
message,
|
|
117
|
+
role = 'status',
|
|
118
|
+
solid,
|
|
119
|
+
time,
|
|
120
|
+
title,
|
|
121
|
+
translucent,
|
|
122
|
+
visible = false,
|
|
123
|
+
onClose,
|
|
124
|
+
onShow,
|
|
125
|
+
...rest
|
|
126
|
+
},
|
|
127
|
+
ref
|
|
128
|
+
) => {
|
|
129
|
+
const {
|
|
130
|
+
close,
|
|
131
|
+
entered,
|
|
132
|
+
forkedRef,
|
|
133
|
+
getTransitionClass,
|
|
134
|
+
textId,
|
|
135
|
+
titleId,
|
|
136
|
+
transitionProps,
|
|
137
|
+
visible: _visible
|
|
138
|
+
} = useDismissibleTransition({ onClose, onShow, ref, visible })
|
|
139
|
+
|
|
140
|
+
// The autohide timer is only meaningful once the show transition has actually finished (see
|
|
141
|
+
// `autohide`'s JSDoc) — gating on `_visible` alone would start it the instant `visible` flips
|
|
142
|
+
// true, while the toast is still fading/sliding in.
|
|
143
|
+
const autoDismissProps = useAutoDismiss({
|
|
144
|
+
enabled: autohide,
|
|
145
|
+
delay,
|
|
146
|
+
visible: _visible && entered,
|
|
147
|
+
onHide: close
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
const contextValues = {
|
|
151
|
+
visible: _visible,
|
|
152
|
+
close
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const _className = classNames(
|
|
156
|
+
'toast',
|
|
157
|
+
{
|
|
158
|
+
fade: animation,
|
|
159
|
+
context: !!color,
|
|
160
|
+
solid: Boolean(solid && color),
|
|
161
|
+
translucent
|
|
162
|
+
},
|
|
163
|
+
color,
|
|
164
|
+
className
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
const hasHeaderShorthand = icon != null || title != null || time != null
|
|
168
|
+
const headerGetsCloseButton = closeButton && (hasHeaderShorthand || message == null)
|
|
169
|
+
const bodyGetsCloseButton = closeButton && !hasHeaderShorthand && message != null
|
|
170
|
+
const hasHeaderContent = hasHeaderShorthand || headerGetsCloseButton
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<Transition {...transitionProps} timeout={250}>
|
|
174
|
+
{(state) => {
|
|
175
|
+
const transitionClass = getTransitionClass(state)
|
|
176
|
+
return (
|
|
177
|
+
<ToastContext.Provider value={contextValues}>
|
|
178
|
+
<div
|
|
179
|
+
className={classNames(_className, transitionClass)}
|
|
180
|
+
role={role}
|
|
181
|
+
aria-labelledby={title != null ? titleId : undefined}
|
|
182
|
+
aria-describedby={title != null && message != null ? textId : undefined}
|
|
183
|
+
{...mergeProps(rest, autoDismissProps)}
|
|
184
|
+
ref={forkedRef}
|
|
185
|
+
>
|
|
186
|
+
{hasHeaderContent && (
|
|
187
|
+
<ToastHeader
|
|
188
|
+
icon={icon}
|
|
189
|
+
time={time}
|
|
190
|
+
titleId={title != null ? titleId : undefined}
|
|
191
|
+
closeButton={headerGetsCloseButton}
|
|
192
|
+
closeLabel={closeLabel}
|
|
193
|
+
>
|
|
194
|
+
{title}
|
|
195
|
+
</ToastHeader>
|
|
196
|
+
)}
|
|
197
|
+
{message != null && (
|
|
198
|
+
<ToastBody
|
|
199
|
+
id={title != null && message != null ? textId : undefined}
|
|
200
|
+
closeButton={bodyGetsCloseButton}
|
|
201
|
+
closeLabel={closeLabel}
|
|
202
|
+
>
|
|
203
|
+
{message}
|
|
204
|
+
</ToastBody>
|
|
205
|
+
)}
|
|
206
|
+
{children}
|
|
207
|
+
{footer != null && (
|
|
208
|
+
<ToastFooter>{typeof footer === 'function' ? footer(close) : footer}</ToastFooter>
|
|
209
|
+
)}
|
|
210
|
+
</div>
|
|
211
|
+
</ToastContext.Provider>
|
|
212
|
+
)
|
|
213
|
+
}}
|
|
214
|
+
</Transition>
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
Toast.displayName = 'Toast'
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { ElementType, ForwardRefRenderFunction, ReactElement } from 'react'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
|
|
4
|
+
import { CloseButton } from '../close-button/CloseButton'
|
|
5
|
+
import { useToast } from '../../hooks'
|
|
6
|
+
import {
|
|
7
|
+
createPolymorphicComponent,
|
|
8
|
+
PolymorphicComponentProps,
|
|
9
|
+
PolymorphicRef
|
|
10
|
+
} from '../../utils/polymorphic'
|
|
11
|
+
|
|
12
|
+
type ToastBodyOwnProps<C extends ElementType> = {
|
|
13
|
+
/**
|
|
14
|
+
* A string of all className you want applied to the base component.
|
|
15
|
+
*/
|
|
16
|
+
className?: string
|
|
17
|
+
/**
|
|
18
|
+
* Adds a close button alongside the body content, so a toast composed without a
|
|
19
|
+
* `ToastHeader` still gets a dismiss control without any manual layout markup.
|
|
20
|
+
*/
|
|
21
|
+
closeButton?: boolean
|
|
22
|
+
/**
|
|
23
|
+
* Overrides the close button's accessible name (defaults to `'Close'`). Set this for
|
|
24
|
+
* non-English UIs.
|
|
25
|
+
*/
|
|
26
|
+
closeLabel?: string
|
|
27
|
+
/**
|
|
28
|
+
* Component used for the root node. Either a string to use a HTML element or a component.
|
|
29
|
+
*/
|
|
30
|
+
component?: C
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type ToastBodyProps<C extends ElementType = 'div'> = PolymorphicComponentProps<
|
|
34
|
+
C,
|
|
35
|
+
ToastBodyOwnProps<C>
|
|
36
|
+
>
|
|
37
|
+
|
|
38
|
+
type ToastBodyComponent = (<C extends ElementType = 'div'>(
|
|
39
|
+
props: ToastBodyProps<C> & { ref?: PolymorphicRef<C> }
|
|
40
|
+
) => ReactElement | null) & { displayName?: string }
|
|
41
|
+
|
|
42
|
+
function ToastBodyRender<C extends ElementType = 'div'>(
|
|
43
|
+
{ children, className, closeButton, closeLabel, component, ...rest }: ToastBodyProps<C>,
|
|
44
|
+
ref: PolymorphicRef<C>
|
|
45
|
+
) {
|
|
46
|
+
const { close } = useToast()
|
|
47
|
+
const Component = component ?? 'div'
|
|
48
|
+
const _className = classNames(
|
|
49
|
+
'toast-body',
|
|
50
|
+
{ 'd-flex align-items-start justify-content-between gap-small': closeButton },
|
|
51
|
+
className
|
|
52
|
+
)
|
|
53
|
+
return (
|
|
54
|
+
<Component className={_className} {...rest} ref={ref}>
|
|
55
|
+
{closeButton ? <div>{children}</div> : children}
|
|
56
|
+
{closeButton && <CloseButton label={closeLabel} onClick={close} />}
|
|
57
|
+
</Component>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const ToastBody = createPolymorphicComponent<ToastBodyComponent>(
|
|
62
|
+
ToastBodyRender as ForwardRefRenderFunction<Element, ToastBodyProps<ElementType>>,
|
|
63
|
+
'ToastBody'
|
|
64
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
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 ToastFooterOwnProps<C extends ElementType> = {
|
|
11
|
+
/**
|
|
12
|
+
* A string of all className you want applied to the base 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
|
+
|
|
21
|
+
export type ToastFooterProps<C extends ElementType = 'div'> = PolymorphicComponentProps<
|
|
22
|
+
C,
|
|
23
|
+
ToastFooterOwnProps<C>
|
|
24
|
+
>
|
|
25
|
+
|
|
26
|
+
type ToastFooterComponent = (<C extends ElementType = 'div'>(
|
|
27
|
+
props: ToastFooterProps<C> & { ref?: PolymorphicRef<C> }
|
|
28
|
+
) => ReactElement | null) & { displayName?: string }
|
|
29
|
+
|
|
30
|
+
function ToastFooterRender<C extends ElementType = 'div'>(
|
|
31
|
+
{ children, className, component, ...rest }: ToastFooterProps<C>,
|
|
32
|
+
ref: PolymorphicRef<C>
|
|
33
|
+
) {
|
|
34
|
+
const Component = component ?? 'div'
|
|
35
|
+
const _className = classNames('toast-footer', className)
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Component className={_className} {...rest} ref={ref}>
|
|
39
|
+
{children}
|
|
40
|
+
</Component>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const ToastFooter = createPolymorphicComponent<ToastFooterComponent>(
|
|
45
|
+
ToastFooterRender as ForwardRefRenderFunction<Element, ToastFooterProps<ElementType>>,
|
|
46
|
+
'ToastFooter'
|
|
47
|
+
)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import React, { ElementType, ForwardRefRenderFunction, ReactElement, ReactNode } from 'react'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
|
|
4
|
+
import { CloseButton } from '../close-button/CloseButton'
|
|
5
|
+
import { useToast } from '../../hooks'
|
|
6
|
+
import {
|
|
7
|
+
createPolymorphicComponent,
|
|
8
|
+
PolymorphicComponentProps,
|
|
9
|
+
PolymorphicRef
|
|
10
|
+
} from '../../utils/polymorphic'
|
|
11
|
+
import { ToastIcon } from './ToastIcon'
|
|
12
|
+
|
|
13
|
+
type ToastHeaderOwnProps<C extends ElementType> = {
|
|
14
|
+
/**
|
|
15
|
+
* Heading, rendered before `time` as a `<strong>`.
|
|
16
|
+
*/
|
|
17
|
+
children?: ReactNode
|
|
18
|
+
/**
|
|
19
|
+
* A string of all className you want applied to the base component.
|
|
20
|
+
*/
|
|
21
|
+
className?: string
|
|
22
|
+
/**
|
|
23
|
+
* Automatically add a close button to the header.
|
|
24
|
+
*/
|
|
25
|
+
closeButton?: boolean
|
|
26
|
+
/**
|
|
27
|
+
* Overrides the close button's accessible name (defaults to `'Close'`). Set this for
|
|
28
|
+
* non-English UIs.
|
|
29
|
+
*/
|
|
30
|
+
closeLabel?: string
|
|
31
|
+
/**
|
|
32
|
+
* Component used for the root node. Either a string to use a HTML element or a component.
|
|
33
|
+
*/
|
|
34
|
+
component?: C
|
|
35
|
+
/**
|
|
36
|
+
* Leading icon. A string is rendered as `<ToastIcon name={icon} />` and hidden from
|
|
37
|
+
* assistive technology by default, since it duplicates the heading visually. Pass any other
|
|
38
|
+
* node for a fully custom icon (typically a logo or avatar) — a custom node is left as-is,
|
|
39
|
+
* since it may carry its own meaningful accessible name (e.g. an avatar's `alt` text).
|
|
40
|
+
*/
|
|
41
|
+
icon?: string | ReactNode
|
|
42
|
+
/**
|
|
43
|
+
* Trailing timestamp, rendered after the heading.
|
|
44
|
+
*/
|
|
45
|
+
time?: ReactNode
|
|
46
|
+
/**
|
|
47
|
+
* Sets the `id` on the rendered heading element, for `aria-labelledby` wiring. Set
|
|
48
|
+
* automatically by `Toast` when both `title` and `message` are used together; only needed
|
|
49
|
+
* here for manual wiring in a fully custom composition.
|
|
50
|
+
*/
|
|
51
|
+
titleId?: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type ToastHeaderProps<C extends ElementType = 'div'> = PolymorphicComponentProps<
|
|
55
|
+
C,
|
|
56
|
+
ToastHeaderOwnProps<C>
|
|
57
|
+
>
|
|
58
|
+
|
|
59
|
+
type ToastHeaderComponent = (<C extends ElementType = 'div'>(
|
|
60
|
+
props: ToastHeaderProps<C> & { ref?: PolymorphicRef<C> }
|
|
61
|
+
) => ReactElement | null) & { displayName?: string }
|
|
62
|
+
|
|
63
|
+
function ToastHeaderRender<C extends ElementType = 'div'>(
|
|
64
|
+
{
|
|
65
|
+
children,
|
|
66
|
+
className,
|
|
67
|
+
closeButton,
|
|
68
|
+
closeLabel,
|
|
69
|
+
component,
|
|
70
|
+
icon,
|
|
71
|
+
time,
|
|
72
|
+
titleId,
|
|
73
|
+
...rest
|
|
74
|
+
}: ToastHeaderProps<C>,
|
|
75
|
+
ref: PolymorphicRef<C>
|
|
76
|
+
) {
|
|
77
|
+
const { close } = useToast()
|
|
78
|
+
const Component = component ?? 'div'
|
|
79
|
+
const _className = classNames('toast-header', className)
|
|
80
|
+
return (
|
|
81
|
+
<Component className={_className} {...rest} ref={ref}>
|
|
82
|
+
{icon != null &&
|
|
83
|
+
(typeof icon === 'string' ? (
|
|
84
|
+
<span aria-hidden="true" className="me-small">
|
|
85
|
+
<ToastIcon name={icon} />
|
|
86
|
+
</span>
|
|
87
|
+
) : (
|
|
88
|
+
<span className="me-small">{icon}</span>
|
|
89
|
+
))}
|
|
90
|
+
{children != null && (
|
|
91
|
+
<strong id={titleId} className="me-auto">
|
|
92
|
+
{children}
|
|
93
|
+
</strong>
|
|
94
|
+
)}
|
|
95
|
+
{time != null && <small>{time}</small>}
|
|
96
|
+
{closeButton && <CloseButton label={closeLabel} onClick={close} />}
|
|
97
|
+
</Component>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const ToastHeader = createPolymorphicComponent<ToastHeaderComponent>(
|
|
102
|
+
ToastHeaderRender as ForwardRefRenderFunction<Element, ToastHeaderProps<ElementType>>,
|
|
103
|
+
'ToastHeader'
|
|
104
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
|
|
4
|
+
import { Icon, IconProps } from '../icon/Icon'
|
|
5
|
+
|
|
6
|
+
export type ToastIconProps = IconProps & {
|
|
7
|
+
/**
|
|
8
|
+
* A string of all className you want applied to the base component.
|
|
9
|
+
*/
|
|
10
|
+
className?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const ToastIcon = forwardRef<HTMLSpanElement | SVGSVGElement, ToastIconProps>(
|
|
14
|
+
({ className, ...rest }, ref) => {
|
|
15
|
+
const _className = classNames('toast-icon', className)
|
|
16
|
+
|
|
17
|
+
return <Icon className={_className} {...rest} ref={ref} />
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
ToastIcon.displayName = 'ToastIcon'
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React, { forwardRef, HTMLAttributes } from 'react'
|
|
2
|
+
import { createPortal } from 'react-dom'
|
|
3
|
+
import classNames from 'classnames'
|
|
4
|
+
|
|
5
|
+
import { useToastRegionQueue } from '../../hooks'
|
|
6
|
+
import { Toast } from './Toast'
|
|
7
|
+
import { toastQueue } from './toastQueue'
|
|
8
|
+
|
|
9
|
+
export interface ToasterProps extends HTMLAttributes<HTMLDivElement> {
|
|
10
|
+
/**
|
|
11
|
+
* Overrides the toast region's accessible name (defaults to `'Notifications'`, per
|
|
12
|
+
* react-aria). Set this for non-English UIs, or to distinguish multiple toasters on the
|
|
13
|
+
* same page.
|
|
14
|
+
*/
|
|
15
|
+
'aria-label'?: string
|
|
16
|
+
/**
|
|
17
|
+
* A string of all className you want applied to the base component.
|
|
18
|
+
*/
|
|
19
|
+
className?: string
|
|
20
|
+
/**
|
|
21
|
+
* Describes the placement of your component.
|
|
22
|
+
*
|
|
23
|
+
* @type 'top-start' | 'top-center' | 'top-end' | 'middle-start' | 'middle-center' | 'middle-end' | 'bottom-start' | 'bottom-center' | 'bottom-end' | string
|
|
24
|
+
*/
|
|
25
|
+
placement?:
|
|
26
|
+
| 'top-start'
|
|
27
|
+
| 'top-center'
|
|
28
|
+
| 'top-end'
|
|
29
|
+
| 'middle-start'
|
|
30
|
+
| 'middle-center'
|
|
31
|
+
| 'middle-end'
|
|
32
|
+
| 'bottom-start'
|
|
33
|
+
| 'bottom-center'
|
|
34
|
+
| 'bottom-end'
|
|
35
|
+
| string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Renders the shared `toastQueue` (see `toastQueue.ts` — `addToast()` is how toasts get added
|
|
39
|
+
// to it from anywhere in the app) plus any statically-passed `children`, e.g. a permanently
|
|
40
|
+
// pinned toast alongside dynamic ones.
|
|
41
|
+
export const Toaster = forwardRef<HTMLDivElement, ToasterProps>(
|
|
42
|
+
({ children, className, placement, ...rest }, ref) => {
|
|
43
|
+
const { forkedRef, regionProps, state } = useToastRegionQueue(toastQueue, ref)
|
|
44
|
+
|
|
45
|
+
const _className = classNames(
|
|
46
|
+
'toaster toast-container p-medium',
|
|
47
|
+
{
|
|
48
|
+
'position-fixed': placement,
|
|
49
|
+
'position-static': !placement,
|
|
50
|
+
'top-0': placement && placement.includes('top'),
|
|
51
|
+
'top-50 translate-middle-y': placement && placement.includes('middle'),
|
|
52
|
+
'bottom-0': placement && placement.includes('bottom'),
|
|
53
|
+
'start-0': placement && placement.includes('start'),
|
|
54
|
+
'start-50 translate-middle-x': placement && placement.includes('center'),
|
|
55
|
+
'end-0': placement && placement.includes('end')
|
|
56
|
+
},
|
|
57
|
+
className
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const toaster = (toasterRef?: React.Ref<HTMLDivElement>) => {
|
|
61
|
+
return state.visibleToasts.length > 0 || children ? (
|
|
62
|
+
<div className={_className} {...regionProps} {...rest} ref={toasterRef}>
|
|
63
|
+
{children}
|
|
64
|
+
{state.visibleToasts.map((queued) => {
|
|
65
|
+
const { children: toastChildren, ...toastProps } = queued.content
|
|
66
|
+
return (
|
|
67
|
+
<Toast
|
|
68
|
+
key={queued.key}
|
|
69
|
+
visible
|
|
70
|
+
{...toastProps}
|
|
71
|
+
onClose={() => state.close(queued.key)}
|
|
72
|
+
>
|
|
73
|
+
{toastChildren}
|
|
74
|
+
</Toast>
|
|
75
|
+
)
|
|
76
|
+
})}
|
|
77
|
+
</div>
|
|
78
|
+
) : null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return typeof window !== 'undefined' && placement
|
|
82
|
+
? createPortal(toaster(forkedRef), document.body)
|
|
83
|
+
: toaster(forkedRef)
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
Toaster.displayName = 'Toaster'
|