@moneyforward/mfui-components 4.3.0 → 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.
- package/dist/src/Accordion/Accordion.d.ts +21 -1
- package/dist/src/Accordion/Accordion.js +28 -4
- package/dist/src/Accordion/Accordion.types.d.ts +21 -0
- package/dist/src/Breadcrumbs/Breadcrumbs.js +1 -1
- package/dist/src/CheckboxCard/CheckboxCard.js +3 -1
- package/dist/src/DataTable/DataTable.js +9 -2
- package/dist/src/DataTable/DataTable.types.d.ts +15 -0
- package/dist/src/DataTable/DataTableHeaderRow/DataTableHeaderRow.js +3 -1
- package/dist/src/DataTable/DataTableHeaderRow/DataTableHeaderRow.types.d.ts +19 -1
- package/dist/src/DataTable/DataTableRow/DataTableRow.js +4 -1
- package/dist/src/DataTable/DataTableRow/DataTableRow.types.d.ts +23 -1
- package/dist/src/DateTimeSelection/DatePicker/DatePickerDateCell/index.d.ts +1 -1
- package/dist/src/DateTimeSelection/DatePicker/DatePickerDateCell/index.js +0 -1
- package/dist/src/DateTimeSelection/shared/CalendarGrid/CalendarGrid.js +1 -3
- package/dist/src/DateTimeSelection/shared/CalendarGrid/utilities/computeDataForStyling.js +2 -4
- package/dist/src/Dialog/Dialog.d.ts +1 -1
- package/dist/src/Dialog/Dialog.types.d.ts +1 -1
- package/dist/src/FileDropZone/FileDropZone.d.ts +5 -1
- package/dist/src/FileDropZone/FileDropZone.js +26 -6
- package/dist/src/FileDropZone/FileDropZone.types.d.ts +10 -0
- package/dist/src/MainNavigation/BaseMainNavigation.d.ts +1 -1
- package/dist/src/MainNavigation/BaseMainNavigation.js +2 -2
- package/dist/src/MainNavigation/MainNavigation.js +2 -1
- package/dist/src/MainNavigation/MainNavigation.types.d.ts +9 -0
- package/dist/src/MainNavigation/NarrowViewportMainNavigation.js +2 -2
- package/dist/src/MultipleSelectBox/MultipleSelectBox.js +27 -18
- package/dist/src/MultipleSelectBox/hooks/useInitialFocusOnOptionPanelOpen.js +1 -1
- package/dist/src/NavigationList/NavigationListItem/NavigationListItem.d.ts +3 -0
- package/dist/src/NavigationList/NavigationListItem/NavigationListItem.js +6 -2
- package/dist/src/NavigationList/NavigationListItem/NavigationListItem.types.d.ts +2 -1
- package/dist/src/NumberBox/NumberBox.d.ts +5 -0
- package/dist/src/NumberBox/NumberBox.js +55 -3
- package/dist/src/NumberBox/NumberBox.types.d.ts +35 -0
- package/dist/src/Pagination/Pagination.js +4 -1
- package/dist/src/Popover/Popover.js +3 -3
- package/dist/src/RadioButtonCard/RadioButtonCard.js +3 -1
- package/dist/src/SelectBox/SelectBox.js +20 -11
- package/dist/src/SelectBox/utils/getFlatOptions.js +1 -1
- package/dist/src/SidePane/SidePane.js +87 -7
- package/dist/src/SidePane/SidePane.types.d.ts +39 -0
- package/dist/src/SidePane/hooks/useSidePaneStateController.js +3 -5
- package/dist/src/SplitView/utils/parseSize.d.ts +1 -1
- package/dist/src/SplitView/utils/parseSize.js +4 -2
- package/dist/src/SubNavigation/SubNavigation.d.ts +10 -1
- package/dist/src/SubNavigation/SubNavigation.js +14 -3
- package/dist/src/SubNavigation/SubNavigation.types.d.ts +27 -1
- package/dist/src/Tabs/TabContext.d.ts +3 -1
- package/dist/src/Tabs/TabContext.js +1 -0
- package/dist/src/Tabs/TabItem/TabItem.js +14 -3
- package/dist/src/Tabs/Tabs.js +5 -21
- package/dist/src/Tabs/hooks/useTabRegistry.d.ts +24 -0
- package/dist/src/Tabs/hooks/useTabRegistry.js +130 -0
- package/dist/src/ToggleSwitch/index.d.ts +1 -1
- package/dist/src/ToggleSwitch/index.js +0 -1
- package/dist/src/Tooltip/hooks/useOpenStateDelayed.js +2 -2
- package/dist/src/utilities/dom/containsAcrossShadowRoots.d.ts +21 -0
- package/dist/src/utilities/dom/containsAcrossShadowRoots.js +33 -0
- package/dist/src/utilities/dom/setInputFiles.d.ts +21 -0
- package/dist/src/utilities/dom/setInputFiles.js +41 -0
- package/dist/src/utilities/dom/toFileList.d.ts +20 -0
- package/dist/src/utilities/dom/toFileList.js +26 -0
- package/dist/src/utilities/dom/useFixedColumns.js +4 -2
- package/dist/src/utilities/dom/useFocusTrap.js +1 -1
- package/dist/src/utilities/dom/useOnClickOutside.js +3 -1
- package/dist/src/utilities/dom/useTableEdgeCellMarkers.d.ts +56 -0
- package/dist/src/utilities/dom/useTableEdgeCellMarkers.js +223 -0
- package/dist/styled-system/recipes/data-table-cell-slot-recipe.js +4 -2
- package/dist/styled-system/types/composition.d.ts +1 -1
- package/dist/styles.css +20 -4
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -3
- /package/dist/src/{MultipleSelectBox/utils → utilities/dom}/getDeepActiveElement.d.ts +0 -0
- /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
|
|
46
|
+
const isControlled = open !== undefined;
|
|
47
|
+
const isOpen = open ?? openItems[id] ?? false;
|
|
47
48
|
const toggle = useCallback(() => {
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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';
|
|
@@ -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
|
|
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
|
}
|
|
@@ -14,9 +14,8 @@ export function computeDataForStyling(viewingValue, weekStart) {
|
|
|
14
14
|
const firstDateDay = viewingValue.getDay();
|
|
15
15
|
// The last column of the grid, i.e. the day right before the week starts over
|
|
16
16
|
const weekEnd = (weekStart + 6) % 7;
|
|
17
|
-
const datesHasBorderTopLeftRadius = new Set();
|
|
18
17
|
// First date of the month has borderTopLeftRadius
|
|
19
|
-
datesHasBorderTopLeftRadius
|
|
18
|
+
const datesHasBorderTopLeftRadius = new Set([1]);
|
|
20
19
|
// First date sitting at the start of a row has borderTopLeftRadius
|
|
21
20
|
const firstRowStartDate = firstDate + ((weekStart - firstDateDay + 7) % 7);
|
|
22
21
|
datesHasBorderTopLeftRadius.add(firstRowStartDate);
|
|
@@ -29,9 +28,8 @@ export function computeDataForStyling(viewingValue, weekStart) {
|
|
|
29
28
|
// Last date sitting at the start of a row has borderBottomLeftRadius
|
|
30
29
|
const lastRowStartDate = lastDate.getDate() - ((lastDate.getDay() - weekStart + 7) % 7);
|
|
31
30
|
datesHasBorderBottomLeftRadius.add(lastRowStartDate);
|
|
32
|
-
const datesHasBorderBottomRightRadius = new Set();
|
|
33
31
|
// Last date of the month has borderBottomRightRadius
|
|
34
|
-
datesHasBorderBottomRightRadius
|
|
32
|
+
const datesHasBorderBottomRightRadius = new Set([lastDate.getDate()]);
|
|
35
33
|
// Last date sitting at the end of a row has borderBottomRightRadius
|
|
36
34
|
const lastRowEndDate = lastDate.getDate() - ((lastDate.getDay() - weekEnd + 7) % 7);
|
|
37
35
|
datesHasBorderBottomRightRadius.add(lastRowEndDate);
|
|
@@ -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>;
|
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
* - **Controlled/uncontrolled modes**: Proper state management for both patterns
|
|
16
16
|
* - **Provide 'onClear' (user-initiated only)**: Clear the file selection
|
|
17
17
|
* - **Provide 'onValueCleared' (external changes only)**: Called when controlled value transitions from non-empty to empty
|
|
18
|
-
* - **Drag and drop support**: Visual feedback and enhanced file selection UX
|
|
18
|
+
* - **Drag and drop support**: Visual feedback and enhanced file selection UX.
|
|
19
|
+
* Dropped files are assigned to the underlying `<input type="file">`, so they are included in
|
|
20
|
+
* native form submission (`new FormData(form)`) just like files chosen through the dialog.
|
|
21
|
+
* Without `multiple`, only the first dropped file is kept, matching what the dialog allows.
|
|
22
|
+
* In controlled mode the input is reset after every selection by design, for both paths.
|
|
19
23
|
*
|
|
20
24
|
* ## Internationalization
|
|
21
25
|
* This component supports internationalization through text override props following the MFUI Component Internationalization Strategy.
|
|
@@ -8,6 +8,8 @@ import { ClearButton } from '../shared/ClearButton';
|
|
|
8
8
|
import { HelpMessage } from '../HelpMessage';
|
|
9
9
|
import { Button } from '../Button';
|
|
10
10
|
import { mergeRefs } from '../utilities/dom/mergeRefs';
|
|
11
|
+
import { setInputFiles } from '../utilities/dom/setInputFiles';
|
|
12
|
+
import { toFileList } from '../utilities/dom/toFileList';
|
|
11
13
|
import { useIsNarrowViewport } from '../utilities/responsive/useIsNarrowViewport';
|
|
12
14
|
/**
|
|
13
15
|
* The component for selecting a single file or multiple files for upload by dialog or drag and drop.
|
|
@@ -26,7 +28,11 @@ import { useIsNarrowViewport } from '../utilities/responsive/useIsNarrowViewport
|
|
|
26
28
|
* - **Controlled/uncontrolled modes**: Proper state management for both patterns
|
|
27
29
|
* - **Provide 'onClear' (user-initiated only)**: Clear the file selection
|
|
28
30
|
* - **Provide 'onValueCleared' (external changes only)**: Called when controlled value transitions from non-empty to empty
|
|
29
|
-
* - **Drag and drop support**: Visual feedback and enhanced file selection UX
|
|
31
|
+
* - **Drag and drop support**: Visual feedback and enhanced file selection UX.
|
|
32
|
+
* Dropped files are assigned to the underlying `<input type="file">`, so they are included in
|
|
33
|
+
* native form submission (`new FormData(form)`) just like files chosen through the dialog.
|
|
34
|
+
* Without `multiple`, only the first dropped file is kept, matching what the dialog allows.
|
|
35
|
+
* In controlled mode the input is reset after every selection by design, for both paths.
|
|
30
36
|
*
|
|
31
37
|
* ## Internationalization
|
|
32
38
|
* This component supports internationalization through text override props following the MFUI Component Internationalization Strategy.
|
|
@@ -160,15 +166,29 @@ fileSelectButtonLabel = 'ファイルを選択', instructionText, fileCountLabel
|
|
|
160
166
|
}
|
|
161
167
|
const { files } = event.dataTransfer;
|
|
162
168
|
if (files.length > 0) {
|
|
163
|
-
|
|
169
|
+
/*
|
|
170
|
+
* The drop is handled on the container instead of the input element, so the browser
|
|
171
|
+
* applies none of the input's own behaviour to it: it neither assigns the dropped files
|
|
172
|
+
* to the input nor honours the `multiple` attribute. Copying the files over keeps native
|
|
173
|
+
* form submission (`new FormData(form)`) working for the drag and drop path, and keeping
|
|
174
|
+
* only the first file when `multiple` is not set prevents a selection that the file
|
|
175
|
+
* selection dialog could never produce for the same markup.
|
|
176
|
+
*/
|
|
177
|
+
const droppedFiles = [...files];
|
|
178
|
+
const acceptedFiles = multiple ? droppedFiles : droppedFiles.slice(0, 1);
|
|
179
|
+
const input = inputRef.current;
|
|
180
|
+
const isSynced = input !== null && setInputFiles(input, acceptedFiles);
|
|
181
|
+
/*
|
|
182
|
+
* Create a synthetic change event to reuse existing logic. When the files could not be
|
|
183
|
+
* copied over, `target` stands in for the input and carries them directly, so a drop
|
|
184
|
+
* still updates the visible selection.
|
|
185
|
+
*/
|
|
164
186
|
const syntheticEvent = {
|
|
165
|
-
target: {
|
|
166
|
-
files,
|
|
167
|
-
},
|
|
187
|
+
target: isSynced ? input : { files: toFileList(acceptedFiles) },
|
|
168
188
|
};
|
|
169
189
|
handleChange(syntheticEvent);
|
|
170
190
|
}
|
|
171
|
-
}, [disabled, handleChange]);
|
|
191
|
+
}, [disabled, multiple, handleChange]);
|
|
172
192
|
const handleClear = useCallback(() => {
|
|
173
193
|
if (!isControlled) {
|
|
174
194
|
setInternalValues(undefined);
|
|
@@ -98,6 +98,16 @@ export type FileDropZoneProps = {
|
|
|
98
98
|
* The callback function to be called when files are selected through dialog or drag-and-drop.
|
|
99
99
|
* This is the standard HTML input onChange event.
|
|
100
100
|
*
|
|
101
|
+
* **Note:** For the drag-and-drop path the event is a synthetic object rather than a React event.
|
|
102
|
+
* Its `target` is the underlying `<input type="file">`, which the dropped files are assigned to
|
|
103
|
+
* beforehand, but other event members such as `currentTarget` are not populated. In the rare
|
|
104
|
+
* environments where that assignment is not possible, `target` is a plain `{ files }` object
|
|
105
|
+
* instead and the dropped files do not reach native form submission.
|
|
106
|
+
*
|
|
107
|
+
* **Note:** A drop lands on the container rather than on the input, so the `multiple` attribute
|
|
108
|
+
* is applied by this component instead of the browser. Without `multiple`, only the first
|
|
109
|
+
* dropped file is passed on.
|
|
110
|
+
*
|
|
101
111
|
* @param event - The change event from the file input element
|
|
102
112
|
*
|
|
103
113
|
* @default undefined
|
|
@@ -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;
|