@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,178 @@
|
|
|
1
|
+
import React, { ElementType, ForwardRefRenderFunction, ReactElement } from 'react'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
|
|
4
|
+
import { ContextColor, ContextStyle } from '../../types'
|
|
5
|
+
import {
|
|
6
|
+
createPolymorphicComponent,
|
|
7
|
+
PolymorphicComponentProps,
|
|
8
|
+
PolymorphicRef
|
|
9
|
+
} from '../../utils/polymorphic'
|
|
10
|
+
|
|
11
|
+
type PlaceholderAlign = 'start' | 'center' | 'end'
|
|
12
|
+
|
|
13
|
+
type PlaceholderOwnProps<C extends ElementType> = {
|
|
14
|
+
/**
|
|
15
|
+
* Set the horizontal alignment. Only applies when `src` is set.
|
|
16
|
+
*/
|
|
17
|
+
align?: PlaceholderAlign
|
|
18
|
+
/**
|
|
19
|
+
* A string of all className you want applied to the component.
|
|
20
|
+
*/
|
|
21
|
+
className?: string
|
|
22
|
+
/**
|
|
23
|
+
* Sets the color of the generated placeholder graphic to one of Chassis context colors. Has no
|
|
24
|
+
* effect when `src` is set.
|
|
25
|
+
*/
|
|
26
|
+
color?: ContextColor
|
|
27
|
+
/**
|
|
28
|
+
* Sets the variant of the generated placeholder graphic to one of Chassis context styles. Has no
|
|
29
|
+
* effect when `src` is set.
|
|
30
|
+
*/
|
|
31
|
+
variant?: Exclude<ContextStyle, 'basic' | 'outline'>
|
|
32
|
+
/**
|
|
33
|
+
* Component used for the root node when `src` is set. Either a string to use an HTML element
|
|
34
|
+
* or a component — e.g. a framework's own `Image` component. Its own props (`src`, `fill`,
|
|
35
|
+
* `priority`, etc.) are type-checked at the call site once passed here.
|
|
36
|
+
*/
|
|
37
|
+
component?: C
|
|
38
|
+
/**
|
|
39
|
+
* Make the image responsive, so it never grows larger than its parent. Only applies when `src`
|
|
40
|
+
* is set.
|
|
41
|
+
*/
|
|
42
|
+
fluid?: boolean
|
|
43
|
+
/**
|
|
44
|
+
* Placeholder height.
|
|
45
|
+
*/
|
|
46
|
+
height?: number | string
|
|
47
|
+
/**
|
|
48
|
+
* Give the image a rounded border-radius. Only applies when `src` is set.
|
|
49
|
+
*/
|
|
50
|
+
rounded?: boolean
|
|
51
|
+
/**
|
|
52
|
+
* Renders a real image instead of the generated placeholder graphic, e.g. a default asset or a
|
|
53
|
+
* `placehold.co` URL.
|
|
54
|
+
*/
|
|
55
|
+
src?: string
|
|
56
|
+
/**
|
|
57
|
+
* Text shown in the generated placeholder graphic. Defaults to `{width}x{height}`. Pass `false`
|
|
58
|
+
* to hide it. Has no effect when `src` is set.
|
|
59
|
+
*/
|
|
60
|
+
text?: string | false
|
|
61
|
+
/**
|
|
62
|
+
* Give the image a thumbnail appearance (padding, background, border, box-shadow). Only
|
|
63
|
+
* applies when `src` is set.
|
|
64
|
+
*/
|
|
65
|
+
thumbnail?: boolean
|
|
66
|
+
/**
|
|
67
|
+
* Accessible title for the generated placeholder graphic, rendered as an SVG `<title>`. Pass
|
|
68
|
+
* `false` to hide it. Has no effect when `src` is set.
|
|
69
|
+
*
|
|
70
|
+
* @default 'Placeholder'
|
|
71
|
+
*/
|
|
72
|
+
title?: string | false
|
|
73
|
+
/**
|
|
74
|
+
* Placeholder width.
|
|
75
|
+
*/
|
|
76
|
+
width?: number | string
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type PlaceholderProps<C extends ElementType = 'img'> = PolymorphicComponentProps<
|
|
80
|
+
C,
|
|
81
|
+
PlaceholderOwnProps<C>
|
|
82
|
+
>
|
|
83
|
+
|
|
84
|
+
type PlaceholderComponent = (<C extends ElementType = 'img'>(
|
|
85
|
+
props: PlaceholderProps<C> & { ref?: PolymorphicRef<C> }
|
|
86
|
+
) => ReactElement | null) & { displayName?: string }
|
|
87
|
+
|
|
88
|
+
const alignClassNames = (align: PlaceholderAlign | undefined) => [
|
|
89
|
+
align === 'start' || align === 'end' ? `float-${align}` : null,
|
|
90
|
+
align === 'center' ? 'd-block mx-auto' : null
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
function PlaceholderRender<C extends ElementType = 'img'>(
|
|
94
|
+
{
|
|
95
|
+
align,
|
|
96
|
+
alt,
|
|
97
|
+
className,
|
|
98
|
+
color,
|
|
99
|
+
component,
|
|
100
|
+
fluid,
|
|
101
|
+
height,
|
|
102
|
+
rounded,
|
|
103
|
+
src,
|
|
104
|
+
text,
|
|
105
|
+
thumbnail,
|
|
106
|
+
title = 'Placeholder',
|
|
107
|
+
variant,
|
|
108
|
+
width,
|
|
109
|
+
...rest
|
|
110
|
+
}: PlaceholderProps<C>,
|
|
111
|
+
ref: PolymorphicRef<C>
|
|
112
|
+
) {
|
|
113
|
+
const showTitle = title !== false
|
|
114
|
+
const displayText =
|
|
115
|
+
text === false
|
|
116
|
+
? undefined
|
|
117
|
+
: (text ?? (width !== undefined && height !== undefined ? `${width}x${height}` : undefined))
|
|
118
|
+
const showText = displayText !== undefined
|
|
119
|
+
const label =
|
|
120
|
+
[showTitle && title, showText && displayText].filter(Boolean).join(': ') || undefined
|
|
121
|
+
|
|
122
|
+
if (src) {
|
|
123
|
+
const Component = component ?? 'img'
|
|
124
|
+
const _className = classNames(
|
|
125
|
+
{ image: fluid || thumbnail, fluid, rounded, thumbnail },
|
|
126
|
+
alignClassNames(align),
|
|
127
|
+
className
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<Component
|
|
132
|
+
src={src}
|
|
133
|
+
width={width}
|
|
134
|
+
height={height}
|
|
135
|
+
alt={alt ?? label ?? ''}
|
|
136
|
+
className={_className}
|
|
137
|
+
{...rest}
|
|
138
|
+
ref={ref}
|
|
139
|
+
/>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const _className = classNames(
|
|
144
|
+
'image',
|
|
145
|
+
{ context: Boolean(color || variant) },
|
|
146
|
+
color,
|
|
147
|
+
variant ? variant : 'bg-evident',
|
|
148
|
+
'fg-subtle',
|
|
149
|
+
className
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<svg
|
|
154
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
155
|
+
width={width}
|
|
156
|
+
height={height}
|
|
157
|
+
aria-label={label}
|
|
158
|
+
aria-hidden={label ? undefined : true}
|
|
159
|
+
className={_className}
|
|
160
|
+
preserveAspectRatio="xMidYMid slice"
|
|
161
|
+
role={label ? 'img' : undefined}
|
|
162
|
+
{...rest}
|
|
163
|
+
ref={ref as PolymorphicRef<'svg'>}
|
|
164
|
+
>
|
|
165
|
+
{showTitle && <title>{title}</title>}
|
|
166
|
+
{showText && (
|
|
167
|
+
<text dy=".3em" fill="currentColor" textAnchor="middle" x="50%" y="50%">
|
|
168
|
+
{displayText}
|
|
169
|
+
</text>
|
|
170
|
+
)}
|
|
171
|
+
</svg>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export const Placeholder = createPolymorphicComponent<PlaceholderComponent>(
|
|
176
|
+
PlaceholderRender as ForwardRefRenderFunction<Element, PlaceholderProps<ElementType>>,
|
|
177
|
+
'Placeholder'
|
|
178
|
+
)
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
FC,
|
|
3
|
+
HTMLAttributes,
|
|
4
|
+
ReactElement,
|
|
5
|
+
ReactNode,
|
|
6
|
+
RefObject,
|
|
7
|
+
useEffect,
|
|
8
|
+
useRef
|
|
9
|
+
} from 'react'
|
|
10
|
+
import { createPortal } from 'react-dom'
|
|
11
|
+
import classNames from 'classnames'
|
|
12
|
+
import { mergeProps, useDialog, useOverlayPosition, useOverlayTrigger } from 'react-aria'
|
|
13
|
+
import { useOverlayTriggerState } from 'react-stately'
|
|
14
|
+
import { Transition } from 'react-transition-group'
|
|
15
|
+
|
|
16
|
+
import { getOverlayArrowStyle, getOverlayTransitionClass, useFloatingOverlay } from '../../hooks'
|
|
17
|
+
import { Placement, resolveDataPlacement, toAriaPlacement } from '../../utils/overlayPlacement'
|
|
18
|
+
|
|
19
|
+
interface PopoverPanelProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'content'> {
|
|
20
|
+
arrowProps: HTMLAttributes<HTMLDivElement>
|
|
21
|
+
content: ReactNode | string
|
|
22
|
+
overlayRef: RefObject<HTMLDivElement>
|
|
23
|
+
overlayTriggerProps: HTMLAttributes<HTMLDivElement>
|
|
24
|
+
title?: ReactNode | string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// A distinct component (rather than inline JSX from the `Transition` render prop) so
|
|
28
|
+
// `useDialog`'s own mount-focus effect — which only fires on this component's mount, not on
|
|
29
|
+
// `overlayRef.current` merely becoming non-null — actually runs each time the popover opens.
|
|
30
|
+
// `Transition`'s `unmountOnExit` genuinely unmounts and remounts this component on every
|
|
31
|
+
// open, giving `useDialog` a fresh mount each time.
|
|
32
|
+
const PopoverPanel = ({
|
|
33
|
+
arrowProps,
|
|
34
|
+
content,
|
|
35
|
+
overlayRef,
|
|
36
|
+
overlayTriggerProps,
|
|
37
|
+
title,
|
|
38
|
+
...rest
|
|
39
|
+
}: PopoverPanelProps) => {
|
|
40
|
+
const { dialogProps, titleProps } = useDialog({}, overlayRef)
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div {...mergeProps(overlayTriggerProps, dialogProps, rest)} ref={overlayRef}>
|
|
44
|
+
<div className="popover-arrow" {...arrowProps} style={getOverlayArrowStyle(arrowProps)}></div>
|
|
45
|
+
{title && (
|
|
46
|
+
<div className="popover-header" {...titleProps}>
|
|
47
|
+
{title}
|
|
48
|
+
</div>
|
|
49
|
+
)}
|
|
50
|
+
<div className="popover-body">{content}</div>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface PopoverProps extends Pick<
|
|
56
|
+
HTMLAttributes<HTMLDivElement>,
|
|
57
|
+
'aria-label' | 'aria-labelledby'
|
|
58
|
+
> {
|
|
59
|
+
children: ReactElement
|
|
60
|
+
/**
|
|
61
|
+
* Content node for your component.
|
|
62
|
+
*/
|
|
63
|
+
content: ReactNode | string
|
|
64
|
+
/**
|
|
65
|
+
* Offset of the popover relative to its target, as `[crossAxis, mainAxis]`.
|
|
66
|
+
*/
|
|
67
|
+
offset?: [number, number]
|
|
68
|
+
/**
|
|
69
|
+
* Callback fired when the component requests to be hidden.
|
|
70
|
+
*/
|
|
71
|
+
onHide?: () => void
|
|
72
|
+
/**
|
|
73
|
+
* Callback fired when the component requests to be shown.
|
|
74
|
+
*/
|
|
75
|
+
onShow?: () => void
|
|
76
|
+
/**
|
|
77
|
+
* Title node for your component.
|
|
78
|
+
*/
|
|
79
|
+
title?: ReactNode | string
|
|
80
|
+
/**
|
|
81
|
+
* Describes the preferred placement of your component. Chassis will flip it to keep it in
|
|
82
|
+
* view.
|
|
83
|
+
*/
|
|
84
|
+
placement?: Placement
|
|
85
|
+
/**
|
|
86
|
+
* Toggle the visibility of popover component.
|
|
87
|
+
*/
|
|
88
|
+
visible?: boolean
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const Popover: FC<PopoverProps> = ({
|
|
92
|
+
children,
|
|
93
|
+
content,
|
|
94
|
+
placement = 'right',
|
|
95
|
+
offset: offsetProp = [0, 8],
|
|
96
|
+
onHide,
|
|
97
|
+
onShow,
|
|
98
|
+
title,
|
|
99
|
+
visible,
|
|
100
|
+
...rest
|
|
101
|
+
}) => {
|
|
102
|
+
const arrowRef = useRef<HTMLDivElement>(null)
|
|
103
|
+
const triggerRef = useRef<HTMLElement | null>(null)
|
|
104
|
+
const floatingRef = useRef<HTMLDivElement>(null)
|
|
105
|
+
|
|
106
|
+
const state = useOverlayTriggerState({ defaultOpen: !!visible })
|
|
107
|
+
const {
|
|
108
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
109
|
+
triggerProps: { onPress: _onPress, ...triggerProps },
|
|
110
|
+
overlayProps: overlayTriggerProps
|
|
111
|
+
} = useOverlayTrigger({ type: 'dialog' }, state, triggerRef)
|
|
112
|
+
|
|
113
|
+
const portalContainer = useFloatingOverlay({
|
|
114
|
+
close: state.close,
|
|
115
|
+
isOpen: state.isOpen,
|
|
116
|
+
open: state.open,
|
|
117
|
+
onHide,
|
|
118
|
+
onShow,
|
|
119
|
+
triggerRef,
|
|
120
|
+
visible
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// Escape should always close the popover, regardless of where focus currently is — a
|
|
124
|
+
// mouse-opened popover leaves focus on the trigger, not inside the panel. Mirrors
|
|
125
|
+
// `Menu.tsx`'s own window-level Escape handling. `onExited` below already returns focus to the
|
|
126
|
+
// trigger once the exit transition finishes, so this doesn't need to do that itself.
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (!state.isOpen) return undefined
|
|
129
|
+
|
|
130
|
+
const handleEscape = (event: KeyboardEvent) => {
|
|
131
|
+
if (event.key !== 'Escape') return
|
|
132
|
+
state.close()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
window.addEventListener('keydown', handleEscape)
|
|
136
|
+
return () => window.removeEventListener('keydown', handleEscape)
|
|
137
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
138
|
+
}, [state.isOpen, state.close])
|
|
139
|
+
|
|
140
|
+
// Deferred so the click that opened the popover doesn't immediately close it again — mirrors
|
|
141
|
+
// `Menu.tsx`'s own outside-click handling.
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (!state.isOpen) return undefined
|
|
144
|
+
|
|
145
|
+
const handleDismiss = (event: MouseEvent) => {
|
|
146
|
+
const target = event.target as Node
|
|
147
|
+
if (triggerRef.current?.contains(target)) return
|
|
148
|
+
if (floatingRef.current?.contains(target)) return
|
|
149
|
+
state.close()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const id = window.setTimeout(() => {
|
|
153
|
+
window.addEventListener('click', handleDismiss)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
return () => {
|
|
157
|
+
window.clearTimeout(id)
|
|
158
|
+
window.removeEventListener('click', handleDismiss)
|
|
159
|
+
}
|
|
160
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
161
|
+
}, [state.isOpen, state.close])
|
|
162
|
+
|
|
163
|
+
const {
|
|
164
|
+
overlayProps,
|
|
165
|
+
arrowProps,
|
|
166
|
+
placement: resolvedPlacement
|
|
167
|
+
} = useOverlayPosition({
|
|
168
|
+
targetRef: triggerRef,
|
|
169
|
+
overlayRef: floatingRef,
|
|
170
|
+
placement: toAriaPlacement(placement),
|
|
171
|
+
offset: offsetProp[1],
|
|
172
|
+
crossOffset: offsetProp[0],
|
|
173
|
+
arrowSize: 8,
|
|
174
|
+
arrowRef,
|
|
175
|
+
isOpen: state.isOpen,
|
|
176
|
+
// `useOverlayPosition` closes on any window scroll via a backward-compat `WeakMap` that
|
|
177
|
+
// `useOverlayTrigger` populates for `triggerRef.current` above. Passing `null` opts out so
|
|
178
|
+
// the popover repositions with its trigger instead of vanishing — matching
|
|
179
|
+
// `Autocomplete`/`Combobox`, neither of which is wired into that map.
|
|
180
|
+
onClose: null
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const floatingStyle: React.CSSProperties = {
|
|
184
|
+
position: overlayProps.style?.position as React.CSSProperties['position'],
|
|
185
|
+
top: overlayProps.style?.top,
|
|
186
|
+
left: overlayProps.style?.left
|
|
187
|
+
}
|
|
188
|
+
const placementAttr = resolveDataPlacement(placement, resolvedPlacement)
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<>
|
|
192
|
+
{React.cloneElement(children, {
|
|
193
|
+
ref: (node: HTMLElement | null) => {
|
|
194
|
+
triggerRef.current = node
|
|
195
|
+
},
|
|
196
|
+
...triggerProps,
|
|
197
|
+
onClick: (event: React.MouseEvent) => {
|
|
198
|
+
children.props.onClick?.(event)
|
|
199
|
+
state.toggle()
|
|
200
|
+
}
|
|
201
|
+
})}
|
|
202
|
+
{typeof window !== 'undefined' &&
|
|
203
|
+
createPortal(
|
|
204
|
+
<Transition
|
|
205
|
+
in={state.isOpen}
|
|
206
|
+
mountOnEnter
|
|
207
|
+
nodeRef={floatingRef}
|
|
208
|
+
onExited={() => {
|
|
209
|
+
const trigger = triggerRef.current
|
|
210
|
+
if (trigger && document.contains(trigger)) {
|
|
211
|
+
trigger.focus()
|
|
212
|
+
}
|
|
213
|
+
}}
|
|
214
|
+
timeout={{
|
|
215
|
+
enter: 0,
|
|
216
|
+
exit: 200
|
|
217
|
+
}}
|
|
218
|
+
unmountOnExit
|
|
219
|
+
>
|
|
220
|
+
{(transitionState) => {
|
|
221
|
+
const transitionClass = getOverlayTransitionClass(transitionState)
|
|
222
|
+
return (
|
|
223
|
+
<PopoverPanel
|
|
224
|
+
className={classNames('popover cx-popover-auto', transitionClass)}
|
|
225
|
+
data-cx-placement={placementAttr}
|
|
226
|
+
style={floatingStyle}
|
|
227
|
+
overlayTriggerProps={overlayTriggerProps}
|
|
228
|
+
overlayRef={floatingRef}
|
|
229
|
+
arrowProps={arrowProps}
|
|
230
|
+
title={title}
|
|
231
|
+
content={content}
|
|
232
|
+
{...rest}
|
|
233
|
+
/>
|
|
234
|
+
)
|
|
235
|
+
}}
|
|
236
|
+
</Transition>,
|
|
237
|
+
portalContainer ?? document.body
|
|
238
|
+
)}
|
|
239
|
+
</>
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
Popover.displayName = 'Popover'
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
CSSProperties,
|
|
3
|
+
ElementType,
|
|
4
|
+
ForwardRefRenderFunction,
|
|
5
|
+
ReactElement,
|
|
6
|
+
ReactNode,
|
|
7
|
+
useId
|
|
8
|
+
} from 'react'
|
|
9
|
+
import classNames from 'classnames'
|
|
10
|
+
|
|
11
|
+
import { ContextColor } from '../../types'
|
|
12
|
+
import {
|
|
13
|
+
createPolymorphicComponent,
|
|
14
|
+
PolymorphicComponentProps,
|
|
15
|
+
PolymorphicRef
|
|
16
|
+
} from '../../utils/polymorphic'
|
|
17
|
+
import { ProgressBar } from './ProgressBar'
|
|
18
|
+
|
|
19
|
+
type ProgressStyle = CSSProperties & {
|
|
20
|
+
'--cx-height'?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type ProgressOwnProps<C extends ElementType> = {
|
|
24
|
+
/**
|
|
25
|
+
* Use to animate the stripes right to left via CSS3 animations.
|
|
26
|
+
*/
|
|
27
|
+
animated?: boolean
|
|
28
|
+
/**
|
|
29
|
+
* A string of all className you want applied to the component.
|
|
30
|
+
*/
|
|
31
|
+
className?: string
|
|
32
|
+
/**
|
|
33
|
+
* Sets the color of the component to one of Chassis context colors.
|
|
34
|
+
*/
|
|
35
|
+
color?: ContextColor
|
|
36
|
+
/**
|
|
37
|
+
* Component used for the root node. Either a string to use a HTML element or a component.
|
|
38
|
+
*/
|
|
39
|
+
component?: C
|
|
40
|
+
/**
|
|
41
|
+
* Sets the height of the component, via the `--cx-height` custom property. If you set that
|
|
42
|
+
* value the inner bar (and the striped pattern's tile size) automatically resizes accordingly.
|
|
43
|
+
*/
|
|
44
|
+
height?: number
|
|
45
|
+
/**
|
|
46
|
+
* Shows the current value as text inside the bar, independently of `showValue` — combine both
|
|
47
|
+
* to get a `label` + `showValue` caption above the bar alongside an `inlineValue` reading
|
|
48
|
+
* inside it.
|
|
49
|
+
*/
|
|
50
|
+
inlineValue?: boolean
|
|
51
|
+
/**
|
|
52
|
+
* A text label describing the progress. Rendered in a caption row above the bar. Also used as
|
|
53
|
+
* the default accessible name when no `aria-label`/`aria-labelledby` is supplied.
|
|
54
|
+
*/
|
|
55
|
+
label?: ReactNode
|
|
56
|
+
/**
|
|
57
|
+
* Shows the current value as text in the caption row above the bar, alongside `label` when
|
|
58
|
+
* both are set, or alone otherwise. Use `inlineValue` to show it inside the bar instead.
|
|
59
|
+
*/
|
|
60
|
+
showValue?: boolean
|
|
61
|
+
/**
|
|
62
|
+
* Adds a diagonal stripe pattern over the bar's background.
|
|
63
|
+
*/
|
|
64
|
+
striped?: boolean
|
|
65
|
+
/**
|
|
66
|
+
* The percent to progress the ProgressBar (out of 100).
|
|
67
|
+
*/
|
|
68
|
+
value?: number
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type ProgressProps<C extends ElementType = 'div'> = PolymorphicComponentProps<
|
|
72
|
+
C,
|
|
73
|
+
ProgressOwnProps<C>
|
|
74
|
+
>
|
|
75
|
+
|
|
76
|
+
type ProgressComponent = (<C extends ElementType = 'div'>(
|
|
77
|
+
props: ProgressProps<C> & { ref?: PolymorphicRef<C> }
|
|
78
|
+
) => ReactElement | null) & { displayName?: string }
|
|
79
|
+
|
|
80
|
+
function ProgressRender<C extends ElementType = 'div'>(
|
|
81
|
+
{
|
|
82
|
+
'aria-label': ariaLabel,
|
|
83
|
+
animated,
|
|
84
|
+
children,
|
|
85
|
+
className,
|
|
86
|
+
color,
|
|
87
|
+
component,
|
|
88
|
+
height,
|
|
89
|
+
inlineValue,
|
|
90
|
+
label,
|
|
91
|
+
showValue,
|
|
92
|
+
striped,
|
|
93
|
+
style,
|
|
94
|
+
value = 0,
|
|
95
|
+
...rest
|
|
96
|
+
}: ProgressProps<C>,
|
|
97
|
+
ref: PolymorphicRef<C>
|
|
98
|
+
) {
|
|
99
|
+
const Component = component ?? 'div'
|
|
100
|
+
const _className = classNames('progress', className)
|
|
101
|
+
const clampedValue = Math.min(100, Math.max(0, value))
|
|
102
|
+
|
|
103
|
+
const barChildren =
|
|
104
|
+
children !== undefined ? children : inlineValue ? `${clampedValue}%` : undefined
|
|
105
|
+
const hasCaption = Boolean(label || showValue)
|
|
106
|
+
|
|
107
|
+
const labelId = useId()
|
|
108
|
+
const isNodeLabel = label != null && typeof label !== 'string'
|
|
109
|
+
|
|
110
|
+
const _style: ProgressStyle = { ...style }
|
|
111
|
+
if (height !== undefined) _style['--cx-height'] = `${height}px`
|
|
112
|
+
|
|
113
|
+
const progressElement = (
|
|
114
|
+
<Component
|
|
115
|
+
{...rest}
|
|
116
|
+
className={_className}
|
|
117
|
+
role="progressbar"
|
|
118
|
+
aria-valuenow={clampedValue}
|
|
119
|
+
aria-valuemin={0}
|
|
120
|
+
aria-valuemax={100}
|
|
121
|
+
aria-label={ariaLabel ?? (typeof label === 'string' ? label : undefined)}
|
|
122
|
+
aria-labelledby={!ariaLabel && isNodeLabel ? labelId : undefined}
|
|
123
|
+
style={_style}
|
|
124
|
+
ref={ref}
|
|
125
|
+
>
|
|
126
|
+
<ProgressBar animated={animated} color={color} striped={striped} value={clampedValue}>
|
|
127
|
+
{barChildren}
|
|
128
|
+
</ProgressBar>
|
|
129
|
+
</Component>
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if (!hasCaption) return progressElement
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<div className="d-flex flex-column gap-xsmall">
|
|
136
|
+
<div
|
|
137
|
+
className={classNames('d-flex', label ? 'justify-content-between' : 'justify-content-end')}
|
|
138
|
+
>
|
|
139
|
+
{label ? (
|
|
140
|
+
<span className="font-strong" id={isNodeLabel ? labelId : undefined}>
|
|
141
|
+
{label}
|
|
142
|
+
</span>
|
|
143
|
+
) : null}
|
|
144
|
+
{showValue ? <span className="font-strong">{clampedValue}%</span> : null}
|
|
145
|
+
</div>
|
|
146
|
+
{progressElement}
|
|
147
|
+
</div>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const Progress = createPolymorphicComponent<ProgressComponent>(
|
|
152
|
+
ProgressRender as ForwardRefRenderFunction<Element, ProgressProps<ElementType>>,
|
|
153
|
+
'Progress'
|
|
154
|
+
)
|
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
|
|
11
|
+
type ProgressBarOwnProps<C extends ElementType> = {
|
|
12
|
+
/**
|
|
13
|
+
* Use to animate the stripes right to left via CSS3 animations.
|
|
14
|
+
*/
|
|
15
|
+
animated?: boolean
|
|
16
|
+
/**
|
|
17
|
+
* A string of all className you want applied to the component.
|
|
18
|
+
*/
|
|
19
|
+
className?: string
|
|
20
|
+
/**
|
|
21
|
+
* Sets the color of the component to one of Chassis context colors.
|
|
22
|
+
*/
|
|
23
|
+
color?: ContextColor
|
|
24
|
+
/**
|
|
25
|
+
* Component used for the root node. Either a string to use a HTML element or a component.
|
|
26
|
+
*/
|
|
27
|
+
component?: C
|
|
28
|
+
/**
|
|
29
|
+
* Adds a diagonal stripe pattern over the bar's background.
|
|
30
|
+
*/
|
|
31
|
+
striped?: boolean
|
|
32
|
+
/**
|
|
33
|
+
* The percent to progress the ProgressBar.
|
|
34
|
+
*/
|
|
35
|
+
value?: number
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type ProgressBarProps<C extends ElementType = 'div'> = PolymorphicComponentProps<
|
|
39
|
+
C,
|
|
40
|
+
ProgressBarOwnProps<C>
|
|
41
|
+
>
|
|
42
|
+
|
|
43
|
+
type ProgressBarComponent = (<C extends ElementType = 'div'>(
|
|
44
|
+
props: ProgressBarProps<C> & { ref?: PolymorphicRef<C> }
|
|
45
|
+
) => ReactElement | null) & { displayName?: string }
|
|
46
|
+
|
|
47
|
+
function ProgressBarRender<C extends ElementType = 'div'>(
|
|
48
|
+
{
|
|
49
|
+
children,
|
|
50
|
+
animated,
|
|
51
|
+
className,
|
|
52
|
+
color,
|
|
53
|
+
component,
|
|
54
|
+
striped,
|
|
55
|
+
style,
|
|
56
|
+
value = 0,
|
|
57
|
+
...rest
|
|
58
|
+
}: ProgressBarProps<C>,
|
|
59
|
+
ref: PolymorphicRef<C>
|
|
60
|
+
) {
|
|
61
|
+
const Component = component ?? 'div'
|
|
62
|
+
const clampedValue = Math.min(100, Math.max(0, value))
|
|
63
|
+
const _className = classNames(
|
|
64
|
+
'progress-bar',
|
|
65
|
+
color && `bg-${color} fg-contrast`,
|
|
66
|
+
{
|
|
67
|
+
striped,
|
|
68
|
+
animated
|
|
69
|
+
},
|
|
70
|
+
className
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<Component
|
|
75
|
+
{...rest}
|
|
76
|
+
className={_className}
|
|
77
|
+
style={{ width: `${clampedValue}%`, ...style }}
|
|
78
|
+
ref={ref}
|
|
79
|
+
>
|
|
80
|
+
<span className="mx-2xsmall">{children}</span>
|
|
81
|
+
</Component>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const ProgressBar = createPolymorphicComponent<ProgressBarComponent>(
|
|
86
|
+
ProgressBarRender as ForwardRefRenderFunction<Element, ProgressBarProps<ElementType>>,
|
|
87
|
+
'ProgressBar'
|
|
88
|
+
)
|