@moneyforward/mfui-components 3.29.1 → 3.31.1

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 (129) hide show
  1. package/dist/src/Button/Button.d.ts +4 -4
  2. package/dist/src/CheckboxCard/CheckboxCard.d.ts +2 -2
  3. package/dist/src/CheckboxCard/CheckboxCard.js +1 -1
  4. package/dist/src/DataTable/DataTable.js +2 -2
  5. package/dist/src/DataTable/DataTable.types.d.ts +9 -0
  6. package/dist/src/DataTable/DataTableCell/DataTableCell.js +3 -2
  7. package/dist/src/DataTable/DataTableHeaderCell/DataTableHeaderCell.js +2 -1
  8. package/dist/src/DataTable/DataTableProvider.d.ts +2 -2
  9. package/dist/src/DataTable/DataTableProvider.js +4 -1
  10. package/dist/src/DataTable/DataTableRow/DataTableRow.js +34 -3
  11. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerProvider/DateRangePickerProvider.d.ts +2 -2
  12. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerTrigger/constants.d.ts +4 -4
  13. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerTrigger/hooks/useDateRangeTriggerInputClick.d.ts +1 -1
  14. package/dist/src/DateTimeSelection/DateRangePicker/utilities/createDateRangePickerTestUtility.d.ts +1 -1
  15. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerMonthCell/constants.d.ts +5 -5
  16. package/dist/src/DateTimeSelection/TimePicker/TimePicker.d.ts +9 -9
  17. package/dist/src/DateTimeSelection/shared/BasePicker/basePickerUtilities/useBasePickerValueController.d.ts +1 -1
  18. package/dist/src/DateTimeSelection/shared/BasePicker/constants.d.ts +1 -1
  19. package/dist/src/DateTimeSelection/shared/BasePicker/constants.js +2 -0
  20. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerPopover/BaseRangePickerCalendar/constants.d.ts +6 -6
  21. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerProvider/BaseRangePickerProvider.d.ts +2 -2
  22. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/BaseRangePickerTrigger.js +1 -1
  23. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/hooks/useDateRangeTriggerInputClick.d.ts +1 -1
  24. package/dist/src/DateTimeSelection/shared/BaseRangePicker/utilities/createBaseRangePickerTestUtility.d.ts +1 -1
  25. package/dist/src/DateTimeSelection/shared/YearSelector/YearSelector.js +7 -1
  26. package/dist/src/DateTimeSelection/shared/accessibility/constants.d.ts +6 -6
  27. package/dist/src/Dialog/Dialog.d.ts +4 -4
  28. package/dist/src/Disclosure/Disclosure.d.ts +21 -10
  29. package/dist/src/DropdownMenu/DropdownMenu.d.ts +3 -3
  30. package/dist/src/DropdownMenu/DropdownMenu.js +26 -2
  31. package/dist/src/DropdownMenu/DropdownMenuButtonTrigger.d.ts +2 -2
  32. package/dist/src/DropdownMenu/DropdownMenuContainer/DropdownMenuContainer.js +28 -3
  33. package/dist/src/DropdownMenu/DropdownMenuContainer/DropdownMenuContainer.types.d.ts +5 -2
  34. package/dist/src/DropdownMenu/DropdownMenuItem/DropdownMenuItem.d.ts +1 -1
  35. package/dist/src/DropdownMenu/DropdownMenuItem/DropdownMenuItem.js +29 -6
  36. package/dist/src/DropdownMenu/DropdownMenuItem/DropdownMenuItem.types.d.ts +6 -0
  37. package/dist/src/EmbeddedSidePane/EmbeddedSidePane.d.ts +1 -1
  38. package/dist/src/FileBox/FileBox.d.ts +4 -4
  39. package/dist/src/FileBox/FileBox.js +1 -1
  40. package/dist/src/FileDropZone/FileDropZone.d.ts +4 -4
  41. package/dist/src/FileDropZone/utils/fileHelpers.d.ts +3 -3
  42. package/dist/src/FilterTrigger/FilterTrigger.d.ts +2 -2
  43. package/dist/src/Heading/Heading.d.ts +2 -2
  44. package/dist/src/HelpMessage/HelpMessage.d.ts +3 -3
  45. package/dist/src/IconButton/IconButton.d.ts +6 -6
  46. package/dist/src/KeyValue/KeyValue.d.ts +2 -2
  47. package/dist/src/MainNavigation/BaseMainNavigation.d.ts +1 -1
  48. package/dist/src/MainNavigation/BaseMainNavigation.js +4 -2
  49. package/dist/src/MainNavigation/MainNavigation.js +3 -3
  50. package/dist/src/MainNavigation/MainNavigation.types.d.ts +18 -0
  51. package/dist/src/MultipleSelectBox/MultipleSelectBox.js +1 -1
  52. package/dist/src/MultipleSelectBox/hooks/useApplyControls.d.ts +1 -1
  53. package/dist/src/MultipleSelectBox/hooks/useSelectedValues.d.ts +1 -1
  54. package/dist/src/NumberBox/NumberBox.d.ts +1 -1
  55. package/dist/src/NumberBox/NumberBox.js +68 -21
  56. package/dist/src/Pagination/ItemsCount/ItemsCount.js +1 -1
  57. package/dist/src/Popover/Popover.js +1 -1
  58. package/dist/src/RadioButton/RadioButton.d.ts +1 -1
  59. package/dist/src/RadioButtonCard/RadioButtonCard.d.ts +2 -2
  60. package/dist/src/RadioButtonCard/RadioButtonCard.js +1 -1
  61. package/dist/src/RadioGroup/RadioGroup.d.ts +1 -1
  62. package/dist/src/SearchBox/SearchBox.js +18 -4
  63. package/dist/src/SectionMessage/SectionMessage.d.ts +5 -5
  64. package/dist/src/SectionMessage/SectionMessage.js +11 -4
  65. package/dist/src/SectionMessage/SectionMessage.types.d.ts +4 -4
  66. package/dist/src/SelectBox/SelectBox.js +1 -1
  67. package/dist/src/SelectBox/SelectBoxOption.js +1 -1
  68. package/dist/src/SelectBox/SelectBoxOptionGroup.js +1 -1
  69. package/dist/src/SidePane/SidePane.d.ts +9 -7
  70. package/dist/src/SidePane/SidePane.js +8 -4
  71. package/dist/src/SidePane/hooks/useSidePaneStateController.d.ts +7 -2
  72. package/dist/src/SidePane/hooks/useSidePaneStateController.js +11 -5
  73. package/dist/src/Sidebar/Sidebar.d.ts +10 -1
  74. package/dist/src/Sidebar/Sidebar.js +40 -10
  75. package/dist/src/Sidebar/Sidebar.types.d.ts +39 -2
  76. package/dist/src/Sidebar/SidebarContext.d.ts +3 -0
  77. package/dist/src/Sidebar/SidebarContext.js +2 -0
  78. package/dist/src/Sidebar/SidebarMenuHeading/SidebarMenuHeading.d.ts +49 -0
  79. package/dist/src/Sidebar/SidebarMenuHeading/SidebarMenuHeading.js +113 -0
  80. package/dist/src/Sidebar/SidebarMenuHeading/SidebarMenuHeading.types.d.ts +36 -0
  81. package/dist/src/Sidebar/SidebarMenuHeading/SidebarMenuHeading.types.js +1 -0
  82. package/dist/src/Sidebar/SidebarMenuHeading/index.d.ts +2 -0
  83. package/dist/src/Sidebar/SidebarMenuHeading/index.js +1 -0
  84. package/dist/src/Sidebar/SidebarNavigationItem.js +6 -3
  85. package/dist/src/Sidebar/hooks/useSidebarResize.d.ts +2 -1
  86. package/dist/src/Sidebar/hooks/useSidebarResize.js +31 -3
  87. package/dist/src/Sidebar/index.d.ts +2 -1
  88. package/dist/src/Stack/Stack.d.ts +5 -5
  89. package/dist/src/StatusLabel/StatusLabel.d.ts +2 -2
  90. package/dist/src/Stepper/Step/Step.js +1 -1
  91. package/dist/src/SubNavigation/SubNavigation.js +1 -1
  92. package/dist/src/Tag/Tag.d.ts +1 -1
  93. package/dist/src/Tag/Tag.js +2 -2
  94. package/dist/src/Tag/Tag.types.d.ts +7 -0
  95. package/dist/src/TextBox/TextBox.d.ts +6 -6
  96. package/dist/src/TextLink/TextLink.d.ts +2 -2
  97. package/dist/src/Textarea/Textarea.d.ts +4 -4
  98. package/dist/src/Toast/Toast.d.ts +3 -3
  99. package/dist/src/Toast/Toast.js +9 -5
  100. package/dist/src/Toast/Toast.types.d.ts +3 -5
  101. package/dist/src/Toast/api/toast.d.ts +18 -2
  102. package/dist/src/Tooltip/Tooltip.d.ts +3 -3
  103. package/dist/src/Tooltip/Tooltip.js +1 -1
  104. package/dist/src/Tooltip/Tooltip.types.d.ts +2 -0
  105. package/dist/src/Tooltip/hooks/useTooltipDisplayController.js +14 -2
  106. package/dist/src/utilities/dom/dispatchNativeInputValueWithEvent.d.ts +1 -1
  107. package/dist/src/utilities/dom/useFocusTrap.d.ts +1 -1
  108. package/dist/src/utilities/react/useIsomorphicLayoutEffect.d.ts +2 -2
  109. package/dist/styled-system/css/conditions.d.ts +3 -3
  110. package/dist/styled-system/helpers.d.ts +36 -35
  111. package/dist/styled-system/jsx/factory-helper.d.ts +4 -4
  112. package/dist/styled-system/recipes/create-recipe.d.ts +2 -2
  113. package/dist/styled-system/recipes/data-table-cell-slot-recipe.d.ts +1 -0
  114. package/dist/styled-system/recipes/data-table-cell-slot-recipe.js +4 -0
  115. package/dist/styled-system/recipes/data-table-header-cell-slot-recipe.d.ts +1 -0
  116. package/dist/styled-system/recipes/data-table-header-cell-slot-recipe.js +4 -0
  117. package/dist/styled-system/recipes/dropdown-menu-item-slot-recipe.d.ts +1 -1
  118. package/dist/styled-system/recipes/dropdown-menu-item-slot-recipe.js +12 -0
  119. package/dist/styled-system/recipes/index.d.ts +2 -1
  120. package/dist/styled-system/recipes/index.js +1 -0
  121. package/dist/styled-system/recipes/section-message-slot-recipe.d.ts +1 -1
  122. package/dist/styled-system/recipes/section-message-slot-recipe.js +3 -1
  123. package/dist/styled-system/recipes/sidebar-menu-heading-slot-recipe.d.ts +33 -0
  124. package/dist/styled-system/recipes/sidebar-menu-heading-slot-recipe.js +44 -0
  125. package/dist/styled-system/recipes/sidebar-slot-recipe.d.ts +1 -1
  126. package/dist/styled-system/recipes/sidebar-slot-recipe.js +4 -0
  127. package/dist/styles.css +158 -14
  128. package/dist/tsconfig.build.tsbuildinfo +1 -1
  129. package/package.json +11 -11
