@moneyforward/mfui-components 3.2.0 → 3.4.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 (33) hide show
  1. package/dist/src/DateTimeSelection/shared/BasePicker/BasePicker.js +2 -2
  2. package/dist/src/DateTimeSelection/shared/CalendarGrid/CalendarGrid.js +3 -1
  3. package/dist/src/DisplayTable/DisplayTable.d.ts +3 -2
  4. package/dist/src/DisplayTable/DisplayTable.js +4 -3
  5. package/dist/src/DisplayTable/DisplayTable.types.d.ts +7 -0
  6. package/dist/src/DisplayTable/DisplayTableBody/DisplayTableBody.js +15 -0
  7. package/dist/src/DisplayTable/DisplayTableCell/DisplayTableCell.js +9 -2
  8. package/dist/src/DisplayTable/DisplayTableHeaderRow/DisplayTableHeaderRow.js +12 -1
  9. package/dist/src/DisplayTable/DisplayTableProvider.d.ts +8 -4
  10. package/dist/src/DisplayTable/DisplayTableProvider.js +12 -3
  11. package/dist/src/DropdownMenu/DropdownMenuItem/DropdownMenuItem.d.ts +1 -1
  12. package/dist/src/DropdownMenu/DropdownMenuItem/DropdownMenuItem.js +16 -4
  13. package/dist/src/DropdownMenu/DropdownMenuItem/DropdownMenuItem.types.d.ts +12 -0
  14. package/dist/src/MainNavigation/BaseMainNavigation.js +1 -1
  15. package/dist/src/MainNavigation/MainNavigation.types.d.ts +8 -0
  16. package/dist/src/MainNavigation/NarrowViewportMainNavigation.js +1 -1
  17. package/dist/src/MultipleSelectBox/MultipleSelectBox.d.ts +1 -1
  18. package/dist/src/MultipleSelectBox/MultipleSelectBox.js +65 -15
  19. package/dist/src/MultipleSelectBox/MultipleSelectBox.types.d.ts +86 -0
  20. package/dist/src/SelectBox/SelectBox.js +4 -4
  21. package/dist/src/SelectBox/SelectBox.types.d.ts +2 -2
  22. package/dist/src/Tag/Tag.js +1 -1
  23. package/dist/styled-system/recipes/display-table-cell-slot-recipe.d.ts +1 -1
  24. package/dist/styled-system/recipes/display-table-cell-slot-recipe.js +4 -0
  25. package/dist/styled-system/recipes/multiple-select-box-slot-recipe.d.ts +1 -1
  26. package/dist/styled-system/recipes/multiple-select-box-slot-recipe.js +24 -0
  27. package/dist/styled-system/recipes/select-box-slot-recipe.d.ts +1 -1
  28. package/dist/styled-system/recipes/select-box-slot-recipe.js +8 -0
  29. package/dist/styles.css +82 -22
  30. package/dist/tsconfig.build.tsbuildinfo +1 -1
  31. package/package.json +2 -2
  32. /package/dist/src/{SelectBox/hooks → utilities/dom}/useInfiniteScroll.d.ts +0 -0
  33. /package/dist/src/{SelectBox/hooks → utilities/dom}/useInfiniteScroll.js +0 -0
