@moneyforward/mfui-components 3.19.0 → 3.21.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 (113) hide show
  1. package/dist/src/CheckboxCard/CheckboxCard.js +1 -7
  2. package/dist/src/DateTimeSelection/DatePicker/DatePicker.js +2 -11
  3. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePicker.d.ts +1 -1
  4. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePicker.js +10 -2
  5. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePicker.types.d.ts +14 -0
  6. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerPopover/DateRangePickerPopover.js +13 -5
  7. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerPopover/utilities/createDateRangePickerPopoverTestUtility.js +2 -2
  8. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerProvider/DateRangePickerProvider.js +11 -12
  9. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerTrigger/hooks/useDateRangeTriggerValueController.js +4 -3
  10. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerTrigger/utilities/createDateRangePickerTriggerTestUtility.js +3 -1
  11. package/dist/src/DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.d.ts +6 -0
  12. package/dist/src/DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.js +33 -0
  13. package/dist/src/DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.types.d.ts +17 -0
  14. package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.d.ts +14 -0
  15. package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.js +42 -0
  16. package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.types.d.ts +78 -0
  17. package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.types.js +1 -0
  18. package/dist/src/DateTimeSelection/FilterDateRangePicker/index.d.ts +2 -0
  19. package/dist/src/DateTimeSelection/FilterDateRangePicker/index.js +1 -0
  20. package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.d.ts +14 -0
  21. package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.js +75 -0
  22. package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.types.d.ts +83 -0
  23. package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.types.js +1 -0
  24. package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.d.ts +6 -0
  25. package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.js +45 -0
  26. package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.types.d.ts +24 -0
  27. package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.types.js +1 -0
  28. package/dist/src/DateTimeSelection/FilterMonthPicker/index.d.ts +2 -0
  29. package/dist/src/DateTimeSelection/FilterMonthPicker/index.js +1 -0
  30. package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.d.ts +15 -0
  31. package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.js +89 -0
  32. package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.types.d.ts +75 -0
  33. package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.types.js +1 -0
  34. package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.d.ts +5 -0
  35. package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.js +54 -0
  36. package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.types.d.ts +19 -0
  37. package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.types.js +1 -0
  38. package/dist/src/DateTimeSelection/FilterMonthRangePicker/index.d.ts +2 -0
  39. package/dist/src/DateTimeSelection/FilterMonthRangePicker/index.js +1 -0
  40. package/dist/src/DateTimeSelection/MonthPicker/MonthPicker.d.ts +1 -1
  41. package/dist/src/DateTimeSelection/MonthPicker/MonthPicker.js +10 -2
  42. package/dist/src/DateTimeSelection/MonthPicker/MonthPicker.types.d.ts +14 -0
  43. package/dist/src/DateTimeSelection/MonthPicker/MonthPickerPanel/MonthCell/MonthCell.js +2 -25
  44. package/dist/src/DateTimeSelection/MonthPicker/MonthPickerPanel/MonthPickerPanel.js +1 -1
  45. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePicker.d.ts +1 -1
  46. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePicker.js +14 -5
  47. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePicker.types.d.ts +21 -0
  48. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerMonthCell/MonthRangePickerMonthCell.d.ts +1 -0
  49. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerMonthCell/MonthRangePickerMonthCell.js +10 -3
  50. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerNavigation/MonthRangePickerNavigation.js +16 -3
  51. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerPanel/MonthRangePickerPanel.js +3 -1
  52. package/dist/src/DateTimeSelection/TimePicker/TimePicker.d.ts +43 -0
  53. package/dist/src/DateTimeSelection/TimePicker/TimePicker.js +85 -0
  54. package/dist/src/DateTimeSelection/TimePicker/TimePicker.types.d.ts +61 -0
  55. package/dist/src/DateTimeSelection/TimePicker/TimePicker.types.js +1 -0
  56. package/dist/src/DateTimeSelection/TimePicker/constants.d.ts +4 -0
  57. package/dist/src/DateTimeSelection/TimePicker/constants.js +12 -0
  58. package/dist/src/DateTimeSelection/TimePicker/index.d.ts +2 -0
  59. package/dist/src/DateTimeSelection/TimePicker/index.js +1 -0
  60. package/dist/src/DateTimeSelection/index.d.ts +4 -0
  61. package/dist/src/DateTimeSelection/index.js +4 -0
  62. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.d.ts +1 -1
  63. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.js +26 -10
  64. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.types.d.ts +19 -0
  65. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerProvider/BaseRangePickerProvider.js +40 -19
  66. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerProvider/BaseRangePickerProvider.types.d.ts +19 -0
  67. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/BaseRangePickerTrigger.js +4 -6
  68. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/hooks/useDateRangeTriggerValueController.js +4 -3
  69. package/dist/src/DateTimeSelection/shared/CalendarLocale/CalendarLocaleContext.d.ts +1 -1
  70. package/dist/src/DateTimeSelection/shared/CalendarLocale/CalendarLocaleContext.js +1 -1
  71. package/dist/src/DateTimeSelection/shared/DayCell/DayCell.js +2 -3
  72. package/dist/src/DateTimeSelection/shared/MonthGrid/MonthGrid.js +3 -3
  73. package/dist/src/DateTimeSelection/shared/YearSelector/YearSelector.js +1 -1
  74. package/dist/src/DateTimeSelection/shared/utilities/dateParsing.js +27 -9
  75. package/dist/src/DateTimeSelection/shared/utilities/japaneseCalendar.d.ts +36 -8
  76. package/dist/src/DateTimeSelection/shared/utilities/japaneseCalendar.js +82 -15
  77. package/dist/src/DateTimeSelection/shared/utilities/monthCellMonthFormat.d.ts +14 -0
  78. package/dist/src/DateTimeSelection/shared/utilities/monthCellMonthFormat.js +35 -0
  79. package/dist/src/MultilineTextBox/index.d.ts +10 -2
  80. package/dist/src/MultilineTextBox/index.js +9 -1
  81. package/dist/src/MultipleSelectBox/MultipleSelectBoxTrigger/MultipleSelectBoxTrigger.js +1 -3
  82. package/dist/src/RadioButtonCard/RadioButtonCard.js +1 -7
  83. package/dist/src/TextBox/TextBox.js +2 -6
  84. package/dist/src/{MultilineTextBox/MultilineTextBox.d.ts → Textarea/Textarea.d.ts} +5 -2
  85. package/dist/src/{MultilineTextBox/MultilineTextBox.js → Textarea/Textarea.js} +9 -5
  86. package/dist/src/{MultilineTextBox/MultilineTextBox.types.d.ts → Textarea/Textarea.types.d.ts} +2 -2
  87. package/dist/src/Textarea/Textarea.types.js +1 -0
  88. package/dist/src/Textarea/index.d.ts +2 -0
  89. package/dist/src/Textarea/index.js +1 -0
  90. package/dist/src/Tooltip/Tooltip.js +12 -3
  91. package/dist/src/Typography/Typography.js +1 -3
  92. package/dist/src/index.d.ts +1 -0
  93. package/dist/src/index.js +1 -0
  94. package/dist/styled-system/recipes/filter-date-range-picker-slot-recipe.d.ts +33 -0
  95. package/dist/styled-system/recipes/filter-date-range-picker-slot-recipe.js +48 -0
  96. package/dist/styled-system/recipes/filter-month-picker-slot-recipe.d.ts +33 -0
  97. package/dist/styled-system/recipes/filter-month-picker-slot-recipe.js +44 -0
  98. package/dist/styled-system/recipes/filter-month-range-picker-slot-recipe.d.ts +33 -0
  99. package/dist/styled-system/recipes/filter-month-range-picker-slot-recipe.js +44 -0
  100. package/dist/styled-system/recipes/index.d.ts +5 -1
  101. package/dist/styled-system/recipes/index.js +5 -1
  102. package/dist/styled-system/recipes/textarea-slot-recipe.d.ts +52 -0
  103. package/dist/styled-system/recipes/textarea-slot-recipe.js +64 -0
  104. package/dist/styled-system/recipes/time-picker-slot-recipe.d.ts +44 -0
  105. package/dist/styled-system/recipes/time-picker-slot-recipe.js +62 -0
  106. package/dist/styles.css +601 -25
  107. package/dist/tsconfig.build.tsbuildinfo +1 -1
  108. package/package.json +13 -11
  109. package/dist/src/DateTimeSelection/shared/BasePicker/YearSelector/YearSelector.d.ts +0 -18
  110. package/dist/src/DateTimeSelection/shared/BasePicker/YearSelector/YearSelector.js +0 -36
  111. package/dist/styled-system/recipes/multiline-text-box-slot-recipe.d.ts +0 -52
  112. package/dist/styled-system/recipes/multiline-text-box-slot-recipe.js +0 -64
  113. /package/dist/src/{MultilineTextBox/MultilineTextBox.types.js → DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.types.js} +0 -0