@@ -8,8 +8,8 @@ import { type RadioButtonCardResponsiveSlot } from './RadioButtonCard.types';
8
8
  * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio
9
9
  */
10
10
  export declare const RadioButtonCard: import("react").ForwardRefExoticComponent<{
11
- layout?: import("../../styled-system/recipes").RadioButtonCardSlotRecipeVariant["layout"];
11
+ layout?: import("../../styled-system/recipes").RadioButtonCardSlotRecipeVariant['layout'];
12
12
  label: string;
13
13
  renderDetailsSlot?: RadioButtonCardResponsiveSlot | import("./RadioButtonCard.types").RadioButtonCardRenderDetailsSlot;
14
- disableResponsive?: import("../../styled-system/recipes").RadioButtonCardSlotRecipeVariant["disableResponsive"];
14
+ disableResponsive?: import("../../styled-system/recipes").RadioButtonCardSlotRecipeVariant['disableResponsive'];
15
15
  } & Omit<import("..").RadioButtonProps, "label"> & import("react").RefAttributes<HTMLInputElement>>;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef, useMemo } from 'react';
3
3
  import { cx } from '../../styled-system/css';
4
4
  import { FocusIndicator } from '../FocusIndicator';
@@ -10,4 +10,4 @@
10
10
  *
11
11
  * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div
12
12
  */
