@chassis-ui/react 0.1.3 → 0.2.0
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/dist/accordion.d.ts +2 -0
- package/dist/accordion.js +1 -0
- package/dist/autocomplete.d.ts +2 -0
- package/dist/autocomplete.js +1 -0
- package/dist/avatar.d.ts +2 -0
- package/dist/avatar.js +1 -0
- package/dist/badge.d.ts +2 -0
- package/dist/badge.js +1 -0
- package/dist/breadcrumb.d.ts +2 -0
- package/dist/breadcrumb.js +1 -0
- package/dist/button-group.d.ts +2 -0
- package/dist/button-group.js +1 -0
- package/dist/button.d.ts +3 -0
- package/dist/button.js +1 -0
- package/dist/calendar.d.ts +3 -0
- package/dist/calendar.js +1 -0
- package/dist/card.d.ts +2 -0
- package/dist/card.js +1 -0
- package/dist/carousel.d.ts +2 -0
- package/dist/carousel.js +1 -0
- package/dist/checkbox.d.ts +3 -0
- package/dist/checkbox.js +1 -0
- package/dist/chip-input.d.ts +2 -0
- package/dist/chip-input.js +1 -0
- package/dist/chip.d.ts +2 -0
- package/dist/chip.js +1 -0
- package/dist/chunks/Button-Cptd4Yk7.d.ts +65 -0
- package/dist/chunks/Button-I0aUy5h-.js +1 -0
- package/dist/chunks/Chip-D0Cw0mAr.js +1 -0
- package/dist/chunks/CloseButton-DJM7FRKh.js +1 -0
- package/dist/chunks/ComboboxItem-C9TpoJOI.d.ts +54 -0
- package/dist/chunks/ComboboxListBox-BhO8uRNg.js +1 -0
- package/dist/chunks/FormHelp-Be2PQL0y.js +1 -0
- package/dist/chunks/FormLabel-CYcQiFUI.js +1 -0
- package/dist/chunks/Icon-BIwkD-kx.js +1 -0
- package/dist/chunks/Icon-CioraWLF.d.ts +36 -0
- package/dist/chunks/Link-B0Y1xgYi.d.ts +69 -0
- package/dist/chunks/Link-Cq7FNWpm.js +1 -0
- package/dist/chunks/MenuItemDef-D0VDtYIQ.d.ts +82 -0
- package/dist/chunks/RangeCalendar-COnSfurr.js +1 -0
- package/dist/chunks/accordion-okRicuG5.js +1 -0
- package/dist/chunks/autocomplete-B0xTc6kg.js +1 -0
- package/dist/chunks/avatar-Bjm0DlPO.js +1 -0
- package/dist/chunks/badge-Bj8mf8lu.js +1 -0
- package/dist/chunks/breadcrumb-DDxnx_iS.js +1 -0
- package/dist/chunks/breakpoints-SLMTNHF9.d.ts +5 -0
- package/dist/chunks/breakpoints-o_2Ev7ir.js +1 -0
- package/dist/chunks/button-group-CPZYA6aZ.js +1 -0
- package/dist/chunks/card-DfF2K_5p.js +1 -0
- package/dist/chunks/carousel-DEvwcA7H.js +1 -0
- package/dist/chunks/checkbox-BFPS0pD6.js +1 -0
- package/dist/chunks/chip-input-CB1lE6vP.js +1 -0
- package/dist/chunks/collapse-WifcChO8.js +1 -0
- package/dist/chunks/color-input-CyRaLR_G.js +1 -0
- package/dist/chunks/combobox-DWEj-Sv7.js +1 -0
- package/dist/chunks/datagrid-nbnNThsT.js +2 -0
- package/dist/chunks/dateRangePresets-R1FtA1zp.d.ts +41 -0
- package/dist/chunks/datepicker-CslLKMm1.js +1 -0
- package/dist/chunks/dialogTransition-i9vw0i2Z.js +1 -0
- package/dist/chunks/direction-CPhdeL3A.js +1 -0
- package/dist/chunks/drawer-zReFjoJG.js +1 -0
- package/dist/chunks/file-input-RRqZSzjH.js +1 -0
- package/dist/chunks/flex-BTvpgaRv.js +1 -0
- package/dist/chunks/floating-input-CIC9Lm2a.js +1 -0
- package/dist/chunks/focus-ring-SR_LwaN7.js +1 -0
- package/dist/chunks/focusRedirect-BrvK3U_o.js +1 -0
- package/dist/chunks/form-GpdAyWWi.js +1 -0
- package/dist/chunks/form-field-BjQ9SIjx.js +1 -0
- package/dist/chunks/grid-CuFw89yV.js +1 -0
- package/dist/chunks/icon-mxYpL5nL.js +1 -0
- package/dist/chunks/iconConfig-Bu6Pt9o3.d.ts +69 -0
- package/dist/chunks/iconSlot-BmWxJh27.js +1 -0
- package/dist/chunks/index--0_IpTDT.d.ts +17 -0
- package/dist/chunks/index-6uob77s9.d.ts +80 -0
- package/dist/chunks/index-8PhBSbNQ.d.ts +93 -0
- package/dist/chunks/index-B0-8n-Gs.d.ts +105 -0
- package/dist/chunks/index-B8uaSJA9.d.ts +95 -0
- package/dist/chunks/index-BA-raEMm.d.ts +28 -0
- package/dist/chunks/index-BDfh8YBc.d.ts +39 -0
- package/dist/chunks/index-BKMW0rkw.d.ts +80 -0
- package/dist/chunks/index-BO9jLOIb.d.ts +58 -0
- package/dist/chunks/index-BO_1MN66.d.ts +87 -0
- package/dist/chunks/index-BTDgfLxK.d.ts +94 -0
- package/dist/chunks/index-BTTeKpiE.d.ts +363 -0
- package/dist/chunks/index-BVGGIaPX.d.ts +110 -0
- package/dist/chunks/index-Bi8FFadV.d.ts +98 -0
- package/dist/chunks/index-Bkj17NEQ.d.ts +50 -0
- package/dist/chunks/index-BlgkO3dn.d.ts +280 -0
- package/dist/chunks/index-BxTlIaZu.d.ts +301 -0
- package/dist/chunks/index-C61tPIJ-.d.ts +50 -0
- package/dist/chunks/index-CCXJXToc.d.ts +52 -0
- package/dist/chunks/index-CJzKu7QI.d.ts +40 -0
- package/dist/chunks/index-COsIPd8L.d.ts +62 -0
- package/dist/chunks/index-COsYSDrz.d.ts +109 -0
- package/dist/chunks/index-CRfm7W6C.d.ts +230 -0
- package/dist/chunks/index-CVUMS0x7.d.ts +73 -0
- package/dist/chunks/index-CaQ1JY5D.d.ts +49 -0
- package/dist/chunks/index-Cf2n0l0U.d.ts +138 -0
- package/dist/chunks/index-CftERHnE.d.ts +24 -0
- package/dist/chunks/index-ClT3E_hv.d.ts +302 -0
- package/dist/chunks/index-CsMIgBsd.d.ts +39 -0
- package/dist/chunks/index-Ctuk1xBU.d.ts +99 -0
- package/dist/chunks/index-D2VVO8rx.d.ts +49 -0
- package/dist/chunks/index-D949AidR.d.ts +40 -0
- package/dist/chunks/index-D9PZHY_I.d.ts +135 -0
- package/dist/chunks/index-DAoPg59I.d.ts +80 -0
- package/dist/chunks/index-DB_ToPdz.d.ts +202 -0
- package/dist/chunks/index-DEb9zXlC.d.ts +210 -0
- package/dist/chunks/index-DHGgezXU.d.ts +58 -0
- package/dist/chunks/index-DJJocp_q.d.ts +132 -0
- package/dist/chunks/index-DKma0jQw.d.ts +104 -0
- package/dist/chunks/index-DPV62IdF.d.ts +118 -0
- package/dist/chunks/index-DR8sYtvd.d.ts +296 -0
- package/dist/chunks/index-Dcc_W6Kv.d.ts +146 -0
- package/dist/chunks/index-Dfg4Ftb8.d.ts +68 -0
- package/dist/chunks/index-Dfy1YW5j.d.ts +73 -0
- package/dist/chunks/index-Dh5CaHTP.d.ts +44 -0
- package/dist/chunks/index-DtbUXxjz.d.ts +53 -0
- package/dist/chunks/index-Dum6Y9XR.d.ts +187 -0
- package/dist/chunks/index-HrKN6s8z.d.ts +66 -0
- package/dist/chunks/index-JllMXhVx.d.ts +176 -0
- package/dist/chunks/index-XjLvaH4G.d.ts +67 -0
- package/dist/chunks/index-dh3TKanY.d.ts +235 -0
- package/dist/chunks/index-nklPDKlC.d.ts +137 -0
- package/dist/chunks/index-qgZ5VGmc.d.ts +177 -0
- package/dist/chunks/index-tOodmT36.d.ts +129 -0
- package/dist/chunks/index-t_x6jj7l.d.ts +96 -0
- package/dist/chunks/input-adorn-C_EHML68.js +1 -0
- package/dist/chunks/input-group-iI7azfvE.js +1 -0
- package/dist/chunks/list-CMr-wB9d.js +1 -0
- package/dist/chunks/menu-CXUc-Gva.js +1 -0
- package/dist/chunks/modal-ByIj71Mq.js +1 -0
- package/dist/chunks/nav-BldxFo_B.js +1 -0
- package/dist/chunks/navbar-BwcsQuBk.js +1 -0
- package/dist/chunks/notification-DdqigVB1.js +1 -0
- package/dist/chunks/otp-input-CXe_wkII.js +1 -0
- package/dist/chunks/overlayPlacement-B7843a-Q.d.ts +6 -0
- package/dist/chunks/overlayPlacement-BtS90IVf.js +1 -0
- package/dist/chunks/pagination-BOpEAYW2.js +1 -0
- package/dist/chunks/password-strength-BfLiYiGj.js +1 -0
- package/dist/chunks/placeholder-pg95KELl.js +1 -0
- package/dist/chunks/polymorphic-CACPOv1_.js +1 -0
- package/dist/chunks/polymorphic-CVHbTHSN.d.ts +40 -0
- package/dist/chunks/popover-BpSfnZzk.js +1 -0
- package/dist/chunks/progress-DlrZ71aV.js +1 -0
- package/dist/chunks/radio-BV7Sq6D1.js +1 -0
- package/dist/chunks/range-input-BN3430j-.js +1 -0
- package/dist/chunks/renderFormCheck-BXBs6_ov.js +1 -0
- package/dist/chunks/renderFormCheck-CYWG-Rbt.d.ts +23 -0
- package/dist/chunks/renderFormField-fBEny6JV.d.ts +14 -0
- package/dist/chunks/renderFormField-wiaxvvLG.js +1 -0
- package/dist/chunks/renderMenuItemContent-CmqT3YUQ.js +1 -0
- package/dist/chunks/select-CclIIux8.js +1 -0
- package/dist/chunks/skeleton-CKq76sE_.js +1 -0
- package/dist/chunks/spacingClassName-DVhk8m3P.js +1 -0
- package/dist/chunks/spinner-BEq4PAlu.js +1 -0
- package/dist/chunks/stack-D3retZ-W.js +1 -0
- package/dist/chunks/static-table-D0M4oc_t.js +1 -0
- package/dist/chunks/stepper-CTa6e8uv.js +1 -0
- package/dist/chunks/switch-BmHpq4tL.js +1 -0
- package/dist/chunks/table-Fme9GbAR.js +1 -0
- package/dist/chunks/tableClassName-CZA2Bse8.js +1 -0
- package/dist/chunks/tabs-BnbMJM_b.js +1 -0
- package/dist/chunks/text-input-DD3d08Yf.js +1 -0
- package/dist/chunks/textarea-C0qQdCcu.js +1 -0
- package/dist/chunks/toast-BQhZ2pv5.js +1 -0
- package/dist/chunks/tooltip-DHtVAYUo.js +1 -0
- package/dist/chunks/triggerElement-BIqA4KqZ.js +1 -0
- package/dist/chunks/types-CS_40Txv.d.ts +34 -0
- package/dist/chunks/useButtonSemantics-CILM_lhi.js +1 -0
- package/dist/chunks/useDialogElement-BRIU60cT.js +1 -0
- package/dist/chunks/useDisabledAnchorGuard-Cz58UCXy.js +1 -0
- package/dist/chunks/useFloatingOverlay-CcmDZKMK.js +1 -0
- package/dist/chunks/useForkedRef-DVEfT-KI.js +1 -0
- package/dist/chunks/useFormField-BOqmBTC4.js +1 -0
- package/dist/chunks/useIsomorphicLayoutEffect-BbqRwuqn.js +1 -0
- package/dist/chunks/useToastRegionQueue-BH6bWZ73.js +1 -0
- package/dist/chunks/validationClassName-gRGs4KBm.js +1 -0
- package/dist/close-button.d.ts +2 -0
- package/dist/close-button.js +1 -0
- package/dist/collapse.d.ts +2 -0
- package/dist/collapse.js +1 -0
- package/dist/color-input.d.ts +2 -0
- package/dist/color-input.js +1 -0
- package/dist/combobox.d.ts +3 -0
- package/dist/combobox.js +1 -0
- package/dist/datagrid.d.ts +2 -0
- package/dist/datagrid.js +1 -0
- package/dist/datepicker.d.ts +2 -0
- package/dist/datepicker.js +1 -0
- package/dist/drawer.d.ts +2 -0
- package/dist/drawer.js +1 -0
- package/dist/file-input.d.ts +2 -0
- package/dist/file-input.js +1 -0
- package/dist/flex.d.ts +2 -0
- package/dist/flex.js +1 -0
- package/dist/floating-input.d.ts +2 -0
- package/dist/floating-input.js +1 -0
- package/dist/form-field.d.ts +2 -0
- package/dist/form-field.js +1 -0
- package/dist/form.d.ts +2 -0
- package/dist/form.js +1 -0
- package/dist/grid.d.ts +2 -0
- package/dist/grid.js +1 -0
- package/dist/icon.d.ts +4 -0
- package/dist/icon.js +1 -0
- package/dist/index.d.ts +72 -6369
- package/dist/index.js +1 -2
- package/dist/input-adorn.d.ts +2 -0
- package/dist/input-adorn.js +1 -0
- package/dist/input-group.d.ts +2 -0
- package/dist/input-group.js +1 -0
- package/dist/link.d.ts +3 -0
- package/dist/link.js +1 -0
- package/dist/list.d.ts +2 -0
- package/dist/list.js +1 -0
- package/dist/menu.d.ts +3 -0
- package/dist/menu.js +1 -0
- package/dist/modal.d.ts +2 -0
- package/dist/modal.js +1 -0
- package/dist/nav.d.ts +2 -0
- package/dist/nav.js +1 -0
- package/dist/navbar.d.ts +2 -0
- package/dist/navbar.js +1 -0
- package/dist/notification.d.ts +2 -0
- package/dist/notification.js +1 -0
- package/dist/otp-input.d.ts +2 -0
- package/dist/otp-input.js +1 -0
- package/dist/pagination.d.ts +2 -0
- package/dist/pagination.js +1 -0
- package/dist/password-strength.d.ts +2 -0
- package/dist/password-strength.js +1 -0
- package/dist/placeholder.d.ts +2 -0
- package/dist/placeholder.js +1 -0
- package/dist/popover.d.ts +2 -0
- package/dist/popover.js +1 -0
- package/dist/progress.d.ts +2 -0
- package/dist/progress.js +1 -0
- package/dist/radio.d.ts +2 -0
- package/dist/radio.js +1 -0
- package/dist/range-input.d.ts +2 -0
- package/dist/range-input.js +1 -0
- package/dist/select.d.ts +2 -0
- package/dist/select.js +1 -0
- package/dist/skeleton.d.ts +2 -0
- package/dist/skeleton.js +1 -0
- package/dist/spinner.d.ts +2 -0
- package/dist/spinner.js +1 -0
- package/dist/stack.d.ts +2 -0
- package/dist/stack.js +1 -0
- package/dist/static-table.d.ts +2 -0
- package/dist/static-table.js +1 -0
- package/dist/stepper.d.ts +2 -0
- package/dist/stepper.js +1 -0
- package/dist/style.css +1 -1
- package/dist/switch.d.ts +2 -0
- package/dist/switch.js +1 -0
- package/dist/table.d.ts +2 -0
- package/dist/table.js +1 -0
- package/dist/tabs.d.ts +2 -0
- package/dist/tabs.js +1 -0
- package/dist/text-input.d.ts +2 -0
- package/dist/text-input.js +1 -0
- package/dist/textarea.d.ts +2 -0
- package/dist/textarea.js +1 -0
- package/dist/toast.d.ts +2 -0
- package/dist/toast.js +1 -0
- package/dist/tooltip.d.ts +2 -0
- package/dist/tooltip.js +1 -0
- package/package.json +62 -4
- package/src/components/accordion/index.ts +4 -0
- package/src/components/autocomplete/index.ts +4 -0
- package/src/components/avatar/index.ts +4 -0
- package/src/components/badge/index.ts +4 -0
- package/src/components/breadcrumb/index.ts +4 -0
- package/src/components/button/index.ts +4 -0
- package/src/components/button-group/index.ts +4 -0
- package/src/components/calendar/index.ts +4 -0
- package/src/components/card/index.ts +4 -0
- package/src/components/carousel/CarouselControlButton.tsx +11 -4
- package/src/components/carousel/CarouselControlNext.tsx +6 -0
- package/src/components/carousel/CarouselControlPrev.tsx +6 -0
- package/src/components/carousel/CarouselPlayPause.tsx +27 -4
- package/src/components/carousel/index.ts +4 -0
- package/src/components/checkbox/index.ts +4 -0
- package/src/components/chip/index.ts +4 -0
- package/src/components/chip-input/index.ts +4 -0
- package/src/components/close-button/index.ts +4 -0
- package/src/components/collapse/index.ts +4 -0
- package/src/components/color-input/index.ts +4 -0
- package/src/components/combobox/ComboboxListBox.tsx +2 -2
- package/src/components/combobox/index.ts +4 -0
- package/src/components/datagrid/index.ts +4 -0
- package/src/components/datepicker/index.ts +4 -0
- package/src/components/drawer/index.ts +8 -0
- package/src/components/file-input/index.ts +4 -0
- package/src/components/flex/index.ts +4 -0
- package/src/components/floating-input/index.ts +4 -0
- package/src/components/form/index.ts +4 -0
- package/src/components/form-field/index.ts +4 -0
- package/src/components/grid/index.ts +4 -0
- package/src/components/icon/Icon.tsx +69 -38
- package/src/components/icon/IconProvider.tsx +40 -0
- package/src/components/icon/index.ts +7 -0
- package/src/components/input-adorn/index.ts +4 -0
- package/src/components/input-group/index.ts +4 -0
- package/src/components/link/index.ts +4 -0
- package/src/components/list/index.ts +4 -0
- package/src/components/menu/index.ts +4 -0
- package/src/components/modal/index.ts +8 -0
- package/src/components/nav/index.ts +4 -0
- package/src/components/navbar/NavbarToggler.tsx +9 -3
- package/src/components/navbar/index.ts +4 -0
- package/src/components/notification/Notification.tsx +6 -5
- package/src/components/notification/index.ts +8 -0
- package/src/components/otp-input/index.ts +4 -0
- package/src/components/pagination/Pagination.tsx +16 -3
- package/src/components/pagination/index.ts +8 -0
- package/src/components/password-strength/index.ts +4 -0
- package/src/components/placeholder/index.ts +4 -0
- package/src/components/popover/index.ts +4 -0
- package/src/components/progress/index.ts +4 -0
- package/src/components/radio/index.ts +4 -0
- package/src/components/range-input/index.ts +4 -0
- package/src/components/select/index.ts +4 -0
- package/src/components/skeleton/SkeletonLoader.tsx +13 -6
- package/src/components/skeleton/index.ts +4 -0
- package/src/components/spinner/index.ts +4 -0
- package/src/components/stack/index.ts +4 -0
- package/src/components/static-table/StaticTable.tsx +78 -0
- package/src/components/static-table/index.ts +8 -0
- package/src/components/stepper/index.ts +4 -0
- package/src/components/switch/index.ts +4 -0
- package/src/components/table/Table.tsx +14 -22
- package/src/components/table/index.ts +4 -0
- package/src/components/tabs/index.ts +4 -0
- package/src/components/text-input/index.ts +4 -0
- package/src/components/textarea/index.ts +4 -0
- package/src/components/toast/Toast.tsx +2 -1
- package/src/components/toast/ToastHeader.tsx +4 -3
- package/src/components/toast/index.ts +8 -0
- package/src/components/tooltip/index.ts +4 -0
- package/src/hooks/useIconConfig.ts +6 -0
- package/src/index.ts +24 -4
- package/src/utils/iconConfig.ts +88 -0
- package/src/utils/iconSlot.tsx +41 -0
- package/src/utils/polymorphic.ts +49 -2
- package/src/utils/renderMenuItemContent.tsx +4 -9
- package/src/utils/slot.tsx +78 -0
- package/src/utils/tableClassName.ts +93 -0
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
|
|
26
26
|
import { useForkedRef } from '../../hooks/useForkedRef'
|
|
27
27
|
import { ContextColor } from '../../types'
|
|
28
|
+
import { tableClassName, tableWrapperClassName } from '../../utils/tableClassName'
|
|
28
29
|
import './Table.css'
|
|
29
30
|
|
|
30
31
|
// `GridNode<T>` (from `@react-types/shared`) isn't re-exported by either package's public
|
|
@@ -179,24 +180,20 @@ const TableInner = <T extends object>(
|
|
|
179
180
|
internalRef
|
|
180
181
|
)
|
|
181
182
|
|
|
182
|
-
const _className =
|
|
183
|
-
|
|
183
|
+
const _className = tableClassName({
|
|
184
|
+
align,
|
|
185
|
+
bordered,
|
|
186
|
+
borderless,
|
|
187
|
+
className,
|
|
184
188
|
color,
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
sm,
|
|
191
|
-
stacked: stacked === true,
|
|
192
|
-
striped
|
|
193
|
-
},
|
|
194
|
-
typeof stacked === 'string' ? `max-${stacked}:stacked` : undefined,
|
|
195
|
-
className
|
|
196
|
-
)
|
|
189
|
+
hover,
|
|
190
|
+
sm,
|
|
191
|
+
stacked,
|
|
192
|
+
striped
|
|
193
|
+
})
|
|
197
194
|
|
|
198
195
|
const tableEl = (
|
|
199
|
-
<table {...gridProps} className={_className
|
|
196
|
+
<table {...gridProps} className={_className} ref={ref}>
|
|
200
197
|
{caption && <caption>{caption}</caption>}
|
|
201
198
|
<TableRowGroup className={children[0].props.className} type="thead">
|
|
202
199
|
{state.collection.headerRows.map((headerRow) => (
|
|
@@ -228,13 +225,8 @@ const TableInner = <T extends object>(
|
|
|
228
225
|
</table>
|
|
229
226
|
)
|
|
230
227
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const wrapperClassName = responsive
|
|
234
|
-
? typeof responsive === 'boolean'
|
|
235
|
-
? 'table-responsive'
|
|
236
|
-
: `max-${responsive}:table-responsive`
|
|
237
|
-
: 'table-responsive'
|
|
228
|
+
const wrapperClassName = tableWrapperClassName({ responsive, stacked })
|
|
229
|
+
if (!wrapperClassName) return tableEl
|
|
238
230
|
|
|
239
231
|
return <div className={wrapperClassName}>{tableEl}</div>
|
|
240
232
|
}
|
|
@@ -51,7 +51,8 @@ export interface ToastProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'
|
|
|
51
51
|
*/
|
|
52
52
|
footer?: ReactNode | ((close: () => void) => ReactNode)
|
|
53
53
|
/**
|
|
54
|
-
* Leading icon for the header. A string is
|
|
54
|
+
* Leading icon for the header. A string is an icon name, rendered like
|
|
55
|
+
* `<ToastIcon name={icon} />` (or by `IconProvider`'s `component`); pass
|
|
55
56
|
* any other node for a fully custom icon (typically a logo or avatar). Shorthand for
|
|
56
57
|
* `ToastHeader`'s `icon` prop; hidden from assistive technology by default, since it
|
|
57
58
|
* duplicates `title` visually.
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
PolymorphicComponentProps,
|
|
9
9
|
PolymorphicRef
|
|
10
10
|
} from '../../utils/polymorphic'
|
|
11
|
-
import {
|
|
11
|
+
import { ResolvedIcon } from '../../utils/iconSlot'
|
|
12
12
|
|
|
13
13
|
type ToastHeaderOwnProps<C extends ElementType> = {
|
|
14
14
|
/**
|
|
@@ -33,7 +33,8 @@ type ToastHeaderOwnProps<C extends ElementType> = {
|
|
|
33
33
|
*/
|
|
34
34
|
component?: C
|
|
35
35
|
/**
|
|
36
|
-
* Leading icon. A string is rendered
|
|
36
|
+
* Leading icon. A string is an icon name, rendered like `<ToastIcon name={icon} />` (or by
|
|
37
|
+
* `IconProvider`'s `component`) and hidden from
|
|
37
38
|
* assistive technology by default, since it duplicates the heading visually. Pass any other
|
|
38
39
|
* node for a fully custom icon (typically a logo or avatar) — a custom node is left as-is,
|
|
39
40
|
* since it may carry its own meaningful accessible name (e.g. an avatar's `alt` text).
|
|
@@ -82,7 +83,7 @@ function ToastHeaderRender<C extends ElementType = 'div'>(
|
|
|
82
83
|
{icon != null &&
|
|
83
84
|
(typeof icon === 'string' ? (
|
|
84
85
|
<span aria-hidden="true" className="me-sm">
|
|
85
|
-
<
|
|
86
|
+
<ResolvedIcon value={icon} className="toast-icon" />
|
|
86
87
|
</span>
|
|
87
88
|
) : (
|
|
88
89
|
<span className="me-sm">{icon}</span>
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import '../../utils/suppressFocusRingGlobally'
|
|
4
|
+
|
|
1
5
|
export { Toast } from './Toast'
|
|
2
6
|
export type { ToastProps } from './Toast'
|
|
3
7
|
export { ToastBody } from './ToastBody'
|
|
@@ -16,3 +20,7 @@ export { Toaster } from './Toaster'
|
|
|
16
20
|
export type { ToasterProps } from './Toaster'
|
|
17
21
|
export { addToast, closeToast, toastQueue } from './toastQueue'
|
|
18
22
|
export type { ToastContent } from './toastQueue'
|
|
23
|
+
// Also exported from the package root; repeated here so this folder's subpath entry
|
|
24
|
+
// (`@chassis-ui/react/<folder>`) covers the whole family without reaching back to the root.
|
|
25
|
+
export { useToast } from '../../hooks/useToast'
|
|
26
|
+
export type { UseToastResult } from '../../hooks/useToast'
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { useContext } from 'react'
|
|
2
|
+
|
|
3
|
+
import { IconConfig, IconContext } from '../utils/iconConfig'
|
|
4
|
+
|
|
5
|
+
// The nearest `IconProvider`'s settings, merged with every provider above it; empty outside one.
|
|
6
|
+
export const useIconConfig = (): IconConfig => useContext(IconContext)
|
package/src/index.ts
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
// Side-effect only: installs the single document-level pointerdown listener that works around the
|
|
4
4
|
// browser's `:focus-visible` first-click heuristic misfire — see the module's own comment. Needs to
|
|
5
|
-
// run once wherever this package is used, not per-component, so it's imported
|
|
6
|
-
//
|
|
5
|
+
// run once wherever this package is used, not per-component, so it's imported by entry points
|
|
6
|
+
// (here, and every component folder's `index.ts` barrel, each of which is also a subpath entry)
|
|
7
|
+
// rather than from any individual component.
|
|
7
8
|
import './utils/suppressFocusRingGlobally'
|
|
8
9
|
|
|
9
10
|
import { Accordion, AccordionBody, AccordionHeader, AccordionItem } from './components/accordion'
|
|
@@ -91,7 +92,7 @@ import { Select } from './components/select'
|
|
|
91
92
|
import { Switch } from './components/switch'
|
|
92
93
|
import { TextInput } from './components/text-input'
|
|
93
94
|
import { Textarea } from './components/textarea'
|
|
94
|
-
import { Icon } from './components/icon'
|
|
95
|
+
import { Icon, IconProvider } from './components/icon'
|
|
95
96
|
import { Placeholder } from './components/placeholder'
|
|
96
97
|
import { Link } from './components/link'
|
|
97
98
|
import { List, ListItem } from './components/list'
|
|
@@ -104,6 +105,7 @@ import { Progress, ProgressBar } from './components/progress'
|
|
|
104
105
|
import { Drawer, DrawerBody, DrawerFooter, DrawerHeader, DrawerTitle } from './components/drawer'
|
|
105
106
|
import { Skeleton, SkeletonLoader } from './components/skeleton'
|
|
106
107
|
import { Spinner } from './components/spinner'
|
|
108
|
+
import { StaticTable } from './components/static-table'
|
|
107
109
|
import { Stepper, StepperItem } from './components/stepper'
|
|
108
110
|
import { Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from './components/table'
|
|
109
111
|
import { Tabs, Tab, TabList, TabPanel } from './components/tabs'
|
|
@@ -224,6 +226,7 @@ export {
|
|
|
224
226
|
FormHelp,
|
|
225
227
|
FormLabel,
|
|
226
228
|
Icon,
|
|
229
|
+
IconProvider,
|
|
227
230
|
Placeholder,
|
|
228
231
|
InputGroup,
|
|
229
232
|
InputGroupAddon,
|
|
@@ -265,6 +268,7 @@ export {
|
|
|
265
268
|
Skeleton,
|
|
266
269
|
SkeletonLoader,
|
|
267
270
|
Spinner,
|
|
271
|
+
StaticTable,
|
|
268
272
|
Stepper,
|
|
269
273
|
StepperItem,
|
|
270
274
|
Switch,
|
|
@@ -367,6 +371,15 @@ export type { CloseButtonProps } from './components/close-button'
|
|
|
367
371
|
export type { CollapseProps } from './components/collapse'
|
|
368
372
|
export type { ColorInputProps } from './components/color-input'
|
|
369
373
|
export type { ComboboxGroupProps, ComboboxItemProps, ComboboxProps } from './components/combobox'
|
|
374
|
+
export type {
|
|
375
|
+
DataGridBodyProps,
|
|
376
|
+
DataGridCellProps,
|
|
377
|
+
DataGridColumnProps,
|
|
378
|
+
DataGridHeaderProps,
|
|
379
|
+
DataGridProps,
|
|
380
|
+
DataGridRowProps,
|
|
381
|
+
DataGridSelectionCellProps
|
|
382
|
+
} from './components/datagrid'
|
|
370
383
|
export type {
|
|
371
384
|
DatePickerMultipleProps,
|
|
372
385
|
DatePickerProps,
|
|
@@ -393,7 +406,13 @@ export type {
|
|
|
393
406
|
GridProps,
|
|
394
407
|
RowProps
|
|
395
408
|
} from './components/grid'
|
|
396
|
-
export type {
|
|
409
|
+
export type {
|
|
410
|
+
IconComponentProps,
|
|
411
|
+
IconKey,
|
|
412
|
+
IconProps,
|
|
413
|
+
IconProviderProps,
|
|
414
|
+
IconValue
|
|
415
|
+
} from './components/icon'
|
|
397
416
|
export type { InputAdornProps } from './components/input-adorn'
|
|
398
417
|
export type { InputGroupAddonProps, InputGroupProps } from './components/input-group'
|
|
399
418
|
export type { LinkProps } from './components/link'
|
|
@@ -449,6 +468,7 @@ export type { RangeInputProps } from './components/range-input'
|
|
|
449
468
|
export type { SelectOptionDef, SelectProps } from './components/select'
|
|
450
469
|
export type { SkeletonLoaderProps, SkeletonProps } from './components/skeleton'
|
|
451
470
|
export type { SpinnerProps } from './components/spinner'
|
|
471
|
+
export type { StaticTableProps } from './components/static-table'
|
|
452
472
|
export type { StackProps } from './components/stack'
|
|
453
473
|
export type { StepperItemDef, StepperItemProps, StepperProps } from './components/stepper'
|
|
454
474
|
export type { SwitchProps } from './components/switch'
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { ComponentType, createContext, ReactElement } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The icons this library's own components draw, by purpose rather than by icon name — so a
|
|
5
|
+
* consumer can map each one onto any icon set (see `IconProvider`'s `icons`).
|
|
6
|
+
*/
|
|
7
|
+
export type IconKey = 'check' | 'previous' | 'next' | 'menu' | 'play' | 'pause'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* An icon given to `IconProvider`'s `icons` or to a component's icon prop: a string is an icon
|
|
11
|
+
* name, rendered by `IconProvider`'s `component` (or the built-in `Icon`); an element is rendered
|
|
12
|
+
* as-is, with the component's own class names merged onto it.
|
|
13
|
+
*/
|
|
14
|
+
export type IconValue = string | ReactElement
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Props `IconProvider`'s `component` receives for every icon name it's asked to render.
|
|
18
|
+
*/
|
|
19
|
+
export interface IconComponentProps {
|
|
20
|
+
/**
|
|
21
|
+
* The icon name — an `IconValue` string, or `IconProvider`'s default for that icon.
|
|
22
|
+
*/
|
|
23
|
+
name: string
|
|
24
|
+
/**
|
|
25
|
+
* The class names the rendering component needs on the icon (e.g. `menu-item-check`,
|
|
26
|
+
* `directional-icon`) for chassis-css to position and flip it.
|
|
27
|
+
*/
|
|
28
|
+
className?: string
|
|
29
|
+
[prop: string]: unknown
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IconConfig {
|
|
33
|
+
/**
|
|
34
|
+
* Class names added to every `Icon`, alongside its own `icon` class.
|
|
35
|
+
*/
|
|
36
|
+
className?: string
|
|
37
|
+
/**
|
|
38
|
+
* Replaces the built-in `Icon` for every icon this library's components render by name — the
|
|
39
|
+
* strings in `icons` and their defaults, and a string `icon` on `Toast`/`Notification`. A
|
|
40
|
+
* component reference can't be passed from a React Server Component, so set this in a
|
|
41
|
+
* `'use client'` module.
|
|
42
|
+
*/
|
|
43
|
+
component?: ComponentType<IconComponentProps>
|
|
44
|
+
/**
|
|
45
|
+
* Render `Icon` as an icon-font glyph instead of an SVG sprite reference, unless an `Icon` sets
|
|
46
|
+
* `font` itself. Needs the icon font's stylesheet loaded.
|
|
47
|
+
*/
|
|
48
|
+
font?: boolean
|
|
49
|
+
/**
|
|
50
|
+
* Class-name prefix of the icon font's glyphs: an icon named `check-solid` renders as
|
|
51
|
+
* `{fontPrefix}check-solid`.
|
|
52
|
+
*
|
|
53
|
+
* @default 'cx-'
|
|
54
|
+
*/
|
|
55
|
+
fontPrefix?: string
|
|
56
|
+
/**
|
|
57
|
+
* The icon to render for each of the library's own icons, by purpose. Unset ones keep their
|
|
58
|
+
* defaults (`check-solid`, `chevron-left-outline`, `chevron-right-outline`, `bars-outline`,
|
|
59
|
+
* `play-solid`, `pause-solid`).
|
|
60
|
+
*/
|
|
61
|
+
icons?: Partial<Record<IconKey, IconValue>>
|
|
62
|
+
/**
|
|
63
|
+
* URL of the SVG sprite `Icon` references, e.g. `/static/icons/chassis-icons.svg`. Leave empty
|
|
64
|
+
* (the default) when the sprite is embedded in the page, so icons render as `href="#name"`.
|
|
65
|
+
* Browsers don't load an external sprite from another origin.
|
|
66
|
+
*
|
|
67
|
+
* @default ''
|
|
68
|
+
*/
|
|
69
|
+
sprite?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// The chassis-icons names this library used before its icons became configurable. Both
|
|
73
|
+
// previous/next consumers (Pagination, Carousel) now share the outline chevrons — Pagination used
|
|
74
|
+
// the solid ones.
|
|
75
|
+
export const DEFAULT_ICONS: Record<IconKey, string> = {
|
|
76
|
+
check: 'check-solid',
|
|
77
|
+
previous: 'chevron-left-outline',
|
|
78
|
+
next: 'chevron-right-outline',
|
|
79
|
+
menu: 'bars-outline',
|
|
80
|
+
play: 'play-solid',
|
|
81
|
+
pause: 'pause-solid'
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const DEFAULT_FONT_PREFIX = 'cx-'
|
|
85
|
+
|
|
86
|
+
// Lives in `utils/` rather than `components/icon/` because it's read outside that folder too —
|
|
87
|
+
// by `IconSlot` (`./iconSlot.tsx`), which every icon-drawing component renders through.
|
|
88
|
+
export const IconContext = createContext<IconConfig>({})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React, { cloneElement, ReactElement } from 'react'
|
|
2
|
+
import { mergeProps } from 'react-aria'
|
|
3
|
+
|
|
4
|
+
import { Icon } from '../components/icon/Icon'
|
|
5
|
+
import { useIconConfig } from '../hooks/useIconConfig'
|
|
6
|
+
import { DEFAULT_ICONS, IconKey, IconValue } from './iconConfig'
|
|
7
|
+
|
|
8
|
+
// Every icon this library's own components draw goes through here, never through `<Icon>`
|
|
9
|
+
// directly, so a consumer can swap in their own icons (`IconProvider`'s `icons`/`component`, or a
|
|
10
|
+
// component's own icon prop). The rendering component passes the class names chassis-css hooks
|
|
11
|
+
// onto (`menu-item-check` positions the check, `navbar-toggler-icon` sizes and colors it,
|
|
12
|
+
// `carousel-icon-play`/`-pause` are toggled by state, `directional-icon` flips in RTL); those are
|
|
13
|
+
// merged onto whatever icon ends up rendered, so a custom icon lands in the same place a built-in
|
|
14
|
+
// one would. `aria-hidden` defaults on: each of these icons sits beside its own visible or
|
|
15
|
+
// visually-hidden label.
|
|
16
|
+
//
|
|
17
|
+
// A custom *element* deliberately doesn't get the `icon` class itself: chassis-css's `.icon` sets
|
|
18
|
+
// `fill`, which would paint over an outline icon set's `fill="none"` (Lucide, Heroicons outline,
|
|
19
|
+
// ...). It keeps its own size and color — `currentColor` icons still follow the control's text.
|
|
20
|
+
type IconRenderProps = { className?: string } & Record<string, unknown>
|
|
21
|
+
|
|
22
|
+
export function ResolvedIcon({ value, ...props }: IconRenderProps & { value: IconValue }) {
|
|
23
|
+
const { component: Component } = useIconConfig()
|
|
24
|
+
const iconProps = { 'aria-hidden': true, ...props }
|
|
25
|
+
|
|
26
|
+
if (typeof value !== 'string') {
|
|
27
|
+
const element = value as ReactElement<Record<string, unknown>>
|
|
28
|
+
return cloneElement(element, mergeProps(iconProps, element.props))
|
|
29
|
+
}
|
|
30
|
+
if (Component) return <Component name={value} {...iconProps} />
|
|
31
|
+
return <Icon name={value} {...iconProps} />
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function IconSlot({
|
|
35
|
+
icon,
|
|
36
|
+
override,
|
|
37
|
+
...props
|
|
38
|
+
}: IconRenderProps & { icon: IconKey; override?: IconValue }) {
|
|
39
|
+
const { icons } = useIconConfig()
|
|
40
|
+
return <ResolvedIcon value={override ?? icons?.[icon] ?? DEFAULT_ICONS[icon]} {...props} />
|
|
41
|
+
}
|
package/src/utils/polymorphic.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ComponentPropsWithoutRef,
|
|
3
3
|
ComponentPropsWithRef,
|
|
4
|
+
createElement,
|
|
4
5
|
ElementType,
|
|
5
6
|
ForwardRefRenderFunction,
|
|
6
7
|
forwardRef,
|
|
7
8
|
Ref
|
|
8
9
|
} from 'react'
|
|
9
10
|
|
|
11
|
+
import { devWarning } from './devWarning'
|
|
12
|
+
import { getSlotChild, Slot, SlotProvider } from './slot'
|
|
13
|
+
|
|
10
14
|
// Ref type inferred from a polymorphic component's currently-selected `component` element type.
|
|
11
15
|
export type PolymorphicRef<C extends ElementType> = ComponentPropsWithRef<C>['ref']
|
|
12
16
|
|
|
@@ -26,6 +30,21 @@ export type PolymorphicRef<C extends ElementType> = ComponentPropsWithRef<C>['re
|
|
|
26
30
|
export type PolymorphicRefWithFallback<C extends ElementType, Fallback extends HTMLElement> =
|
|
27
31
|
PolymorphicRef<C> | Ref<Fallback>
|
|
28
32
|
|
|
33
|
+
/**
|
|
34
|
+
* The `asChild` prop every polymorphic component accepts alongside `component` — see
|
|
35
|
+
* `createPolymorphicComponent` below for how it's implemented once for all of them.
|
|
36
|
+
*/
|
|
37
|
+
export interface AsChildProps {
|
|
38
|
+
/**
|
|
39
|
+
* Render the single child element in place of this component's own element, with this
|
|
40
|
+
* component's classes, props and ref merged onto it: `<Button asChild><Link href="/login">Log
|
|
41
|
+
* in</Link></Button>`. The alternative to `component` for React Server Components — an element
|
|
42
|
+
* can be passed from the server to this package's client components, a component reference
|
|
43
|
+
* (`component={Link}`) can't.
|
|
44
|
+
*/
|
|
45
|
+
asChild?: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
29
48
|
/**
|
|
30
49
|
* Props for a polymorphic component: `OwnProps` (which must declare `component?: C`) plus
|
|
31
50
|
* whatever props `C` itself accepts, minus any name already claimed by `OwnProps` so the two
|
|
@@ -33,7 +52,8 @@ export type PolymorphicRefWithFallback<C extends ElementType, Fallback extends H
|
|
|
33
52
|
* type-checking/autocomplete for that component's own props at the call site.
|
|
34
53
|
*/
|
|
35
54
|
export type PolymorphicComponentProps<C extends ElementType, OwnProps extends object> = OwnProps &
|
|
36
|
-
|
|
55
|
+
AsChildProps &
|
|
56
|
+
Omit<ComponentPropsWithoutRef<C>, keyof OwnProps | keyof AsChildProps>
|
|
37
57
|
|
|
38
58
|
/**
|
|
39
59
|
* Wraps a polymorphic component's render function in `forwardRef` and stamps `displayName` — the
|
|
@@ -41,13 +61,40 @@ export type PolymorphicComponentProps<C extends ElementType, OwnProps extends ob
|
|
|
41
61
|
* fully generic component type (e.g. `ButtonComponent`) that `forwardRef`'s own return type can't
|
|
42
62
|
* express, since `forwardRef` only accepts a non-generic render function — the caller still casts
|
|
43
63
|
* the render function to a concrete `Element`-typed instance before passing it in here.
|
|
64
|
+
*
|
|
65
|
+
* Also implements `asChild` for every polymorphic component at once (#23): the render function
|
|
66
|
+
* gets `component={Slot}` and the child element's own children, so it computes its classes,
|
|
67
|
+
* props and ref exactly as it would for any component reference (`component={Link}`) — `Slot`
|
|
68
|
+
* then clones the caller's child element with all of that merged on (see `./slot`). No render
|
|
69
|
+
* function needs to know `asChild` exists.
|
|
44
70
|
*/
|
|
45
71
|
export function createPolymorphicComponent<T>(
|
|
46
72
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
73
|
render: ForwardRefRenderFunction<Element, any>,
|
|
48
74
|
displayName: string
|
|
49
75
|
): T {
|
|
50
|
-
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
77
|
+
const Component = forwardRef<Element, any>(({ asChild, ...props }, ref) => {
|
|
78
|
+
const child = asChild ? getSlotChild(props.children, displayName) : null
|
|
79
|
+
if (!child) return render(props, ref)
|
|
80
|
+
|
|
81
|
+
devWarning(
|
|
82
|
+
props.component !== undefined,
|
|
83
|
+
`${displayName}: both \`asChild\` and \`component\` were passed; \`asChild\` wins and ` +
|
|
84
|
+
`\`component\` is ignored.`
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return createElement(
|
|
88
|
+
SlotProvider,
|
|
89
|
+
{ value: child },
|
|
90
|
+
render(
|
|
91
|
+
{ ...props, component: Slot, children: (child.props as PropsWithChildren).children },
|
|
92
|
+
ref
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
}) as unknown as T & { displayName?: string }
|
|
51
96
|
Component.displayName = displayName
|
|
52
97
|
return Component
|
|
53
98
|
}
|
|
99
|
+
|
|
100
|
+
type PropsWithChildren = { children?: unknown }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { IconSlot } from './iconSlot'
|
|
4
4
|
|
|
5
5
|
export interface MenuItemContentDef {
|
|
6
6
|
/**
|
|
@@ -16,13 +16,9 @@ export interface MenuItemContentDef {
|
|
|
16
16
|
*/
|
|
17
17
|
description?: ReactNode
|
|
18
18
|
/**
|
|
19
|
-
* Renders a `.menu-item-check` at the trailing edge when true.
|
|
19
|
+
* Renders a `.menu-item-check` at the trailing edge when true — `IconProvider`'s `check` icon.
|
|
20
20
|
*/
|
|
21
21
|
selected?: boolean
|
|
22
|
-
/**
|
|
23
|
-
* Icon name for the selected-state check. Defaults to chassis-css's own doc example.
|
|
24
|
-
*/
|
|
25
|
-
checkIcon?: string
|
|
26
22
|
}
|
|
27
23
|
|
|
28
24
|
// Chassis-css defines identical markup for rich item content in both Menu and Combobox items
|
|
@@ -36,8 +32,7 @@ export const renderMenuItemContent = ({
|
|
|
36
32
|
icon,
|
|
37
33
|
label,
|
|
38
34
|
description,
|
|
39
|
-
selected
|
|
40
|
-
checkIcon = 'check-solid'
|
|
35
|
+
selected
|
|
41
36
|
}: MenuItemContentDef): ReactNode => {
|
|
42
37
|
if (icon == null && description == null && !selected) return label
|
|
43
38
|
|
|
@@ -56,7 +51,7 @@ export const renderMenuItemContent = ({
|
|
|
56
51
|
) : (
|
|
57
52
|
label
|
|
58
53
|
)}
|
|
59
|
-
{selected && <
|
|
54
|
+
{selected && <IconSlot icon="check" className="menu-item-check" />}
|
|
60
55
|
</>
|
|
61
56
|
)
|
|
62
57
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
cloneElement,
|
|
3
|
+
createContext,
|
|
4
|
+
forwardRef,
|
|
5
|
+
Fragment,
|
|
6
|
+
isValidElement,
|
|
7
|
+
ReactElement,
|
|
8
|
+
ReactNode,
|
|
9
|
+
Ref,
|
|
10
|
+
useContext,
|
|
11
|
+
version
|
|
12
|
+
} from 'react'
|
|
13
|
+
import { mergeProps } from 'react-aria'
|
|
14
|
+
|
|
15
|
+
import { useForkedRef } from '../hooks/useForkedRef'
|
|
16
|
+
import { devWarning } from './devWarning'
|
|
17
|
+
|
|
18
|
+
// Backs the `asChild` prop every polymorphic component accepts (see `createPolymorphicComponent`).
|
|
19
|
+
// The component renders `Slot` as its `component`, exactly like any other component reference;
|
|
20
|
+
// `Slot` then renders the caller's child element in its place, with the props the component
|
|
21
|
+
// computed (className, handlers, ARIA attributes, ref) merged onto it.
|
|
22
|
+
//
|
|
23
|
+
// The child element travels through context rather than a prop because a render function spreads
|
|
24
|
+
// its `rest` props onto `component` in its own way (some filter them, some don't forward every
|
|
25
|
+
// one) — context reaches `Slot` no matter how the render function wires it up. `Slot` resets the
|
|
26
|
+
// context for its own subtree, so a polymorphic component nested *inside* the child doesn't pick
|
|
27
|
+
// up the outer one's element.
|
|
28
|
+
type SlotElement = ReactElement<Record<string, unknown>>
|
|
29
|
+
|
|
30
|
+
const SlotContext = createContext<SlotElement | null>(null)
|
|
31
|
+
|
|
32
|
+
export const SlotProvider = SlotContext.Provider
|
|
33
|
+
|
|
34
|
+
// React 19 moved `ref` onto `props` (and warns on reading `element.ref`); React 18 only has
|
|
35
|
+
// `element.ref`. The package supports both (`peerDependencies.react: >=18`).
|
|
36
|
+
const REACT_19 = parseInt(version, 10) >= 19
|
|
37
|
+
|
|
38
|
+
function getElementRef(element: SlotElement): Ref<unknown> | undefined {
|
|
39
|
+
return REACT_19
|
|
40
|
+
? (element.props as { ref?: Ref<unknown> }).ref
|
|
41
|
+
: (element as unknown as { ref?: Ref<unknown> }).ref
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// `Record` rather than a declared prop list: `Slot` forwards whatever the render function hands it.
|
|
45
|
+
type SlotProps = Record<string, unknown>
|
|
46
|
+
|
|
47
|
+
export const Slot = forwardRef<unknown, SlotProps>(function Slot({ children, ...slotProps }, ref) {
|
|
48
|
+
const element = useContext(SlotContext)
|
|
49
|
+
const forkedRef = useForkedRef(ref, element ? getElementRef(element) : undefined)
|
|
50
|
+
if (!element) return null
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<SlotContext.Provider value={null}>
|
|
54
|
+
{cloneElement(
|
|
55
|
+
element,
|
|
56
|
+
// react-aria's `mergeProps`: the child's own props win over the component's, except that
|
|
57
|
+
// classNames concatenate, event handlers chain (the component's first), and ids merge.
|
|
58
|
+
{ ...mergeProps(slotProps, element.props), ref: forkedRef } as Record<string, unknown>,
|
|
59
|
+
children as ReactNode
|
|
60
|
+
)}
|
|
61
|
+
</SlotContext.Provider>
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// The single element `asChild` renders in place of the component's own element, or `null` (with
|
|
66
|
+
// a dev warning) when `children` isn't exactly one — the component then falls back to rendering
|
|
67
|
+
// its default element, so a misuse degrades to a working, if unstyled-as-intended, UI.
|
|
68
|
+
export function getSlotChild(children: ReactNode, displayName: string): SlotElement | null {
|
|
69
|
+
if (isValidElement<Record<string, unknown>>(children) && children.type !== Fragment) {
|
|
70
|
+
return children
|
|
71
|
+
}
|
|
72
|
+
devWarning(
|
|
73
|
+
true,
|
|
74
|
+
`${displayName}: \`asChild\` expects exactly one React element as its child (not text, a ` +
|
|
75
|
+
`fragment, or several elements); rendering the default element instead.`
|
|
76
|
+
)
|
|
77
|
+
return null
|
|
78
|
+
}
|