@moneyforward/mfui-components 4.3.1 → 4.5.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.
Files changed (53) hide show
  1. package/dist/src/Accordion/Accordion.d.ts +21 -1
  2. package/dist/src/Accordion/Accordion.js +28 -4
  3. package/dist/src/Accordion/Accordion.types.d.ts +21 -0
  4. package/dist/src/Breadcrumbs/Breadcrumbs.js +1 -1
  5. package/dist/src/CheckboxCard/CheckboxCard.js +3 -1
  6. package/dist/src/DataTable/DataTable.js +9 -2
  7. package/dist/src/DataTable/DataTable.types.d.ts +15 -0
  8. package/dist/src/DataTable/DataTableHeaderRow/DataTableHeaderRow.js +3 -1
  9. package/dist/src/DataTable/DataTableHeaderRow/DataTableHeaderRow.types.d.ts +19 -1
  10. package/dist/src/DataTable/DataTableRow/DataTableRow.js +4 -1
  11. package/dist/src/DataTable/DataTableRow/DataTableRow.types.d.ts +23 -1
  12. package/dist/src/DateTimeSelection/DatePicker/DatePickerDateCell/index.d.ts +1 -1
  13. package/dist/src/DateTimeSelection/DatePicker/DatePickerDateCell/index.js +0 -1
  14. package/dist/src/DateTimeSelection/shared/CalendarGrid/CalendarGrid.js +1 -3
  15. package/dist/src/Dialog/Dialog.d.ts +1 -1
  16. package/dist/src/Dialog/Dialog.types.d.ts +1 -1
  17. package/dist/src/MainNavigation/BaseMainNavigation.d.ts +1 -1
  18. package/dist/src/MainNavigation/BaseMainNavigation.js +2 -2
  19. package/dist/src/MainNavigation/MainNavigation.js +2 -1
  20. package/dist/src/MainNavigation/MainNavigation.types.d.ts +9 -0
  21. package/dist/src/MainNavigation/NarrowViewportMainNavigation.js +2 -2
  22. package/dist/src/MultipleSelectBox/MultipleSelectBox.js +27 -18
  23. package/dist/src/MultipleSelectBox/hooks/useInitialFocusOnOptionPanelOpen.js +1 -1
  24. package/dist/src/NavigationList/NavigationListItem/NavigationListItem.d.ts +3 -0
  25. package/dist/src/NavigationList/NavigationListItem/NavigationListItem.js +6 -2
  26. package/dist/src/NavigationList/NavigationListItem/NavigationListItem.types.d.ts +2 -1
  27. package/dist/src/NumberBox/NumberBox.d.ts +5 -0
  28. package/dist/src/NumberBox/NumberBox.js +53 -2
  29. package/dist/src/NumberBox/NumberBox.types.d.ts +35 -0
  30. package/dist/src/Pagination/Pagination.js +4 -1
  31. package/dist/src/Popover/Popover.js +1 -2
  32. package/dist/src/RadioButtonCard/RadioButtonCard.js +3 -1
  33. package/dist/src/SelectBox/SelectBox.js +20 -11
  34. package/dist/src/SelectBox/utils/getFlatOptions.js +1 -1
  35. package/dist/src/SidePane/SidePane.js +87 -7
  36. package/dist/src/SidePane/SidePane.types.d.ts +39 -0
  37. package/dist/src/SubNavigation/SubNavigation.d.ts +10 -1
  38. package/dist/src/SubNavigation/SubNavigation.js +14 -3
  39. package/dist/src/SubNavigation/SubNavigation.types.d.ts +27 -1
  40. package/dist/src/Tabs/hooks/useTabRegistry.js +5 -5
  41. package/dist/src/ToggleSwitch/index.d.ts +1 -1
  42. package/dist/src/ToggleSwitch/index.js +0 -1
  43. package/dist/src/utilities/dom/containsAcrossShadowRoots.d.ts +21 -0
  44. package/dist/src/utilities/dom/containsAcrossShadowRoots.js +33 -0
  45. package/dist/src/utilities/dom/useFocusTrap.js +1 -1
  46. package/dist/src/utilities/dom/useTableEdgeCellMarkers.d.ts +56 -0
  47. package/dist/src/utilities/dom/useTableEdgeCellMarkers.js +223 -0
  48. package/dist/styled-system/recipes/data-table-cell-slot-recipe.js +4 -2
  49. package/dist/styles.css +20 -4
  50. package/dist/tsconfig.build.tsbuildinfo +1 -1
  51. package/package.json +1 -1
  52. /package/dist/src/{MultipleSelectBox/utils → utilities/dom}/getDeepActiveElement.d.ts +0 -0
  53. /package/dist/src/{MultipleSelectBox/utils → utilities/dom}/getDeepActiveElement.js +0 -0
@@ -1,6 +1,6 @@
1
1
  import { type AccordionProps, type AccordionItemProps } from './Accordion.types';
2
2
  declare function AccordionBase({ children, size, width, defaultOpenItems }: AccordionProps): import("react/jsx-runtime").JSX.Element;
3
- declare function AccordionItem({ children, id, enableAutoUnmount }: AccordionItemProps): import("react/jsx-runtime").JSX.Element;
3
+ declare function AccordionItem({ children, id, enableAutoUnmount, open, onOpenStateChanged }: AccordionItemProps): import("react/jsx-runtime").JSX.Element;
4
4
  /**
5
5
  * The `Accordion` component.
6
6
  *
@@ -24,6 +24,26 @@ declare function AccordionItem({ children, id, enableAutoUnmount }: AccordionIte
24
24
  * </Accordion.Item>
25
25
  * </Accordion>
26
26
  * ```
27
+ *
28
+ * Each `Accordion.Item` can also be controlled with the `open` and `onOpenStateChanged` props, which lets a trigger
29
+ * outside the Accordion tree drive it. In that case you own the `aria-expanded` and `aria-controls` attributes of
30
+ * your own trigger, and `enableAutoUnmount={false}` keeps the collapsed content mounted.
31
+ *
32
+ * @example
33
+ * ```jsx
34
+ * const [isOpen, setIsOpen] = useState(false);
35
+ *
36
+ * <>
37
+ * <button type="button" aria-expanded={isOpen} aria-controls="accordion-1" onClick={() => setIsOpen(!isOpen)}>
38
+ * Toggle
39
+ * </button>
40
+ * <Accordion>
41
+ * <Accordion.Item id="accordion-1" open={isOpen} enableAutoUnmount={false}>
42
+ * <Accordion.Target>Content for section 1</Accordion.Target>
43
+ * </Accordion.Item>
44
+ * </Accordion>
45
+ * </>
46
+ * ```
27
47
  */