13
- export declare const RadioGroup: import("react").ForwardRefExoticComponent<Pick<import("..").RadioButtonProps, "disabled" | "name" | "value" | "defaultValue" | "onChange"> & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "onChange"> & import("react").RefAttributes<HTMLDivElement>>;
13
+ export declare const RadioGroup: import("react").ForwardRefExoticComponent<Pick<import("..").RadioButtonProps, "defaultValue" | "disabled" | "name" | "onChange" | "value"> & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "onChange"> & import("react").RefAttributes<HTMLDivElement>>;
@@ -14,10 +14,24 @@ import { normalizeFullWidth } from '../utilities/string/normalizeFullWidth';
14
14
  export const SearchBox = forwardRef(({ className, normalizeFullWidth: shouldNormalizeFullWidth = false, onChange, ...rest }, ref) => {
15
15
  const handleChange = useCallback((event) => {
16
16
  if (shouldNormalizeFullWidth) {
17
- // Rewrite the native input so the displayed value reflects the normalization
18
- // and the consumer's onChange sees the canonical half-width / composed-kana form.
19
- // eslint-disable-next-line no-param-reassign
20
- event.target.value = normalizeFullWidth(event.target.value);
17
+ const { value, selectionStart } = event.target;
18
+ const normalizedValue = normalizeFullWidth(value);
19
+ // Only rewrite the native input when normalization actually changes the value.
20
+ // Assigning to `value` resets the caret to the end even when the string is
21
+ // unchanged, so skipping the no-op assignment keeps the caret in place while
22
+ // the user edits mid-string.
23
+ if (normalizedValue !== value) {
24
+ // Map the caret by normalizing only the text before it. This keeps the caret
25
+ // correct even when normalization changes the string length (e.g. `パ` -> `パ`).
26
+ const caretPosition = selectionStart === null
27
+ ? normalizedValue.length
28
+ : normalizeFullWidth(value.slice(0, selectionStart)).length;
29
+ // Rewrite the native input so the displayed value reflects the normalization
30
+ // and the consumer's onChange sees the canonical half-width / composed-kana form.
31
+ // eslint-disable-next-line no-param-reassign
32
+ event.target.value = normalizedValue;
33
+ event.target.setSelectionRange(caretPosition, caretPosition);
34
+ }
21
35
  }
22
36
  onChange?.(event);
23
37
  }, [shouldNormalizeFullWidth, onChange]);
@@ -9,12 +9,12 @@
9
9
  * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div
10
10
  */
11
11
  export declare const SectionMessage: import("react").ForwardRefExoticComponent<{
12
- messageType?: import("../../styled-system/recipes").SectionMessageSlotRecipeVariant["messageType"];
13
- region?: import("../../styled-system/recipes").SectionMessageSlotRecipeVariant["region"];
12
+ messageType?: import("../../styled-system/recipes").SectionMessageSlotRecipeVariant['messageType'];
13
+ region?: import("../../styled-system/recipes").SectionMessageSlotRecipeVariant['region'];
14
14
  actionSlot?: import("react").ReactNode;
15
- onClose?: import("..").IconButtonProps["onClick"];
15
+ onClose?: import("..").IconButtonProps['onClick'];
16
16
  children?: import("react").ReactNode;
17
- statusIconProps?: Pick<import("react").ComponentPropsWithoutRef<"svg">, "aria-label">;
18
- closeButtonProps?: Pick<import("react").ComponentPropsWithoutRef<"svg">, "aria-label">;
17
+ statusIconProps?: Pick<import("react").ComponentPropsWithoutRef<'svg'>, 'aria-label'>;
18
+ closeButtonProps?: Pick<import("react").ComponentPropsWithoutRef<'svg'>, 'aria-label'>;
19
19
  disableResponsive?: boolean;
20
20
  } & Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef } from 'react';
3
- import { Error, Approval, Caution, Close } from '@moneyforward/mfui-icons-react';
3
+ import { Error, Approval, Caution, Close, Pending } from '@moneyforward/mfui-icons-react';
4
4
  import { cx } from '../../styled-system/css';
5
5
  import { sectionMessageSlotRecipe } from '../../styled-system/recipes';
6
6
  import { IconButton } from '../IconButton';
7
+ import { ProgressIndicator } from '../ProgressIndicator';
7
8
  import { Typography } from '../Typography';
8
9
  /**
9
10
  * The SectionMessage component is used to display an inline message inside a page or some container.
@@ -21,11 +22,17 @@ export const SectionMessage = forwardRef(({ messageType = 'neutral', region = 'i
21
22
  region,
22
23
  disableResponsive,
23
24
  });
25
+ // The status is conveyed by the message content itself, so the status icon is decorative
26
+ // and hidden from assistive technology unless a label is explicitly provided.
27
+ const statusIconLabel = statusIconProps?.['aria-label'];
28
+ const statusIconA11yProps = statusIconLabel === undefined ? { 'aria-hidden': true } : { 'aria-label': statusIconLabel };
24
29
  const StatusIcon = {
25
30
  neutral: null,
26
- success: (_jsx(Approval, { "aria-label": statusIconProps?.['aria-label'] ?? '完了', className: cx(classes.statusIcon, 'mfui-SectionMessage__statusIcon') })),
27
- error: (_jsx(Error, { "aria-label": statusIconProps?.['aria-label'] ?? 'エラー', className: cx(classes.statusIcon, 'mfui-SectionMessage__statusIcon') })),
28
- caution: (_jsx(Caution, { "aria-label": statusIconProps?.['aria-label'] ?? '注意', className: cx(classes.statusIcon, 'mfui-SectionMessage__statusIcon') })),
31
+ success: (_jsx(Approval, { ...statusIconA11yProps, className: cx(classes.statusIcon, 'mfui-SectionMessage__statusIcon') })),
32
+ error: _jsx(Error, { ...statusIconA11yProps, className: cx(classes.statusIcon, 'mfui-SectionMessage__statusIcon') }),
33
+ caution: (_jsx(Caution, { ...statusIconA11yProps, className: cx(classes.statusIcon, 'mfui-SectionMessage__statusIcon') })),
34
+ busy: _jsx(Pending, { ...statusIconA11yProps, className: cx(classes.statusIcon, 'mfui-SectionMessage__statusIcon') }),
35
+ progress: (_jsx(ProgressIndicator, { role: "img", ...statusIconA11yProps, className: cx(classes.statusIcon, 'mfui-SectionMessage__statusIcon') })),
29
36
  }[messageType];
30
37
  const contentIsString = typeof children === 'string';
31
38
  return (_jsxs("div", { ref: ref, className: cx(classes.root, 'mfui-SectionMessage__root', className), ...props, role: "alert", children: [_jsxs("div", { className: cx(classes.topSection, 'mfui-SectionMessage__topSection'), children: [_jsxs("div", { className: cx(classes.content, 'mfui-SectionMessage__content'), children: [StatusIcon, contentIsString ? _jsx(Typography, { variant: "body", children: children }) : children] }), _jsxs("div", { className: cx(classes.rightSection, 'mfui-SectionMessage__rightSection'), children: [_jsx("div", { className: cx(classes.actionSlot, classes.desktopActionSlot, 'mfui-SectionMessage__actionSlot'), "data-mfui-content": "SectionMessage-action-slot-desktop", children: actionSlot }), onClose ? (_jsx(IconButton, { "aria-label": closeButtonProps?.['aria-label'] ?? '閉じる', onClick: onClose, children: _jsx(Close, { "aria-hidden": true, className: cx(classes.closeIcon, 'mfui-SectionMessage__closeIcon') }) })) : null] })] }), actionSlot && !disableResponsive ? (_jsx("div", { className: cx(classes.actionSlot, classes.mobileActionSlot, 'mfui-SectionMessage__actionSlot'), "data-mfui-content": "SectionMessage-action-slot-mobile", children: actionSlot })) : null] }));
@@ -35,10 +35,10 @@ export type SectionMessageProps = {
35
35
  /**
36
36
  * Props for the status icon.
37
37
  *
38
- * @property aria-label - The text alternative for the status icon. By default, `messageType` is:
39
- * - `success`: '完了'
40
- * - `error`: 'エラー'
41
- * - `caution`: '注意'
38
+ * @property aria-label - The text alternative for the status icon.
39
+ * By default, the status icon is decorative and hidden from assistive technology (`aria-hidden`),
40
+ * because the message content itself conveys the status.
41
+ * Pass an `aria-label` only when the icon needs to be announced.
42
42
  */
43
43
  statusIconProps?: Pick<ComponentPropsWithoutRef<'svg'>, 'aria-label'>;
44
44
  /**
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React, { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
4
4
  import { Dropdown as DropdownIcon, Error } from '@moneyforward/mfui-icons-react';
5
5
  import { ClearButton } from '../shared/ClearButton';
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { memo, useMemo, useCallback } from 'react';
4
4
  import { Current } from '@moneyforward/mfui-icons-react';
5
5
  import { FocusIndicator } from '../FocusIndicator';
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { memo } from 'react';
4
4
  import { Typography } from '../Typography';
5
5
  import { SelectBoxOptionComponent } from './SelectBoxOption';
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * The general purpose SidePane component.
3
- * This component extends the props of `<div>` element with `role="dialog"`.
3
+ * This component extends the props of `<div>` element. The extended props and `ref` are
4
+ * applied to the root overlay element covering the viewport, while `role="dialog"` (and
5
+ * `aria-modal="true"` when `enableModal` is set) are set on the inner pane window element.
4
6
  *
5
7
  * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog
6
8
  */
@@ -9,22 +11,22 @@ export declare const SidePane: import("react").ForwardRefExoticComponent<{
9
11
  title?: string;
10
12
  renderHeading?: (options: import("./SidePane.types").SidePaneRenderOptions) => import("react").ReactNode;
11
13
  actionSlot?: import("react").ReactNode;
12
- position?: import("../../styled-system/recipes").SidePaneSlotRecipeVariant["position"];
14
+ position?: import("../../styled-system/recipes").SidePaneSlotRecipeVariant['position'];
13
15
  closeButtonProps?: {
14
16
  isCollapseIcon?: boolean;
15
- } & Pick<import("react").ComponentPropsWithoutRef<"button">, "aria-label">;
17
+ } & Pick<import("react").ComponentPropsWithoutRef<'button'>, 'aria-label'>;
16
18
  targetDOMNode?: HTMLElement | null;
17
19
  enableModal?: boolean;
18
20
  disableBackdropClose?: boolean;
19
21
  enableAutoUnmount?: boolean;
20
- formFooterProps?: Pick<import("..").FormFooterProps, "optionsSlot" | "actionsSlot"> & {
21
- position?: import("..").FormFooterProps["position"];
22
+ formFooterProps?: Pick<import("..").FormFooterProps, 'optionsSlot' | 'actionsSlot'> & {
23
+ position?: import("..").FormFooterProps['position'];
22
24
  };
23
25
  insideProps?: {
24
26
  className?: string;
25
27
  };
26
28
  open?: boolean;
27
29
  onClose?: import("./SidePane.types").OnCloseFunction;
28
- } & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "open" | "onClose"> & {
29
- ref?: (import("react").RefObject<HTMLDivElement | null> | import("react").RefCallback<HTMLDivElement | null> | null) | undefined;
30
+ } & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref">, "onClose" | "open"> & {
31
+ ref?: (import("react").RefCallback<HTMLDivElement | null> | import("react").RefObject<HTMLDivElement | null> | null) | undefined;
30
32
  } & import("react").Attributes>;
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useCallback, useMemo } from 'react';
3
+ import { useCallback, useMemo, useState } from 'react';
4
4
  import { Close, PaneExpansionToRight, PaneExpansionToLeft } from '@moneyforward/mfui-icons-react';