@@ -29,11 +29,5 @@ export const CheckboxCard = forwardRef(({ label, layout = 'vertical', renderDeta
29
29
  disableResponsive,
30
30
  isResponsive: Boolean(responsiveSlot.narrowViewport),
31
31
  });
32
- return (_jsxs("div", { className: cx(classes.root, 'mfui-CheckboxCard__root', className), children: [_jsx(FocusIndicator, { disableNestedFocus: true, children: _jsxs(Typography, { variant: "label", className: cx(classes.inputWrapper, 'mfui-CheckboxCard__inputWrapper'), as: "label", htmlFor: props.id, children: [_jsx(Checkbox, { ref: ref, ...props, className: cx(classes.input, 'mfui-CheckboxCard__input') }), _jsx("span", { className: cx(classes.label, 'mfui-CheckboxCard__label'), children: label })] }) }), responsiveSlot.narrowViewport && disableResponsive ? (
33
- /* When responsive is disabled, render single element but with both contents using CSS for switching */
34
- _jsxs(_Fragment, { children: [_jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsWide, 'mfui-CheckboxCard__details'), children: responsiveSlot.base(checkboxState) }), _jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsNarrow, 'mfui-CheckboxCard__details'), children: responsiveSlot.narrowViewport(checkboxState) })] })) : responsiveSlot.narrowViewport ? (
35
- /* When responsive is enabled, render separate wide/narrow elements */
36
- _jsxs(_Fragment, { children: [_jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsWide, 'mfui-CheckboxCard__details'), children: responsiveSlot.base(checkboxState) }), _jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsNarrow, 'mfui-CheckboxCard__details'), children: responsiveSlot.narrowViewport(checkboxState) })] })) : responsiveSlot.base ? (
37
- /* For function-based renderDetailsSlot or ResponsiveSlot with only base, use single details element */
38
- _jsx("div", { "data-mfui-content": "details", className: cx(classes.details, 'mfui-CheckboxCard__details'), children: responsiveSlot.base(checkboxState) })) : null] }));
32
+ return (_jsxs("div", { className: cx(classes.root, 'mfui-CheckboxCard__root', className), children: [_jsx(FocusIndicator, { disableNestedFocus: true, children: _jsxs(Typography, { variant: "label", className: cx(classes.inputWrapper, 'mfui-CheckboxCard__inputWrapper'), as: "label", htmlFor: props.id, children: [_jsx(Checkbox, { ref: ref, ...props, className: cx(classes.input, 'mfui-CheckboxCard__input') }), _jsx("span", { className: cx(classes.label, 'mfui-CheckboxCard__label'), children: label })] }) }), responsiveSlot.narrowViewport && disableResponsive ? (_jsxs(_Fragment, { children: [_jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsWide, 'mfui-CheckboxCard__details'), children: responsiveSlot.base(checkboxState) }), _jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsNarrow, 'mfui-CheckboxCard__details'), children: responsiveSlot.narrowViewport(checkboxState) })] })) : responsiveSlot.narrowViewport ? (_jsxs(_Fragment, { children: [_jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsWide, 'mfui-CheckboxCard__details'), children: responsiveSlot.base(checkboxState) }), _jsx("div", { "data-mfui-content": "details", className: cx(classes.details, classes.detailsNarrow, 'mfui-CheckboxCard__details'), children: responsiveSlot.narrowViewport(checkboxState) })] })) : responsiveSlot.base ? (_jsx("div", { "data-mfui-content": "details", className: cx(classes.details, 'mfui-CheckboxCard__details'), children: responsiveSlot.base(checkboxState) })) : null] }));
39
33
  });
@@ -4,7 +4,7 @@ import { BasePicker } from '../shared/BasePicker';
4
4
  import { DatePickerPanel } from './DatePickerPanel';