@@ -83,10 +83,10 @@ export function BasePicker({ targetDOMNode, enableAutoUnmount = true, open = fal
83
83
  textBoxProps.wrapperProps?.onKeyDown?.(event);
84
84
  handleTriggerKeyDown(event);
85
85
  },
86
- onClick: !disableAutoOpen ? handleWrapperClick(openPopover) : undefined,
86
+ onClick: !disableAutoOpen && !disabled ? handleWrapperClick(openPopover) : undefined,
87
87
  style: {
88
88
  ...textBoxProps.wrapperProps?.style,
89
- cursor: 'text',
89
+ cursor: disabled ? 'not-allowed' : 'text',
90
90
  },
91
91
  }, onBlur: (event) => {
92
92
  // First call the original input onBlur handler (contains onChange logic)
@@ -12,11 +12,13 @@ export function CalendarGrid({ viewingValue, minDate, maxDate, checkDisabledDate
12
12
  const dates = useDates(viewingValue);
13
13
  const computedDataForStyles = useMemo(() => computeDataForStyling(viewingValue), [viewingValue]);
14
14
  const computedPropsForDates = useMemo(() => {
15
+ const startOfMinDate = minDate ? new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()) : undefined;
16
+ const startOfMaxDate = maxDate ? new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate()) : undefined;
15
17
  const newDates = dates.map((dateNumber) => {
16
18
  if (!dateNumber)
17
19
  return null;
18
20
  const date = new Date(viewingValue.getFullYear(), viewingValue.getMonth(), dateNumber);
19
- const isDisabledByProps = (minDate && date < minDate) || (maxDate && date > maxDate);
21
+ const isDisabledByProps = (startOfMinDate && date < startOfMinDate) || (startOfMaxDate && date > startOfMaxDate);
20
22
  const isDisabledByCallback = checkDisabledDate?.(date);
21
23
  const disabled = isDisabledByProps || !!isDisabledByCallback;
22
24
  const isSelected = value ? checkIsSameDate(value, date) : false;
@@ -5,7 +5,7 @@ import { DisplayTableHeader } from './DisplayTableHeader';
5
5
  import { DisplayTableHeaderCell } from './DisplayTableHeaderCell';
6
6
  import { DisplayTableHeaderRow } from './DisplayTableHeaderRow';
7
7
  import { DisplayTableRow } from './DisplayTableRow';
8
- declare function Base({ title, titleTag, lead, className, fixedHeader, leftFixedColumnIndex, rightFixedColumnIndex, wrapperProps, ...props }: DisplayTableProps): import("react/jsx-runtime").JSX.Element;
8
+ declare function Base({ title, titleTag, lead, className, fixedHeader, leftFixedColumnIndex, rightFixedColumnIndex, wrapperProps, loading, ...props }: DisplayTableProps): import("react/jsx-runtime").JSX.Element;
9
9
  /**
10
10
  * The `DisplayTable` component.
11
11
  *
@@ -17,12 +17,13 @@ declare function Base({ title, titleTag, lead, className, fixedHeader, leftFixed
17
17
  * @example
18
18
  * ```jsx
19
19
  * <DisplayTable title="My Table" lead="Table lead text">
20
+ * <DisplayTable.Header>
20
21
  * <DisplayTable.HeaderRow>
21
22
  * <DisplayTable.HeaderCell>Header 1</DisplayTable.HeaderCell>
22
23
  * <DisplayTable.HeaderCell>Header 2</DisplayTable.HeaderCell>
23
24
  * </DisplayTable.HeaderRow>
24
25
  * </DisplayTable.Header>
25
- * </DisplayTable.Body>
26
+ * <DisplayTable.Body>
26
27
  * <DisplayTable.Row>
27
28
  * <DisplayTable.Cell>Cell 1</DisplayTable.Cell>
28
29
  * <DisplayTable.Cell>Cell 2</DisplayTable.Cell>
@@ -11,9 +11,9 @@ import { DisplayTableRow } from './DisplayTableRow';
11
11
  import { cx } from '../../styled-system/css';
12
12
  import { Heading } from '../Heading';
13
13
  import { Typography } from '../Typography';
14
- function Base({ title, titleTag = 'h4', lead, className, fixedHeader = false, leftFixedColumnIndex, rightFixedColumnIndex, wrapperProps, ...props }) {
14
+ function Base({ title, titleTag = 'h4', lead, className, fixedHeader = false, leftFixedColumnIndex, rightFixedColumnIndex, wrapperProps, loading = false, ...props }) {
15
15
  const classes = displayTableSlotRecipe({ fixedHeader });
16
- return (_jsx(DisplayTableProvider, { fixedHeader: fixedHeader, leftFixedColumnIndex: leftFixedColumnIndex, rightFixedColumnIndex: rightFixedColumnIndex, children: _jsxs("div", { ...wrapperProps, className: cx(classes.wrapper, 'mfui-DisplayTable__wrapper', wrapperProps?.className), children: [_jsxs("div", { className: cx(classes.header, 'mfui-DisplayTable__header'), children: [_jsx(Heading, { variant: "sectionHeading2", tag: titleTag, className: cx(classes.title, 'mfui-DisplayTable__title'), children: title }), lead ? (_jsx(Typography, { tag: "p", className: cx(classes.lead, 'mfui-DisplayTable__lead'), children: lead })) : null] }), _jsx("div", { className: cx(classes.body, 'mfui-DisplayTable__body'), children: _jsx("table", { className: cx(classes.table, 'mfui-DisplayTable__table', className), ...props }) })] }) }));
16
+ return (_jsx(DisplayTableProvider, { fixedHeader: fixedHeader, leftFixedColumnIndex: leftFixedColumnIndex, rightFixedColumnIndex: rightFixedColumnIndex, loading: loading, children: _jsxs("div", { ...wrapperProps, className: cx(classes.wrapper, 'mfui-DisplayTable__wrapper', wrapperProps?.className), children: [_jsxs("div", { className: cx(classes.header, 'mfui-DisplayTable__header'), children: [_jsx(Heading, { variant: "sectionHeading2", tag: titleTag, className: cx(classes.title, 'mfui-DisplayTable__title'), children: title }), lead ? (_jsx(Typography, { tag: "p", className: cx(classes.lead, 'mfui-DisplayTable__lead'), children: lead })) : null] }), _jsx("div", { className: cx(classes.body, 'mfui-DisplayTable__body'), children: _jsx("table", { className: cx(classes.table, 'mfui-DisplayTable__table', className), "aria-live": "polite", "aria-busy": loading, "aria-label": loading ? 'データを読み込み中' : undefined, ...props }) })] }) }));
17
17
  }
18
18
  /**
19
19
  * The `DisplayTable` component.
@@ -26,12 +26,13 @@ function Base({ title, titleTag = 'h4', lead, className, fixedHeader = false, le
26
26
  * @example
27
27
  * ```jsx
28
28
  * <DisplayTable title="My Table" lead="Table lead text">
29
+ * <DisplayTable.Header>
29
30
  * <DisplayTable.HeaderRow>
30
31
  * <DisplayTable.HeaderCell>Header 1</DisplayTable.HeaderCell>
31
32
  * <DisplayTable.HeaderCell>Header 2</DisplayTable.HeaderCell>
32
33
  * </DisplayTable.HeaderRow>
33
34
  * </DisplayTable.Header>
34
- * </DisplayTable.Body>
35
+ * <DisplayTable.Body>
35
36
  * <DisplayTable.Row>
36
37
  * <DisplayTable.Cell>Cell 1</DisplayTable.Cell>
37
38
  * <DisplayTable.Cell>Cell 2</DisplayTable.Cell>
@@ -74,4 +74,11 @@ export type DisplayTableProps = {
74
74
  * @property className - The class name of the wrapper element.
75
75
  */
76
76
  wrapperProps?: Pick<ComponentPropsWithoutRef<'div'>, 'className'>;
77
+ /**
78
+ * Whether the table is loading.
79
+ * If `true`, four rows with the skeleton cell will be shown instead of the table content.
80
+ *
81
+ * @default false
82
+ */
83
+ loading?: boolean;
77
84
  } & ComponentPropsWithoutRef<'table'>;
@@ -1,7 +1,22 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
2
3
  import { displayTableBodySlotRecipe } from '../../../styled-system/recipes';
3
4
  import { cx } from '../../../styled-system/css';
5
+ import { useDisplayTableContext } from '../DisplayTableProvider';
6
+ import { DisplayTableRow } from '../DisplayTableRow';
7
+ import { DisplayTableCell } from '../DisplayTableCell';
8
+ const NUMBER_OF_LOADING_ROWS = 4;
4
9
  export function DisplayTableBody({ className, ...props }) {
10
+ const { numberColumns, loading } = useDisplayTableContext();
5
11
  const classes = displayTableBodySlotRecipe();
12
+ // Memoize loading rows to avoid recreating arrays on every render
13
+ const loadingRows = useMemo(() => {
14
+ if (!loading)
15
+ return null;
16
+ return Array.from({ length: NUMBER_OF_LOADING_ROWS }).map((_, rowIndex) => (_jsx(DisplayTableRow, { children: Array.from({ length: numberColumns }).map((_, colIndex) => (_jsx(DisplayTableCell, { columnIndex: colIndex }, colIndex))) }, rowIndex)));
17
+ }, [loading, numberColumns]);
18
+ if (loading) {
19
+ return (_jsx("tbody", { className: cx(classes.root, 'mfui-DisplayTableBody__root', className), ...props, children: loadingRows }));
20
+ }
6
21
  return _jsx("tbody", { className: cx(classes.root, 'mfui-DisplayTableBody__root', className), ...props });
7
22
  }
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { displayTableCellSlotRecipe } from '../../../styled-system/recipes';
3
3
  import { Typography } from '../../Typography';
4
+ import { Skeleton } from '../../Skeleton';
4
5
  import { useDisplayTableContext } from '../DisplayTableProvider';
5
6
  import { cx } from '../../../styled-system/css';
6
7
  import { useFixedColumns } from '../../utilities/dom/useFixedColumns';
7
8
  export function DisplayTableCell({ children, className, depth = '0', columnIndex, style, type = 'text', isTotal = false, ...props }) {
8
- const { leftFixedColumnIndex, rightFixedColumnIndex } = useDisplayTableContext();
9
+ const { leftFixedColumnIndex, rightFixedColumnIndex, loading } = useDisplayTableContext();
9
10
  const { cellRef, isFixedColumn, isEdgeFixedColumn, fixedPositionStyles } = useFixedColumns({
10
11
  columnIndex,
11
12
  leftFixedColumnIndex,
@@ -27,5 +28,11 @@ export function DisplayTableCell({ children, className, depth = '0', columnIndex
27
28
  // Row headers are fixed columns on the left side
28
29
  const isRowHeader = isFixedColumn && isEdgeFixedColumn === 'left';
29
30
  const Tag = isRowHeader ? 'th' : 'td';
30
- return (_jsx(Tag, { ref: cellRef, className: cx(classes.root, 'mfui-DisplayTableCell__root', className), style: { ...style, ...fixedPositionStyles }, ...props, children: _jsx(Typography, { variant: getTypographyVariant(), children: children }) }));
31
+ const cellContent = (() => {
32
+ if (loading) {
33
+ return (_jsx("div", { className: cx(classes.skeletonCell, 'mfui-DisplayTableCell__skeletonCell'), children: _jsx(Skeleton, {}) }));
34
+ }
35
+ return _jsx(Typography, { variant: getTypographyVariant(), children: children });
36
+ })();
37
+ return (_jsx(Tag, { ref: cellRef, className: cx(classes.root, 'mfui-DisplayTableCell__root', className), style: { ...style, ...fixedPositionStyles }, ...props, children: cellContent }));
31
38
  }
@@ -1,11 +1,22 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Children, cloneElement, isValidElement } from 'react';
2
+ import { Children, cloneElement, isValidElement, useMemo, useEffect } from 'react';
3
3
  import { displayTableHeaderRowSlotRecipe } from '../../../styled-system/recipes';
4
4
  import { DisplayTableHeaderCell } from '../DisplayTableHeaderCell';
5
5
  import { cx } from '../../../styled-system/css';
6
6
  import { isComponentOrWrapped } from '../../utilities/react/isComponentOrWrapped';
7
+ import { useDisplayTableContext } from '../DisplayTableProvider';
7
8
  export function DisplayTableHeaderRow({ children, className, ...props }) {
9
+ const { setNumberColumns } = useDisplayTableContext();
8
10
  const classes = displayTableHeaderRowSlotRecipe();
11
+ // Count the number of header cells
12
+ const numberOfColumns = useMemo(() => {
13
+ const headerCells = Children.toArray(children).filter((child) => isValidElement(child) && isComponentOrWrapped(child, DisplayTableHeaderCell));
14
+ return headerCells.length;
15
+ }, [children]);
16
+ // Update context with the number of columns
17
+ useEffect(() => {
18
+ setNumberColumns(numberOfColumns);
19
+ }, [numberOfColumns, setNumberColumns]);
9
20
  return (_jsx("tr", { className: cx(classes.root, 'mfui-DisplayTableHeaderRow__root', className), ...props, children: Children.map(children, (child, index) => {
10
21
  if (!isValidElement(child)) {
11
22
  return child;
@@ -3,20 +3,24 @@ import { type DisplayTableProps } from './DisplayTable.types';
3
3
  /**
4
4
  * The props for the `DisplayTableContext` component.
5
5
  */
6
- export type DisplayTableContextProps = Pick<DisplayTableProps, 'fixedHeader' | 'leftFixedColumnIndex' | 'rightFixedColumnIndex'>;
6
+ export type DisplayTableContextProps = Pick<DisplayTableProps, 'fixedHeader' | 'leftFixedColumnIndex' | 'rightFixedColumnIndex' | 'loading'>;
7
+ export type DisplayTableProviderValues = {
8
+ numberColumns: number;
9
+ setNumberColumns: (number: number) => void;
10
+ } & DisplayTableContextProps;
7
11
  /**
8
12
  * The context for the `DisplayTable` component.
9
13
  *
10
14
  * This should only be used in DisplayTable component.
11
15
  */
12
- export declare const DisplayTableContext: import("react").Context<DisplayTableContextProps>;
16
+ export declare const DisplayTableContext: import("react").Context<DisplayTableProviderValues>;
13
17
  /**
14
18
  * The hook to use the `DisplayTableContext`.
15
19
  */
16
- export declare const useDisplayTableContext: () => DisplayTableContextProps;
20
+ export declare const useDisplayTableContext: () => DisplayTableProviderValues;
17
21
  /**
18
22
  * The provider for the `DisplayTableContext`.
19
23
  *
20
24
  * This should only be used in DisplayTable component.
21
25
  */
22
- export declare function DisplayTableProvider({ children, fixedHeader, leftFixedColumnIndex, rightFixedColumnIndex, }: PropsWithChildren<DisplayTableContextProps>): import("react/jsx-runtime").JSX.Element;
26
+ export declare function DisplayTableProvider({ children, fixedHeader, leftFixedColumnIndex, rightFixedColumnIndex, loading, }: PropsWithChildren<DisplayTableContextProps>): import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { createContext, useContext, useMemo } from 'react';
3
+ import { createContext, useContext, useMemo, useState } from 'react';
4
4
  /**
5
5
  * The context for the `DisplayTable` component.
6
6
  *
@@ -10,6 +10,11 @@ export const DisplayTableContext = createContext({
10
10
  fixedHeader: false,
11
11
  leftFixedColumnIndex: undefined,
12
12
  rightFixedColumnIndex: undefined,
13
+ loading: false,
14
+ numberColumns: 0,
15
+ setNumberColumns: () => {
16
+ // Do nothing
17
+ },
13
18
  });
14
19
  /**
15
20
  * The hook to use the `DisplayTableContext`.
@@ -20,11 +25,15 @@ export const useDisplayTableContext = () => useContext(DisplayTableContext);
20
25
  *
21
26
  * This should only be used in DisplayTable component.
22
27
  */
23
- export function DisplayTableProvider({ children, fixedHeader = false, leftFixedColumnIndex, rightFixedColumnIndex, }) {
28
+ export function DisplayTableProvider({ children, fixedHeader = false, leftFixedColumnIndex, rightFixedColumnIndex, loading = false, }) {
29
+ const [numberColumns, setNumberColumns] = useState(0);
24
30
  const contextValue = useMemo(() => ({
25
31
  fixedHeader,
26
32
  leftFixedColumnIndex,
27
33
  rightFixedColumnIndex,
28
- }), [fixedHeader, leftFixedColumnIndex, rightFixedColumnIndex]);
34
+ loading,
35
+ numberColumns,
36
+ setNumberColumns,
37
+ }), [fixedHeader, leftFixedColumnIndex, rightFixedColumnIndex, loading, numberColumns]);
29
38
  return _jsx(DisplayTableContext.Provider, { value: contextValue, children: children });
30
39
  }
@@ -12,4 +12,4 @@ import { type DropdownMenuItemProps } from './DropdownMenuItem.types';
12
12
  *
13
13
  * Otherwise, it will be rendered as a div element.
14
14
  */
15
- export declare function DropdownMenuItem({ children, className, href, target, customLinkComponent, onClick, 'aria-labelledby': ariaLabelledby, ...rest }: DropdownMenuItemProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function DropdownMenuItem({ children, className, href, target, customLinkComponent, onClick, disabled, 'aria-labelledby': ariaLabelledby, ...rest }: DropdownMenuItemProps): import("react/jsx-runtime").JSX.Element;
@@ -20,7 +20,7 @@ import { useDropdownMenuContext } from '../DropdownMenuContext';
20
20
  *
21
21
  * Otherwise, it will be rendered as a div element.
22
22
  */
23
- export function DropdownMenuItem({ children, className, href, target, customLinkComponent, onClick, 'aria-labelledby': ariaLabelledby, ...rest }) {
23
+ export function DropdownMenuItem({ children, className, href, target, customLinkComponent, onClick, disabled, 'aria-labelledby': ariaLabelledby, ...rest }) {
24
24
  const classes = dropdownMenuItemSlotRecipe();
25
25
  const { closeMenuPopover } = useDropdownMenuContext();
26
26
  const id = useId();
@@ -48,11 +48,13 @@ export function DropdownMenuItem({ children, className, href, target, customLink
48
48
  }
49
49
  }, []);
50
50
  const handleClick = useCallback((event) => {
51
+ if (disabled)
52
+ return;
51
53
  if (onClick) {
52
54
  onClick(event);
53
55
  closeMenuPopover?.();
54
56
  }
55
- }, [onClick, closeMenuPopover]);
57
+ }, [disabled, onClick, closeMenuPopover]);
56
58
  const isButton = useMemo(() => onClick && !href, [onClick, href]);
57
59
  const Tag = useMemo(() => {
58
60
  if (href) {
@@ -66,6 +68,16 @@ export function DropdownMenuItem({ children, className, href, target, customLink
66
68
  // If the href is provided, it should be an anchor element and focusable.
67
69
  // If the href is undefined and onClick is provided, it should be a button element and focusable.
68
70
  // If both are undefined, it should be a div element and not focusable.
69
- const isFocusable = !!href || !!onClick;
70
- return (_jsx(FocusIndicator, { position: "inside", children: _jsx(Tag, { ...rest, ref: itemRef, ...(ariaLabelledby && { id }), "data-mfui-menu-item": true, role: "menuitem", className: cx(classes.root, 'mfui-DropdownMenuItem__root', className), tabIndex: -1, href: href, target: target, "data-mfui-focusable": isFocusable, type: onClick ? 'button' : undefined, "aria-labelledby": ariaLabelledby ? `${ariaLabelledby} ${id}` : undefined, onKeyDown: handleKeyDown, onClick: isButton ? handleClick : undefined, children: typeof children === 'string' ? _jsx(Typography, { variant: "body", children: children }) : children }) }));
71
+ // If disabled is true, the menu item is not focusable.
72
+ const isFocusable = (!!href || !!onClick) && !disabled;
73
+ // Anchor navigation is suppressed when disabled to prevent unintended navigation.
74
+ const resolvedHref = isButton || disabled ? undefined : href;
75
+ // For button elements, use the native disabled attribute for full browser semantics.
76
+ // For anchor and div elements, use aria-disabled and data-disabled since they have no native disabled support.
77
+ const disabledProps = isButton
78
+ ? { disabled }
79
+ : disabled
80
+ ? { 'aria-disabled': true, 'data-disabled': '' }
81
+ : {};
82
+ return (_jsx(FocusIndicator, { position: "inside", children: _jsx(Tag, { ...rest, ref: itemRef, ...(ariaLabelledby && { id }), "data-mfui-menu-item": true, role: "menuitem", className: cx(classes.root, 'mfui-DropdownMenuItem__root', className), tabIndex: -1, href: resolvedHref, target: target, "data-mfui-focusable": isFocusable, type: onClick ? 'button' : undefined, "aria-labelledby": ariaLabelledby ? `${ariaLabelledby} ${id}` : undefined, ...disabledProps, onKeyDown: handleKeyDown, onClick: isButton ? handleClick : undefined, children: typeof children === 'string' ? _jsx(Typography, { variant: "body", children: children }) : children }) }));
71
83
  }
@@ -16,6 +16,10 @@ type AnchorProps = {
16
16
  * This property is ignored if the href property is provided.
17
17
  */
18
18
  onClick?: undefined;
19
+ /**
20
+ * When true, the menu item is disabled and not interactive.
21
+ */
22
+ disabled?: boolean;
19
23
  };
20
24
  type DivProps = {
21
25
  /**
@@ -34,6 +38,10 @@ type DivProps = {
34
38
  * If not provided and the href prop is not provided, the menu item will be rendered as a div element.
35
39
  */
36
40
  onClick?: undefined;
41
+ /**
42
+ * When true, the menu item is disabled and not interactive.
43
+ */
44
+ disabled?: boolean;
37
45
  };
38
46
  type ButtonProps = {
39
47
  /**
@@ -52,6 +60,10 @@ type ButtonProps = {
52
60
  * The handler to call when the menu item is clicked or the Enter key is pressed.
53
61
  */
54
62
  onClick?: ComponentPropsWithoutRef<'button'>['onClick'];
63
+ /**
64
+ * When true, the menu item is disabled and not interactive.
65
+ */
66
+ disabled?: boolean;
55
67
  };
56
68
  /**
57
69
  * The props for the MenuItem component.
@@ -37,7 +37,7 @@ export function BaseMainNavigation({ className, featureShortcut, navigationItems
37
37
  else {
38
38
  setOpenPopoverIndex(null);
39
39
  }
40
- } })) : (_jsxs("details", { open: hasCurrentNavigationInChildren(navigation.children), className: cx(classes.parentDetails, 'mfui-BaseMainNavigation__parentDetails'), children: [_jsx(FocusIndicator, { position: "inside", children: _jsxs("summary", { className: cx(classes.parentSummary, 'mfui-BaseMainNavigation__parentSummary'), children: [_jsx("div", { className: cx(classes.parentSummaryIcon, 'mfui-BaseMainNavigation__parentSummaryIcon'), children: navigation.icon }), _jsx(Typography, { variant: "controlLabel", children: navigation.label }), _jsx(DisclosureBasicCollapsed, { className: "mfui-BaseMainNavigation__icon_collapsed", "aria-hidden": "true", "data-mfui-content": "main-navigation-icon-collapsed" }), _jsx(DisclosureBasicExpanded, { className: "mfui-BaseMainNavigation__icon_expanded", "aria-hidden": "true", "data-mfui-content": "main-navigation-icon-expanded" })] }) }), _jsx("ul", { className: cx(classes.childrenList, 'mfui-BaseMainNavigation__childrenList'), children: navigation.children.map((child, childIndex) => (_jsx("li", { className: cx(classes.childrenListItem, 'mfui-BaseMainNavigation__childrenListItem'), children: _jsx(FocusIndicator, { position: "inside", children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: child.isExternal, href: child.href, className: cx(classes.childrenListItemAnchor, 'mfui-BaseMainNavigation__childrenListItemAnchor'), "aria-current": child.isCurrent ? 'page' : undefined, children: _jsx(Typography, { variant: "controlLabel", children: child.label }) }) }) }, childIndex))) })] }))) : (_jsx(Tooltip, { className: cx(classes.listItemTooltip, 'mfui-BaseMainNavigation__listItemTooltip'), content: navigation.label, disabled: !isCollapsed, placement: "right", children: _jsx(FocusIndicator, { position: "inside", children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: navigation.isExternal, href: navigation.href, className: cx(classes.listItemAnchor, 'mfui-BaseMainNavigation__listItemAnchor'), "aria-label": isCollapsed ? navigation.label : undefined, "aria-current": navigation.isCurrent ? 'page' : undefined, children: _jsxs("div", { className: cx(classes.labelGroup, 'mfui-BaseMainNavigation__labelGroup'), children: [_jsxs("div", { className: cx(classes.iconAndLabel, 'mfui-BaseMainNavigation__iconAndLabel'), children: [_jsx("div", { className: cx(classes.listItemAnchorIcon, 'mfui-BaseMainNavigation__listItemAnchorIcon'), children: navigation.icon }), isCollapsed ? null : _jsx(Typography, { variant: "controlLabel", children: navigation.label })] }), navigation.locked ? (_jsx(Lock, { className: cx(classes.lockIcon, 'mfui-BaseMainNavigation__lockIcon'), "aria-label": navigation.lockIconProps?.['aria-label'] ?? 'ロックされています' })) : null, !isCollapsed && navigation.statusSlot && navigation.locked !== true
40
+ } })) : (_jsxs("details", { open: navigation.isOpenByDefault || hasCurrentNavigationInChildren(navigation.children), className: cx(classes.parentDetails, 'mfui-BaseMainNavigation__parentDetails'), children: [_jsx(FocusIndicator, { position: "inside", children: _jsxs("summary", { className: cx(classes.parentSummary, 'mfui-BaseMainNavigation__parentSummary'), children: [_jsx("div", { className: cx(classes.parentSummaryIcon, 'mfui-BaseMainNavigation__parentSummaryIcon'), children: navigation.icon }), _jsx(Typography, { variant: "controlLabel", children: navigation.label }), _jsx(DisclosureBasicCollapsed, { className: "mfui-BaseMainNavigation__icon_collapsed", "aria-hidden": "true", "data-mfui-content": "main-navigation-icon-collapsed" }), _jsx(DisclosureBasicExpanded, { className: "mfui-BaseMainNavigation__icon_expanded", "aria-hidden": "true", "data-mfui-content": "main-navigation-icon-expanded" })] }) }), _jsx("ul", { className: cx(classes.childrenList, 'mfui-BaseMainNavigation__childrenList'), children: navigation.children.map((child, childIndex) => (_jsx("li", { className: cx(classes.childrenListItem, 'mfui-BaseMainNavigation__childrenListItem'), children: _jsx(FocusIndicator, { position: "inside", children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: child.isExternal, href: child.href, className: cx(classes.childrenListItemAnchor, 'mfui-BaseMainNavigation__childrenListItemAnchor'), "aria-current": child.isCurrent ? 'page' : undefined, children: _jsx(Typography, { variant: "controlLabel", children: child.label }) }) }) }, childIndex))) })] }))) : (_jsx(Tooltip, { className: cx(classes.listItemTooltip, 'mfui-BaseMainNavigation__listItemTooltip'), content: navigation.label, disabled: !isCollapsed, placement: "right", children: _jsx(FocusIndicator, { position: "inside", children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: navigation.isExternal, href: navigation.href, className: cx(classes.listItemAnchor, 'mfui-BaseMainNavigation__listItemAnchor'), "aria-label": isCollapsed ? navigation.label : undefined, "aria-current": navigation.isCurrent ? 'page' : undefined, children: _jsxs("div", { className: cx(classes.labelGroup, 'mfui-BaseMainNavigation__labelGroup'), children: [_jsxs("div", { className: cx(classes.iconAndLabel, 'mfui-BaseMainNavigation__iconAndLabel'), children: [_jsx("div", { className: cx(classes.listItemAnchorIcon, 'mfui-BaseMainNavigation__listItemAnchorIcon'), children: navigation.icon }), isCollapsed ? null : _jsx(Typography, { variant: "controlLabel", children: navigation.label })] }), navigation.locked ? (_jsx(Lock, { className: cx(classes.lockIcon, 'mfui-BaseMainNavigation__lockIcon'), "aria-label": navigation.lockIconProps?.['aria-label'] ?? 'ロックされています' })) : null, !isCollapsed && navigation.statusSlot && navigation.locked !== true
41
41
  ? navigation.statusSlot
42
42
  : null] }) }) }) })) }, index))) }) }), enableCollapsible ? (_jsx("div", { className: cx(classes.footer, 'mfui-BaseMainNavigation__footer'), children: _jsx(Tooltip, { content: toggleButtonLabel, placement: "right", children: _jsx(ToggleButton, { className: cx(classes.toggleButton, 'mfui-BaseMainNavigation__toggleButton'), iconClassName: cx(classes.toggleButtonIcon, 'mfui-BaseMainNavigation__toggleButtonIcon'), isCollapsed: isCollapsed, "aria-label": toggleButtonLabel, targetId: navId, handleClickToggleButton: handleClickToggleButton }) }) })) : null] }));
43
43
  }
@@ -81,6 +81,14 @@ type ParentNavigationItem = {
81
81
  * This property is not allowed for the parent navigation items.
82
82
  */
83
83
  isCurrent?: undefined;
84
+ /**
85
+ * Whether the nested navigation is open by default.
86
+ * When set to true, the parent navigation item is expanded on initial render,
87
+ * regardless of whether any child has `isCurrent: true`.
88
+ *
89
+ * @default false
90
+ */
91
+ isOpenByDefault?: boolean;
84
92
  /**
85
93
  * Nested navigation items.
86
94
  *
@@ -47,5 +47,5 @@ export const NarrowViewportMainNavigation = forwardRef(({ className, featureShor
47
47
  }
48
48
  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) => {
49
49
  event.stopPropagation();
50
- }, 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, _jsx("nav", { className: cx(classes.nav, 'mfui-NarrowViewportMainNavigation__nav'), children: _jsx("ul", { className: cx(classes.list, 'mfui-NarrowViewportMainNavigation__list'), children: navigationItems.map((navigation, index) => (_jsx("li", { className: cx(classes.listItem, 'mfui-NarrowViewportMainNavigation__listItem'), children: navigation.children ? (_jsxs("details", { open: hasCurrentNavigationInChildren(navigation.children), className: cx(classes.parentDetails, 'mfui-NarrowViewportMainNavigation__parentDetails'), children: [_jsx(FocusIndicator, { position: "inside", children: _jsxs("summary", { className: cx(classes.parentSummary, 'mfui-NarrowViewportMainNavigation__parentSummary'), children: [_jsx("div", { className: cx(classes.parentSummaryIcon, 'mfui-NarrowViewportMainNavigation__parentSummaryIcon'), children: navigation.icon }), _jsx(Typography, { variant: "controlLabel", children: navigation.label }), _jsx(DisclosureBasicCollapsed, { className: "mfui-NarrowViewportMainNavigation__icon_collapsed", "aria-hidden": "true", "data-mfui-content": "main-navigation-icon-collapsed" }), _jsx(DisclosureBasicExpanded, { className: "mfui-NarrowViewportMainNavigation__icon_expanded", "aria-hidden": "true", "data-mfui-content": "main-navigation-icon-expanded" })] }) }), _jsx("ul", { className: cx(classes.childrenList, 'mfui-NarrowViewportMainNavigation__childrenList'), children: navigation.children.map((child, childIndex) => (_jsx("li", { className: cx(classes.childrenListItem, 'mfui-NarrowViewportMainNavigation__childrenListItem'), children: _jsx(FocusIndicator, { position: "inside", children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: child.isExternal, href: child.href, className: cx(classes.childrenListItemAnchor, 'mfui-NarrowViewportMainNavigation__childrenListItemAnchor'), "aria-current": child.isCurrent ? 'page' : undefined, onClick: handleCloseMainNavigation, children: _jsx(Typography, { variant: "controlLabel", children: child.label }) }) }) }, childIndex))) })] })) : (_jsx(FocusIndicator, { position: "inside", children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: navigation.isExternal, href: navigation.href, className: cx(classes.listItemAnchor, 'mfui-NarrowViewportMainNavigation__listItemAnchor'), "aria-current": navigation.isCurrent ? 'page' : undefined, children: _jsxs("div", { className: cx(classes.labelGroup, 'mfui-NarrowViewportMainNavigation__labelGroup'), children: [_jsxs("div", { className: cx(classes.iconAndLabel, 'mfui-NarrowViewportMainNavigation__iconAndLabel'), children: [_jsx("div", { className: cx(classes.listItemAnchorIcon, 'mfui-NarrowViewportMainNavigation__listItemAnchorIcon'), children: navigation.icon }), _jsx(Typography, { variant: "controlLabel", children: navigation.label })] }), navigation.locked ? (_jsx(Lock, { className: cx(classes.lockIcon, 'mfui-NarrowViewportMainNavigation__lockIcon'), "aria-label": navigation.lockIconProps?.['aria-label'] ?? 'ロックされています' })) : null] }) }) })) }, index))) }) })] })] }) }));
50
+ }, 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, _jsx("nav", { className: cx(classes.nav, 'mfui-NarrowViewportMainNavigation__nav'), children: _jsx("ul", { className: cx(classes.list, 'mfui-NarrowViewportMainNavigation__list'), children: navigationItems.map((navigation, index) => (_jsx("li", { className: cx(classes.listItem, 'mfui-NarrowViewportMainNavigation__listItem'), children: navigation.children ? (_jsxs("details", { open: navigation.isOpenByDefault || hasCurrentNavigationInChildren(navigation.children), className: cx(classes.parentDetails, 'mfui-NarrowViewportMainNavigation__parentDetails'), children: [_jsx(FocusIndicator, { position: "inside", children: _jsxs("summary", { className: cx(classes.parentSummary, 'mfui-NarrowViewportMainNavigation__parentSummary'), children: [_jsx("div", { className: cx(classes.parentSummaryIcon, 'mfui-NarrowViewportMainNavigation__parentSummaryIcon'), children: navigation.icon }), _jsx(Typography, { variant: "controlLabel", children: navigation.label }), _jsx(DisclosureBasicCollapsed, { className: "mfui-NarrowViewportMainNavigation__icon_collapsed", "aria-hidden": "true", "data-mfui-content": "main-navigation-icon-collapsed" }), _jsx(DisclosureBasicExpanded, { className: "mfui-NarrowViewportMainNavigation__icon_expanded", "aria-hidden": "true", "data-mfui-content": "main-navigation-icon-expanded" })] }) }), _jsx("ul", { className: cx(classes.childrenList, 'mfui-NarrowViewportMainNavigation__childrenList'), children: navigation.children.map((child, childIndex) => (_jsx("li", { className: cx(classes.childrenListItem, 'mfui-NarrowViewportMainNavigation__childrenListItem'), children: _jsx(FocusIndicator, { position: "inside", children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: child.isExternal, href: child.href, className: cx(classes.childrenListItemAnchor, 'mfui-NarrowViewportMainNavigation__childrenListItemAnchor'), "aria-current": child.isCurrent ? 'page' : undefined, onClick: handleCloseMainNavigation, children: _jsx(Typography, { variant: "controlLabel", children: child.label }) }) }) }, childIndex))) })] })) : (_jsx(FocusIndicator, { position: "inside", children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: navigation.isExternal, href: navigation.href, className: cx(classes.listItemAnchor, 'mfui-NarrowViewportMainNavigation__listItemAnchor'), "aria-current": navigation.isCurrent ? 'page' : undefined, children: _jsxs("div", { className: cx(classes.labelGroup, 'mfui-NarrowViewportMainNavigation__labelGroup'), children: [_jsxs("div", { className: cx(classes.iconAndLabel, 'mfui-NarrowViewportMainNavigation__iconAndLabel'), children: [_jsx("div", { className: cx(classes.listItemAnchorIcon, 'mfui-NarrowViewportMainNavigation__listItemAnchorIcon'), children: navigation.icon }), _jsx(Typography, { variant: "controlLabel", children: navigation.label })] }), navigation.locked ? (_jsx(Lock, { className: cx(classes.lockIcon, 'mfui-NarrowViewportMainNavigation__lockIcon'), "aria-label": navigation.lockIconProps?.['aria-label'] ?? 'ロックされています' })) : null] }) }) })) }, index))) }) })] })] }) }));
51
51
  });
@@ -4,4 +4,4 @@ import { type MultipleSelectBoxProps, type AllowedValueTypes } from './MultipleS
4
4
  * This component is separated from the SelectBox component because it has a different behavior.
5
5
  * This component switches the variants of looks and behaviors depends on the props: size, invalid, disabled.
6
6
  */
7
- export declare function MultipleSelectBox<T extends AllowedValueTypes = string, AdditionalProps extends Record<string, unknown> = Record<string, never>>({ id, triggerProps, triggerWrapperProps, size, options, defaultValue, placeholder, emptyMessage, disabled, invalid, targetDOMNode, name, onChange, value, showDisplayValueAsTag, renderDisplayValue, enableSearchOptions, notFoundMessage, searchBoxProps, loading, onSearchOptions, clearButtonProps, disableClearButton, optionPanelProps, enableAllOptionsControls, enableApplyControls, selectAllButtonProps, clearAllButtonProps, selectedCountProps, cancelButtonProps, applyButtonProps, renderOption, onOpenStateChanged, enableAutoUnmount, onBlur, showGroupOptionDivider, enableVirtualization, virtualizationOptions, }: MultipleSelectBoxProps<T, AdditionalProps>): import("react/jsx-runtime").JSX.Element;
7
+ export declare function MultipleSelectBox<T extends AllowedValueTypes = string, AdditionalProps extends Record<string, unknown> = Record<string, never>>({ id, triggerProps, triggerWrapperProps, size, options, defaultValue, placeholder, emptyMessage, disabled, invalid, targetDOMNode, name, onChange, value, showDisplayValueAsTag, renderDisplayValue, enableSearchOptions, notFoundMessage, searchBoxProps, loading, onSearchOptions, clearButtonProps, disableClearButton, optionPanelProps, popoverWrapperProps, enableAllOptionsControls, enableApplyControls, selectAllButtonProps, clearAllButtonProps, selectedCountProps, cancelButtonProps, applyButtonProps, renderOption, onOpenStateChanged, enableAutoUnmount, onBlur, showGroupOptionDivider, enableVirtualization, virtualizationOptions, infiniteScroll, }: MultipleSelectBoxProps<T, AdditionalProps>): import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useCallback, useId, useMemo, useRef } from 'react';
4
- import { CheckboxChecked, CheckboxUnchecked } from '@moneyforward/mfui-icons-react';
4
+ import { CheckboxChecked, CheckboxUnchecked, Error } from '@moneyforward/mfui-icons-react';
5
5
  import { FocusIndicator } from '../FocusIndicator';
6
6
  import { Typography } from '../Typography';
7
7
  import { useFocusTrap } from '../utilities/dom/useFocusTrap';
@@ -13,6 +13,7 @@ import { SearchBox } from '../SearchBox';
13
13
  import { Button } from '../Button';
14
14
  import { HStack, VStack } from '../Stack';
15
15
  import { Skeleton } from '../Skeleton';
16
+ import { ProgressIndicator } from '../ProgressIndicator';
16
17
  import { useInitialFocusOnOptionPanelOpen } from './hooks/useInitialFocusOnOptionPanelOpen';
17
18
  import { useSelectedValues } from './hooks/useSelectedValues';
18
19
  import { useOptionKeyboardNavigation } from './hooks/useOptionKeyboardNavigation';
@@ -23,6 +24,7 @@ import { cx } from '../../styled-system/css';
23
24
  import { flattenOptions } from './utils/flattenOptions';
24
25
  import { isSelectableOption, isOptionDisabled, isOptionSelected } from './utils/isSelectableOption';
25
26
  import { useVirtualizedMultipleSelectBoxOptions } from './hooks/useVirtualizedMultipleSelectBoxOptions';
27
+ import { useInfiniteScroll } from '../utilities/dom/useInfiniteScroll';
26
28
  // How many skeleton items to render while the component is loading.
27
29
  const SKELETON_ITEM_COUNT = 4;
28
30
  /**
@@ -32,12 +34,13 @@ const SKELETON_ITEM_COUNT = 4;
32
34
  */
33
35
  export function MultipleSelectBox({ id, triggerProps, triggerWrapperProps, size, options = [], defaultValue,
34
36
  // eslint-disable-next-line @typescript-eslint/no-deprecated
35
- placeholder, emptyMessage, disabled, invalid, targetDOMNode, name, onChange, value, showDisplayValueAsTag, renderDisplayValue, enableSearchOptions = false, notFoundMessage, searchBoxProps, loading = false, onSearchOptions, clearButtonProps, disableClearButton = false, optionPanelProps, enableAllOptionsControls = false, enableApplyControls = false, selectAllButtonProps, clearAllButtonProps, selectedCountProps, cancelButtonProps, applyButtonProps, renderOption, onOpenStateChanged, enableAutoUnmount = true, onBlur, showGroupOptionDivider, enableVirtualization = false, virtualizationOptions, }) {
37
+ placeholder, emptyMessage, disabled, invalid, targetDOMNode, name, onChange, value, showDisplayValueAsTag, renderDisplayValue, enableSearchOptions = false, notFoundMessage, searchBoxProps, loading = false, onSearchOptions, clearButtonProps, disableClearButton = false, optionPanelProps, popoverWrapperProps, enableAllOptionsControls = false, enableApplyControls = false, selectAllButtonProps, clearAllButtonProps, selectedCountProps, cancelButtonProps, applyButtonProps, renderOption, onOpenStateChanged, enableAutoUnmount = true, onBlur, showGroupOptionDivider, enableVirtualization = false, virtualizationOptions, infiniteScroll, }) {
36
38
  const classes = multipleSelectBoxSlotRecipe({ showGroupOptionDivider });
37
39
  const triggerRef = useRef(null);
38
40
  const listBoxId = useId();
39
41
  const optionPanelRef = useRef(null);
40
42
  const listBoxRef = useRef(null);
43
+ const scrollWrapperRef = useRef(null);
41
44
  const searchInputRef = useRef(null);
42
45
  const selectAllButtonRef = useRef(null);
43
46
  const clearAllButtonRef = useRef(null);
@@ -71,11 +74,30 @@ placeholder, emptyMessage, disabled, invalid, targetDOMNode, name, onChange, val
71
74
  // Initialize virtualization
72
75
  const { virtualizer, virtualItems, totalSize, isVirtualized } = useVirtualizedMultipleSelectBoxOptions({
73
76
  enabled: shouldVirtualize,
74
- parentRef: listBoxRef,
77
+ parentRef: scrollWrapperRef,
75
78
  flatOptions: filteredOptions.filter((option) => 'value' in option),
76
79
  estimateSize: virtualizationOptions?.estimateSize,
77
80
  overscan: virtualizationOptions?.overscan,
78
81
  });
82
+ // Extract infinite scroll configuration with defaults
83
+ const baseEnabledInfiniteScroll = infiniteScroll?.enabled ?? false;
84
+ const onLoadMore = infiniteScroll?.onLoadMore;
85
+ const hasNextPage = infiniteScroll?.hasNextPage ?? true;
86
+ const hasPreviousPage = infiniteScroll?.hasPreviousPage ?? true;
87
+ const infiniteScrollThreshold = infiniteScroll?.threshold ?? 100;
88
+ const infiniteScrollErrorMessage = infiniteScroll?.errorMessage ?? '読み込みに失敗しました';
89
+ const infiniteScrollRetryButtonText = infiniteScroll?.retryButtonText ?? '再読み込み';
90
+ // Initialize infinite scroll
91
+ const { isLoading: isInfiniteScrollLoading, error: infiniteScrollError, handleScroll: handleInfiniteScroll, retryLoad: retryInfiniteScroll, } = useInfiniteScroll({
92
+ onLoadMore,
93
+ hasNextPage,
94
+ hasPreviousPage,
95
+ }, {
96
+ enabled: baseEnabledInfiniteScroll,
97
+ threshold: infiniteScrollThreshold,
98
+ });
99
+ // Disable infinite scroll when there's an error
100
+ const enableInfiniteScroll = baseEnabledInfiniteScroll && !infiniteScrollError;
79
101
  const { temporaryValues, updateTemporaryValues, initializeTemporaryValues, handleCancelButtonClick, handleApplyButtonClick, } = useApplyControls({
80
102
  onValuesChange: updateSelectedValues,
81
103
  closeOptionPanel,
@@ -262,18 +284,46 @@ placeholder, emptyMessage, disabled, invalid, targetDOMNode, name, onChange, val
262
284
  handleOptionFocus,
263
285
  tabbableOptionIndex,
264
286
  ]);
287
+ // Render infinite scroll error message with retry button
288
+ const renderInfiniteScrollError = useCallback(() => infiniteScrollError ? (_jsxs("li", { className: cx(classes.infiniteScrollError, 'mfui-MultipleSelectBox__infiniteScrollError'), role: "alert", children: [_jsxs("div", { className: cx(classes.infiniteScrollErrorMessage, 'mfui-MultipleSelectBox__infiniteScrollErrorMessage'), "aria-live": "polite", children: [_jsx(Error, { "aria-hidden": true, className: cx(classes.infiniteScrollErrorIcon, 'mfui-MultipleSelectBox__infiniteScrollErrorIcon') }), _jsx(Typography, { variant: "body", children: infiniteScrollErrorMessage })] }), _jsx("div", { className: cx(classes.infiniteScrollErrorButton, 'mfui-MultipleSelectBox__infiniteScrollErrorButton'), children: _jsx(Button, { size: "small", onClick: (event) => {
289
+ event.stopPropagation();
290
+ retryInfiniteScroll();
291
+ }, children: infiniteScrollRetryButtonText }) })] })) : null, [
292
+ infiniteScrollError,
293
+ classes.infiniteScrollError,
294
+ classes.infiniteScrollErrorIcon,
295
+ classes.infiniteScrollErrorMessage,
296
+ classes.infiniteScrollErrorButton,
297
+ retryInfiniteScroll,
298
+ infiniteScrollErrorMessage,
299
+ infiniteScrollRetryButtonText,
300
+ ]);
301
+ // Render infinite scroll loading indicator
302
+ const renderInfiniteScrollLoading = useCallback(() => isInfiniteScrollLoading && enableInfiniteScroll ? (_jsx("div", { className: cx(classes.infiniteScrollLoading, 'mfui-MultipleSelectBox__infiniteScrollLoading'), children: _jsx(ProgressIndicator, {}) })) : null, [isInfiniteScrollLoading, enableInfiniteScroll, classes.infiniteScrollLoading]);
265
303
  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,
266
304
  // eslint-disable-next-line @typescript-eslint/no-deprecated
267
- placeholder: placeholder, size: size, listBoxId: listBoxId, isOptionPanelOpen: isOptionPanelOpen, invalid: invalid, showDisplayValueAsTag: showDisplayValueAsTag, renderDisplayValue: renderDisplayValue, clearButtonProps: clearButtonProps, disableClearButton: disableClearButton, updateSelectedValues: updateSelectedValues, onClick: togglePopover, onKeyDown: handleTriggerKeyDown, onBlur: handleTriggerBlur })), contentProps: { className: classes.popover }, minWidth: optionPanelProps?.minWidth, allowedPlacements: optionPanelProps?.allowedPlacements, renderContent: () => (_jsxs("div", { ref: optionPanelRef, className: cx(classes.optionPanel, 'mfui-MultipleSelectBox__optionPanel', optionPanelProps?.className), tabIndex: -1, onKeyDown: handleKeyDownMenu, children: [enableSearchOptions || enableAllOptionsControls ? (_jsxs(VStack, { className: cx(classes.menuHeader, 'mfui-MultipleSelectBox__menuHeader'), gap: "8px", children: [enableSearchOptions ? (_jsx(SearchBox, { ref: searchInputRef, enableClearButton: true, value: searchText, textBoxSize: "small", autoComplete: "off", onChange: onSearchTextChange, ...searchBoxProps })) : null, enableAllOptionsControls ? (_jsxs(HStack, { justifyContent: "space-between", children: [_jsx(Button, { ref: selectAllButtonRef, size: "small", onClick: handleSelectAll, children: selectAllButtonProps?.label ?? 'すべて選択' }), _jsx(Button, { ref: clearAllButtonRef, size: "small", onClick: handleClearAll, children: clearAllButtonProps?.label ?? 'すべて解除' })] })) : null] })) : null, _jsx("ul", { ref: listBoxRef, role: "listbox", id: listBoxId, className: cx(classes.listBox, 'mfui-MultipleSelectBox__listBox'), tabIndex: -1, style: isVirtualized && totalSize > 0
268
- ? {
269
- height: `${String(totalSize)}px`,
270
- position: 'relative',
271
- }
272
- : undefined, children: loading ? (Array.from({ length: SKELETON_ITEM_COUNT }).map((_, index) => (_jsx("li", { className: cx(classes.skeletonItem, 'mfui-MultipleSelectBox__skeletonItem'), children: _jsx(Skeleton, {}) }, index)))) : isVirtualized && virtualItems.length > 0 ? (
273
- // Virtualized rendering with group support
274
- renderVirtualizedItems()) : filteredOptions.length > 0 ? (renderNonVirtualizedItems()) : (_jsx("li", { className: cx(classes.emptyMessage, 'mfui-MultipleSelectBox__emptyMessage'), children: _jsx(Typography, { variant: "body", children: enableSearchOptions && searchText && notFoundMessage
275
- ? notFoundMessage
276
- : options.length > 0
277
- ? notFoundMessage
278
- : emptyMessage }) })) }), enableApplyControls ? (_jsxs(HStack, { className: cx(classes.menuFooter, 'mfui-MultipleSelectBox__menuFooter'), justifyContent: "space-between", alignItems: "center", children: [_jsx(Typography, { variant: "body", color: "neutral.600", children: selectedCountProps?.render?.(temporaryValues.size) ?? `${String(temporaryValues.size)}件選択中` }), _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] })), open: isOptionPanelOpen, targetDOMNode: targetDOMNode, enableAutoUnmount: enableAutoUnmount, onOpenStateChanged: toggleOptionPanel, onBlur: onBlur }));
305
+ placeholder: placeholder, size: size, listBoxId: listBoxId, isOptionPanelOpen: isOptionPanelOpen, invalid: invalid, showDisplayValueAsTag: showDisplayValueAsTag, renderDisplayValue: renderDisplayValue, clearButtonProps: clearButtonProps, disableClearButton: disableClearButton, updateSelectedValues: updateSelectedValues, onClick: togglePopover, onKeyDown: handleTriggerKeyDown, onBlur: handleTriggerBlur })), contentProps: { className: classes.popover }, minWidth: optionPanelProps?.minWidth, maxHeight: popoverWrapperProps?.maxHeight, allowedPlacements: optionPanelProps?.allowedPlacements, renderContent: () => (_jsxs("div", { ref: optionPanelRef, className: cx(classes.optionPanel, 'mfui-MultipleSelectBox__optionPanel', optionPanelProps?.className), tabIndex: -1, onKeyDown: handleKeyDownMenu, children: [enableSearchOptions || enableAllOptionsControls ? (_jsxs(VStack, { className: cx(classes.menuHeader, 'mfui-MultipleSelectBox__menuHeader'), gap: "8px", children: [enableSearchOptions ? (_jsx(SearchBox, { ref: searchInputRef, enableClearButton: true, value: searchText, textBoxSize: "small", autoComplete: "off", onChange: onSearchTextChange, ...searchBoxProps })) : null, enableAllOptionsControls ? (_jsxs(HStack, { justifyContent: "space-between", children: [_jsx(Button, { ref: selectAllButtonRef, size: "small", onClick: handleSelectAll, children: selectAllButtonProps?.label ?? 'すべて選択' }), _jsx(Button, { ref: clearAllButtonRef, size: "small", onClick: handleClearAll, children: clearAllButtonProps?.label ?? 'すべて解除' })] })) : null] })) : null, _jsx("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
306
+ ? {
307
+ height: `${String(totalSize)}px`,
308
+ position: 'relative',
309
+ }
310
+ : undefined, children: loading
311
+ ? Array.from({ length: SKELETON_ITEM_COUNT }).map((_, index) => (_jsx("li", { className: cx(classes.skeletonItem, 'mfui-MultipleSelectBox__skeletonItem'), children: _jsx(Skeleton, {}) }, index)))
312
+ : isVirtualized && virtualItems.length > 0
313
+ ? // Virtualized rendering with group support
314
+ [...renderVirtualizedItems(), renderInfiniteScrollLoading(), renderInfiniteScrollError()].filter(Boolean)
315
+ : filteredOptions.length > 0
316
+ ? [
317
+ ...renderNonVirtualizedItems(),
318
+ renderInfiniteScrollLoading(),
319
+ renderInfiniteScrollError(),
320
+ ].filter(Boolean)
321
+ : [
322
+ _jsx("li", { className: cx(classes.emptyMessage, 'mfui-MultipleSelectBox__emptyMessage'), children: _jsx(Typography, { variant: "body", children: enableSearchOptions && searchText && notFoundMessage
323
+ ? notFoundMessage
324
+ : options.length > 0
325
+ ? notFoundMessage
326
+ : emptyMessage }) }, "empty"),
327
+ renderInfiniteScrollError(),
328
+ ].filter(Boolean) }) }), enableApplyControls ? (_jsxs(HStack, { className: cx(classes.menuFooter, 'mfui-MultipleSelectBox__menuFooter'), justifyContent: "space-between", alignItems: "center", children: [_jsx(Typography, { variant: "body", color: "neutral.600", children: selectedCountProps?.render?.(temporaryValues.size) ?? `${String(temporaryValues.size)}件選択中` }), _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] })), open: isOptionPanelOpen, targetDOMNode: targetDOMNode, enableAutoUnmount: enableAutoUnmount, onOpenStateChanged: toggleOptionPanel, onBlur: onBlur }));
279
329
  }