5
5
  import { cx } from '../../styled-system/css';
6
6
  import { Heading } from '../Heading';
@@ -13,16 +13,20 @@ import { Portal, TargetDomNodeProvider, useAutomaticTargetDomNode } from '../Por
13
13
  import { FormFooter } from '../FormFooter';
14
14
  /**
15
15
  * The general purpose SidePane component.
16
- * This component extends the props of `<div>` element with `role="dialog"`.
16
+ * This component extends the props of `<div>` element. The extended props and `ref` are
17
+ * applied to the root overlay element covering the viewport, while `role="dialog"` (and
18
+ * `aria-modal="true"` when `enableModal` is set) are set on the inner pane window element.
17
19
  *
18
20
  * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog
19
21
  */
20
22
  export const SidePane = compatibleForwardRef(({ open, title, renderHeading, onClose, className, children, actionSlot, formFooterProps, position = 'right', enableModal = false, enableAutoUnmount = true, disableBackdropClose = false, closeButtonProps, targetDOMNode, insideProps, ...props }, ref) => {
21
23
  const classes = sidePaneSlotRecipe({ position, nonModal: !enableModal });
24
+ const [insideElement, setInsideElement] = useState(null);
22
25
  const { localOpen, handleCloseSidePane, handleStopCloseSidePane, sidePaneElement, setSidePaneElement } = useSidePaneStateController({
23
26
  open,
24
27
  onClose,
25
28
  ref,
29
+ insideElement,
26
30
  });
27
31
  const handleBackdropClick = useCallback(() => {
28
32
  if (!disableBackdropClose) {
@@ -56,8 +60,8 @@ export const SidePane = compatibleForwardRef(({ open, title, renderHeading, onCl
56
60
  const shouldRenderContent = enableAutoUnmount ? localOpen : true;
57
61
  const resolvedFormFooterPosition = formFooterProps?.position ?? 'sticky';
58
62
  const headingNode = title ? (_jsx(Heading, { variant: "pageHeading2", tag: "h2", children: title })) : null;
59
- return (_jsx(TargetDomNodeProvider, { targetDomNode: sidePaneElement, children: shouldRenderContent ? (_jsx(Portal, { targetDOMNode: targetElement, children: _jsxs("div", { ref: setSidePaneElement, role: "dialog", ...props, className: cx(classes.root, 'mfui-SidePane__root', className), tabIndex: -1, style: {
63
+ return (_jsx(TargetDomNodeProvider, { targetDomNode: sidePaneElement, children: shouldRenderContent ? (_jsx(Portal, { targetDOMNode: targetElement, children: _jsxs("div", { ref: setSidePaneElement, ...props, className: cx(classes.root, 'mfui-SidePane__root', className), style: {
60
64
  ...props.style,
61
65
  ...(!enableAutoUnmount && !localOpen && { display: 'none' }),
62
- }, onKeyDown: handleOnKeyDown, children: [enableModal ? (_jsx("div", { "data-mfui-content": "backdrop", className: cx(classes.backdrop, 'mfui-SidePane__backdrop'), onClick: handleBackdropClick })) : null, _jsxs("div", { "data-mfui-content": "inside", className: cx(classes.inside, 'mfui-SidePane__inside', insideProps?.className), onClick: handleStopCloseSidePane, children: [_jsxs("header", { className: cx(classes.header, 'mfui-SidePane__header'), children: [renderHeading ? (renderHeading({ headingNode })) : (_jsx("div", { className: cx(classes.title, 'mfui-SidePane__title'), children: headingNode })), _jsxs("div", { className: cx(classes.actionSlotWrapper, 'mfui-SidePane__actionSlotWrapper'), children: [!!actionSlot && (_jsx("div", { className: cx(classes.actionSlot, 'mfui-SidePane__actionSlot'), children: actionSlot })), _jsx("div", { className: cx(classes.closeButtonWrapper, 'mfui-SidePane__closeButtonWrapper'), children: _jsx(IconButton, { "aria-label": closeButtonProps?.['aria-label'] ?? '閉じる', onClick: handleCloseSidePane, children: closeButtonIcon }) })] })] }), _jsxs("main", { className: cx(classes.content, 'mfui-SidePane__content'), children: [_jsx("div", { className: cx(classes.contentBody, 'mfui-SidePane__contentBody'), children: children }), resolvedFormFooterPosition === 'stacking' ? (_jsx("div", { className: cx(classes.footer, 'mfui-SidePane__footer'), children: _jsx(FormFooter, { sectionPosition: "fill", ...formFooterProps, position: resolvedFormFooterPosition }) })) : null] }), formFooterProps && resolvedFormFooterPosition !== 'stacking' ? (_jsx("div", { className: cx(classes.footer, 'mfui-SidePane__footer'), children: _jsx(FormFooter, { sectionPosition: "fill", ...formFooterProps, position: resolvedFormFooterPosition }) })) : null] })] }) })) : null }));
66
+ }, onKeyDown: handleOnKeyDown, children: [enableModal ? (_jsx("div", { "data-mfui-content": "backdrop", className: cx(classes.backdrop, 'mfui-SidePane__backdrop'), onClick: handleBackdropClick })) : null, _jsxs("div", { ref: setInsideElement, role: "dialog", "aria-modal": enableModal ? 'true' : undefined, tabIndex: -1, "data-mfui-content": "inside", className: cx(classes.inside, 'mfui-SidePane__inside', insideProps?.className), onClick: handleStopCloseSidePane, children: [_jsxs("header", { className: cx(classes.header, 'mfui-SidePane__header'), children: [renderHeading ? (renderHeading({ headingNode })) : (_jsx("div", { className: cx(classes.title, 'mfui-SidePane__title'), children: headingNode })), _jsxs("div", { className: cx(classes.actionSlotWrapper, 'mfui-SidePane__actionSlotWrapper'), children: [!!actionSlot && (_jsx("div", { className: cx(classes.actionSlot, 'mfui-SidePane__actionSlot'), children: actionSlot })), _jsx("div", { className: cx(classes.closeButtonWrapper, 'mfui-SidePane__closeButtonWrapper'), children: _jsx(IconButton, { "aria-label": closeButtonProps?.['aria-label'] ?? '閉じる', onClick: handleCloseSidePane, children: closeButtonIcon }) })] })] }), _jsxs("main", { className: cx(classes.content, 'mfui-SidePane__content'), children: [_jsx("div", { className: cx(classes.contentBody, 'mfui-SidePane__contentBody'), children: children }), resolvedFormFooterPosition === 'stacking' ? (_jsx("div", { className: cx(classes.footer, 'mfui-SidePane__footer'), children: _jsx(FormFooter, { sectionPosition: "fill", ...formFooterProps, position: resolvedFormFooterPosition }) })) : null] }), formFooterProps && resolvedFormFooterPosition !== 'stacking' ? (_jsx("div", { className: cx(classes.footer, 'mfui-SidePane__footer'), children: _jsx(FormFooter, { sectionPosition: "fill", ...formFooterProps, position: resolvedFormFooterPosition }) })) : null] })] }) })) : null }));
63
67
  });
@@ -3,6 +3,7 @@ type UseSidePaneStateController = {
3
3
  open?: boolean;
4
4
  onClose?: () => void;
5
5
  ref?: ForwardedRef<HTMLDivElement | null>;
6
+ insideElement?: HTMLElement | null;
6
7
  };
7
8
  /**
8
9
  * Custom hook to control the open/close state of a SidePane.
@@ -15,9 +16,13 @@ type UseSidePaneStateController = {
15
16
  *
16
17
  * @param options.onClose - Callback function to execute when closing the SidePane.
17
18
  *
18
- * @returns Object containing functions to handle closing the SidePane and ref Object to attach Dialog element.
19
+ * @param options.insideElement - The pane window element with `role="dialog"`. It receives
20
+ * default focus when the SidePane opens, keeping focus inside the `aria-modal` boundary.
21
+ *
22
+ * @returns Object containing the local open state, close/click handlers, and a callback
23
+ * ref (`setSidePaneElement`) to attach to the root overlay element.
19
24
  */
20
- export declare function useSidePaneStateController({ open, onClose, ref }: UseSidePaneStateController): {
25
+ export declare function useSidePaneStateController({ open, onClose, ref, insideElement }: UseSidePaneStateController): {
21
26
  localOpen: boolean | undefined;
22
27
  handleCloseSidePane: () => void;
23
28
  handleStopCloseSidePane: (event: React.MouseEvent<HTMLDivElement>) => void;
@@ -12,9 +12,13 @@ import { useSidePaneOpenStateWithAnimation } from './useSidePaneOpenStateWithAni
12
12
  *
13
13
  * @param options.onClose - Callback function to execute when closing the SidePane.
14
14
  *
15
- * @returns Object containing functions to handle closing the SidePane and ref Object to attach Dialog element.
15
+ * @param options.insideElement - The pane window element with `role="dialog"`. It receives
16
+ * default focus when the SidePane opens, keeping focus inside the `aria-modal` boundary.
17
+ *
18
+ * @returns Object containing the local open state, close/click handlers, and a callback
19
+ * ref (`setSidePaneElement`) to attach to the root overlay element.
16
20
  */
17
- export function useSidePaneStateController({ open, onClose, ref }) {
21
+ export function useSidePaneStateController({ open, onClose, ref, insideElement }) {
18
22
  const [sidePaneElement, setSidePaneElement] = useState(null);
19
23
  const { localOpen, openSidePane, closeSidePane } = useSidePaneOpenStateWithAnimation({ open, sidePaneElement });
20
24
  const setSidePaneElementRef = useCallback((element) => {
@@ -49,12 +53,14 @@ export function useSidePaneStateController({ open, onClose, ref }) {
49
53
  const handleStopCloseSidePane = useCallback((event) => {
50
54
  event.stopPropagation();
51
55
  }, []);
52
- // Autofocus to the SidePane itself when it opens
56
+ // Autofocus to the pane window (`role="dialog"` element) when it opens.
57
+ // Screen readers honor `aria-modal` only when focus is on the `role="dialog"`
58
+ // element or its descendants, so the root wrapper must not be the focus target.
53
59
  useEffect(() => {
54
60
  if (!localOpen)
55
61
  return;
56
- sidePaneElement?.focus();
57
- }, [sidePaneElement, localOpen]);
62
+ (insideElement ?? sidePaneElement)?.focus();
63
+ }, [insideElement, sidePaneElement, localOpen]);
58
64
  return {
59
65
  localOpen,
60
66
  handleCloseSidePane,
@@ -2,8 +2,9 @@ import { type SidebarProps } from './Sidebar.types';
2
2
  import { SidebarServiceMenu } from './SidebarServiceMenu';
3
3
  import { SidebarTenantMenu } from './SidebarTenantMenu';
4
4
  import { SidebarUserMenu } from './SidebarUserMenu';
5
+ import { SidebarTenantMenuHeading, SidebarUserMenuHeading } from './SidebarMenuHeading';
5
6
  import { SidebarNavigationItem } from './SidebarNavigationItem';
6
- declare function SidebarBase({ className, logoSlot, mainNavigationItems, extraNavigationSlot, footerSlot, serviceMenuSlot, footerIconMenuSlot, tenantSlot, userSlot, minWidth, maxWidth, defaultWidth, onWidthChange, mainNavigationLabel, extraNavigationLabel, resizeHandleLabel, }: SidebarProps): import("react/jsx-runtime").JSX.Element;
7
+ declare function SidebarBase({ className, logoSlot, mainNavigationItems, extraNavigationSlot, footerSlot, serviceMenuSlot, footerIconMenuSlot, tenantSlot, userSlot, minWidth, maxWidth, defaultWidth, onWidthChange, enableCollapsible, isCollapsed: controlledIsCollapsed, onCollapsedChange, collapseButtonLabel, expandButtonLabel, mainNavigationLabel, extraNavigationLabel, resizeHandleLabel, }: SidebarProps): import("react/jsx-runtime").JSX.Element;
7
8
  /**
8
9
  * The sidebar component.
9
10
  * A navigation layout component that combines a logo header, a navigation area,
@@ -20,6 +21,10 @@ declare function SidebarBase({ className, logoSlot, mainNavigationItems, extraNa
20
21
  * - `Sidebar.ServiceMenu` — the header service menu trigger, intended to wrap a list of services.
21
22
  * - `Sidebar.TenantMenu` — the footer tenant (business) name and its menu.
22
23
  * - `Sidebar.UserMenu` — the footer user (account) name and its menu.
24
+ * - `Sidebar.TenantMenuHeading` — the heading block inside `Sidebar.TenantMenu`: the tenant name and
25
+ * its identification-number row (optionally copyable to the clipboard).
26
+ * - `Sidebar.UserMenuHeading` — the heading block inside `Sidebar.UserMenu`: the user name and
27
+ * its identification-number row (optionally copyable to the clipboard).
23
28
  *
24
29
  * @example
25
30
  * ```tsx
@@ -59,5 +64,9 @@ export declare const Sidebar: typeof SidebarBase & {
59
64
  TenantMenu: typeof SidebarTenantMenu;
60
65
  /** @see {@link SidebarUserMenu} */
61
66
  UserMenu: typeof SidebarUserMenu;
67
+ /** @see {@link SidebarTenantMenuHeading} */
68
+ TenantMenuHeading: typeof SidebarTenantMenuHeading;
69
+ /** @see {@link SidebarUserMenuHeading} */
70
+ UserMenuHeading: typeof SidebarUserMenuHeading;
62
71
  };
63
72
  export {};
@@ -1,28 +1,50 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { SidebarController } from '@moneyforward/mfui-icons-react';
4
+ import { useId, useMemo, useState } from 'react';
3
5
  import { cx } from '../../styled-system/css';
4
6
  import { sidebarSlotRecipe } from '../../styled-system/recipes';
7
+ import { IconButton } from '../IconButton';
8
+ import { Tooltip } from '../Tooltip';
5
9
  import { SidebarServiceMenu } from './SidebarServiceMenu';
6
10
  import { SidebarTenantMenu } from './SidebarTenantMenu';
7
11
  import { SidebarUserMenu } from './SidebarUserMenu';
12
+ import { SidebarTenantMenuHeading, SidebarUserMenuHeading } from './SidebarMenuHeading';
8
13
  import { SidebarNavigationItem } from './SidebarNavigationItem';
14
+ import { SidebarContext } from './SidebarContext';
9
15
  import { useSidebarResize } from './hooks/useSidebarResize';
10
- function SidebarBase({ className, logoSlot, mainNavigationItems, extraNavigationSlot, footerSlot, serviceMenuSlot, footerIconMenuSlot, tenantSlot, userSlot, minWidth, maxWidth, defaultWidth, onWidthChange, mainNavigationLabel = 'メインナビゲーション', extraNavigationLabel = '追加ナビゲーション', resizeHandleLabel = 'サイドバーの幅を調整', }) {
16
+ function SidebarBase({ className, logoSlot, mainNavigationItems, extraNavigationSlot, footerSlot, serviceMenuSlot, footerIconMenuSlot, tenantSlot, userSlot, minWidth, maxWidth, defaultWidth, onWidthChange, enableCollapsible = false, isCollapsed: controlledIsCollapsed, onCollapsedChange, collapseButtonLabel = 'ナビゲーションを折りたたむ', expandButtonLabel = 'ナビゲーションを拡げる', mainNavigationLabel = 'メインナビゲーション', extraNavigationLabel = '追加ナビゲーション', resizeHandleLabel = 'サイドバーの幅を調整', }) {
17
+ const [localIsCollapsed, setLocalIsCollapsed] = useState(controlledIsCollapsed ?? false);
18
+ const isControlled = controlledIsCollapsed !== undefined;
19
+ const isCollapsed = enableCollapsible ? (isControlled ? controlledIsCollapsed : localIsCollapsed) : false;
20
+ const navId = useId();
11
21
  const classes = sidebarSlotRecipe();
12
22
  const { width, minWidth: resolvedMinWidth, maxWidth: resolvedMaxWidth, rootRef, isDragging, handlePointerDown, handleKeyDown, } = useSidebarResize({
13
23
  minWidth,
14
24
  maxWidth,
15
25
  defaultWidth,
16
26
  onWidthChange,
27
+ isCollapsed,
17
28
  });
18
- return (_jsxs("div", { ref: rootRef, className: cx(classes.root, 'mfui-Sidebar__root', className), style: { width: `${String(width)}px` }, "data-mfui-dragging": isDragging ? true : undefined, children: [_jsx("header", { className: cx(classes.header, 'mfui-Sidebar__header'), children: _jsxs("div", { className: cx(classes.leftPane, 'mfui-Sidebar__leftPane'), children: [serviceMenuSlot ? (_jsx("div", { className: cx(classes.serviceMenu, 'mfui-Sidebar__serviceMenu'), children: serviceMenuSlot })) : null, _jsx("div", { className: cx(classes.logo, 'mfui-Sidebar__logo'), children: logoSlot })] }) }), _jsxs("div", { className: cx(classes.navigationWrapper, 'mfui-Sidebar__navigationWrapper'), children: [mainNavigationItems && mainNavigationItems.length > 0 ? (_jsx("nav", { "aria-label": mainNavigationLabel, className: cx(classes.mainNavigation, 'mfui-Sidebar__mainNavigation'), children: _jsx("ul", { className: cx(classes.navigationList, 'mfui-Sidebar__navigationList'), children: mainNavigationItems.map((item, index) => {
19
- if ('type' in item) {
20
- return (_jsx("li", { "aria-hidden": "true", className: cx(classes.navigationDivider, 'mfui-Sidebar__navigationDivider') }, index));
21
- }
22
- return _jsx(SidebarNavigationItem, { ...item }, index);
23
- }) }) })) : null, extraNavigationSlot ? (_jsx("nav", { "aria-label": extraNavigationLabel, className: cx(classes.extraNavigation, 'mfui-Sidebar__extraNavigation'), children: extraNavigationSlot })) : null] }), _jsxs("div", { className: cx(classes.footer, 'mfui-Sidebar__footer'), children: [footerSlot ? _jsx("div", { className: cx(classes.footerSlot, 'mfui-Sidebar__footerSlot'), children: footerSlot }) : null, _jsx("div", { className: cx(classes.footerIconMenu, 'mfui-Sidebar__footerIconMenu'), children: footerIconMenuSlot }), tenantSlot || userSlot ? (_jsxs("div", { className: cx(classes.footerUserIdMenu, 'mfui-Sidebar__footerUserIdMenu'), children: [tenantSlot ? (_jsx("div", { className: cx(classes.footerUserIdMenuItem, 'mfui-Sidebar__footerUserIdMenuItem'), children: tenantSlot })) : null, userSlot ? (_jsx("div", { className: cx(classes.footerUserIdMenuItem, 'mfui-Sidebar__footerUserIdMenuItem'), children: userSlot })) : null] })) : null] }), _jsx("div", { "data-mfui-handle": true, "data-mfui-dragging": isDragging ? true : undefined, role: "separator", "aria-orientation": "vertical", "aria-label": resizeHandleLabel, "aria-valuenow": width, "aria-valuemin": resolvedMinWidth, "aria-valuemax": resolvedMaxWidth,
24
- // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
25
- tabIndex: 0, className: cx(classes.handle, 'mfui-Sidebar__handle'), onPointerDown: handlePointerDown, onKeyDown: handleKeyDown })] }));
29
+ const hasMainNavigation = !!mainNavigationItems?.length;
30
+ const toggleButtonLabel = isCollapsed ? expandButtonLabel : collapseButtonLabel;
31
+ const contextValue = useMemo(() => ({ isCollapsed }), [isCollapsed]);
32
+ const handleToggle = () => {
33
+ const next = !isCollapsed;
34
+ if (!isControlled) {
35
+ setLocalIsCollapsed(next);
36
+ }
37
+ onCollapsedChange?.(next);
38
+ };
39
+ return (_jsx(SidebarContext.Provider, { value: contextValue, children: _jsxs("div", { ref: rootRef, className: cx(classes.root, 'mfui-Sidebar__root', className), style: { width: `${String(width)}px` }, "data-mfui-collapsed": isCollapsed ? true : undefined, "data-mfui-dragging": isDragging ? true : undefined, children: [_jsxs("header", { className: cx(classes.header, 'mfui-Sidebar__header'), children: [_jsx("div", { className: cx(classes.leftPane, 'mfui-Sidebar__leftPane'), children: !isCollapsed ? (_jsxs(_Fragment, { children: [serviceMenuSlot ? (_jsx("div", { className: cx(classes.serviceMenu, 'mfui-Sidebar__serviceMenu'), children: serviceMenuSlot })) : null, _jsx("div", { className: cx(classes.logo, 'mfui-Sidebar__logo'), children: logoSlot })] })) : null }), enableCollapsible ? (_jsx("div", { className: cx(classes.rightPane, 'mfui-Sidebar__rightPane'), children: _jsx(SidebarToggleButton, { isCollapsed: isCollapsed, ariaLabel: toggleButtonLabel, navId: hasMainNavigation ? navId : undefined, onClick: handleToggle }) })) : null] }), _jsxs("div", { className: cx(classes.navigationWrapper, 'mfui-Sidebar__navigationWrapper'), children: [hasMainNavigation ? (_jsx("nav", { id: navId, "aria-label": mainNavigationLabel, className: cx(classes.mainNavigation, 'mfui-Sidebar__mainNavigation'), children: _jsx("ul", { className: cx(classes.navigationList, 'mfui-Sidebar__navigationList'), children: mainNavigationItems.map((item, index) => {
40
+ if ('type' in item) {
41
+ return (_jsx("li", { "aria-hidden": "true", className: cx(classes.navigationDivider, 'mfui-Sidebar__navigationDivider') }, index));
42
+ }
43
+ return _jsx(SidebarNavigationItem, { ...item }, index);
44
+ }) }) })) : null, extraNavigationSlot ? (_jsx("nav", { "aria-label": extraNavigationLabel, className: cx(classes.extraNavigation, 'mfui-Sidebar__extraNavigation'), children: extraNavigationSlot })) : null] }), _jsxs("div", { className: cx(classes.footer, 'mfui-Sidebar__footer'), children: [footerSlot ? _jsx("div", { className: cx(classes.footerSlot, 'mfui-Sidebar__footerSlot'), children: footerSlot }) : null, _jsx("div", { className: cx(classes.footerIconMenu, 'mfui-Sidebar__footerIconMenu'), children: typeof footerIconMenuSlot === 'function' ? footerIconMenuSlot({ isCollapsed }) : footerIconMenuSlot }), !isCollapsed && (tenantSlot || userSlot) ? (_jsxs("div", { className: cx(classes.footerUserIdMenu, 'mfui-Sidebar__footerUserIdMenu'), children: [tenantSlot ? (_jsx("div", { className: cx(classes.footerUserIdMenuItem, 'mfui-Sidebar__footerUserIdMenuItem'), children: tenantSlot })) : null, userSlot ? (_jsx("div", { className: cx(classes.footerUserIdMenuItem, 'mfui-Sidebar__footerUserIdMenuItem'), children: userSlot })) : null] })) : null] }), _jsx("div", { "data-mfui-handle": true, "data-mfui-dragging": isDragging ? true : undefined, "data-mfui-collapsed": isCollapsed ? true : undefined, role: isCollapsed ? undefined : 'separator', "aria-orientation": isCollapsed ? undefined : 'vertical', "aria-label": isCollapsed ? undefined : resizeHandleLabel, "aria-valuenow": isCollapsed ? undefined : width, "aria-valuemin": isCollapsed ? undefined : resolvedMinWidth, "aria-valuemax": isCollapsed ? undefined : resolvedMaxWidth, tabIndex: isCollapsed ? -1 : 0, className: cx(classes.handle, 'mfui-Sidebar__handle'), onPointerDown: handlePointerDown, onKeyDown: handleKeyDown })] }) }));
45
+ }
46
+ function SidebarToggleButton({ isCollapsed, ariaLabel, navId, onClick, }) {
47
+ return (_jsx(Tooltip, { content: ariaLabel, placement: isCollapsed ? 'right' : 'bottom', children: _jsx(IconButton, { "aria-pressed": !isCollapsed, "aria-controls": navId, "aria-label": ariaLabel, className: "mfui-Sidebar__toggleButton", onClick: onClick, children: _jsx(SidebarController, { "aria-hidden": true }) }) }));
26
48
  }
27
49
  /**
28
50
  * The sidebar component.
@@ -40,6 +62,10 @@ function SidebarBase({ className, logoSlot, mainNavigationItems, extraNavigation
40
62
  * - `Sidebar.ServiceMenu` — the header service menu trigger, intended to wrap a list of services.
41
63
  * - `Sidebar.TenantMenu` — the footer tenant (business) name and its menu.
42
64
  * - `Sidebar.UserMenu` — the footer user (account) name and its menu.
65
+ * - `Sidebar.TenantMenuHeading` — the heading block inside `Sidebar.TenantMenu`: the tenant name and
66
+ * its identification-number row (optionally copyable to the clipboard).
67
+ * - `Sidebar.UserMenuHeading` — the heading block inside `Sidebar.UserMenu`: the user name and
68
+ * its identification-number row (optionally copyable to the clipboard).
43
69
  *
44
70
  * @example
45
71
  * ```tsx
@@ -79,4 +105,8 @@ export const Sidebar = Object.assign(SidebarBase, {
79
105
  TenantMenu: SidebarTenantMenu,
80
106
  /** @see {@link SidebarUserMenu} */
81
107
  UserMenu: SidebarUserMenu,
108
+ /** @see {@link SidebarTenantMenuHeading} */
109
+ TenantMenuHeading: SidebarTenantMenuHeading,
110
+ /** @see {@link SidebarUserMenuHeading} */
111
+ UserMenuHeading: SidebarUserMenuHeading,
82
112
  });
@@ -54,6 +54,12 @@ export type SidebarNavigationDivider = {
54
54
  type: 'divider';
55
55
  };
56
56
  export type SidebarNavigationEntry = SidebarNavigationItem | SidebarNavigationDivider;
57
+ export type SidebarFooterIconMenuSlotState = {
58
+ /**
59
+ * Whether the sidebar is currently collapsed.
60
+ */
61
+ isCollapsed: boolean;
62
+ };
57
63
  export type SidebarProps = {
58
64
  /**
59
65
  * Additional class names to apply to the component.
@@ -85,9 +91,10 @@ export type SidebarProps = {
85
91
  serviceMenuSlot: ReactNode;
86
92
  /**
87
93
  * Elements to be displayed in the footer icon menu area.
88
- * Icon buttons with dropdowns should open upward (placement: top).
94
+ * Pass a function to adapt the content to the current collapsed state, such as switching tooltip placement.
95
+ * The plain node form still works for static content.
89
96
  */
90
- footerIconMenuSlot: ReactNode;
97
+ footerIconMenuSlot: ReactNode | ((state: SidebarFooterIconMenuSlotState) => ReactNode);
91
98
  /**
92
99
  * Elements to be displayed below the footer icon menu.
93
100
  * Intended for the tenant (business) name and its menu.
@@ -121,6 +128,36 @@ export type SidebarProps = {
121
128
  * Use this to persist the value in your own storage (e.g. via an API call).
122
129
  */
123
130
  onWidthChange: (width: number) => void;
131
+ /**
132
+ * Whether the sidebar can be collapsed to an icon-only rail.
133
+ * When `true`, a toggle button is rendered in the header.
134
+ *
135
+ * @default false
136
+ */
137
+ enableCollapsible?: boolean;
138
+ /**
139
+ * The collapsed state of the sidebar (controlled).
140
+ * When provided, the component behaves as a controlled component.
141
+ * Only effective when `enableCollapsible` is `true`.
142
+ */
143
+ isCollapsed?: boolean;
144
+ /**
145
+ * Called when the collapse toggle is clicked with the new collapsed state.
146
+ * Only called when `enableCollapsible` is `true`.
147
+ */
148
+ onCollapsedChange?: (isCollapsed: boolean) => void;
149
+ /**
150
+ * Accessible name for the toggle button when expanded (clicking will collapse).
151
+ *
152
+ * @default 'ナビゲーションを折りたたむ'
153
+ */
154
+ collapseButtonLabel?: string;
155
+ /**
156
+ * Accessible name for the toggle button when collapsed (clicking will expand).
157
+ *
158
+ * @default 'ナビゲーションを拡げる'
159
+ */
160
+ expandButtonLabel?: string;
124
161
  /**
125
162
  * The accessible name for the primary navigation landmark.
126
163
  *
@@ -0,0 +1,3 @@
1
+ export declare const SidebarContext: import("react").Context<{
2
+ isCollapsed: boolean;
3
+ }>;
@@ -0,0 +1,2 @@
1
+ import { createContext } from 'react';
2
+ export const SidebarContext = createContext({ isCollapsed: false });
@@ -0,0 +1,49 @@
1
+ import { type SidebarTenantMenuHeadingProps, type SidebarUserMenuHeadingProps } from './SidebarMenuHeading.types';
2
+ /**
3
+ * The heading block of the Sidebar tenant menu: the tenant name heading and the
4
+ * identification-number row. Place it as the first child of `Sidebar.TenantMenu`.
5
+ *
6
+ * Pass the `copy` prop to make the identification-number row copy its value to the clipboard,
7
+ * with a tooltip and a screen-reader announcement. The feedback after copying reads `コピーしました`
8
+ * by default and can be changed via `copy.copiedLabel`. Without `copy`, the row is static text.
9
+ *
10
+ * @param props - The props of the `Sidebar.TenantMenuHeading` component.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * <Sidebar.TenantMenu name="株式会社サンプル" tooltipLabel="事業者">
15
+ * <Sidebar.TenantMenuHeading
16
+ * name="株式会社サンプル"
17
+ * identificationNumber="事業者番号:0123-4567"
18
+ * copy={{ text: '0123-4567', label: '事業者番号をコピーする' }}
19
+ * />
20
+ * <DropdownMenu.Divider />
21
+ * <DropdownMenu.Item href="https://example.com">事業者設定</DropdownMenu.Item>
22
+ * </Sidebar.TenantMenu>
23
+ * ```
24
+ */
25
+ export declare function SidebarTenantMenuHeading(props: SidebarTenantMenuHeadingProps): import("react/jsx-runtime").JSX.Element;
26
+ /**
27
+ * The heading block of the Sidebar user menu: the user (account) name heading and the
28
+ * identification-number row. Place it as the first child of `Sidebar.UserMenu`.
29
+ *
30
+ * Pass the `copy` prop to make the identification-number row copy its value to the clipboard,
31
+ * with a tooltip and a screen-reader announcement. The feedback after copying reads `コピーしました`
32
+ * by default and can be changed via `copy.copiedLabel`. Without `copy`, the row is static text.
33
+ *
34
+ * @param props - The props of the `Sidebar.UserMenuHeading` component.
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * <Sidebar.UserMenu name="山田 太郎" tooltipLabel="アカウント">
39
+ * <Sidebar.UserMenuHeading
40
+ * name="山田 太郎"
41
+ * identificationNumber="従業員番号:1234"
42
+ * copy={{ text: '1234', label: '従業員番号をコピーする' }}
43
+ * />
44
+ * <DropdownMenu.Divider />
45
+ * <DropdownMenu.Item onClick={handleLogout}>ログアウト</DropdownMenu.Item>
46
+ * </Sidebar.UserMenu>
47
+ * ```
48
+ */
49
+ export declare function SidebarUserMenuHeading(props: SidebarUserMenuHeadingProps): import("react/jsx-runtime").JSX.Element;