5
5
  import { CalendarLocaleProvider } from '../shared/CalendarLocale/CalendarLocaleContext';
6
6
  import { DisplayJapaneseCalendarProvider } from '../shared/CalendarLocale/DisplayJapaneseCalendarContext';
7
- import { dateToEnglishEraShort } from '../shared/utilities/japaneseCalendar';
7
+ import { dateToEnglishEraShort, dateToJapaneseEra } from '../shared/utilities/japaneseCalendar';
8
8
  /**
9
9
  * DatePicker component
10
10
  *
@@ -15,16 +15,7 @@ export function DatePicker({ format = 'YYYY/MM/DD', checkDisabledDate, minDate,
15
15
  const customFormatValue = useMemo(() => {
16
16
  if (!displayJapaneseCalendar)
17
17
  return;
18
- if (calendarLocale === 'en') {
19
- return (date) => dateToEnglishEraShort(date);
20
- }
21
- const formatter = new Intl.DateTimeFormat('ja-JP-u-ca-japanese', {
22
- era: 'long',
23
- year: 'numeric',
24
- month: 'narrow',
25
- day: 'numeric',
26
- });
27
- return (date) => formatter.format(date);
18
+ return calendarLocale === 'en' ? dateToEnglishEraShort : dateToJapaneseEra;
28
19
  }, [displayJapaneseCalendar, calendarLocale]);
29
20
  return (_jsx(BasePicker, { ...props, format: format, baseFormat: "YYYY-MM-DD", calendarLocale: calendarLocale, customFormatValue: customFormatValue, renderPopoverContent: ({ value, onChange }) => (_jsx(DisplayJapaneseCalendarProvider, { value: displayJapaneseCalendar, children: _jsx(CalendarLocaleProvider, { value: calendarLocale, children: _jsx(DatePickerPanel, { value: value, checkDisabledDate: checkDisabledDate, minDate: minDate, maxDate: maxDate, prevMonthIconButtonProps: prevMonthIconButtonProps, nextMonthIconButtonProps: nextMonthIconButtonProps, todayButtonProps: todayButtonProps, onChange: onChange }) }) })) }));
30
21
  }
@@ -5,4 +5,4 @@ import { type DateRangePickerProps } from './DateRangePicker.types';
5
5
  *
6
6
  * @param props - DateRangePicker props
7
7
  */
