@chassis-ui/react 0.1.0 → 0.1.2
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/README.md +11 -0
- package/dist/index.d.ts +682 -237
- package/dist/index.js +2 -2
- package/dist/style.css +1 -1
- package/package.json +32 -25
- package/src/components/accordion/Accordion.tsx +2 -2
- package/src/components/accordion/AccordionItem.tsx +6 -6
- package/src/components/autocomplete/Autocomplete.tsx +4 -4
- package/src/components/avatar/Avatar.tsx +5 -2
- package/src/components/breadcrumb/Breadcrumb.tsx +11 -2
- package/src/components/button/Button.tsx +7 -4
- package/src/components/button-group/ButtonGroup.tsx +2 -2
- package/src/components/calendar/Calendar.tsx +56 -42
- package/src/components/calendar/CalendarMonthGrid.tsx +3 -1
- package/src/components/calendar/CalendarMonthYearPicker.tsx +4 -2
- package/src/components/calendar/CalendarYearGrid.tsx +5 -3
- package/src/components/calendar/RangeCalendar.scss +8 -8
- package/src/components/calendar/RangeCalendar.tsx +56 -44
- package/src/components/calendar/index.ts +1 -0
- package/src/components/calendar/labels.tsx +71 -0
- package/src/components/card/Card.tsx +4 -4
- package/src/components/card/CardGroup.tsx +1 -1
- package/src/components/card/CardImage.tsx +1 -1
- package/src/components/carousel/Carousel.tsx +20 -9
- package/src/components/carousel/CarouselControlButton.tsx +1 -1
- package/src/components/carousel/CarouselPlayPause.tsx +1 -1
- package/src/components/carousel/carouselEngine.ts +3 -3
- package/src/components/checkbox/Checkbox.tsx +12 -11
- package/src/components/checkbox/CheckboxGroup.tsx +1 -1
- package/src/components/chip/Chip.tsx +5 -4
- package/src/components/chip-input/ChipInput.tsx +12 -4
- package/src/components/chip-input/ChipList.tsx +2 -2
- package/src/components/close-button/CloseButton.tsx +2 -2
- package/src/components/color-input/ColorInput.tsx +2 -2
- package/src/components/combobox/Combobox.tsx +2 -2
- package/src/components/datagrid/DataGrid.scss +203 -0
- package/src/components/datagrid/DataGrid.tsx +259 -0
- package/src/components/datagrid/DataGridBody.tsx +162 -0
- package/src/components/datagrid/DataGridCell.tsx +47 -0
- package/src/components/datagrid/DataGridColumn.tsx +109 -0
- package/src/components/datagrid/DataGridHeader.tsx +47 -0
- package/src/components/datagrid/DataGridPinBehavior.tsx +225 -0
- package/src/components/datagrid/DataGridRow.tsx +48 -0
- package/src/components/datagrid/DataGridSelectionCell.tsx +74 -0
- package/src/components/datagrid/DataGridTableLayout.ts +30 -0
- package/src/components/datagrid/index.ts +14 -0
- package/src/components/datagrid/useColumnGeometry.ts +161 -0
- package/src/components/datepicker/ClearButton.tsx +4 -1
- package/src/components/datepicker/DatePicker.scss +2 -2
- package/src/components/datepicker/DatePicker.tsx +179 -147
- package/src/components/datepicker/DateRangePicker.tsx +107 -89
- package/src/components/datepicker/renderDatePickerShell.tsx +1 -1
- package/src/components/drawer/Drawer.tsx +3 -1
- package/src/components/file-input/FileInput.tsx +2 -2
- package/src/components/floating-input/FloatingInput.tsx +8 -8
- package/src/components/form/renderFormCheck.tsx +3 -3
- package/src/components/form-field/FormField.tsx +8 -8
- package/src/components/form-field/renderFormField.tsx +14 -3
- package/src/components/grid/Col.tsx +1 -1
- package/src/components/grid/GridItem.tsx +1 -1
- package/src/components/grid/Row.tsx +1 -1
- package/src/components/input-group/InputGroup.tsx +2 -2
- package/src/components/list/List.tsx +5 -5
- package/src/components/menu/Menu.tsx +10 -0
- package/src/components/menu/MenuSubmenu.tsx +3 -3
- package/src/components/menu/MenuSubmenuBack.tsx +1 -1
- package/src/components/menu/MenuToggle.tsx +2 -2
- package/src/components/modal/Modal.tsx +5 -3
- package/src/components/navbar/Navbar.tsx +1 -1
- package/src/components/navbar/NavbarBrand.tsx +5 -2
- package/src/components/notification/NotificationStack.tsx +1 -1
- package/src/components/otp-input/OtpBox.tsx +1 -1
- package/src/components/otp-input/OtpInput.tsx +12 -11
- package/src/components/pagination/Pagination.tsx +2 -2
- package/src/components/pagination/PaginationItem.tsx +23 -4
- package/src/components/popover/Popover.tsx +40 -14
- package/src/components/progress/Progress.tsx +1 -1
- package/src/components/progress/ProgressBar.tsx +1 -1
- package/src/components/radio/Radio.tsx +2 -2
- package/src/components/radio/RadioGroup.tsx +1 -1
- package/src/components/select/Select.tsx +8 -8
- package/src/components/skeleton/Skeleton.tsx +1 -1
- package/src/components/spinner/Spinner.tsx +2 -2
- package/src/components/stepper/Stepper.tsx +5 -5
- package/src/components/switch/Switch.tsx +3 -3
- package/src/components/table/Table.css +2 -2
- package/src/components/table/Table.tsx +30 -7
- package/src/components/text-input/TextInput.tsx +2 -2
- package/src/components/textarea/Textarea.tsx +2 -2
- package/src/components/toast/ToastBody.tsx +1 -1
- package/src/components/toast/ToastHeader.tsx +2 -2
- package/src/components/toast/Toaster.tsx +1 -1
- package/src/components/tooltip/Tooltip.tsx +23 -7
- package/src/hooks/useAutoDismiss.ts +4 -1
- package/src/hooks/useDialogElement.ts +10 -0
- package/src/hooks/useFloatingOverlay.ts +12 -2
- package/src/hooks/usePagination.ts +6 -2
- package/src/index.ts +226 -0
- package/src/types.ts +26 -26
- package/src/utils/breakpoints.ts +1 -1
- package/src/utils/devWarning.ts +58 -0
- package/src/utils/direction.ts +14 -0
- package/src/utils/focusRedirect.ts +10 -16
- package/src/utils/polymorphic.ts +18 -1
- package/src/utils/suppressFocusRingGlobally.ts +22 -7
- package/src/utils/triggerElement.ts +25 -0
|
@@ -13,6 +13,7 @@ import { DateField } from './DateField'
|
|
|
13
13
|
import { renderDatePickerShell } from './renderDatePickerShell'
|
|
14
14
|
import './DatePicker.scss'
|
|
15
15
|
import './DateRangePicker.scss'
|
|
16
|
+
import { CalendarLabels, CalendarLabelsProvider } from '../calendar/labels'
|
|
16
17
|
|
|
17
18
|
export interface DateRangePickerProps extends Omit<
|
|
18
19
|
HTMLAttributes<HTMLDivElement>,
|
|
@@ -72,6 +73,14 @@ export interface DateRangePickerProps extends Omit<
|
|
|
72
73
|
* shown but cannot be selected.
|
|
73
74
|
*/
|
|
74
75
|
isDateUnavailable?: (date: DateValue) => boolean
|
|
76
|
+
/**
|
|
77
|
+
* Overrides for the strings this component and its calendar render themselves rather than
|
|
78
|
+
* getting from the active locale — the clear adornment and the calendar's own year-view
|
|
79
|
+
* arrows/announcements. Date segment order, month and weekday names all follow `I18nProvider`'s
|
|
80
|
+
* locale via react-aria and need no override. Merged over the English defaults, so passing one
|
|
81
|
+
* key leaves the rest alone.
|
|
82
|
+
*/
|
|
83
|
+
labels?: Partial<CalendarLabels>
|
|
75
84
|
/**
|
|
76
85
|
* Whether the calendar popover is open (controlled).
|
|
77
86
|
*/
|
|
@@ -110,9 +119,9 @@ export interface DateRangePickerProps extends Omit<
|
|
|
110
119
|
*/
|
|
111
120
|
presets?: DateRangePreset[]
|
|
112
121
|
/**
|
|
113
|
-
* Size the component
|
|
122
|
+
* Size the component sm or lg.
|
|
114
123
|
*/
|
|
115
|
-
size?: '
|
|
124
|
+
size?: 'sm' | 'lg'
|
|
116
125
|
/**
|
|
117
126
|
* ISO 8601 dates (`YYYY-MM-DD`) to mark unselectable, as a convenience alternative to
|
|
118
127
|
* `isDateUnavailable` for data-driven cases (e.g. booked dates fetched from an API). Composed
|
|
@@ -162,6 +171,7 @@ export const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>(
|
|
|
162
171
|
isDateUnavailable,
|
|
163
172
|
isOpen,
|
|
164
173
|
label,
|
|
174
|
+
labels,
|
|
165
175
|
maxValue,
|
|
166
176
|
minValue,
|
|
167
177
|
name,
|
|
@@ -248,95 +258,103 @@ export const DateRangePicker = forwardRef<HTMLDivElement, DateRangePickerProps>(
|
|
|
248
258
|
|
|
249
259
|
const { dialogProps: domDialogProps } = useDialog(dialogProps, calendarRef)
|
|
250
260
|
|
|
251
|
-
return
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
calendar: (
|
|
256
|
-
<RangeCalendar
|
|
257
|
-
{...domDialogProps}
|
|
258
|
-
autoFocus
|
|
259
|
-
disabled={disabled}
|
|
260
|
-
firstDayOfWeek={firstDayOfWeek}
|
|
261
|
-
isDateUnavailable={combinedIsDateUnavailable}
|
|
262
|
-
maxValue={maxValue}
|
|
263
|
-
minValue={minValue}
|
|
264
|
-
onChange={calendarProps.onChange}
|
|
265
|
-
presets={presets}
|
|
266
|
-
ref={calendarRef}
|
|
267
|
-
value={calendarProps.value}
|
|
268
|
-
visibleMonths={visibleMonths}
|
|
269
|
-
/>
|
|
270
|
-
),
|
|
271
|
-
className,
|
|
272
|
-
// `state.value` is always a `{ start, end }` object, never `null` itself — even with
|
|
273
|
-
// nothing picked yet — so the endpoints are what actually indicate a selection to
|
|
274
|
-
// clear (matching the hidden-input `value`s below, which check the same way).
|
|
275
|
-
clearButton: (state.value?.start || state.value?.end) && !disabled && (
|
|
276
|
-
<ClearButton
|
|
277
|
-
onPress={() => {
|
|
278
|
-
state.setValue(null)
|
|
279
|
-
// See `DatePicker`'s identical `ClearButton` usage for why this is needed —
|
|
280
|
-
// this button unmounts itself once cleared, so focus needs somewhere to land.
|
|
281
|
-
toggleButtonRef.current?.focus()
|
|
282
|
-
}}
|
|
283
|
-
/>
|
|
284
|
-
),
|
|
285
|
-
disabled,
|
|
286
|
-
field: (
|
|
287
|
-
<>
|
|
288
|
-
<DateField fieldProps={startFieldProps} />
|
|
289
|
-
<span aria-hidden="true" className="daterangepicker-separator">
|
|
290
|
-
–
|
|
291
|
-
</span>
|
|
292
|
-
<DateField fieldProps={endFieldProps} />
|
|
293
|
-
</>
|
|
294
|
-
),
|
|
295
|
-
fieldClassName: 'd-flex w-100',
|
|
296
|
-
groupProps: {
|
|
297
|
-
...mergeProps(groupProps, rest),
|
|
298
|
-
'aria-describedby': describedBy,
|
|
299
|
-
'aria-labelledby': labelledBy
|
|
300
|
-
},
|
|
301
|
-
groupRef: forkedGroupRef,
|
|
302
|
-
invalid,
|
|
303
|
-
isOpen: state.isOpen,
|
|
304
|
-
overlayDismissProps,
|
|
305
|
-
overlayRef,
|
|
306
|
-
overlayStyle,
|
|
307
|
-
placementAttr,
|
|
308
|
-
size,
|
|
309
|
-
toggleButton: (
|
|
310
|
-
<CalendarToggleButton buttonProps={buttonProps} ref={toggleButtonRef} state={state} />
|
|
311
|
-
),
|
|
312
|
-
valid
|
|
313
|
-
})}
|
|
314
|
-
{name && (
|
|
261
|
+
return (
|
|
262
|
+
<CalendarLabelsProvider labels={labels}>
|
|
263
|
+
{renderFormField({
|
|
264
|
+
children: (
|
|
315
265
|
<>
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
266
|
+
{renderDatePickerShell({
|
|
267
|
+
calendar: (
|
|
268
|
+
<RangeCalendar
|
|
269
|
+
{...domDialogProps}
|
|
270
|
+
autoFocus
|
|
271
|
+
disabled={disabled}
|
|
272
|
+
firstDayOfWeek={firstDayOfWeek}
|
|
273
|
+
isDateUnavailable={combinedIsDateUnavailable}
|
|
274
|
+
maxValue={maxValue}
|
|
275
|
+
minValue={minValue}
|
|
276
|
+
onChange={calendarProps.onChange}
|
|
277
|
+
presets={presets}
|
|
278
|
+
ref={calendarRef}
|
|
279
|
+
value={calendarProps.value}
|
|
280
|
+
visibleMonths={visibleMonths}
|
|
281
|
+
/>
|
|
282
|
+
),
|
|
283
|
+
className,
|
|
284
|
+
// `state.value` is always a `{ start, end }` object, never `null` itself — even with
|
|
285
|
+
// nothing picked yet — so the endpoints are what actually indicate a selection to
|
|
286
|
+
// clear (matching the hidden-input `value`s below, which check the same way).
|
|
287
|
+
clearButton: (state.value?.start || state.value?.end) && !disabled && (
|
|
288
|
+
<ClearButton
|
|
289
|
+
onPress={() => {
|
|
290
|
+
state.setValue(null)
|
|
291
|
+
// See `DatePicker`'s identical `ClearButton` usage for why this is needed —
|
|
292
|
+
// this button unmounts itself once cleared, so focus needs somewhere to land.
|
|
293
|
+
toggleButtonRef.current?.focus()
|
|
294
|
+
}}
|
|
295
|
+
/>
|
|
296
|
+
),
|
|
297
|
+
disabled,
|
|
298
|
+
field: (
|
|
299
|
+
<>
|
|
300
|
+
<DateField fieldProps={startFieldProps} />
|
|
301
|
+
<span aria-hidden="true" className="daterangepicker-separator">
|
|
302
|
+
–
|
|
303
|
+
</span>
|
|
304
|
+
<DateField fieldProps={endFieldProps} />
|
|
305
|
+
</>
|
|
306
|
+
),
|
|
307
|
+
fieldClassName: 'd-flex w-100',
|
|
308
|
+
groupProps: {
|
|
309
|
+
...mergeProps(groupProps, rest),
|
|
310
|
+
'aria-describedby': describedBy,
|
|
311
|
+
'aria-labelledby': labelledBy
|
|
312
|
+
},
|
|
313
|
+
groupRef: forkedGroupRef,
|
|
314
|
+
invalid,
|
|
315
|
+
isOpen: state.isOpen,
|
|
316
|
+
overlayDismissProps,
|
|
317
|
+
overlayRef,
|
|
318
|
+
overlayStyle,
|
|
319
|
+
placementAttr,
|
|
320
|
+
size,
|
|
321
|
+
toggleButton: (
|
|
322
|
+
<CalendarToggleButton
|
|
323
|
+
buttonProps={buttonProps}
|
|
324
|
+
ref={toggleButtonRef}
|
|
325
|
+
state={state}
|
|
326
|
+
/>
|
|
327
|
+
),
|
|
328
|
+
valid
|
|
329
|
+
})}
|
|
330
|
+
{name && (
|
|
331
|
+
<>
|
|
332
|
+
<input
|
|
333
|
+
disabled={disabled}
|
|
334
|
+
name={`${name}Start`}
|
|
335
|
+
type="hidden"
|
|
336
|
+
value={state.value?.start ? state.value.start.toString() : ''}
|
|
337
|
+
/>
|
|
338
|
+
<input
|
|
339
|
+
disabled={disabled}
|
|
340
|
+
name={`${name}End`}
|
|
341
|
+
type="hidden"
|
|
342
|
+
value={state.value?.end ? state.value.end.toString() : ''}
|
|
343
|
+
/>
|
|
344
|
+
</>
|
|
345
|
+
)}
|
|
328
346
|
</>
|
|
329
|
-
)
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
347
|
+
),
|
|
348
|
+
help,
|
|
349
|
+
ids: { feedback: feedbackId, help: helpId, label: labelId },
|
|
350
|
+
invalid,
|
|
351
|
+
invalidFeedback,
|
|
352
|
+
label,
|
|
353
|
+
valid,
|
|
354
|
+
validFeedback
|
|
355
|
+
})}
|
|
356
|
+
</CalendarLabelsProvider>
|
|
357
|
+
)
|
|
340
358
|
}
|
|
341
359
|
)
|
|
342
360
|
|
|
@@ -69,7 +69,7 @@ export interface DrawerProps extends Omit<
|
|
|
69
69
|
/**
|
|
70
70
|
* Renders as a drawer only below this breakpoint — inline as a flex container above it.
|
|
71
71
|
*/
|
|
72
|
-
responsive?: '
|
|
72
|
+
responsive?: 'sm' | 'md' | 'lg' | 'xl' | '2xl'
|
|
73
73
|
/**
|
|
74
74
|
* Allow the page behind the drawer to scroll while it's open.
|
|
75
75
|
*/
|
|
@@ -118,6 +118,7 @@ export const Drawer = forwardRef<HTMLDialogElement, DrawerProps>(
|
|
|
118
118
|
fullscreen,
|
|
119
119
|
instant,
|
|
120
120
|
keyboard = true,
|
|
121
|
+
onClick,
|
|
121
122
|
onClose,
|
|
122
123
|
onClosePrevented,
|
|
123
124
|
onHidden,
|
|
@@ -144,6 +145,7 @@ export const Drawer = forwardRef<HTMLDialogElement, DrawerProps>(
|
|
|
144
145
|
if (entry.dialog !== dialog) entry.close()
|
|
145
146
|
}
|
|
146
147
|
},
|
|
148
|
+
onClick,
|
|
147
149
|
onClose,
|
|
148
150
|
onClosePrevented,
|
|
149
151
|
onHidden,
|
|
@@ -39,9 +39,9 @@ export interface FileInputProps extends Omit<InputHTMLAttributes<HTMLInputElemen
|
|
|
39
39
|
*/
|
|
40
40
|
onChange?: ChangeEventHandler<HTMLInputElement>
|
|
41
41
|
/**
|
|
42
|
-
* Size the component
|
|
42
|
+
* Size the component sm or lg.
|
|
43
43
|
*/
|
|
44
|
-
size?: '
|
|
44
|
+
size?: 'sm' | 'lg'
|
|
45
45
|
/**
|
|
46
46
|
* Set component validation state to valid.
|
|
47
47
|
*/
|
|
@@ -3,6 +3,7 @@ import classNames from 'classnames'
|
|
|
3
3
|
|
|
4
4
|
import { FormLabel } from '../form/FormLabel'
|
|
5
5
|
import { FormFieldIds, renderFormField } from '../form-field/renderFormField'
|
|
6
|
+
import { devWarning } from '../../utils/devWarning'
|
|
6
7
|
|
|
7
8
|
export interface FloatingInputProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
|
|
8
9
|
/**
|
|
@@ -63,14 +64,13 @@ export const FloatingInput = forwardRef<HTMLDivElement, FloatingInputProps>(
|
|
|
63
64
|
},
|
|
64
65
|
ref
|
|
65
66
|
) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
67
|
+
devWarning(
|
|
68
|
+
!ids?.input && !ids?.label,
|
|
69
|
+
'FloatingInput: `ids.input`/`ids.label` are not set — the rendered label ' +
|
|
70
|
+
"won't be associated with your control (no `htmlFor`, no `aria-labelledby` target). " +
|
|
71
|
+
'Pass `ids={{ input: yourControlId }}` (or `ids.label` for a group with no single ' +
|
|
72
|
+
'input) so the label has something to point at.'
|
|
73
|
+
)
|
|
74
74
|
|
|
75
75
|
return renderFormField({
|
|
76
76
|
children: (
|
|
@@ -16,9 +16,9 @@ export type ButtonObject = {
|
|
|
16
16
|
*/
|
|
17
17
|
shape?: Shapes
|
|
18
18
|
/**
|
|
19
|
-
* Size the component
|
|
19
|
+
* Size the component sm or lg.
|
|
20
20
|
*/
|
|
21
|
-
size?: '
|
|
21
|
+
size?: 'sm' | 'lg'
|
|
22
22
|
/**
|
|
23
23
|
* Set the button variant to an outlined button or a ghost button.
|
|
24
24
|
*/
|
|
@@ -32,7 +32,7 @@ export interface RenderFormCheckOptions {
|
|
|
32
32
|
input: ReactNode
|
|
33
33
|
invalid?: boolean
|
|
34
34
|
label?: ReactNode
|
|
35
|
-
size?: '
|
|
35
|
+
size?: 'sm' | 'lg'
|
|
36
36
|
valid?: boolean
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react'
|
|
2
2
|
|
|
3
3
|
import { FormFieldIds, renderFormField } from './renderFormField'
|
|
4
|
+
import { devWarning } from '../../utils/devWarning'
|
|
4
5
|
|
|
5
6
|
export interface FormFieldProps {
|
|
6
7
|
/**
|
|
@@ -59,14 +60,13 @@ export const FormField = ({
|
|
|
59
60
|
valid,
|
|
60
61
|
validFeedback
|
|
61
62
|
}: FormFieldProps): ReactNode => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
63
|
+
devWarning(
|
|
64
|
+
!!label && !ids?.input && !ids?.label,
|
|
65
|
+
'FormField: `label` is set but `ids.input`/`ids.label` are not — the rendered label ' +
|
|
66
|
+
"won't be associated with your control (no `htmlFor`, no `aria-labelledby` target). " +
|
|
67
|
+
'Pass `ids={{ input: yourControlId }}` (or `ids.label` for a group with no single ' +
|
|
68
|
+
'input) so the label has something to point at.'
|
|
69
|
+
)
|
|
70
70
|
|
|
71
71
|
return renderFormField({
|
|
72
72
|
children,
|
|
@@ -43,10 +43,21 @@ export const renderFormField = ({
|
|
|
43
43
|
valid,
|
|
44
44
|
validFeedback
|
|
45
45
|
}: RenderFormFieldOptions): ReactNode => {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
// At most one feedback node renders, and `invalid` wins: both carry the same `ids.feedback`,
|
|
47
|
+
// so rendering the pair (which `invalid` + `valid` set together used to do) put a duplicate id
|
|
48
|
+
// in the DOM and left every control's `aria-describedby` pointing at an ambiguous target —
|
|
49
|
+
// axe's `duplicate-id-aria`. `useFormField` only ever budgets one feedback id for the same
|
|
50
|
+
// reason. Contradictory state, but nothing in the types rules it out and ten components route
|
|
51
|
+
// through here.
|
|
52
|
+
const showInvalidFeedback = Boolean(invalid && invalidFeedback)
|
|
53
|
+
const showValidFeedback = Boolean(valid && validFeedback) && !showInvalidFeedback
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
// `className` alone is enough to warrant the wrapper: it has nowhere else to land, and
|
|
56
|
+
// returning `children` bare (the drop-in-compatibility path every field-capable leaf relies on
|
|
57
|
+
// until a consumer opts into wrapping) silently dropped it. Only `FormField` — the standalone
|
|
58
|
+
// "wrap this yourself" component — passes one, so this is exactly the case where the caller
|
|
59
|
+
// asked for a `.form-field` element by hand.
|
|
60
|
+
if (!label && !help && !showInvalidFeedback && !showValidFeedback && !className) {
|
|
50
61
|
return children
|
|
51
62
|
}
|
|
52
63
|
|
|
@@ -31,7 +31,7 @@ export interface ColProps extends HTMLAttributes<HTMLDivElement>, ColLayout {
|
|
|
31
31
|
/**
|
|
32
32
|
* Overrides `span`/`offset`/`order` at a breakpoint and up.
|
|
33
33
|
*
|
|
34
|
-
* @type { Partial<Record<'
|
|
34
|
+
* @type { Partial<Record<'sm' | 'md' | 'lg' | 'xl' | '2xl', { span?: 'auto' | number | string | boolean, offset?: number | string, order?: 'first' | 'last' | number | string }>> }
|
|
35
35
|
*/
|
|
36
36
|
responsive?: Partial<Record<Breakpoint, ColLayout>>
|
|
37
37
|
}
|
|
@@ -39,7 +39,7 @@ type GridItemOwnProps<C extends ElementType> = GridItemLayout & {
|
|
|
39
39
|
/**
|
|
40
40
|
* Overrides `span`/`start` at a breakpoint and up.
|
|
41
41
|
*
|
|
42
|
-
* @type { Partial<Record<'
|
|
42
|
+
* @type { Partial<Record<'sm' | 'md' | 'lg' | 'xl' | '2xl', { span?: number, start?: number }>> }
|
|
43
43
|
*/
|
|
44
44
|
responsive?: Partial<Record<Breakpoint, GridItemLayout>>
|
|
45
45
|
/**
|
|
@@ -40,7 +40,7 @@ export interface RowProps extends HTMLAttributes<HTMLDivElement>, RowLayout {
|
|
|
40
40
|
/**
|
|
41
41
|
* Overrides `cols`/`gutter`/`gutterX`/`gutterY` at a breakpoint and up.
|
|
42
42
|
*
|
|
43
|
-
* @type { Partial<Record<'
|
|
43
|
+
* @type { Partial<Record<'sm' | 'md' | 'lg' | 'xl' | '2xl', { cols?: 'auto' | number | string, gutter?: Spacing | 0, gutterX?: Spacing | 0, gutterY?: Spacing | 0 }>> }
|
|
44
44
|
*/
|
|
45
45
|
responsive?: Partial<Record<Breakpoint, RowLayout>>
|
|
46
46
|
}
|
|
@@ -17,9 +17,9 @@ type InputGroupOwnProps<C extends ElementType> = {
|
|
|
17
17
|
*/
|
|
18
18
|
component?: C
|
|
19
19
|
/**
|
|
20
|
-
* Size the component
|
|
20
|
+
* Size the component sm or lg.
|
|
21
21
|
*/
|
|
22
|
-
size?: '
|
|
22
|
+
size?: 'sm' | 'lg'
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export type InputGroupProps<C extends ElementType = 'div'> = PolymorphicComponentProps<
|
|
@@ -72,11 +72,11 @@ type ListOwnProps<C extends ElementType> = {
|
|
|
72
72
|
*/
|
|
73
73
|
layout?:
|
|
74
74
|
| 'horizontal'
|
|
75
|
-
| '
|
|
76
|
-
| '
|
|
77
|
-
| '
|
|
78
|
-
| '
|
|
79
|
-
| '
|
|
75
|
+
| 'sm:horizontal'
|
|
76
|
+
| 'md:horizontal'
|
|
77
|
+
| 'lg:horizontal'
|
|
78
|
+
| 'xl:horizontal'
|
|
79
|
+
| '2xl:horizontal'
|
|
80
80
|
/**
|
|
81
81
|
* Number list items sequentially using CSS counters. Pair with `component="ol"` for semantic correctness.
|
|
82
82
|
*/
|
|
@@ -220,8 +220,18 @@ function MenuRender<C extends ElementType = typeof Fragment>(
|
|
|
220
220
|
// reflecting the new `.show` class by the time this effect runs, since React commits the
|
|
221
221
|
// render before passive effects execute. Falls back to firing synchronously when no
|
|
222
222
|
// `MenuList` is mounted to measure (nothing to transition, so nothing to wait for).
|
|
223
|
+
//
|
|
224
|
+
// All four report *transitions*, so none fires for the initial commit — mounting closed is not
|
|
225
|
+
// a hide. Without this guard, mounting fired `onHide` (and scheduled `onHidden`) for every menu
|
|
226
|
+
// on the page before `onShow` had ever fired once, which `Menu.spec.tsx` used to have to
|
|
227
|
+
// `mockClear()` past.
|
|
228
|
+
const mountedRef = useRef(false)
|
|
229
|
+
|
|
223
230
|
useEffect(() => {
|
|
224
231
|
const overlay = overlayRef.current
|
|
232
|
+
const isInitialCommit = !mountedRef.current
|
|
233
|
+
mountedRef.current = true
|
|
234
|
+
if (isInitialCommit) return undefined
|
|
225
235
|
|
|
226
236
|
if (state.isOpen) {
|
|
227
237
|
onShow?.()
|
|
@@ -49,7 +49,7 @@ export interface MenuSubmenuProps extends Omit<HTMLAttributes<HTMLDivElement>, '
|
|
|
49
49
|
*/
|
|
50
50
|
placement?: Placement
|
|
51
51
|
/**
|
|
52
|
-
* Switches to a view-replacement pattern below the `
|
|
52
|
+
* Switches to a view-replacement pattern below the `sm` breakpoint. Pair with a
|
|
53
53
|
* `MenuSubmenuBack` as the first item of the nested menu.
|
|
54
54
|
*/
|
|
55
55
|
stacked?: boolean
|
|
@@ -268,10 +268,10 @@ export const MenuSubmenu = forwardRef<HTMLDivElement, MenuSubmenuProps>(
|
|
|
268
268
|
</div>
|
|
269
269
|
)
|
|
270
270
|
|
|
271
|
-
// `stacked`'s
|
|
271
|
+
// `stacked`'s sm-breakpoint CSS (`.menu:has(.submenu-stacked.show) ...`) needs the
|
|
272
272
|
// panel to be a real DOM descendant of `.menu`/`.submenu` to match — portaling would
|
|
273
273
|
// break that ancestor relationship, so it renders inline instead. Trade-off: above the
|
|
274
|
-
// `
|
|
274
|
+
// `sm` breakpoint a `stacked` submenu no longer escapes ancestor overflow/stacking-
|
|
275
275
|
// context clipping the way a portaled one does; accepted since `stacked` targets mobile
|
|
276
276
|
// nav/drawer usage, not floating dropdowns.
|
|
277
277
|
return mounted && !stacked ? createPortal(panel, document.body) : panel
|
|
@@ -11,7 +11,7 @@ export interface MenuSubmenuBackProps extends ButtonHTMLAttributes<HTMLButtonEle
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// First item of a stacked submenu's nested menu (see `MenuSubmenu`'s `stacked` prop). Closes
|
|
14
|
-
// the submenu and returns focus to its trigger — visible only below the `
|
|
14
|
+
// the submenu and returns focus to its trigger — visible only below the `sm` breakpoint.
|
|
15
15
|
export const MenuSubmenuBack = forwardRef<HTMLButtonElement, MenuSubmenuBackProps>(
|
|
16
16
|
({ children, className, onClick, type = 'button', ...rest }, ref) => {
|
|
17
17
|
const actions = useContext(SubmenuActionsContext)
|
|
@@ -52,9 +52,9 @@ type MenuToggleOwnProps<C extends ElementType> = {
|
|
|
52
52
|
*/
|
|
53
53
|
shape?: Shapes
|
|
54
54
|
/**
|
|
55
|
-
* Size the component
|
|
55
|
+
* Size the component sm or lg. Only applies to the default `Button` root.
|
|
56
56
|
*/
|
|
57
|
-
size?: '
|
|
57
|
+
size?: 'sm' | 'lg'
|
|
58
58
|
/**
|
|
59
59
|
* Sets the context style of the component. Only applies to the default `Button` root.
|
|
60
60
|
*/
|
|
@@ -24,7 +24,7 @@ export interface ModalProps extends Omit<
|
|
|
24
24
|
* Set modal to cover the entire user viewport. A breakpoint value goes fullscreen only
|
|
25
25
|
* below that breakpoint.
|
|
26
26
|
*/
|
|
27
|
-
fullscreen?: boolean | '
|
|
27
|
+
fullscreen?: boolean | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
|
|
28
28
|
/**
|
|
29
29
|
* Disable the open/close transition entirely.
|
|
30
30
|
*/
|
|
@@ -63,9 +63,9 @@ export interface ModalProps extends Omit<
|
|
|
63
63
|
*/
|
|
64
64
|
scrollable?: boolean
|
|
65
65
|
/**
|
|
66
|
-
* Size the component
|
|
66
|
+
* Size the component sm, lg, or extra lg.
|
|
67
67
|
*/
|
|
68
|
-
size?: '
|
|
68
|
+
size?: 'sm' | 'lg' | 'xl'
|
|
69
69
|
/**
|
|
70
70
|
* Toggle the visibility of modal component.
|
|
71
71
|
*/
|
|
@@ -97,6 +97,7 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
|
|
|
97
97
|
instant,
|
|
98
98
|
keyboard = true,
|
|
99
99
|
modal = true,
|
|
100
|
+
onClick,
|
|
100
101
|
onClose,
|
|
101
102
|
onClosePrevented,
|
|
102
103
|
onHidden,
|
|
@@ -115,6 +116,7 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
|
|
|
115
116
|
instant,
|
|
116
117
|
isModal: modal,
|
|
117
118
|
keyboard,
|
|
119
|
+
onClick,
|
|
118
120
|
onClose,
|
|
119
121
|
onClosePrevented,
|
|
120
122
|
onHidden,
|
|
@@ -24,7 +24,7 @@ type NavbarOwnProps<C extends ElementType> = {
|
|
|
24
24
|
/**
|
|
25
25
|
* Defines optional container wrapping children elements.
|
|
26
26
|
*/
|
|
27
|
-
container?: boolean | '
|
|
27
|
+
container?: boolean | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'fluid'
|
|
28
28
|
/**
|
|
29
29
|
* Opts this navbar into the framework's dark or light theming, independent of the page's own
|
|
30
30
|
* theme.
|
|
@@ -4,7 +4,8 @@ import classNames from 'classnames'
|
|
|
4
4
|
import {
|
|
5
5
|
createPolymorphicComponent,
|
|
6
6
|
PolymorphicComponentProps,
|
|
7
|
-
PolymorphicRef
|
|
7
|
+
PolymorphicRef,
|
|
8
|
+
PolymorphicRefWithFallback
|
|
8
9
|
} from '../../utils/polymorphic'
|
|
9
10
|
|
|
10
11
|
type NavbarBrandOwnProps<C extends ElementType> = {
|
|
@@ -30,7 +31,9 @@ export type NavbarBrandProps<C extends ElementType = 'span'> = PolymorphicCompon
|
|
|
30
31
|
>
|
|
31
32
|
|
|
32
33
|
type NavbarBrandComponent = (<C extends ElementType = 'span'>(
|
|
33
|
-
props: NavbarBrandProps<C> & {
|
|
34
|
+
props: NavbarBrandProps<C> & {
|
|
35
|
+
ref?: PolymorphicRefWithFallback<C, HTMLSpanElement | HTMLAnchorElement>
|
|
36
|
+
}
|
|
34
37
|
) => ReactElement | null) & { displayName?: string }
|
|
35
38
|
|
|
36
39
|
function NavbarBrandRender<C extends ElementType = 'span'>(
|
|
@@ -12,7 +12,7 @@ interface OtpBoxProps {
|
|
|
12
12
|
onChangeValue: (value: string) => void
|
|
13
13
|
onKeyDownBox: (event: KeyboardEvent<HTMLInputElement>) => void
|
|
14
14
|
onPasteBox: (event: ClipboardEvent<HTMLInputElement>) => void
|
|
15
|
-
size?: '
|
|
15
|
+
size?: 'sm' | 'lg'
|
|
16
16
|
valid?: boolean
|
|
17
17
|
value: string
|
|
18
18
|
}
|