28
48
  export declare const Accordion: typeof AccordionBase & {
29
49
  /**
@@ -41,12 +41,16 @@ function AccordionBase({ children, size = 'medium', width, defaultOpenItems = []
41
41
  };
42
42
  return (_jsx("div", { style: containerStyle, children: _jsx(AccordionContext.Provider, { value: contextValue, children: children }) }));
43
43
  }
44
- function AccordionItem({ children, id, enableAutoUnmount = true }) {
44
+ function AccordionItem({ children, id, enableAutoUnmount = true, open, onOpenStateChanged }) {
45
45
  const { openItems, toggleItem } = useAccordionContext();
46
- const isOpen = openItems[id] || false;
46
+ const isControlled = open !== undefined;
47
+ const isOpen = open ?? openItems[id] ?? false;
47
48
  const toggle = useCallback(() => {
48
- toggleItem(id);
49
- }, [toggleItem, id]);
49
+ if (!isControlled) {
50
+ toggleItem(id);
51
+ }
52
+ onOpenStateChanged?.(!isOpen);
53
+ }, [isControlled, toggleItem, id, onOpenStateChanged, isOpen]);
50
54
  const contextValue = useMemo(() => ({
51
55
  isOpen,
52
56
  toggle,
@@ -100,6 +104,26 @@ const AccordionTarget = forwardRef(({ children, className, style, ...props }, re
100
104
  * </Accordion.Item>
101
105
  * </Accordion>
102
106
  * ```
107
+ *
108
+ * Each `Accordion.Item` can also be controlled with the `open` and `onOpenStateChanged` props, which lets a trigger
109
+ * outside the Accordion tree drive it. In that case you own the `aria-expanded` and `aria-controls` attributes of
110
+ * your own trigger, and `enableAutoUnmount={false}` keeps the collapsed content mounted.
111
+ *
112
+ * @example
113
+ * ```jsx
114
+ * const [isOpen, setIsOpen] = useState(false);
115
+ *
116
+ * <>
117
+ * <button type="button" aria-expanded={isOpen} aria-controls="accordion-1" onClick={() => setIsOpen(!isOpen)}>
118
+ * Toggle
119
+ * </button>
120
+ * <Accordion>
121
+ * <Accordion.Item id="accordion-1" open={isOpen} enableAutoUnmount={false}>
122
+ * <Accordion.Target>Content for section 1</Accordion.Target>
123
+ * </Accordion.Item>
124
+ * </Accordion>
125
+ * </>
126
+ * ```
103
127
  */
104
128
  export const Accordion = Object.assign(AccordionBase, {
105
129
  /**
@@ -42,6 +42,27 @@ export type AccordionItemProps = {
42
42
  * @default true
43
43
  */
44
44
  enableAutoUnmount?: boolean;
45
+ /**
46
+ * The open state of this accordion item.
47
+ * You only need to use this prop if you want to control the item programmatically, for example when the trigger
48
+ * lives outside the Accordion tree. While this prop is defined, it takes precedence over the `defaultOpenItems` of
49
+ * the Accordion and over the internal state, so you have to update it yourself in response to
50
+ * `onOpenStateChanged`. Supply it consistently: do not switch an item between controlled and uncontrolled.
51
+ *
52
+ * When you drive the item from your own trigger outside the Accordion, you own its `aria-expanded` and
53
+ * `aria-controls` attributes. `Accordion.Target` renders the item `id` as its DOM `id`, so
54
+ * `aria-controls={id}` points at it. Note that with the default `enableAutoUnmount` the target is unmounted
55
+ * while collapsed, so `aria-controls` refers to an element that does not exist yet; set
56
+ * `enableAutoUnmount={false}` to keep the target in the DOM.
57
+ */
58
+ open?: boolean;
59
+ /**
60
+ * Callback fired when the open state of this accordion item is toggled by `Accordion.Trigger`.
61
+ * It is called in both controlled and uncontrolled mode.
62
+ *
63
+ * @param open - The new open state of the accordion item.
64
+ */
65
+ onOpenStateChanged?: (open: boolean) => void;
45
66
  };
46
67
  /**
47
68
  * Props for the Accordion.Trigger component
@@ -102,7 +102,7 @@ export const Breadcrumbs = forwardRef(({ items, wrap = false, customLinkComponen
102
102
  if (wrap || !listRef.current)
103
103
  return;
104
104
  const observer = new ResizeObserver((entries) => {
105
- const entry = entries[0];
105
+ const [entry] = entries;
106
106
  if (entry) {
107
107
  setContainerWidth(entry.contentRect.width);
108
108
  }
@@ -29,5 +29,7 @@ export const CheckboxCard = forwardRef(({ label, layout = 'vertical', renderDeta
29
29
  disableResponsive,
30
30
  isResponsive: Boolean(responsiveSlot.narrowViewport),
31
31
  });
32
- return (_jsxs("div", { className: cx(classes.root, 'mfui-CheckboxCard__root', className), children: [_jsx(FocusIndicator, { disableNestedFocus: true, children: _jsxs(Typography, { variant: "label", className: cx(classes.inputWrapper, 'mfui-CheckboxCard__inputWrapper'), as: "label", htmlFor: props.id, children: [_jsx(Checkbox, { ref: ref, ...props, className: cx(classes.input, 'mfui-CheckboxCard__input') }), _jsx("span", { className: cx(classes.label, 'mfui-CheckboxCard__label'), children: label })] }) }), responsiveSlot.narrowViewport && disableResponsive ? (_jsxs(_Fragment, { children: [_jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsWide, 'mfui-CheckboxCard__details'), children: responsiveSlot.base(checkboxState) }), _jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsNarrow, 'mfui-CheckboxCard__details'), children: responsiveSlot.narrowViewport(checkboxState) })] })) : responsiveSlot.narrowViewport ? (_jsxs(_Fragment, { children: [_jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsWide, 'mfui-CheckboxCard__details'), children: responsiveSlot.base(checkboxState) }), _jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsNarrow, 'mfui-CheckboxCard__details'), children: responsiveSlot.narrowViewport(checkboxState) })] })) : responsiveSlot.base ? (_jsx("div", { "data-mfui-content": "details", className: cx(classes.details, 'mfui-CheckboxCard__details'), children: responsiveSlot.base(checkboxState) })) : null] }));
32
+ return (_jsxs("div", { className: cx(classes.root, 'mfui-CheckboxCard__root', className), children: [_jsx(FocusIndicator, { disableNestedFocus: true, children: _jsxs(Typography, { variant: "label", className: cx(classes.inputWrapper, 'mfui-CheckboxCard__inputWrapper'), as: "label", htmlFor: props.id, children: [_jsx(Checkbox, { ref: ref, ...props, className: cx(classes.input, 'mfui-CheckboxCard__input') }), _jsx("span", { className: cx(classes.label, 'mfui-CheckboxCard__label'), children: label })] }) }), responsiveSlot.narrowViewport ? (_jsxs(_Fragment, { children: [_jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsWide, 'mfui-CheckboxCard__details'), children: responsiveSlot.base(checkboxState) }), _jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsNarrow, 'mfui-CheckboxCard__details'), children: responsiveSlot.narrowViewport(checkboxState) })] })) : (
33
+ /* For function-based renderDetailsSlot or ResponsiveSlot with only base, use single details element */
34
+ responsiveSlot.base && (_jsx("div", { "data-mfui-content": "details", className: cx(classes.details, 'mfui-CheckboxCard__details'), children: responsiveSlot.base(checkboxState) })))] }));
33
35
  });
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Children, isValidElement, useMemo, forwardRef } from 'react';
3
+ import { Children, isValidElement, useMemo, useRef, forwardRef } from 'react';
4
4
  import { DataTableProvider } from './DataTableProvider';