8
- export declare function DateRangePicker({ format, calendarLocale, ...restProps }: DateRangePickerProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function DateRangePicker({ format, calendarLocale, displayJapaneseCalendar, ...restProps }: DateRangePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -6,15 +6,23 @@ import { jsx as _jsx } from "react/jsx-runtime";
6
6
  * All logic and props are inherited from BaseRangePicker.
7
7
  * See BaseRangePicker for implementation details.
8
8
  */
9
+ import { useMemo } from 'react';
9
10
  import { BaseRangePicker } from '../shared/BaseRangePicker';
10
11
  import { CalendarLocaleProvider } from '../shared/CalendarLocale/CalendarLocaleContext';
12
+ import { DisplayJapaneseCalendarProvider } from '../shared/CalendarLocale/DisplayJapaneseCalendarContext';
11
13
  import { DateRangePickerPopover } from './DateRangePickerPopover/DateRangePickerPopover';
14
+ import { dateToEnglishEraShort, dateToJapaneseEra } from '../shared/utilities/japaneseCalendar';
12
15
  /**
13
16
  * DateRangePicker component for selecting date ranges.
14
17
  * Extends BaseRangePicker with date-specific functionality.
15
18
  *
16
19
  * @param props - DateRangePicker props
17
20
  */
18
- export function DateRangePicker({ format = 'YYYY/MM/DD', calendarLocale = 'ja', ...restProps }) {
19
- return (_jsx(BaseRangePicker, { ...restProps, format: format, calendarLocale: calendarLocale, renderPopoverContent: () => (_jsx(CalendarLocaleProvider, { value: calendarLocale, children: _jsx(DateRangePickerPopover, {}) })) }));
21
+ export function DateRangePicker({ format = 'YYYY/MM/DD', calendarLocale = 'ja', displayJapaneseCalendar = false, ...restProps }) {
22
+ const customFormatValue = useMemo(() => {
23
+ if (!displayJapaneseCalendar)
24
+ return;
25
+ return calendarLocale === 'en' ? dateToEnglishEraShort : dateToJapaneseEra;
26
+ }, [displayJapaneseCalendar, calendarLocale]);
27
+ return (_jsx(BaseRangePicker, { ...restProps, format: format, calendarLocale: calendarLocale, customFormatValue: customFormatValue, renderPopoverContent: () => (_jsx(DisplayJapaneseCalendarProvider, { value: displayJapaneseCalendar, children: _jsx(CalendarLocaleProvider, { value: calendarLocale, children: _jsx(DateRangePickerPopover, {}) }) })) }));
20
28
  }
@@ -10,4 +10,18 @@ export type DateRangePickerProps = BaseRangePickerProps & {
10
10
  * @default 'ja'
11
11
  */
12
12
  calendarLocale?: BasePickerProps['calendarLocale'];
13
+ /**
14
+ * Whether to display the date in Japanese calendar format (e.g., "令和7年1月15日")
15
+ * When enabled, the text inputs will show dates in Japanese calendar format
16
+ * while internal value handling remains unchanged.
17
+ *
18
+ * @default false
19
+ *
20
+ * @remarks
21
+ * - When combined with `calendarLocale="en"`, dates are romanized with
22
+ * zero-padded month/day (e.g., "Reiwa 7/01/15").
23
+ * - Pre-Meiji dates (year < 1868) fall back to the Gregorian year
24
+ * (e.g., "1867/12/31").
25
+ */
26
+ displayJapaneseCalendar?: boolean;
13
27
  };
@@ -13,6 +13,8 @@ import { useFocusTrap } from '../../../utilities/dom/useFocusTrap';
13
13
  import { getFocusableNodes } from '../../../utilities/dom/getFocusableNodes';
14
14
  import { getBasePickerLabel } from '../../shared/BasePicker/constants';
15
15
  import { useCalendarLocale } from '../../shared/CalendarLocale/CalendarLocaleContext';
16
+ import { useDisplayJapaneseCalendar } from '../../shared/CalendarLocale/DisplayJapaneseCalendarContext';
17
+ import { dateToEnglishEraMonthShort, dateToJapaneseEraMonth } from '../../shared/utilities/japaneseCalendar';
16
18
  // Utility function to add months to a date
17
19
  const addMonths = (date, months) => new Date(date.getFullYear(), date.getMonth() + months, 1);
18
20
  /**
@@ -38,6 +40,7 @@ const addMonths = (date, months) => new Date(date.getFullYear(), date.getMonth()
38
40
  export function DateRangePickerPopover() {
39
41
  const { viewingMonth, setViewingMonth, setPendingFocusDate, isDateDisabled, minDate, maxDate } = useBaseRangePickerContext();
40
42
  const calendarLocale = useCalendarLocale();
43
+ const displayJapaneseCalendar = useDisplayJapaneseCalendar();
41
44
  const rightViewingMonth = useMemo(() => addMonths(viewingMonth, 1), [viewingMonth]);
42
45
  // Check if navigation buttons should be disabled based on constraints
43
46
  const isPreviousMonthDisabled = useCallback(() => {
@@ -129,14 +132,19 @@ export function DateRangePickerPopover() {
129
132
  // Localized labels
130
133
  const previousMonthLabel = useMemo(() => getBasePickerLabel('GO_TO_PREV_MONTH', calendarLocale), [calendarLocale]);
131
134
  const nextMonthLabel = useMemo(() => getBasePickerLabel('GO_TO_NEXT_MONTH', calendarLocale), [calendarLocale]);
132
- // Localized month format functions
133
- const formatMonth = useMemo(() => (date) => {
135
+ // Localized month labels (Gregorian vs Japanese-calendar era, matching trigger formatting)
136
+ const formatMonth = useMemo(() => {
137
+ const firstOfMonth = (d) => new Date(d.getFullYear(), d.getMonth(), 1);
138
+ if (displayJapaneseCalendar) {
139
+ const formatEra = calendarLocale === 'ja' ? dateToJapaneseEraMonth : dateToEnglishEraMonthShort;
140
+ return (date) => formatEra(firstOfMonth(date));
141
+ }
134
142
  if (calendarLocale === 'ja') {
135
- return `${String(date.getFullYear())}年${String(date.getMonth() + 1)}月`;
143
+ return (date) => `${String(firstOfMonth(date).getFullYear())}年${String(firstOfMonth(date).getMonth() + 1)}月`;
136
144
  }
137
145
  const monthFormatter = new Intl.DateTimeFormat('en-US', { month: 'long' });
138
- return `${monthFormatter.format(date)} ${String(date.getFullYear())}`;
139
- }, [calendarLocale]);
146
+ return (date) => `${monthFormatter.format(firstOfMonth(date))} ${String(firstOfMonth(date).getFullYear())}`;
147
+ }, [calendarLocale, displayJapaneseCalendar]);
140
148
  return (_jsxs("div", { className: cx(classNames.root, 'mfui-DateRangePickerPopover__root'), onKeyDown: handleOnKeyDown, children: [_jsxs("div", { className: cx(classNames.navigation, 'mfui-DateRangePickerPopover__navigation'), children: [_jsxs("div", { className: cx(classNames.navigationColumn, 'mfui-DateRangePickerPopover__navigationColumn'), children: [_jsx(IconButton, { "aria-label": previousMonthLabel, disabled: isPreviousMonthDisabled(), onClick: () => {
141
149
  if (!isPreviousMonthDisabled()) {
142
150
  setViewingMonth(addMonths(viewingMonth, -1));
@@ -51,8 +51,8 @@ export function createDateRangePickerPopoverTestUtility({ user }) {
51
51
  return getAllDateCells().filter((element) => element.dataset['mfuiInRange'] === 'true');
52
52
  }
53
53
  function getLeftMonthLabel() {
54
- // Look for month labels - handles both Japanese (YYYY年MM月) and English (Month YYYY) formats
55
- const monthLabels = screen.getAllByText(/年.*月|\w+ \d{4}/);
54
+ // Month navigation headings: Gregorian JA/EN, Japanese-era JA, or romanized-era EN (e.g. Reiwa 7/01)
55
+ const monthLabels = screen.getAllByText(/年[0-90-9]*月|\d{4}年\d{1,2}月|\b(?:January|February|March|April|May|June|July|August|September|October|November|December)\b \d{4}|\b(?:Reiwa|Heisei|Showa|Taisho|Meiji)\s+\d{1,2}\/\d{2}|(?:令和|平成|昭和|大正|明治)(?:元|[0-90-9]{1,2})年[0-90-9]{1,2}月/);
56
56
  return monthLabels[0]?.textContent ?? null;
57
57
  }
58
58
  // Actions
@@ -5,6 +5,7 @@ import { flushSync } from 'react-dom';
5
5
  import { dayjs } from '../../../utilities/date/dayjs';
6
6
  import { useUpdateEffect } from '../../../utilities/effect/useUpdateEffect';
7
7
  import { useTransformedState } from '../../../utilities/state/useTransformedState';
8
+ import { parseInputDateSimple } from '../../shared/utilities/dateParsing';
8
9
  // eslint-disable-next-line @typescript-eslint/no-empty-function
9
10
  const noop = () => { };
10
11
  // Normalizes a date to the first of the month
@@ -92,14 +93,13 @@ const useDateRangePickerContextValue = ({ value, defaultValue, disabled = false,
92
93
  const handleStartDateStringChange = useCallback((value) => {
93
94
  setDateStrings([value, endDateString]);
94
95
  // Parse and update both viewing month and temporary selection for immediate visual feedback
95
- const parsedDate = dayjs(value, format, true);
96
- if (parsedDate.isValid() && !isDateOutsideConstraints(parsedDate.toDate(), minDate, maxDate)) {
97
- const newDate = parsedDate.toDate();
98
- setViewingMonth(newDate);
96
+ const parsedDate = parseInputDateSimple(value, format);
97
+ if (parsedDate && !isDateOutsideConstraints(parsedDate, minDate, maxDate)) {
98
+ setViewingMonth(parsedDate);
99
99
  // Update temporary selection for immediate calendar visual feedback
100
- setTemporaryStart(newDate);
100
+ setTemporaryStart(parsedDate);
101
101
  // Set pending focus date so when calendar opens, it focuses on this date
102
- setPendingFocusDate(parsedDate.format('YYYY-MM-DD'));
102
+ setPendingFocusDate(dayjs(parsedDate).format('YYYY-MM-DD'));
103
103
  }
104
104
  else if (value === '') {
105
105
  // Clear temporary selection if input is empty
@@ -110,14 +110,13 @@ const useDateRangePickerContextValue = ({ value, defaultValue, disabled = false,
110
110
  const handleEndDateStringChange = useCallback((value) => {
111
111
  setDateStrings([startDateString, value]);
112
112
  // Parse and update both viewing month and temporary selection for immediate visual feedback
113
- const parsedDate = dayjs(value, format, true);
114
- if (parsedDate.isValid() && !isDateOutsideConstraints(parsedDate.toDate(), minDate, maxDate)) {
115
- const newDate = parsedDate.toDate();
116
- setViewingMonth(newDate);
113
+ const parsedDate = parseInputDateSimple(value, format);
114
+ if (parsedDate && !isDateOutsideConstraints(parsedDate, minDate, maxDate)) {
115
+ setViewingMonth(parsedDate);
117
116
  // Update temporary selection for immediate calendar visual feedback
118
- setTemporaryEnd(newDate);
117
+ setTemporaryEnd(parsedDate);
119
118
  // Set pending focus date so when calendar opens, it focuses on this date
120
- setPendingFocusDate(parsedDate.format('YYYY-MM-DD'));
119
+ setPendingFocusDate(dayjs(parsedDate).format('YYYY-MM-DD'));
121
120
  }
122
121
  else if (value === '') {
123
122
  // Clear temporary selection if input is empty
@@ -1,6 +1,7 @@
1
1
  import { useMemo } from 'react';
2
2
  import { dayjs } from '../../../../utilities/date/dayjs';
3
3
  import { useDateRangePickerContext } from '../../DateRangePickerProvider';
4
+ import { parseInputDateSimple } from '../../../shared/utilities/dateParsing';
4
5
  const createInputProps = (baseProps, value, format, onStringChange, onDateChange, otherDate, isDateDisabled) => ({
5
6
  ...baseProps,
6
7
  value,
@@ -15,12 +16,12 @@ const createInputProps = (baseProps, value, format, onStringChange, onDateChange
15
16
  if (event.defaultPrevented)
16
17
  return;
17
18
  baseProps.onBlur?.(event);
18
- const dateValue = dayjs(value, format, true);
19
- if (!dateValue.isValid()) {
19
+ const parsedDate = parseInputDateSimple(value, format);
20
+ if (!parsedDate) {
20
21
  onDateChange(undefined, otherDate);
21
22
  return;
22
23
  }
23
- const currentDate = dateValue.startOf('day').toDate();
24
+ const currentDate = dayjs(parsedDate).startOf('day').toDate();
24
25
  // Check if the date is disabled due to constraints
25
26
  if (isDateDisabled?.(currentDate)) {
26
27
  // Clear the input if the date violates constraints
@@ -12,7 +12,9 @@ export function createDateRangePickerTriggerTestUtility({ user }) {
12
12
  return screen.queryByRole('button', { name: LOCALE.CLEAR_BUTTON_LABEL });
13
13
  }
14
14
  function getCalendarButton() {
15
- return screen.getByRole('button', { name: LOCALE.CALENDAR_BUTTON_LABEL });
15
+ return screen.getByRole('button', {
16
+ name: new RegExp(`^(?:${LOCALE.CALENDAR_BUTTON_LABEL}|Open calendar)$`),
17
+ });
16
18
  }
17
19
  // Value Queries
18
20
  function getStartValue() {
@@ -0,0 +1,6 @@
1
+ import { type InternalDateRangePickerContentProps } from './DateRangePickerContent.types';
2
+ /**
3
+ * Internal component that accesses BaseRangePickerContext for display and clear handling.
4
+ * Must be rendered inside a BaseRangePickerProvider.
5
+ */
6
+ export declare function InternalDateRangePickerContent({ classes, triggerRef, dialogId, label, format, disabled, className, clearButtonProps, targetDOMNode, isCalendarOpen, toggleCalendar, calendarLocale, }: InternalDateRangePickerContentProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,33 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback } from 'react';
4
+ import { Calendar as CalendarIcon } from '@moneyforward/mfui-icons-react';
5
+ import { Popover } from '../../../Popover';
6
+ import { FocusIndicator } from '../../../FocusIndicator';
7
+ import { ClearButton } from '../../../shared';
8
+ import { Typography } from '../../../Typography';
9
+ import { cx } from '../../../../styled-system/css';
10
+ import { dayjs } from '../../../utilities/date/dayjs';
11
+ import { DateRangePickerPopover } from '../../DateRangePicker/DateRangePickerPopover';
12
+ import { CalendarLocaleProvider } from '../../shared/CalendarLocale/CalendarLocaleContext';
13
+ import { useBaseRangePickerContext } from '../../shared/BaseRangePicker/BaseRangePickerProvider';
14
+ /**
15
+ * Internal component that accesses BaseRangePickerContext for display and clear handling.
16
+ * Must be rendered inside a BaseRangePickerProvider.
17
+ */
18
+ export function InternalDateRangePickerContent({ classes, triggerRef, dialogId, label, format, disabled, className, clearButtonProps, targetDOMNode, isCalendarOpen, toggleCalendar, calendarLocale, }) {
19
+ const { startDate, endDate, handleClear: contextHandleClear, setPendingFocusDate } = useBaseRangePickerContext();
20
+ const formattedStartDate = startDate ? dayjs(startDate).format(format) : undefined;
21
+ const formattedEndDate = endDate ? dayjs(endDate).format(format) : undefined;
22
+ const hasValue = !!(formattedStartDate && formattedEndDate);
23
+ const formattedValue = hasValue ? `${formattedStartDate}〜${formattedEndDate}` : undefined;
24
+ const handleTriggerClear = useCallback((event) => {
25
+ event.stopPropagation();
26
+ contextHandleClear();
27
+ }, [contextHandleClear]);
28
+ const handlePopoverOpen = useCallback(() => {
29
+ const dateToFocus = startDate ?? endDate ?? new Date();
30
+ setPendingFocusDate(dayjs(dateToFocus).format('YYYY-MM-DD'));
31
+ }, [startDate, endDate, setPendingFocusDate]);
32
+ return (_jsx(Popover, { open: isCalendarOpen, targetDOMNode: targetDOMNode, minWidth: "min-content", allowedPlacements: ['bottom-start', 'top-start'], renderTrigger: ({ setTriggerRef, togglePopover, handleTriggerKeyDown, handleTriggerBlur }) => (_jsxs("div", { ref: setTriggerRef, "data-selected": hasValue, className: cx(classes.wrapper, 'mfui-FilterDateRangePicker__wrapper'), children: [_jsx(FocusIndicator, { children: _jsxs("button", { ref: triggerRef, type: "button", disabled: disabled, className: cx(classes.trigger, 'mfui-FilterDateRangePicker__trigger', className), "data-selected": hasValue, "data-mfui-has-clear-button": hasValue, "aria-controls": dialogId, "aria-expanded": isCalendarOpen, "aria-haspopup": "dialog", onClick: togglePopover, onKeyDown: handleTriggerKeyDown, onBlur: handleTriggerBlur, children: [_jsx("span", { className: cx(classes.content, 'mfui-FilterDateRangePicker__content'), "data-mfui-content": "filter-date-range-picker-display-value", children: hasValue ? (_jsxs(Typography, { variant: "strongBody", children: [_jsx("span", { "data-mfui-content": "filter-date-range-picker-label", children: `${label}:` }), _jsx("span", { "data-mfui-content": "filter-date-range-picker-value", children: formattedValue })] })) : (_jsx(Typography, { variant: "controlLabel", children: _jsx("span", { "data-mfui-content": "filter-date-range-picker-label", children: label }) })) }), _jsx("span", { className: cx(classes.calendarIcon, 'mfui-FilterDateRangePicker__calendarIcon'), children: _jsx(CalendarIcon, { "aria-hidden": true }) })] }) }), hasValue ? (_jsx("div", { className: cx(classes.clearButtonWrapper, 'mfui-FilterDateRangePicker__clearButtonWrapper'), children: _jsx(ClearButton, { "aria-label": clearButtonProps?.['aria-label'] ?? '値をクリアする', "data-mfui-content": "filter-date-range-picker-clear-button", disabled: disabled, onClick: handleTriggerClear }) })) : null] })), renderContent: () => (_jsx("div", { id: dialogId, role: "dialog", "aria-label": label, className: cx(classes.popoverWrapper, 'mfui-FilterDateRangePicker__popoverWrapper'), children: _jsx(CalendarLocaleProvider, { value: calendarLocale, children: _jsx(DateRangePickerPopover, {}) }) })), onOpenStateChanged: toggleCalendar, onOpen: handlePopoverOpen }));
33
+ }
@@ -0,0 +1,17 @@
1
+ import { type filterDateRangePickerSlotRecipe } from '../../../../styled-system/recipes';
2
+ import { type FilterDateRangePickerProps } from '../FilterDateRangePicker.types';
3
+ import { type BasePickerProps } from '../../shared/BasePicker';
4
+ export type InternalDateRangePickerContentProps = {
5
+ classes: ReturnType<typeof filterDateRangePickerSlotRecipe>;
6
+ triggerRef: React.RefObject<HTMLButtonElement | null>;
7
+ dialogId: string;
8
+ label: string;
9
+ format: string;
10
+ disabled: boolean;
11
+ className: string | undefined;
12
+ clearButtonProps: FilterDateRangePickerProps['clearButtonProps'];
13
+ targetDOMNode: FilterDateRangePickerProps['targetDOMNode'];
14
+ isCalendarOpen: boolean;
15
+ toggleCalendar: () => void;
16
+ calendarLocale: NonNullable<BasePickerProps['calendarLocale']>;
17
+ };
@@ -0,0 +1,14 @@
1
+ import { type FilterDateRangePickerProps } from './FilterDateRangePicker.types';
2
+ /**
3
+ * FilterDateRangePicker component.
4
+ *
5
+ * A filter trigger component that allows selecting a date range via a dual-calendar picker.
6
+ * Displays the label alone when no range is selected, and "label:startDate〜endDate"
7
+ * after a range is applied. Conforms to the FilterSelectBox visual style.
8
+ *
9
+ * Note: The trigger markup intentionally does not reuse FilterTrigger because
10
+ * FilterTrigger hardcodes the DropdownIcon and adds a hidden <input> element,
11
+ * neither of which are appropriate here. FilterDateRangePicker uses CalendarIcon and
12
+ * has no form-field semantics.
13
+ */
14
+ export declare function FilterDateRangePicker({ label, value, defaultValue, onChange, open, onOpenStateChanged, format, clearButtonProps, disabled, className, targetDOMNode, minDate, maxDate, calendarLocale, }: FilterDateRangePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useId, useRef } from 'react';
4
+ import { filterDateRangePickerSlotRecipe } from '../../../styled-system/recipes';
5
+ import { useDisclosure } from '../../utilities/state/useDisclosure';
6
+ import { BaseRangePickerProvider } from '../shared/BaseRangePicker/BaseRangePickerProvider';
7
+ import { InternalDateRangePickerContent } from './DateRangePickerContent/DateRangePickerContent';
8
+ const DEFAULT_FORMAT = 'YYYY/MM/DD';
9
+ /**
10
+ * FilterDateRangePicker component.
11
+ *
12
+ * A filter trigger component that allows selecting a date range via a dual-calendar picker.
13
+ * Displays the label alone when no range is selected, and "label:startDate〜endDate"
14
+ * after a range is applied. Conforms to the FilterSelectBox visual style.
15
+ *
16
+ * Note: The trigger markup intentionally does not reuse FilterTrigger because
17
+ * FilterTrigger hardcodes the DropdownIcon and adds a hidden <input> element,
18
+ * neither of which are appropriate here. FilterDateRangePicker uses CalendarIcon and
19
+ * has no form-field semantics.
20
+ */
21
+ export function FilterDateRangePicker({ label, value, defaultValue, onChange, open, onOpenStateChanged, format = DEFAULT_FORMAT, clearButtonProps, disabled = false, className, targetDOMNode, minDate, maxDate, calendarLocale = 'ja', }) {
22
+ const classes = filterDateRangePickerSlotRecipe();
23
+ const triggerRef = useRef(null);
24
+ const dialogId = useId();
25
+ const { isOpen: isCalendarOpen, open: openCalendar, close: closeCalendar, toggle: toggleCalendar, } = useDisclosure({
26
+ // disabled takes precedence: never open the calendar when the trigger is disabled.
27
+ value: disabled ? false : open,
28
+ // onToggle fires for Escape and outside-click; focus trigger on close.
29
+ onToggle: (nextOpen) => {
30
+ if (!nextOpen) {
31
+ triggerRef.current?.focus();
32
+ }
33
+ onOpenStateChanged?.(nextOpen);
34
+ },
35
+ // onClose fires for programmatic close (Apply/Cancel buttons); focus trigger there too.
36
+ onClose: () => {
37
+ triggerRef.current?.focus();
38
+ onOpenStateChanged?.(false);
39
+ },
40
+ });
41
+ return (_jsx(BaseRangePickerProvider, { value: value, defaultValue: defaultValue, format: format, isOpen: isCalendarOpen, open: openCalendar, close: closeCalendar, minDate: minDate, maxDate: maxDate, disabled: disabled, onChange: onChange, children: _jsx(InternalDateRangePickerContent, { classes: classes, triggerRef: triggerRef, dialogId: dialogId, label: label, format: format, disabled: disabled, className: className, clearButtonProps: clearButtonProps, targetDOMNode: targetDOMNode, isCalendarOpen: isCalendarOpen, toggleCalendar: toggleCalendar, calendarLocale: calendarLocale }) }));
42
+ }
@@ -0,0 +1,78 @@
1
+ import { type PopoverProps } from '../../Popover';
2
+ import { type BasePickerProps } from '../shared/BasePicker';
3
+ import { type DateRangeInput } from '../shared/BaseRangePicker/BaseRangePicker.types';
4
+ export type FilterDateRangePickerProps = {
5
+ /**
6
+ * The label to display for the filter.
7
+ */
8
+ label: string;
9
+ /**
10
+ * The selected date range value (controlled).
11
+ * Pass `[undefined, undefined]` to clear the selection in controlled mode.
12
+ */
13
+ value?: DateRangeInput;
14
+ /**
15
+ * The default selected date range value for uncontrolled usage.
16
+ */
17
+ defaultValue?: DateRangeInput;
18
+ /**
19
+ * Callback fired when the selected date range changes.
20
+ * Called with `[startDate, endDate]` when a range is applied, or `[undefined, undefined]` when cleared.
21
+ *
22
+ * @param dates - The new selected date range.
23
+ */
24
+ onChange?: (dates: DateRangeInput) => void;
25
+ /**
26
+ * Whether the calendar popover is open (controlled).
27
+ */
28
+ open?: boolean;
29
+ /**
30
+ * Callback fired when the open state of the calendar changes.
31
+ *
32
+ * @param open - The new open state.
33
+ */
34
+ onOpenStateChanged?: (open: boolean) => void;
35
+ /**
36
+ * The display format for the selected dates.
37
+ * Follows dayjs format tokens.
38
+ *
39
+ * @default 'YYYY/MM/DD'
40
+ */
41
+ format?: string;
42
+ /**
43
+ * Props for the clear button, shown automatically when a date range is selected.
44
+ *
45
+ * @property aria-label - The alternative text for the clear button. Default is '値をクリアする'.
46
+ */
47
+ clearButtonProps?: {
48
+ 'aria-label'?: string;
49
+ };
50
+ /**
51
+ * Whether the component is disabled.
52
+ *
53
+ * @default false
54
+ */
55
+ disabled?: boolean;
56
+ /**
57
+ * Additional class name applied to the trigger button.
58
+ */
59
+ className?: string;
60
+ /**
61
+ * The target DOM node to render the calendar popover into.
62
+ */
63
+ targetDOMNode?: PopoverProps['targetDOMNode'];
64
+ /**
65
+ * The minimum selectable date.
66
+ */
67
+ minDate?: Date;
68
+ /**
69
+ * The maximum selectable date.
70
+ */
71
+ maxDate?: Date;
72
+ /**
73
+ * The locale of the calendar (affects navigation labels and month display format).
74
+ *
75
+ * @default 'ja'
76
+ */
77
+ calendarLocale?: BasePickerProps['calendarLocale'];
78
+ };
@@ -0,0 +1,2 @@
1
+ export { FilterDateRangePicker } from './FilterDateRangePicker';
2
+ export type { FilterDateRangePickerProps } from './FilterDateRangePicker.types';
@@ -0,0 +1 @@
1
+ export { FilterDateRangePicker } from './FilterDateRangePicker';
@@ -0,0 +1,14 @@
1
+ import { type FilterMonthPickerProps } from './FilterMonthPicker.types';
2
+ /**
3
+ * FilterMonthPicker component.
4
+ *
5
+ * A filter trigger component that allows selecting a month via a month picker.
6
+ * Displays the label alone when no month is selected, and "label:formatted month"
7
+ * after a month is selected. Conforms to the FilterSelectBox visual style.
8
+ *
9
+ * Note: The trigger markup intentionally does not reuse FilterTrigger because
10
+ * FilterTrigger hardcodes the DropdownIcon and adds a hidden <input> element,
11
+ * neither of which are appropriate here. FilterMonthPicker uses CalendarIcon and
12
+ * has no form-field semantics.
13
+ */
14
+ export declare function FilterMonthPicker({ label, value, defaultValue, onChange, open, onOpenStateChanged, format, clearButtonProps, disabled, className, targetDOMNode, checkDisabledMonth, minMonth, maxMonth, calendarLocale, }: FilterMonthPickerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,75 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useId, useRef, useState } from 'react';
4
+ import { BasePickerProvider } from '../shared/BasePicker/BasePickerProvider';
5
+ import { useDisclosure } from '../../utilities/state/useDisclosure';
6
+ import { dayjs } from '../../utilities/date/dayjs';
7
+ import { filterMonthPickerSlotRecipe } from '../../../styled-system/recipes';
8
+ import { InternalMonthPickerPopover } from './MonthPickerPopover/MonthPickerPopover';
9
+ const DEFAULT_FORMAT = 'YYYY/MM';
10
+ /**
11
+ * FilterMonthPicker component.
12
+ *
13
+ * A filter trigger component that allows selecting a month via a month picker.
14
+ * Displays the label alone when no month is selected, and "label:formatted month"
15
+ * after a month is selected. Conforms to the FilterSelectBox visual style.
16
+ *
17
+ * Note: The trigger markup intentionally does not reuse FilterTrigger because
18
+ * FilterTrigger hardcodes the DropdownIcon and adds a hidden <input> element,
19
+ * neither of which are appropriate here. FilterMonthPicker uses CalendarIcon and
20
+ * has no form-field semantics.
21
+ */
22
+ export function FilterMonthPicker({ label, value, defaultValue, onChange, open, onOpenStateChanged, format = DEFAULT_FORMAT, clearButtonProps, disabled = false, className, targetDOMNode, checkDisabledMonth, minMonth, maxMonth, calendarLocale = 'ja', }) {
23
+ const classes = filterMonthPickerSlotRecipe();
24
+ const triggerRef = useRef(null);
25
+ const dialogId = useId();
26
+ const [internalValue, setInternalValue] = useState(defaultValue);
27
+ const currentValue = value !== undefined ? value : internalValue;
28
+ // When a controlled value transitions back to undefined, clear any stale internal value
29
+ // so the component doesn't show a previously-selected uncontrolled month.
30
+ const previousValueRef = useRef(value);
31
+ useEffect(() => {
32
+ const previousValue = previousValueRef.current;
33
+ previousValueRef.current = value;
34
+ if (previousValue !== undefined && value === undefined) {
35
+ setInternalValue(undefined);
36
+ }
37
+ }, [value]);
38
+ const { isOpen: isPickerOpen, toggle: togglePicker, close: closePicker, } = useDisclosure({
39
+ // disabled takes precedence: never open the picker when the trigger is disabled.
40
+ value: disabled ? false : open,
41
+ // onToggle fires for Escape and outside-click (via togglePicker); focus trigger on close.
42
+ onToggle: (nextOpen) => {
43
+ if (!nextOpen) {
44
+ triggerRef.current?.focus();
45
+ }
46
+ onOpenStateChanged?.(nextOpen);
47
+ },
48
+ // onClose fires for programmatic close (month selection); focus trigger there too.
49
+ onClose: () => {
50
+ triggerRef.current?.focus();
51
+ },
52
+ });
53
+ const handleValueChange = useCallback((date) => {
54
+ if (value === undefined) {
55
+ setInternalValue(date);
56
+ }
57
+ onChange?.(date);
58
+ // closePicker() fires useDisclosure.onClose (returns focus to trigger) but intentionally
59
+ // does NOT fire onToggle. The explicit onOpenStateChanged call below is the single path
60
+ // for notifying the parent when a month selection closes the popover.
61
+ closePicker();
62
+ onOpenStateChanged?.(false);
63
+ }, [value, onChange, closePicker, onOpenStateChanged]);
64
+ const handleClear = useCallback((event) => {
65
+ event.stopPropagation();
66
+ if (value === undefined) {
67
+ setInternalValue(undefined);
68
+ }
69
+ onChange?.();
70
+ triggerRef.current?.focus();
71
+ }, [value, onChange, triggerRef]);
72
+ const formattedValue = currentValue ? dayjs(currentValue).format(format) : undefined;
73
+ const hasValue = !!formattedValue;
74
+ return (_jsx(BasePickerProvider, { value: currentValue, baseFormat: "YYYY-MM", children: _jsx(InternalMonthPickerPopover, { currentValue: currentValue, defaultValue: defaultValue, isPickerOpen: isPickerOpen, togglePicker: togglePicker, handleValueChange: handleValueChange, handleClear: handleClear, disabled: disabled, className: className, classes: classes, formattedValue: formattedValue, hasValue: hasValue, label: label, clearButtonProps: clearButtonProps, checkDisabledMonth: checkDisabledMonth, minMonth: minMonth, maxMonth: maxMonth, calendarLocale: calendarLocale, triggerRef: triggerRef, targetDOMNode: targetDOMNode, dialogId: dialogId }) }));
75
+ }
@@ -0,0 +1,83 @@
1
+ import { type PopoverProps } from '../../Popover';
2
+ import { type BasePickerProps } from '../shared/BasePicker';
3
+ export type FilterMonthPickerProps = {
4
+ /**
5
+ * The label to display for the filter.
6
+ */
7
+ label: string;
8
+ /**
9
+ * The selected month value (controlled).
10
+ */
11
+ value?: Date;
12
+ /**
13
+ * The default selected month value for uncontrolled usage.
14
+ */
15
+ defaultValue?: Date;
16
+ /**
17
+ * Callback fired when the selected month changes.
18
+ *
19
+ * @param value - The new selected month, or undefined when cleared.
20
+ */
21
+ onChange?: (value?: Date) => void;
22
+ /**
23
+ * Whether the month picker popover is open (controlled).
24
+ */
25
+ open?: boolean;
26
+ /**
27
+ * Callback fired when the open state of the picker changes.
28
+ *
29
+ * @param open - The new open state.
30
+ */
31
+ onOpenStateChanged?: (open: boolean) => void;
32
+ /**
33
+ * The display format for the selected month.
34
+ * Follows dayjs format tokens.
35
+ *
36
+ * @default 'YYYY/MM'
37
+ */
38
+ format?: string;
39
+ /**
40
+ * Props for the clear button, shown automatically when a month is selected.
41
+ *
42
+ * @property aria-label - The alternative text for the clear button. Default is '値をクリアする'.
43
+ */
44
+ clearButtonProps?: {
45
+ 'aria-label'?: string;
46
+ };
47
+ /**
48
+ * Whether the component is disabled.
49
+ *
50
+ * @default false
51
+ */
52
+ disabled?: boolean;
53
+ /**
54
+ * Additional class name applied to the trigger button.
55
+ */
56
+ className?: string;
57
+ /**
58
+ * The target DOM node to render the month picker popover into.
59
+ */
60
+ targetDOMNode?: PopoverProps['targetDOMNode'];
61
+ /**
62
+ * Function to check if a month should be disabled in the picker.
63
+ *
64
+ * @param date - The date to check (first day of the month).
65
+ *
66
+ * @returns true if the month should be disabled.
67
+ */
68
+ checkDisabledMonth?: (date: Date) => boolean;
69
+ /**
70
+ * The minimum selectable month.
71
+ */
72
+ minMonth?: Date;
73
+ /**
74
+ * The maximum selectable month.
75
+ */
76
+ maxMonth?: Date;
77
+ /**
78
+ * The locale of the calendar (affects navigation labels and month display format).
79
+ *
80
+ * @default 'ja'
81
+ */
82
+ calendarLocale?: BasePickerProps['calendarLocale'];
83
+ };