5
5
  import { dataTableSlotRecipe } from '../../styled-system/recipes';
6
6
  import { DataTableBody } from './DataTableBody';
@@ -13,8 +13,15 @@ import { DataTableRow } from './DataTableRow';
13
13
  import { DataTableSortButton } from './DataTableSortButton';
14
14
  import { cx } from '../../styled-system/css';
15
15
  import { isComponentOrWrapped } from '../utilities/react/isComponentOrWrapped';
16
+ import { mergeRefs } from '../utilities/dom/mergeRefs';
17
+ import { useTableEdgeCellMarkers } from '../utilities/dom/useTableEdgeCellMarkers';
16
18
  const Base = forwardRef(({ className, size = 'medium', enableRowSelection = false, enableRowClickSelection = false, fixedHeader = false, showVerticalBorder = false, leftFixedColumnIndex, rightFixedColumnIndex, layout = 'default', wrapperProps, loading = false, emptyStateSlot, children, ...props }, ref) => {
17
19
  const classes = dataTableSlotRecipe({ layout });
20
+ const tableRef = useRef(null);
21
+ // `:first-child` / `:last-child` cannot tell which grid column a cell renders in, so the
22
+ // table-edge spacing of the cell recipes misfires on the rows a `rowSpan` extends into.
23
+ // This marks the displaced cells so that spacing can be cancelled on them.
24
+ useTableEdgeCellMarkers(tableRef);
18
25
  // Check if DataTable has any rows by looking for DataTable.Body with DataTable.Row children
19
26
  const hasRows = useMemo(() => {
20
27
  if (loading)
@@ -39,7 +46,7 @@ const Base = forwardRef(({ className, size = 'medium', enableRowSelection = fals
39
46
  }, [children, loading]);
40
47
  const shouldShowEmptyState = !hasRows && !!emptyStateSlot && !loading;
41
48
  return (_jsx(DataTableProvider, { size: size, enableRowSelection: enableRowSelection, enableRowClickSelection: enableRowClickSelection, fixedHeader: fixedHeader, showVerticalBorder: showVerticalBorder, leftFixedColumnIndex: leftFixedColumnIndex, rightFixedColumnIndex: rightFixedColumnIndex, loading: loading, layout: layout, children: _jsxs("div", { ...wrapperProps, className: cx(classes.wrapper, 'mfui-DataTable__wrapper',
42
- /* TODO: Remove in v5.0.0 */ 'mfui-DataGrid__wrapper', wrapperProps?.className), children: [_jsx("table", { ref: ref, className: cx(classes.table, 'mfui-DataTable__table',
49
+ /* TODO: Remove in v5.0.0 */ 'mfui-DataGrid__wrapper', wrapperProps?.className), children: [_jsx("table", { ref: mergeRefs(ref, tableRef), className: cx(classes.table, 'mfui-DataTable__table',
43
50
  /* TODO: Remove in v5.0.0 */ 'mfui-DataGrid__table', className), ...props, children: children }), shouldShowEmptyState ? (_jsx("div", { className: cx(classes.emptyState, 'mfui-DataTable__emptyState',
44
51
  /* TODO: Remove in v5.0.0 */ 'mfui-DataGrid__emptyState'), role: "status", "aria-live": "polite", children: emptyStateSlot })) : null] }) }));
45
52
  });
@@ -9,6 +9,15 @@ import { type DataTableSlotRecipeVariant } from '../../styled-system/recipes';
9
9
  * - `amount`: Right-aligned numeric content (amounts, balances, etc.)
10
10
  */
11
11
  export type CellDataType = 'text' | 'amount';
12
+ /**
13
+ * The function that wraps the built-in selection checkbox of `DataTable.HeaderRow` or `DataTable.Row`.
14
+ *
15
+ * It receives the selection checkbox element and returns the content to render in the checkbox cell.
16
+ * The returned content must contain the passed element, because the row owns the checkbox itself.
17
+ *
18
+ * @param checkbox - The built-in selection checkbox element.
19
+ */
20
+ export type DataTableSelectionCheckboxWrapper = (checkbox: ReactNode) => ReactNode;
12
21
  /**
13
22
  * The props for the `DataTable` component.
14
23
  *
@@ -81,6 +90,9 @@ export type DataTableProps = {
81
90
  * If the fixed column width might be changed with any interaction, such as disclosure, removing row or translating,
82
91
  * please add `table-layout: fixed` to the DataTable and specify the width of the each column.
83
92
  *
93
+ * **Not supported together with merged cells.** The index is counted in source order, which a
94
+ * `colSpan` / `rowSpan` shifts, so the fixed column is resolved on the wrong cells.
95
+ *
84
96
  * @default undefined
85
97
  */
86
98
  leftFixedColumnIndex?: number;
@@ -91,6 +103,9 @@ export type DataTableProps = {
91
103
  * If the fixed column width might be changed with any interaction, such as disclosure, removing row or translating,
92
104
  * please add `table-layout: fixed` to the DataTable and specify the width of the each column.
93
105
  *
106
+ * **Not supported together with merged cells.** The index is counted in source order, which a
107
+ * `colSpan` / `rowSpan` shifts, so the fixed column is resolved on the wrong cells.
108
+ *
94
109
  * @default undefined
95
110
  */
96
111
  rightFixedColumnIndex?: number;
@@ -9,13 +9,15 @@ import { isComponentOrWrapped } from '../../utilities/react/isComponentOrWrapped
9
9
  export const DataTableHeaderRow = forwardRef(({ children, checkboxProps, className, ...props }, ref) => {
10
10
  const { enableRowSelection, setNumberColumns } = useDataTableContext();
11
11
  const classes = dataTableHeaderRowSlotRecipe();
12
+ // Building the element before wrapping keeps everything the header row owns on the Checkbox itself.
13
+ const selectionCheckbox = (_jsx(Checkbox, { checked: checkboxProps?.checked, indeterminate: checkboxProps?.indeterminate, "aria-label": checkboxProps?.['aria-label'] ?? 'すべての行を選択', disabled: checkboxProps?.disabled, onChange: checkboxProps?.onChange, onClick: checkboxProps?.onClick }));
12
14
  // Count the number of header cells
13
15
  useEffect(() => {
14
16
  const headerCells = Children.toArray(children).filter((child) => isValidElement(child) && isComponentOrWrapped(child, DataTableHeaderCell));
15
17
  setNumberColumns(headerCells.length);
16
18
  }, [children, enableRowSelection, setNumberColumns]);
17
19
  return (_jsxs("tr", { className: cx(classes.root, 'mfui-DataTableHeaderRow__root',
18
- /* TODO: Remove in v5.0.0 */ 'mfui-DataGridHeaderRow__root', className), ...props, ref: ref, children: [enableRowSelection ? (_jsx(DataTableHeaderCell, { columnIndex: 0, disabled: checkboxProps?.disabled, children: _jsx(Checkbox, { checked: checkboxProps?.checked, indeterminate: checkboxProps?.indeterminate, "aria-label": checkboxProps?.['aria-label'] ?? 'すべての行を選択', disabled: checkboxProps?.disabled, onChange: checkboxProps?.onChange, onClick: checkboxProps?.onClick }) })) : null, Children.map(children, (child, index) => {
20
+ /* TODO: Remove in v5.0.0 */ 'mfui-DataGridHeaderRow__root', className), ...props, ref: ref, children: [enableRowSelection ? (_jsx(DataTableHeaderCell, { columnIndex: 0, disabled: checkboxProps?.disabled, children: checkboxProps?.wrapper ? checkboxProps.wrapper(selectionCheckbox) : selectionCheckbox })) : null, Children.map(children, (child, index) => {
19
21
  if (!isValidElement(child)) {
20
22
  return child;
21
23
  }
@@ -1,5 +1,6 @@
1
1
  import { type ComponentPropsWithRef } from 'react';
2
2
  import { type CheckboxProps } from '../../Checkbox';
3
+ import { type DataTableSelectionCheckboxWrapper } from '../DataTable.types';
3
4
  /**
4
5
  * The props for the `DataTableHeaderRow` component.
5
6
  *
@@ -24,6 +25,23 @@ export type DataTableHeaderRowProps = {
24
25
  * @property onClick - The handler called when the checkbox is clicked (for event propagation control).
25
26
  *
26
27
  * @property disabled - Whether the checkbox is disabled.
28
+ *
29
+ * @property wrapper - The function that wraps the checkbox, for example with the `Tooltip` component.
30
+ * It receives the checkbox element and must render it as it is, because the header row owns the checkbox itself.
31
+ * Keep the `Tooltip` component with its default `trigger` (`['hover', 'focus']`) so that keyboard users can also
32
+ * read the content; a tooltip opened by a click would duplicate the action of the checkbox.
33
+ * When the content restates the accessible name of the checkbox, pass `hideFromScreenReader` to the `Tooltip`
34
+ * component to avoid announcing the same information twice.
35
+ *
36
+ * ```tsx
37
+ * <DataTable.HeaderRow
38
+ * checkboxProps={{
39
+ * wrapper: (checkbox) => <Tooltip content="すべて選択">{checkbox}</Tooltip>,
40
+ * }}
41
+ * >
42
+ * ```
27
43
  */
28
- checkboxProps?: Pick<CheckboxProps, 'aria-label' | 'checked' | 'indeterminate' | 'onChange' | 'disabled' | 'onClick'>;
44
+ checkboxProps?: Pick<CheckboxProps, 'aria-label' | 'checked' | 'indeterminate' | 'onChange' | 'disabled' | 'onClick'> & {
45
+ wrapper?: DataTableSelectionCheckboxWrapper;
46
+ };
29
47
  } & ComponentPropsWithRef<'tr'>;
@@ -62,6 +62,9 @@ export const DataTableRow = forwardRef(({ children, disabled = false, highlighte
62
62
  const toggleSelectionByRowInteraction = () => {
63
63
  selectionCheckboxRef.current?.click();
64
64
  };
65
+ // Building the element before wrapping keeps everything the row owns on the Checkbox itself, so that the
66
+ // ref-based toggle and the recipe selectors keep working with a consumer-supplied wrapper in between.
67
+ const selectionCheckbox = (_jsx(Checkbox, { ref: selectionCheckboxRef, className: cx(classes.selectionCheckbox, 'mfui-DataTableRow__selectionCheckbox'), checked: checkboxProps?.checked, "aria-label": checkboxProps?.['aria-label'] ?? '行を選択', disabled: disabled || checkboxProps?.disabled, tabIndex: isRowClickSelectable ? -1 : undefined, onChange: checkboxProps?.onChange, onClick: checkboxProps?.onClick }));
65
68
  const handleClickRow = (event) => {
66
69
  if (disabled || isClickFromNestedInteractiveElement(event)) {
67
70
  return;
@@ -84,7 +87,7 @@ export const DataTableRow = forwardRef(({ children, disabled = false, highlighte
84
87
  return (_jsxs("tr", { ref: ref, className: cx(classes.root, 'mfui-DataTableRow__root',
85
88
  /* TODO: Remove in v5.0.0 */ 'mfui-DataGridRow__root', className), ...(ariaExpanded !== undefined && { 'aria-expanded': ariaExpanded }), ...props,
86
89
  // When the row is clickable, the role should be 'button' in order to allow screen readers to understand the row is clickable.
87
- role: isInteractive ? 'button' : 'row', tabIndex: isInteractive ? 0 : undefined, ...(disabled && { 'aria-disabled': disabled }), onClick: handleClickRow, onKeyDown: handleKeyDownRow, children: [isSelectable ? (_jsx(DataTableCell, { columnIndex: 0, isRowHighlighted: isRowHighlighted, disabled: disabled, children: _jsx(Checkbox, { ref: selectionCheckboxRef, className: cx(classes.selectionCheckbox, 'mfui-DataTableRow__selectionCheckbox'), checked: checkboxProps?.checked, "aria-label": checkboxProps?.['aria-label'] ?? '行を選択', disabled: disabled || checkboxProps?.disabled, tabIndex: isRowClickSelectable ? -1 : undefined, onChange: checkboxProps?.onChange, onClick: checkboxProps?.onClick }) })) : null, Children.map(children, (child, index) => {
90
+ role: isInteractive ? 'button' : 'row', tabIndex: isInteractive ? 0 : undefined, ...(disabled && { 'aria-disabled': disabled }), onClick: handleClickRow, onKeyDown: handleKeyDownRow, children: [isSelectable ? (_jsx(DataTableCell, { columnIndex: 0, isRowHighlighted: isRowHighlighted, disabled: disabled, children: checkboxProps?.wrapper ? checkboxProps.wrapper(selectionCheckbox) : selectionCheckbox })) : null, Children.map(children, (child, index) => {
88
91
  if (!isValidElement(child)) {
89
92
  return child;
90
93
  }
@@ -1,5 +1,6 @@
1
1
  import { type ComponentPropsWithRef } from 'react';
2
2
  import { type CheckboxProps } from '../../Checkbox';
3
+ import { type DataTableSelectionCheckboxWrapper } from '../DataTable.types';
3
4
  /**
4
5
  * The props for the `DataTableRow` component.
5
6
  *
@@ -38,8 +39,29 @@ export type DataTableRowProps = {
38
39
  * @property onClick - The handler called when the checkbox is clicked (for event propagation control).
39
40
  *
40
41
  * @property disabled - Whether the checkbox is disabled.
42
+ *
43
+ * @property wrapper - The function that wraps the checkbox, for example with the `Tooltip` component.
44
+ * It receives the checkbox element and must render it as it is, because the row owns the checkbox itself.
45
+ * Keep the `Tooltip` component with its default `trigger` (`['hover', 'focus']`) so that keyboard users can also
46
+ * read the content; a tooltip opened by a click would duplicate the action of the checkbox.
47
+ * When the content restates the accessible name of the checkbox, pass `hideFromScreenReader` to the `Tooltip`
48
+ * component to avoid announcing the same information twice.
49
+ *
50
+ * Note that when the `enableRowClickSelection` prop of the `DataTable` component is `true`, the checkbox is removed
51
+ * from the tab order and the focusable element is the row, which is outside the wrapper. A tooltip in the wrapper is
52
+ * then opened by hover only, so do not put information that keyboard users need in it in that case.
53
+ *
54
+ * ```tsx
55
+ * <DataTable.Row
56
+ * checkboxProps={{
57
+ * wrapper: (checkbox) => <Tooltip content="この行を選択">{checkbox}</Tooltip>,
58
+ * }}
59
+ * >
60
+ * ```
41
61
  */
42
- checkboxProps?: Pick<CheckboxProps, 'aria-label' | 'checked' | 'onChange' | 'disabled' | 'onClick'>;
62
+ checkboxProps?: Pick<CheckboxProps, 'aria-label' | 'checked' | 'onChange' | 'disabled' | 'onClick'> & {
63
+ wrapper?: DataTableSelectionCheckboxWrapper;
64
+ };
43
65
  /**
44
66
  * The callback function that is called when the row is clicked.
45
67
  * If provided, the row can be focused and the focus indicator is displayed.
@@ -1,2 +1,2 @@
1
1
  export * from './DatePickerDateCell';
2
- export * from './DatePickerDateCell.types';
2
+ export type * from './DatePickerDateCell.types';
@@ -1,2 +1 @@
1
1
  export * from './DatePickerDateCell';
2
- export * from './DatePickerDateCell.types';
@@ -57,7 +57,7 @@ export function CalendarGrid({ viewingValue, minDate, maxDate, checkDisabledDate
57
57
  let tabbableDateIndex = -1;
58
58
  // First, try to find the selected date if it exists and is not disabled
59
59
  if (value) {
60
- tabbableDateIndex = newDates.findIndex((dateItem) => dateItem && dateItem.selected && !dateItem.disabled);
60
+ tabbableDateIndex = newDates.findIndex((dateItem) => dateItem?.selected && !dateItem.disabled);
61
61
  }
62
62
  // If no selected date or selected date is disabled, try today's date
63
63
  if (tabbableDateIndex === -1) {
@@ -101,8 +101,6 @@ export function CalendarGrid({ viewingValue, minDate, maxDate, checkDisabledDate
101
101
  .map((_, index) => ((weekStart + index) % 7))
102
102
  .map((weekday) => (_jsx(DayCell, { day: weekday }, weekday))), computedPropsForDates.map((dateItem, index) => {
103
103
  // All props are generated internally and guaranteed to have required fields
104
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
105
- // @ts-ignore
106
104
  if (!dateItem) {
107
105
  return (_jsx("div", { className: cx(classes.emptyDateCell, 'mfui-CalendarGrid__emptyDateCell') }, `empty-date-${String(index)}`));
108
106
  }
@@ -19,6 +19,6 @@ export declare const Dialog: import("react").ForwardRefExoticComponent<{
19
19
  enableAutoUnmount?: boolean;
20
20
  disableBackdropClose?: boolean;
21
21
  customFocusElement?: import("react").RefObject<HTMLElement | null> | HTMLElement | null;
22
- } & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "onClose" | "open"> & {
22
+ } & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "onClose" | "open" | "title"> & {
23
23
  ref?: (import("react").RefCallback<HTMLDivElement> | import("react").RefObject<HTMLDivElement | null> | null) | undefined;
24
24
  } & import("react").Attributes>;
@@ -97,4 +97,4 @@ export type DialogProps = {
97
97
  * ```
98
98
  */
99
99
  customFocusElement?: RefObject<HTMLElement | null> | HTMLElement | null;
100
- } & Omit<ComponentPropsWithoutRef<'div'>, 'open' | 'onClose'>;
100
+ } & Omit<ComponentPropsWithoutRef<'div'>, 'open' | 'onClose' | 'title'>;
@@ -3,4 +3,4 @@ import { type MainNavigationProps } from './MainNavigation.types';
3
3
  * The component for the main navigation on the normal viewport.
4
4
  * This component must only be used in MainNavigation.tsx, do not export it.
5
5
  */
6
- export declare function BaseMainNavigation({ className, featureShortcut, navigationItems, enableCollapsible, nestedNavigationDisplay, width, customLinkComponent, disableResponsive, isCollapsed: controlledIsCollapsed, onCollapsedChange, collapseButtonProps, expandButtonProps, }: MainNavigationProps): import("react/jsx-runtime").JSX.Element;
6
+ export declare function BaseMainNavigation({ className, featureShortcut, navigationItems, navLandmarkLabel, enableCollapsible, nestedNavigationDisplay, width, customLinkComponent, disableResponsive, isCollapsed: controlledIsCollapsed, onCollapsedChange, collapseButtonProps, expandButtonProps, }: MainNavigationProps): import("react/jsx-runtime").JSX.Element;
@@ -13,7 +13,7 @@ import { Popover } from '../Popover';
13
13
  * The component for the main navigation on the normal viewport.
14
14
  * This component must only be used in MainNavigation.tsx, do not export it.
15
15
  */
16
- export function BaseMainNavigation({ className, featureShortcut, navigationItems, enableCollapsible = false, nestedNavigationDisplay = 'accordion', width = 'normal', customLinkComponent, disableResponsive, isCollapsed: controlledIsCollapsed, onCollapsedChange, collapseButtonProps, expandButtonProps, }) {
16
+ export function BaseMainNavigation({ className, featureShortcut, navigationItems, navLandmarkLabel, enableCollapsible = false, nestedNavigationDisplay = 'accordion', width = 'normal', customLinkComponent, disableResponsive, isCollapsed: controlledIsCollapsed, onCollapsedChange, collapseButtonProps, expandButtonProps, }) {
17
17
  const [localIsCollapsed, setLocalIsCollapsed] = useState(controlledIsCollapsed ?? false);
18
18
  const [openPopoverIndex, setOpenPopoverIndex] = useState(null);
19
19
  const isControlled = controlledIsCollapsed !== undefined;
@@ -31,7 +31,7 @@ export function BaseMainNavigation({ className, featureShortcut, navigationItems
31
31
  const toggleButtonLabel = isCollapsed
32
32
  ? (expandButtonProps?.label ?? 'ナビゲーションを拡げる')
33
33
  : (collapseButtonProps?.label ?? 'ナビゲーションを折りたたむ');
34
- return (_jsxs("div", { "data-mfui-collapsed": isCollapsed, className: cx(classes.root, 'mfui-BaseMainNavigation__root', className), children: [featureShortcut ? (_jsx("div", { className: cx(classes.featureShortcut, 'mfui-BaseMainNavigation__featureShortcut'), children: _jsx(FocusIndicator, { children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: featureShortcut.isExternal, href: featureShortcut.href, className: cx(classes.featureShortcutAnchor, 'mfui-BaseMainNavigation__featureShortcutAnchor'), children: _jsx(Typography, { variant: "controlLabel", children: featureShortcut.label }) }) }) })) : null, _jsxs("nav", { id: navId, className: cx(classes.nav, 'mfui-BaseMainNavigation__nav'), children: [_jsx("ul", { className: cx(classes.list, 'mfui-BaseMainNavigation__list'), children: navigationItems.map((navigation, index) => {
34
+ return (_jsxs("div", { "data-mfui-collapsed": isCollapsed, className: cx(classes.root, 'mfui-BaseMainNavigation__root', className), children: [featureShortcut ? (_jsx("div", { className: cx(classes.featureShortcut, 'mfui-BaseMainNavigation__featureShortcut'), children: _jsx(FocusIndicator, { children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: featureShortcut.isExternal, href: featureShortcut.href, className: cx(classes.featureShortcutAnchor, 'mfui-BaseMainNavigation__featureShortcutAnchor'), children: _jsx(Typography, { variant: "controlLabel", children: featureShortcut.label }) }) }) })) : null, _jsxs("nav", { id: navId, "aria-label": navLandmarkLabel, className: cx(classes.nav, 'mfui-BaseMainNavigation__nav'), children: [_jsx("ul", { className: cx(classes.list, 'mfui-BaseMainNavigation__list'), children: navigationItems.map((navigation, index) => {
35
35
  const isGroupLabel = navigation.children && 'isGroupLabel' in navigation && navigation.isGroupLabel;
36
36
  const isGroupMenu = 'isGroupMenu' in navigation && navigation.isGroupMenu;
37
37
  if (isGroupLabel || isGroupMenu) {
@@ -12,11 +12,12 @@ import { NarrowViewportMainNavigation } from './NarrowViewportMainNavigation';
12
12
  * `MainNavigation` is intended to be paired with `GlobalHeader`. If you need a self-contained
13
13
  * layout that already includes both header and navigation features, use `Sidebar` instead.
14
14
  */
15
- export const MainNavigation = forwardRef(({ className, featureShortcut, navigationItems, disableResponsive = false, width = 'normal', customLinkComponent, enableCollapsible, nestedNavigationDisplay, isCollapsed, onCollapsedChange, collapseButtonProps, expandButtonProps, }, ref) => {
15
+ export const MainNavigation = forwardRef(({ className, featureShortcut, navigationItems, navLandmarkLabel = 'メイン', disableResponsive = false, width = 'normal', customLinkComponent, enableCollapsible, nestedNavigationDisplay, isCollapsed, onCollapsedChange, collapseButtonProps, expandButtonProps, }, ref) => {
16
16
  const commonProps = {
17
17
  className,
18
18
  featureShortcut,
19
19
  navigationItems,
20
+ navLandmarkLabel,
20
21
  width,
21
22
  customLinkComponent,
22
23
  };
@@ -186,6 +186,15 @@ export type MainNavigationProps = {
186
186
  * The navigation items.
187
187
  */
188
188
  navigationItems: NavigationItem[];
189
+ /**
190
+ * The accessible name for the navigation landmark, applied as `aria-label` to both the sidebar `<nav>`
191
+ * and the narrow viewport drawer `<nav>`.
192
+ *
193
+ * Avoid including the role name (e.g. 'ナビゲーション'); assistive technology already announces it.
194
+ *
195
+ * @default 'メイン'
196
+ */
197
+ navLandmarkLabel?: string;
189
198
  /**
190
199
  * Whether the navigation is collapsible.
191
200
  *
@@ -13,7 +13,7 @@ import { Portal } from '../Portal';
13
13
  * The component for the main navigation on the narrow viewport.
14
14
  * This component must only be used in MainNavigation.tsx, do not export it.
15
15
  */
16
- export const NarrowViewportMainNavigation = forwardRef(({ className, featureShortcut, navigationItems, width = 'normal', customLinkComponent }, ref) => {
16
+ export const NarrowViewportMainNavigation = forwardRef(({ className, featureShortcut, navigationItems, navLandmarkLabel, width = 'normal', customLinkComponent }, ref) => {
17
17
  const classes = narrowViewportMainNavigationSlotRecipe({ width });
18
18
  const InternalLinkTag = customLinkComponent ?? 'a';
19
19
  const navId = useId();
@@ -48,7 +48,7 @@ export const NarrowViewportMainNavigation = forwardRef(({ className, featureShor
48
48
  }
49
49
  return (_jsx(Portal, { children: _jsxs("div", { ref: dialogRef, role: "dialog", className: cx(classes.root, 'mfui-NarrowViewportMainNavigation__root', className), tabIndex: -1, onKeyDown: handleOnKeyDown, children: [_jsx("div", { "data-mfui-content": "backdrop", className: cx(classes.backdrop, 'mfui-NarrowViewportMainNavigation__backdrop'), onClick: handleCloseMainNavigation }), _jsxs("div", { "data-mfui-content": "inside", className: cx(classes.container, 'mfui-NarrowViewportMainNavigation__container'), onClick: (event) => {
50
50
  event.stopPropagation();
51
- }, children: [featureShortcut ? (_jsx("div", { className: cx(classes.featureShortcut, 'mfui-NarrowViewportMainNavigation__featureShortcut'), children: _jsx(FocusIndicator, { children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: featureShortcut.isExternal, href: featureShortcut.href, className: cx(classes.featureShortcutAnchor, 'mfui-NarrowViewportMainNavigation__featureShortcutAnchor'), onClick: handleCloseMainNavigation, children: _jsx(Typography, { variant: "controlLabel", children: featureShortcut.label }) }) }) })) : null, _jsxs("nav", { className: cx(classes.nav, 'mfui-NarrowViewportMainNavigation__nav'), children: [_jsx("ul", { className: cx(classes.list, 'mfui-NarrowViewportMainNavigation__list'), children: navigationItems.map((navigation, index) => {
51
+ }, children: [featureShortcut ? (_jsx("div", { className: cx(classes.featureShortcut, 'mfui-NarrowViewportMainNavigation__featureShortcut'), children: _jsx(FocusIndicator, { children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: featureShortcut.isExternal, href: featureShortcut.href, className: cx(classes.featureShortcutAnchor, 'mfui-NarrowViewportMainNavigation__featureShortcutAnchor'), onClick: handleCloseMainNavigation, children: _jsx(Typography, { variant: "controlLabel", children: featureShortcut.label }) }) }) })) : null, _jsxs("nav", { "aria-label": navLandmarkLabel, className: cx(classes.nav, 'mfui-NarrowViewportMainNavigation__nav'), children: [_jsx("ul", { className: cx(classes.list, 'mfui-NarrowViewportMainNavigation__list'), children: navigationItems.map((navigation, index) => {
52
52
  const isGroupLabel = navigation.children && 'isGroupLabel' in navigation && navigation.isGroupLabel;
53
53
  if (isGroupLabel) {
54
54
  return null;
@@ -339,29 +339,38 @@ placeholder, emptyMessage, disabled, invalid, required, caution, targetDOMNode,
339
339
  `${String(localSelectedValuesSet.size)}件選択中`) })) : null;
340
340
  const applyControlsNode = enableApplyControls ? (_jsxs(HStack, { gap: "horizontal.0-1of2", children: [_jsx(Button, { ref: cancelButtonRef, size: "small", priority: "secondary", onClick: handleCancelButtonClick, children: cancelButtonProps?.label ?? 'キャンセル' }), _jsx(Button, { ref: applyButtonRef, size: "small", priority: "primary", onClick: handleApplyButtonClick, children: applyButtonProps?.label ?? '適用' })] })) : null;
341
341
  const resolvedNotFoundMessage = typeof notFoundMessage === 'function' ? notFoundMessage(searchText.trim()) : notFoundMessage;
342
+ // Early returns rather than a four-level ternary: the branches are mutually exclusive
343
+ // and each returns a different shape (skeletons, virtualized rows, options, or the
344
+ // empty message).
345
+ const renderListContent = () => {
346
+ if (loading) {
347
+ return Array.from({ length: SKELETON_ITEM_COUNT }).map((_, index) => (_jsx("li", { className: cx(classes.skeletonItem, 'mfui-MultipleSelectBox__skeletonItem'), children: _jsx(Skeleton, {}) }, index)));
348
+ }
349
+ if (isVirtualized && virtualItems.length > 0) {
350
+ return renderVirtualizedItems();
351
+ }
352
+ if (filteredOptions.length > 0) {
353
+ return [
354
+ ...renderNonVirtualizedItems(),
355
+ !isVirtualized && renderInfiniteScrollLoading(),
356
+ !isVirtualized && renderInfiniteScrollError(),
357
+ ].filter(Boolean);
358
+ }
359
+ return [
360
+ _jsx("li", { className: cx(classes.emptyMessage, 'mfui-MultipleSelectBox__emptyMessage'), children: _jsx(Typography, { variant: "body", children: enableSearchOptions && searchText && resolvedNotFoundMessage
361
+ ? resolvedNotFoundMessage
362
+ : options.length > 0
363
+ ? resolvedNotFoundMessage
364
+ : emptyMessage }) }, "empty"),
365
+ !isVirtualized && renderInfiniteScrollError(),
366
+ ].filter(Boolean);
367
+ };
342
368
  const optionsNode = (_jsxs("div", { ref: scrollWrapperRef, className: cx(classes.scrollWrapper, 'mfui-MultipleSelectBox__scrollWrapper'), onScroll: enableInfiniteScroll ? handleInfiniteScroll : undefined, children: [_jsx("ul", { ref: listBoxRef, role: "listbox", id: listBoxId, className: cx(classes.listBox, 'mfui-MultipleSelectBox__listBox'), tabIndex: -1, style: isVirtualized && totalSize > 0
343
369
  ? {
344
370
  height: `${String(totalSize)}px`,
345
371
  position: 'relative',
346
372
  }
347
- : undefined, children: loading
348
- ? Array.from({ length: SKELETON_ITEM_COUNT }).map((_, index) => (_jsx("li", { className: cx(classes.skeletonItem, 'mfui-MultipleSelectBox__skeletonItem'), children: _jsx(Skeleton, {}) }, index)))
349
- : isVirtualized && virtualItems.length > 0
350
- ? renderVirtualizedItems()
351
- : filteredOptions.length > 0
352
- ? [
353
- ...renderNonVirtualizedItems(),
354
- !isVirtualized && renderInfiniteScrollLoading(),
355
- !isVirtualized && renderInfiniteScrollError(),
356
- ].filter(Boolean)
357
- : [
358
- _jsx("li", { className: cx(classes.emptyMessage, 'mfui-MultipleSelectBox__emptyMessage'), children: _jsx(Typography, { variant: "body", children: enableSearchOptions && searchText && resolvedNotFoundMessage
359
- ? resolvedNotFoundMessage
360
- : options.length > 0
361
- ? resolvedNotFoundMessage
362
- : emptyMessage }) }, "empty"),
363
- !isVirtualized && renderInfiniteScrollError(),
364
- ].filter(Boolean) }), isVirtualized ? renderInfiniteScrollLoading() : null, isVirtualized ? renderInfiniteScrollError('div') : null, _jsx(OptionGroupLabelDescriptions, { groups: rendersHiddenGroupLabels ? optionGroupLabels : [] })] }));
373
+ : undefined, children: renderListContent() }), isVirtualized ? renderInfiniteScrollLoading() : null, isVirtualized ? renderInfiniteScrollError('div') : null, _jsx(OptionGroupLabelDescriptions, { groups: rendersHiddenGroupLabels ? optionGroupLabels : [] })] }));
365
374
  const createOptionAreaNode = isCreateOptionVisible ? (_jsxs("div", { className: cx(classes.createOptionArea, 'mfui-MultipleSelectBox__createOptionArea'), children: [_jsxs("button", { "data-mfui-content": "create-new-option", type: "button", className: cx(classes.createOptionButton, 'mfui-MultipleSelectBox__createOptionButton'), disabled: isCreating, onClick: handleCreateOption, children: [_jsx(Add, { "aria-hidden": true }), createOptionProps?.renderLabel?.(searchText.trim()) ?? (_jsxs(_Fragment, { children: [_jsx(Typography, { variant: "strongBody", children: searchText.trim() }), _jsx(Typography, { variant: "body", children: "\u3092\u8FFD\u52A0" })] }))] }), createError ? (_jsx("div", { className: cx(classes.createOptionErrorMessage, 'mfui-MultipleSelectBox__createOptionErrorMessage'), children: _jsx(HelpMessage, { messageType: "error", children: createOptionProps?.renderErrorMessage?.(searchText.trim()) ??
366
375
  `${searchText.trim()}を追加できませんでした。` }) })) : null] })) : null;
367
376
  return (_jsx(Popover, { renderTrigger: ({ setTriggerRef, togglePopover, handleTriggerKeyDown, handleTriggerBlur }) => (_jsx(MultipleSelectBoxTrigger, { ref: triggerRef, wrapperRef: setTriggerRef, selectedOptions: localSelectedOptions, id: id, disabled: disabled, triggerProps: triggerProps, triggerWrapperProps: triggerWrapperProps, name: name,
@@ -1,5 +1,5 @@
1
1
  import { useEffect } from 'react';
2
- import { getDeepActiveElement } from '../utils/getDeepActiveElement';
2
+ import { getDeepActiveElement } from '../../utilities/dom/getDeepActiveElement';
3
3
  import { isSelectableOption } from '../utils/isSelectableOption';
4
4
  /**
5
5
  * Control the focus when the option panel is opened.
@@ -6,5 +6,8 @@ import { type NavigationListItemProps } from './NavigationListItem.types';
6
6
  * If the href prop is provided, the item will be rendered as an anchor element.
7
7
  * If the onClick prop is provided (without href), it will be rendered as a button element.
8
8
  * Otherwise, it will be rendered as a div element and not interactive.
9
+ *
10
+ * The trailing navigation indicator is displayed whenever the item can be activated,
11
+ * that is when either the href or the onClick prop is provided.
9
12
  */
10
13
  export declare const NavigationListItem: import("react").ForwardRefExoticComponent<NavigationListItemProps & import("react").RefAttributes<HTMLLIElement>>;
@@ -16,6 +16,9 @@ const ARIA_DISABLED_PROPS = { 'aria-disabled': true, 'data-mfui-disabled': '' };
16
16
  * If the href prop is provided, the item will be rendered as an anchor element.
17
17
  * If the onClick prop is provided (without href), it will be rendered as a button element.
18
18
  * Otherwise, it will be rendered as a div element and not interactive.
19
+ *
20
+ * The trailing navigation indicator is displayed whenever the item can be activated,
21
+ * that is when either the href or the onClick prop is provided.
19
22
  */
20
23
  export const NavigationListItem = forwardRef(({ title, icon, description, leadingContent, href, target, customLinkComponent, onClick, disabled, className, ...rest }, ref) => {
21
24
  const classes = navigationListItemSlotRecipe();
@@ -26,7 +29,8 @@ export const NavigationListItem = forwardRef(({ title, icon, description, leadin
26
29
  return 'button';
27
30
  return 'div';
28
31
  }, [href, customLinkComponent, onClick]);
29
- const isInteractive = (href !== undefined || onClick !== undefined) && !disabled;
32
+ const isActivatable = href !== undefined || onClick !== undefined;
33
+ const isInteractive = isActivatable && !disabled;
30
34
  const resolvedHref = disabled ? undefined : href;
31
35
  const disabledProps = useMemo(() => {
32
36
  if (!disabled)
@@ -35,6 +39,6 @@ export const NavigationListItem = forwardRef(({ title, icon, description, leadin
35
39
  return BUTTON_DISABLED_PROPS;
36
40
  return ARIA_DISABLED_PROPS;
37
41
  }, [disabled, onClick, href]);
38
- const innerElement = (_jsx(FocusIndicator, { hideWhiteBorder: true, position: "inside", children: _jsxs(Tag, { ...rest, href: resolvedHref, target: target, type: href === undefined && onClick !== undefined ? 'button' : undefined, "data-mfui-focusable": isInteractive, className: cx(classes.root, 'mfui-NavigationListItem__root', className), onClick: disabled ? undefined : onClick, ...disabledProps, children: [leadingContent != null ? (_jsx("span", { className: cx(classes.leading, 'mfui-NavigationListItem__leading'), children: leadingContent })) : null, _jsxs("span", { className: cx(classes.content, 'mfui-NavigationListItem__content'), children: [_jsxs("span", { className: cx(classes.title, 'mfui-NavigationListItem__title'), children: [icon != null ? (_jsx("span", { className: cx(classes.titleIcon, 'mfui-NavigationListItem__titleIcon'), children: icon })) : null, _jsx(Typography, { variant: "contentHeading", children: title })] }), description != null ? _jsx(Typography, { variant: "body", children: description }) : null] }), href !== undefined ? (_jsx("span", { className: cx(classes.trailing, 'mfui-NavigationListItem__trailing'), children: _jsx(NavigationToDetail, { "aria-hidden": true }) })) : null] }) }));
42
+ const innerElement = (_jsx(FocusIndicator, { hideWhiteBorder: true, position: "inside", children: _jsxs(Tag, { ...rest, href: resolvedHref, target: target, type: href === undefined && onClick !== undefined ? 'button' : undefined, "data-mfui-focusable": isInteractive, className: cx(classes.root, 'mfui-NavigationListItem__root', className), onClick: disabled ? undefined : onClick, ...disabledProps, children: [leadingContent != null ? (_jsx("span", { className: cx(classes.leading, 'mfui-NavigationListItem__leading'), children: leadingContent })) : null, _jsxs("span", { className: cx(classes.content, 'mfui-NavigationListItem__content'), children: [_jsxs("span", { className: cx(classes.title, 'mfui-NavigationListItem__title'), children: [icon != null ? (_jsx("span", { className: cx(classes.titleIcon, 'mfui-NavigationListItem__titleIcon'), children: icon })) : null, _jsx(Typography, { variant: "contentHeading", children: title })] }), description != null ? _jsx(Typography, { variant: "body", children: description }) : null] }), isActivatable ? (_jsx("span", { className: cx(classes.trailing, 'mfui-NavigationListItem__trailing'), children: _jsx(NavigationToDetail, { "aria-hidden": true }) })) : null] }) }));
39
43
  return _jsx("li", { ref: ref, children: innerElement });
40
44
  });
@@ -25,7 +25,8 @@ type ButtonProps = {
25
25
  customLinkComponent?: undefined;
26
26
  /**
27
27
  * The click handler for the navigation item.
28
- * When provided without href, the item is rendered as a button element.
28
+ * When provided without href, the item is rendered as a button element
29
+ * and displays the same trailing navigation indicator as an anchor item.
29
30
  */
30
31
  onClick: ComponentPropsWithoutRef<'button'>['onClick'];
31
32
  /**
@@ -5,8 +5,13 @@ import React from 'react';
5
5
  * and ignores invalid characters.
6
6
  *
7
7
  * The `onChange` callback receives the raw numeric string without commas.
8
+ *
9
+ * `maxIntegerDigits` and `decimalPlaces` cap how much the user can enter. Edits that would
10
+ * exceed either limit are silently rejected, so state the limit in an adjacent `HelpMessage`.
8
11
  */
9
12
  export declare const NumberBox: React.ForwardRefExoticComponent<Omit<import("..").TextBoxProps, "onChange" | "prefixSlot" | "suffixSlot"> & {
10
13
  onChange?: (value: string) => void;
11
14
  textAlign?: 'left' | 'right';
15
+ maxIntegerDigits?: number;
16
+ decimalPlaces?: number;
12
17
  } & React.RefAttributes<HTMLInputElement>>;