@moneyforward/mfui-components 3.18.0 → 3.20.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 (75) hide show
  1. package/dist/src/CheckboxCard/CheckboxCard.js +1 -7
  2. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerProvider/DateRangePickerProvider.js +11 -12
  3. package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerTrigger/hooks/useDateRangeTriggerValueController.js +4 -3
  4. package/dist/src/DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.d.ts +6 -0
  5. package/dist/src/DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.js +33 -0
  6. package/dist/src/DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.types.d.ts +17 -0
  7. package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.d.ts +14 -0
  8. package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.js +42 -0
  9. package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.types.d.ts +78 -0
  10. package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.types.js +1 -0
  11. package/dist/src/DateTimeSelection/FilterDateRangePicker/index.d.ts +2 -0
  12. package/dist/src/DateTimeSelection/FilterDateRangePicker/index.js +1 -0
  13. package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.d.ts +14 -0
  14. package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.js +75 -0
  15. package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.types.d.ts +83 -0
  16. package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.types.js +1 -0
  17. package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.d.ts +6 -0
  18. package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.js +45 -0
  19. package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.types.d.ts +24 -0
  20. package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.types.js +1 -0
  21. package/dist/src/DateTimeSelection/FilterMonthPicker/index.d.ts +2 -0
  22. package/dist/src/DateTimeSelection/FilterMonthPicker/index.js +1 -0
  23. package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.d.ts +15 -0
  24. package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.js +89 -0
  25. package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.types.d.ts +75 -0
  26. package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.types.js +1 -0
  27. package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.d.ts +5 -0
  28. package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.js +54 -0
  29. package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.types.d.ts +19 -0
  30. package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.types.js +1 -0
  31. package/dist/src/DateTimeSelection/FilterMonthRangePicker/index.d.ts +2 -0
  32. package/dist/src/DateTimeSelection/FilterMonthRangePicker/index.js +1 -0
  33. package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePicker.js +1 -1
  34. package/dist/src/DateTimeSelection/index.d.ts +3 -0
  35. package/dist/src/DateTimeSelection/index.js +3 -0
  36. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.d.ts +1 -1
  37. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.js +26 -10
  38. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.types.d.ts +9 -0
  39. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerProvider/BaseRangePickerProvider.js +32 -15
  40. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerProvider/BaseRangePickerProvider.types.d.ts +10 -0
  41. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/BaseRangePickerTrigger.js +4 -6
  42. package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/hooks/useDateRangeTriggerValueController.js +4 -3
  43. package/dist/src/DateTimeSelection/shared/MonthGrid/MonthGrid.js +3 -3
  44. package/dist/src/DateTimeSelection/shared/utilities/dateParsing.js +11 -0
  45. package/dist/src/MultilineTextBox/index.d.ts +10 -2
  46. package/dist/src/MultilineTextBox/index.js +9 -1
  47. package/dist/src/MultipleSelectBox/MultipleSelectBoxTrigger/MultipleSelectBoxTrigger.js +1 -3
  48. package/dist/src/RadioButtonCard/RadioButtonCard.js +1 -7
  49. package/dist/src/TextBox/TextBox.js +2 -6
  50. package/dist/src/{MultilineTextBox/MultilineTextBox.d.ts → Textarea/Textarea.d.ts} +5 -2
  51. package/dist/src/{MultilineTextBox/MultilineTextBox.js → Textarea/Textarea.js} +9 -5
  52. package/dist/src/{MultilineTextBox/MultilineTextBox.types.d.ts → Textarea/Textarea.types.d.ts} +2 -2
  53. package/dist/src/Textarea/Textarea.types.js +1 -0
  54. package/dist/src/Textarea/index.d.ts +2 -0
  55. package/dist/src/Textarea/index.js +1 -0
  56. package/dist/src/Tooltip/Tooltip.js +12 -3
  57. package/dist/src/Typography/Typography.js +1 -3
  58. package/dist/src/index.d.ts +1 -0
  59. package/dist/src/index.js +1 -0
  60. package/dist/styled-system/recipes/filter-date-range-picker-slot-recipe.d.ts +33 -0
  61. package/dist/styled-system/recipes/filter-date-range-picker-slot-recipe.js +48 -0
  62. package/dist/styled-system/recipes/filter-month-picker-slot-recipe.d.ts +33 -0
  63. package/dist/styled-system/recipes/filter-month-picker-slot-recipe.js +44 -0
  64. package/dist/styled-system/recipes/filter-month-range-picker-slot-recipe.d.ts +33 -0
  65. package/dist/styled-system/recipes/filter-month-range-picker-slot-recipe.js +44 -0
  66. package/dist/styled-system/recipes/index.d.ts +4 -1
  67. package/dist/styled-system/recipes/index.js +4 -1
  68. package/dist/styled-system/recipes/textarea-slot-recipe.d.ts +52 -0
  69. package/dist/styled-system/recipes/textarea-slot-recipe.js +64 -0
  70. package/dist/styles.css +433 -24
  71. package/dist/tsconfig.build.tsbuildinfo +1 -1
  72. package/package.json +11 -9
  73. package/dist/styled-system/recipes/multiline-text-box-slot-recipe.d.ts +0 -52
  74. package/dist/styled-system/recipes/multiline-text-box-slot-recipe.js +0 -64
  75. /package/dist/src/{MultilineTextBox/MultilineTextBox.types.js → DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.types.js} +0 -0
@@ -0,0 +1,89 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useId, useRef, useState } from 'react';
4
+ import { BaseRangePickerProvider } from '../shared/BaseRangePicker/BaseRangePickerProvider';
5
+ import { useDisclosure } from '../../utilities/state/useDisclosure';
6
+ import { dayjs } from '../../utilities/date/dayjs';
7
+ import { filterMonthRangePickerSlotRecipe } from '../../../styled-system/recipes';
8
+ import { SEPARATOR_CONTENT } from '../shared/BaseRangePicker/BaseRangePickerTrigger/constants';
9
+ import { InternalMonthRangePickerPopover } from './MonthRangePickerPopover/MonthRangePickerPopover';
10
+ const DEFAULT_FORMAT = 'YYYY/MM';
11
+ /**
12
+ * FilterMonthRangePicker component.
13
+ *
14
+ * A filter trigger component that allows selecting a month range via the same panel as
15
+ * {@link MonthRangePicker}. Displays the label alone when no range is selected, and
16
+ * `label:start〜end` after both bounds are selected. Conforms to the FilterSelectBox / FilterDateBox
17
+ * visual style.
18
+ *
19
+ * Note: The trigger markup intentionally does not reuse FilterTrigger because
20
+ * FilterTrigger hardcodes the DropdownIcon and adds a hidden `<input>` element,
21
+ * neither of which are appropriate here. FilterMonthRangePicker uses CalendarIcon and
22
+ * has no form-field semantics.
23
+ */
24
+ export function FilterMonthRangePicker({ label, value, defaultValue, onChange, open, onOpenStateChanged, format = DEFAULT_FORMAT, initialDisplayedMonths = 'currentAndNext', clearButtonProps, disabled = false, className, targetDOMNode, minMonth, maxMonth, }) {
25
+ const classes = filterMonthRangePickerSlotRecipe();
26
+ const triggerRef = useRef(null);
27
+ const dialogId = useId();
28
+ const [internalValue, setInternalValue] = useState(() => defaultValue ?? [undefined, undefined]);
29
+ const currentValue = value !== undefined ? value : internalValue;
30
+ const previousValueRef = useRef(value);
31
+ useEffect(() => {
32
+ const previousValue = previousValueRef.current;
33
+ previousValueRef.current = value;
34
+ if (previousValue !== undefined && value === undefined) {
35
+ setInternalValue(defaultValue ?? [undefined, undefined]);
36
+ }
37
+ }, [value, defaultValue]);
38
+ const { isOpen: isPickerOpen, toggle: togglePicker, close: closePicker, open: openPicker, } = useDisclosure({
39
+ value: disabled ? false : open,
40
+ onToggle: (nextOpen) => {
41
+ if (!nextOpen) {
42
+ triggerRef.current?.focus();
43
+ }
44
+ onOpenStateChanged?.(nextOpen);
45
+ },
46
+ onClose: () => {
47
+ triggerRef.current?.focus();
48
+ },
49
+ });
50
+ const closePickerAndNotify = useCallback(() => {
51
+ closePicker();
52
+ onOpenStateChanged?.(false);
53
+ }, [closePicker, onOpenStateChanged]);
54
+ const handleRangeChange = useCallback((dates) => {
55
+ if (value === undefined) {
56
+ setInternalValue(dates);
57
+ }
58
+ onChange?.(dates);
59
+ }, [value, onChange]);
60
+ const handleClear = useCallback((event) => {
61
+ event.stopPropagation();
62
+ if (value === undefined) {
63
+ setInternalValue([undefined, undefined]);
64
+ }
65
+ onChange?.([undefined, undefined]);
66
+ triggerRef.current?.focus();
67
+ }, [value, onChange]);
68
+ const [startMonth, endMonth] = currentValue;
69
+ const formattedValue = startMonth && endMonth
70
+ ? `${dayjs(startMonth).format(format)}${SEPARATOR_CONTENT}${dayjs(endMonth).format(format)}`
71
+ : undefined;
72
+ const hasValue = !!formattedValue;
73
+ const initialViewingDate = (() => {
74
+ if (initialDisplayedMonths !== 'previousAndCurrent') {
75
+ return;
76
+ }
77
+ const startDate = value?.[0] ?? defaultValue?.[0];
78
+ if (!startDate) {
79
+ const today = new Date();
80
+ return new Date(today.getFullYear() - 1, 0, 1);
81
+ }
82
+ const endDate = value?.[1] ?? defaultValue?.[1];
83
+ if (endDate && startDate.getFullYear() !== endDate.getFullYear()) {
84
+ return;
85
+ }
86
+ return new Date(startDate.getFullYear() - 1, 0, 1);
87
+ })();
88
+ return (_jsx(BaseRangePickerProvider, { value: currentValue, defaultValue: defaultValue, disabled: disabled, format: format, isOpen: isPickerOpen, open: openPicker, close: closePickerAndNotify, minDate: minMonth, maxDate: maxMonth, initialDisplayedMonths: initialDisplayedMonths, initialViewingDate: initialViewingDate, onChange: handleRangeChange, children: _jsx(InternalMonthRangePickerPopover, { isPickerOpen: isPickerOpen, togglePicker: togglePicker, handleClear: handleClear, disabled: disabled, className: className, classes: classes, formattedValue: formattedValue, hasValue: hasValue, label: label, clearButtonProps: clearButtonProps, minMonth: minMonth, maxMonth: maxMonth, triggerRef: triggerRef, targetDOMNode: targetDOMNode, dialogId: dialogId }) }));
89
+ }
@@ -0,0 +1,75 @@
1
+ import { type PopoverProps } from '../../Popover';
2
+ import { type DateRangeInput } from '../shared/BaseRangePicker/BaseRangePicker.types';
3
+ export type FilterMonthRangePickerProps = {
4
+ /**
5
+ * The label to display for the filter.
6
+ */
7
+ label: string;
8
+ /**
9
+ * The selected month range (controlled). Each entry is the first day of a month.
10
+ */
11
+ value?: DateRangeInput;
12
+ /**
13
+ * The default month range for uncontrolled usage.
14
+ */
15
+ defaultValue?: DateRangeInput;
16
+ /**
17
+ * Callback fired when the selected month range changes.
18
+ *
19
+ * @param value - The new range `[startMonth, endMonth]`, or partial/empty tuples while interacting.
20
+ */
21
+ onChange?: (value: DateRangeInput) => void;
22
+ /**
23
+ * Whether the month range 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 each bound of the range.
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 complete range 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 range picker popover into.
59
+ */
60
+ targetDOMNode?: PopoverProps['targetDOMNode'];
61
+ /**
62
+ * The minimum selectable month.
63
+ */
64
+ minMonth?: Date;
65
+ /**
66
+ * The maximum selectable month.
67
+ */
68
+ maxMonth?: Date;
69
+ /**
70
+ * Controls which years are initially displayed in the two-panel calendar when there is no value.
71
+ *
72
+ * @default 'currentAndNext'
73
+ */
74
+ initialDisplayedMonths?: 'currentAndNext' | 'previousAndCurrent';
75
+ };
@@ -0,0 +1,5 @@
1
+ import { type InternalMonthRangePickerPopoverProps } from './MonthRangePickerPopover.types';
2
+ /**
3
+ * Internal popover UI for FilterMonthRangePicker. Must render inside BaseRangePickerProvider.
4
+ */
5
+ export declare function InternalMonthRangePickerPopover({ isPickerOpen, togglePicker, handleClear, disabled, className, classes, formattedValue, hasValue, label, clearButtonProps, minMonth, maxMonth, triggerRef, targetDOMNode, dialogId, }: InternalMonthRangePickerPopoverProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,54 @@
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 { MonthRangePickerPanel } from '../../MonthRangePicker/MonthRangePickerPanel';
12
+ import { useBaseRangePickerContext } from '../../shared/BaseRangePicker/BaseRangePickerProvider';
13
+ import { useFocusTrap } from '../../../utilities/dom/useFocusTrap';
14
+ import { getFocusableNodes } from '../../../utilities/dom/getFocusableNodes';
15
+ /**
16
+ * Internal popover UI for FilterMonthRangePicker. Must render inside BaseRangePickerProvider.
17
+ */
18
+ export function InternalMonthRangePickerPopover({ isPickerOpen, togglePicker, handleClear, disabled, className, classes, formattedValue, hasValue, label, clearButtonProps, minMonth, maxMonth, triggerRef, targetDOMNode, dialogId, }) {
19
+ const { viewingMonth, setViewingMonth, temporaryStart, temporaryEnd, startDate, endDate, setPendingFocusDate } = useBaseRangePickerContext();
20
+ const { handleOnKeyDown } = useFocusTrap({
21
+ onPressingTabOnNotFocusableElement: (event) => {
22
+ const nodes = getFocusableNodes(event.currentTarget);
23
+ const focusableNodeAfterFocusingNode = nodes.find((element) =>
24
+ // eslint-disable-next-line no-bitwise
25
+ element.compareDocumentPosition(event.target) & Node.DOCUMENT_POSITION_PRECEDING);
26
+ const firstFocusableNode = nodes.at(0);
27
+ const nextFocusableNode = focusableNodeAfterFocusingNode ?? firstFocusableNode;
28
+ nextFocusableNode?.focus();
29
+ event.preventDefault();
30
+ },
31
+ });
32
+ const handlePopoverOpen = useCallback(() => {
33
+ let dateToFocus;
34
+ if (temporaryStart) {
35
+ dateToFocus = temporaryStart;
36
+ }
37
+ else if (temporaryEnd) {
38
+ dateToFocus = temporaryEnd;
39
+ }
40
+ else if (startDate) {
41
+ dateToFocus = startDate;
42
+ }
43
+ else if (endDate) {
44
+ dateToFocus = endDate;
45
+ }
46
+ else {
47
+ dateToFocus = new Date();
48
+ }
49
+ const monthToFocus = dayjs(dateToFocus).startOf('month').toDate();
50
+ setViewingMonth(monthToFocus);
51
+ setPendingFocusDate(dayjs(monthToFocus).format('YYYY-MM'));
52
+ }, [temporaryStart, temporaryEnd, startDate, endDate, setViewingMonth, setPendingFocusDate]);
53
+ return (_jsx(Popover, { open: isPickerOpen, targetDOMNode: targetDOMNode, renderTrigger: ({ setTriggerRef, togglePopover, handleTriggerKeyDown, handleTriggerBlur }) => (_jsxs("div", { ref: setTriggerRef, "data-selected": hasValue, className: cx(classes.wrapper, 'mfui-FilterMonthRangePicker__wrapper'), children: [_jsx(FocusIndicator, { children: _jsxs("button", { ref: triggerRef, type: "button", disabled: disabled, className: cx(classes.trigger, 'mfui-FilterMonthRangePicker__trigger', className), "data-selected": hasValue, "data-mfui-has-clear-button": hasValue, "aria-controls": dialogId, "aria-expanded": isPickerOpen, "aria-haspopup": "dialog", onClick: togglePopover, onKeyDown: handleTriggerKeyDown, onBlur: handleTriggerBlur, children: [_jsx("span", { className: cx(classes.content, 'mfui-FilterMonthRangePicker__content'), "data-mfui-content": "filter-month-range-picker-display-value", children: hasValue ? (_jsxs(Typography, { variant: "strongBody", children: [_jsx("span", { "data-mfui-content": "filter-month-range-picker-label", children: `${label}:` }), _jsx("span", { "data-mfui-content": "filter-month-range-picker-value", children: formattedValue })] })) : (_jsx(Typography, { variant: "controlLabel", children: _jsx("span", { "data-mfui-content": "filter-month-range-picker-label", children: label }) })) }), _jsx("span", { className: cx(classes.calendarIcon, 'mfui-FilterMonthRangePicker__calendarIcon'), children: _jsx(CalendarIcon, { "aria-hidden": true }) })] }) }), hasValue ? (_jsx("div", { className: cx(classes.clearButtonWrapper, 'mfui-FilterMonthRangePicker__clearButtonWrapper'), children: _jsx(ClearButton, { "aria-label": clearButtonProps?.['aria-label'] ?? '値をクリアする', "data-mfui-content": "filter-month-range-picker-clear-button", disabled: disabled, onClick: handleClear }) })) : null] })), renderContent: () => (_jsx("div", { id: dialogId, role: "dialog", "aria-label": label, className: "mfui-FilterMonthRangePicker__popoverWrapper", onKeyDown: handleOnKeyDown, children: _jsx(MonthRangePickerPanel, { viewingValue: viewingMonth, setViewingValue: setViewingMonth, value: [temporaryStart, temporaryEnd], minMonth: minMonth, maxMonth: maxMonth }) })), minWidth: "min-content", allowedPlacements: ['bottom-start', 'top-start'], onOpenStateChanged: togglePicker, onOpen: handlePopoverOpen }));
54
+ }
@@ -0,0 +1,19 @@
1
+ import { type filterMonthRangePickerSlotRecipe } from '../../../../styled-system/recipes';
2
+ import { type FilterMonthRangePickerProps } from '../FilterMonthRangePicker.types';
3
+ export type InternalMonthRangePickerPopoverProps = {
4
+ isPickerOpen: boolean;
5
+ togglePicker: () => void;
6
+ handleClear: (event: React.MouseEvent<HTMLButtonElement>) => void;
7
+ disabled: boolean;
8
+ className: string | undefined;
9
+ classes: ReturnType<typeof filterMonthRangePickerSlotRecipe>;
10
+ formattedValue: string | undefined;
11
+ hasValue: boolean;
12
+ label: string;
13
+ clearButtonProps: FilterMonthRangePickerProps['clearButtonProps'];
14
+ minMonth: FilterMonthRangePickerProps['minMonth'];
15
+ maxMonth: FilterMonthRangePickerProps['maxMonth'];
16
+ triggerRef: React.RefObject<HTMLButtonElement | null>;
17
+ targetDOMNode: FilterMonthRangePickerProps['targetDOMNode'];
18
+ dialogId: string;
19
+ };
@@ -0,0 +1,2 @@
1
+ export { FilterMonthRangePicker } from './FilterMonthRangePicker';
2
+ export type { FilterMonthRangePickerProps } from './FilterMonthRangePicker.types';
@@ -0,0 +1 @@
1
+ export { FilterMonthRangePicker } from './FilterMonthRangePicker';
@@ -51,7 +51,7 @@ export function MonthRangePicker({ format = 'YYYY/MM', minMonth, maxMonth, start
51
51
  // Same-year value + previousAndCurrent: show previous year on the left
52
52
  return new Date(startDate.getFullYear() - 1, 0, 1);
53
53
  })();
54
- return (_jsx(BaseRangePicker, { ...restProps, format: format, initialDisplayedMonths: initialDisplayedMonths, initialViewingDate: initialViewingDate, startInputProps: {
54
+ return (_jsx(BaseRangePicker, { ...restProps, format: format, pendingFocusDayjsFormat: "YYYY-MM", initialDisplayedMonths: initialDisplayedMonths, initialViewingDate: initialViewingDate, startInputProps: {
55
55
  placeholder: '開始月',
56
56
  ...startInputProps,
57
57
  }, endInputProps: {
@@ -1,5 +1,8 @@
1
1
  export * from './DatePicker';
2
2
  export * from './FilterDatePicker';
3
+ export * from './FilterDateRangePicker';
4
+ export * from './FilterMonthPicker';
5
+ export * from './FilterMonthRangePicker';
3
6
  export * from './MonthPicker';
4
7
  export * from './DateRangePicker';
5
8
  export * from './MonthRangePicker';
@@ -1,5 +1,8 @@
1
1
  export * from './DatePicker';
2
2
  export * from './FilterDatePicker';
3
+ export * from './FilterDateRangePicker';
4
+ export * from './FilterMonthPicker';
5
+ export * from './FilterMonthRangePicker';
3
6
  export * from './MonthPicker';
4
7
  export * from './DateRangePicker';
5
8
  export * from './MonthRangePicker';
@@ -3,4 +3,4 @@ import { type BaseRangePickerProps } from './BaseRangePicker.types';
3
3
  * BaseRangePicker component
4
4
  * A generic component for selecting a range of dates with configurable format
5
5
  */
6
- export declare function BaseRangePicker({ value, defaultValue, disabled, invalid, targetDOMNode, onChange, format, onOpenStateChanged, disableAutoOpen, onBlur, allowedPlacements, enableViewportConstraint, enableClearButton, clearButtonProps, minDate, maxDate, enableAutoUnmount, minWidth, renderPopoverContent, startInputProps, endInputProps, calendarLocale, initialDisplayedMonths, initialViewingDate, }: BaseRangePickerProps): import("react/jsx-runtime").JSX.Element;
6
+ export declare function BaseRangePicker({ value, defaultValue, disabled, invalid, targetDOMNode, onChange, format, onOpenStateChanged, disableAutoOpen, onBlur, allowedPlacements, enableViewportConstraint, enableClearButton, clearButtonProps, minDate, maxDate, enableAutoUnmount, minWidth, renderPopoverContent, startInputProps, endInputProps, calendarLocale, initialDisplayedMonths, initialViewingDate, pendingFocusDayjsFormat, }: BaseRangePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -9,8 +9,8 @@ import { dayjs } from '../../../utilities/date/dayjs';
9
9
  /**
10
10
  * Internal component that has access to BaseRangePickerContext
11
11
  */
12
- function InternalBaseRangePicker({ startInputRef, endInputRef, disabled, invalid, format, enableClearButton, clearButtonProps, targetDOMNode, onBlur, isOpen, open, close, onOpenStateChanged, enableAutoUnmount, allowedPlacements, enableViewportConstraint, minWidth, renderPopoverContent, startInputProps = {}, endInputProps = {}, calendarLocale = 'ja', }) {
13
- const { setPendingFocusDate, temporaryStart, temporaryEnd, viewingMonth, setViewingMonth } = useBaseRangePickerContext();
12
+ function InternalBaseRangePicker({ startInputRef, endInputRef, disabled, invalid, format, enableClearButton, clearButtonProps, targetDOMNode, onBlur, isOpen, open, close, onOpenStateChanged, enableAutoUnmount, allowedPlacements, enableViewportConstraint, minWidth, renderPopoverContent, startInputProps = {}, endInputProps = {}, calendarLocale = 'ja', pendingFocusDayjsFormat = 'YYYY-MM-DD', }) {
13
+ const { setPendingFocusDate, temporaryStart, temporaryEnd, startDate, endDate, viewingMonth, setViewingMonth } = useBaseRangePickerContext();
14
14
  // Custom onOpen handler to prevent auto-focus when clicking on input elements
15
15
  // and implement smart focus management for calendar dates
16
16
  const handlePopoverOpen = useCallback((event) => {
@@ -31,7 +31,7 @@ function InternalBaseRangePicker({ startInputRef, endInputRef, disabled, invalid
31
31
  // 3. Selected start date (if exists)
32
32
  // 4. Selected end date (if exists and no start date)
33
33
  // 5. Current date (today)
34
- // 6. First non-disabled date (handled by CalendarGrid automatically)
34
+ // 6. First non-disabled cell (handled by CalendarGrid / MonthGrid automatically)
35
35
  let dateToFocus;
36
36
  if (temporaryStart) {
37
37
  dateToFocus = temporaryStart;
@@ -39,16 +39,32 @@ function InternalBaseRangePicker({ startInputRef, endInputRef, disabled, invalid
39
39
  else if (temporaryEnd) {
40
40
  dateToFocus = temporaryEnd;
41
41
  }
42
+ else if (startDate) {
43
+ dateToFocus = startDate;
44
+ }
45
+ else if (endDate) {
46
+ dateToFocus = endDate;
47
+ }
42
48
  else {
43
- // Default to today's date - CalendarGrid will handle finding first non-disabled date if today is disabled
49
+ // Default to today's date - the grid will focus the first non-disabled cell if needed
44
50
  dateToFocus = new Date();
45
51
  }
46
- // Set the pending focus date so CalendarGrid can focus on it when it renders
47
- // Use the format from the context to handle both date and month formats
48
- const formattedDate = dayjs(dateToFocus).format(format);
52
+ // Set the pending focus key so the popover grid can focus the matching cell when it renders.
53
+ // Must match DateCell `data-mfui-date` (YYYY-MM-DD) or MonthCell `data-mfui-month` (YYYY-MM).
54
+ const formattedDate = dayjs(dateToFocus).format(pendingFocusDayjsFormat);
49
55
  setPendingFocusDate(formattedDate);
50
56
  open();
51
- }, [startInputRef, endInputRef, temporaryStart, temporaryEnd, setPendingFocusDate, open, format]);
57
+ }, [
58
+ startInputRef,
59
+ endInputRef,
60
+ temporaryStart,
61
+ temporaryEnd,
62
+ startDate,
63
+ endDate,
64
+ setPendingFocusDate,
65
+ open,
66
+ pendingFocusDayjsFormat,
67
+ ]);
52
68
  return (_jsx(Popover, { enableAutomaticPortalTargetResolution: true, open: isOpen, allowedPlacements: allowedPlacements, enableViewportConstraint: enableViewportConstraint, minWidth: minWidth, renderTrigger: ({ setTriggerRef, handleTriggerBlur, handleTriggerKeyDown, openPopover }) => (_jsx(BaseRangePickerTrigger, { ref: setTriggerRef, disabled: disabled, invalid: invalid, format: format, enableClearValue: enableClearButton, clearButtonProps: clearButtonProps, startInputRef: startInputRef, endInputRef: endInputRef, startInputProps: startInputProps, endInputProps: endInputProps, popoverOpenFunction: openPopover, calendarLocale: calendarLocale, onBlur: handleTriggerBlur, onKeyDown: handleTriggerKeyDown })), renderContent: () => renderPopoverContent({
53
69
  viewingValue: viewingMonth,
54
70
  setViewingValue: setViewingMonth,
@@ -61,12 +77,12 @@ function InternalBaseRangePicker({ startInputRef, endInputRef, disabled, invalid
61
77
  * BaseRangePicker component
62
78
  * A generic component for selecting a range of dates with configurable format
63
79
  */
64
- export function BaseRangePicker({ value, defaultValue, disabled, invalid, targetDOMNode, onChange, format = 'YYYY/MM/DD', onOpenStateChanged, disableAutoOpen = false, onBlur, allowedPlacements, enableViewportConstraint, enableClearButton = false, clearButtonProps, minDate, maxDate, enableAutoUnmount = true, minWidth, renderPopoverContent, startInputProps, endInputProps, calendarLocale = 'ja', initialDisplayedMonths, initialViewingDate, }) {
80
+ export function BaseRangePicker({ value, defaultValue, disabled, invalid, targetDOMNode, onChange, format = 'YYYY/MM/DD', onOpenStateChanged, disableAutoOpen = false, onBlur, allowedPlacements, enableViewportConstraint, enableClearButton = false, clearButtonProps, minDate, maxDate, enableAutoUnmount = true, minWidth, renderPopoverContent, startInputProps, endInputProps, calendarLocale = 'ja', initialDisplayedMonths, initialViewingDate, pendingFocusDayjsFormat = 'YYYY-MM-DD', }) {
65
81
  const { isOpen, open, close } = useDisclosure({ value: false });
66
82
  const startInputRef = useRef(null);
67
83
  const endInputRef = useRef(null);
68
84
  // Default renderPopoverContent implementation for backward compatibility
69
85
  const defaultRenderPopoverContent = useCallback(() => _jsx(BaseRangePickerPopover, { calendarLocale: calendarLocale }), [calendarLocale]);
70
86
  const finalRenderPopoverContent = renderPopoverContent ?? defaultRenderPopoverContent;
71
- return (_jsx(BaseRangePickerProvider, { value: value, defaultValue: defaultValue, disabled: disabled, format: format, isOpen: isOpen, open: open, close: close, disableAutoOpen: disableAutoOpen, minDate: minDate, maxDate: maxDate, initialDisplayedMonths: initialDisplayedMonths, initialViewingDate: initialViewingDate, onChange: onChange, children: _jsx(InternalBaseRangePicker, { startInputRef: startInputRef, endInputRef: endInputRef, disabled: disabled, invalid: invalid, format: format, enableClearButton: enableClearButton, clearButtonProps: clearButtonProps, targetDOMNode: targetDOMNode, allowedPlacements: allowedPlacements, enableViewportConstraint: enableViewportConstraint, enableAutoUnmount: enableAutoUnmount, minWidth: minWidth, startInputProps: startInputProps, endInputProps: endInputProps, isOpen: isOpen, open: open, close: close, renderPopoverContent: finalRenderPopoverContent, calendarLocale: calendarLocale, onBlur: onBlur, onOpenStateChanged: onOpenStateChanged }) }));
87
+ return (_jsx(BaseRangePickerProvider, { value: value, defaultValue: defaultValue, disabled: disabled, format: format, isOpen: isOpen, open: open, close: close, disableAutoOpen: disableAutoOpen, minDate: minDate, maxDate: maxDate, initialDisplayedMonths: initialDisplayedMonths, initialViewingDate: initialViewingDate, pendingFocusDayjsFormat: pendingFocusDayjsFormat, onChange: onChange, children: _jsx(InternalBaseRangePicker, { startInputRef: startInputRef, endInputRef: endInputRef, disabled: disabled, invalid: invalid, format: format, enableClearButton: enableClearButton, clearButtonProps: clearButtonProps, targetDOMNode: targetDOMNode, allowedPlacements: allowedPlacements, enableViewportConstraint: enableViewportConstraint, enableAutoUnmount: enableAutoUnmount, minWidth: minWidth, startInputProps: startInputProps, endInputProps: endInputProps, isOpen: isOpen, open: open, close: close, renderPopoverContent: finalRenderPopoverContent, calendarLocale: calendarLocale, pendingFocusDayjsFormat: pendingFocusDayjsFormat, onBlur: onBlur, onOpenStateChanged: onOpenStateChanged }) }));
72
88
  }
@@ -307,4 +307,13 @@ export type BaseRangePickerProps = {
307
307
  * @internal
308
308
  */
309
309
  initialViewingDate?: Date;
310
+ /**
311
+ * dayjs format for pending grid focus strings (`pendingFocusDate` / `pendingFocusMonth`).
312
+ * Must match the grid: `YYYY-MM-DD` for day calendars, `YYYY-MM` for month grids.
313
+ *
314
+ * @internal
315
+ *
316
+ * @default 'YYYY-MM-DD'
317
+ */
318
+ pendingFocusDayjsFormat?: 'YYYY-MM-DD' | 'YYYY-MM';
310
319
  };
@@ -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 '../../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
@@ -97,7 +98,7 @@ function autoCompletePartialRange(dates) {
97
98
  }
98
99
  return dates;
99
100
  }
100
- const useBaseRangePickerContextValue = ({ value, defaultValue, disabled = false, onChange, format = 'YYYY/MM/DD', isOpen = false, open = noop, close = noop, disableAutoOpen, minDate, maxDate, initialDisplayedMonths = 'currentAndNext', initialViewingDate, }) => {
101
+ const useBaseRangePickerContextValue = ({ value, defaultValue, disabled = false, onChange, format = 'YYYY/MM/DD', isOpen = false, open = noop, close = noop, disableAutoOpen, minDate, maxDate, initialDisplayedMonths = 'currentAndNext', initialViewingDate, pendingFocusDayjsFormat = 'YYYY-MM-DD', }) => {
101
102
  const [dates, setDates] = useTransformedState(defaultValue ?? [undefined, undefined], normalizeToStartOfDay);
102
103
  const [dateStrings, setDateStrings] = useState(() => {
103
104
  const [startDate, endDate] = value ?? defaultValue ?? [undefined, undefined];
@@ -176,39 +177,55 @@ const useBaseRangePickerContextValue = ({ value, defaultValue, disabled = false,
176
177
  const handleStartDateStringChange = useCallback((value) => {
177
178
  setDateStrings([value, endDateString]);
178
179
  // Parse and update both viewing month and temporary selection for immediate visual feedback
179
- const parsedDate = dayjs(value, format, true);
180
- if (parsedDate.isValid() && !isDateOutsideConstraints(parsedDate.toDate(), minDate, maxDate, format)) {
181
- const newDate = parsedDate.toDate();
182
- setViewingMonth(newDate);
180
+ const parsedDate = parseInputDateSimple(value, format);
181
+ if (parsedDate && !isDateOutsideConstraints(parsedDate, minDate, maxDate, format)) {
182
+ setViewingMonth(parsedDate);
183
183
  // Update temporary selection for immediate calendar visual feedback
184
- setTemporaryStart(newDate);
184
+ setTemporaryStart(parsedDate);
185
185
  // Set pending focus date so when calendar opens, it focuses on this date
186
- setPendingFocusDate(parsedDate.format('YYYY-MM-DD'));
186
+ setPendingFocusDate(dayjs(parsedDate).format(pendingFocusDayjsFormat));
187
187
  }
188
188
  else if (value === '') {
189
189
  // Clear temporary selection if input is empty
190
190
  setTemporaryStart(undefined);
191
191
  setPendingFocusDate(null);
192
192
  }
193
- }, [endDateString, format, minDate, maxDate, setViewingMonth, setTemporaryStart, setPendingFocusDate]);
193
+ }, [
194
+ endDateString,
195
+ format,
196
+ minDate,
197
+ maxDate,
198
+ setViewingMonth,
199
+ setTemporaryStart,
200
+ setPendingFocusDate,
201
+ pendingFocusDayjsFormat,
202
+ ]);
194
203
  const handleEndDateStringChange = useCallback((value) => {
195
204
  setDateStrings([startDateString, value]);
196
205
  // Parse and update both viewing month and temporary selection for immediate visual feedback
197
- const parsedDate = dayjs(value, format, true);
198
- if (parsedDate.isValid() && !isDateOutsideConstraints(parsedDate.toDate(), minDate, maxDate, format)) {
199
- const newDate = parsedDate.toDate();
200
- setViewingMonth(newDate);
206
+ const parsedDate = parseInputDateSimple(value, format);
207
+ if (parsedDate && !isDateOutsideConstraints(parsedDate, minDate, maxDate, format)) {
208
+ setViewingMonth(parsedDate);
201
209
  // Update temporary selection for immediate calendar visual feedback
202
- setTemporaryEnd(newDate);
210
+ setTemporaryEnd(parsedDate);
203
211
  // Set pending focus date so when calendar opens, it focuses on this date
204
- setPendingFocusDate(parsedDate.format('YYYY-MM-DD'));
212
+ setPendingFocusDate(dayjs(parsedDate).format(pendingFocusDayjsFormat));
205
213
  }
206
214
  else if (value === '') {
207
215
  // Clear temporary selection if input is empty
208
216
  setTemporaryEnd(undefined);
209
217
  setPendingFocusDate(null);
210
218
  }
211
- }, [startDateString, format, minDate, maxDate, setViewingMonth, setTemporaryEnd, setPendingFocusDate]);
219
+ }, [
220
+ startDateString,
221
+ format,
222
+ minDate,
223
+ maxDate,
224
+ setViewingMonth,
225
+ setTemporaryEnd,
226
+ setPendingFocusDate,
227
+ pendingFocusDayjsFormat,
228
+ ]);
212
229
  const handleClear = useCallback(() => {
213
230
  // handleChange already calls setDates and setDateStrings, so we don't need to call them again
214
231
  handleChange([undefined, undefined]);
@@ -97,4 +97,14 @@ export type BaseRangePickerProviderProps = {
97
97
  * @internal
98
98
  */
99
99
  initialViewingDate?: Date;
100
+ /**
101
+ * dayjs format string for `pendingFocusDate` so it matches the focused grid cell's `data-mfui-*` attribute.
102
+ *
103
+ * @internal
104
+ *
105
+ * @default 'YYYY-MM-DD' — matches {@link DateCell} `data-mfui-date`
106
+ *
107
+ * @example 'YYYY-MM' — matches {@link MonthCell} `data-mfui-month` (MonthRangePicker)
108
+ */
109
+ pendingFocusDayjsFormat?: 'YYYY-MM-DD' | 'YYYY-MM';
100
110
  };
@@ -19,7 +19,7 @@ import { useDateRangeTriggerInputClick } from './hooks/useDateRangeTriggerInputC
19
19
  */
20
20
  export const BaseRangePickerTrigger = forwardRef(({ enableClearValue = false, invalid = false, size = 'medium', disabled = false, wrapperProps = {}, clearButtonProps = {}, startInputProps = {}, endInputProps = {}, calendarButtonProps = {}, startInputRef, endInputRef, popoverOpenFunction, format = 'YYYY-MM-DD', calendarLocale = 'ja', ...props }, ref) => {
21
21
  const isTouchDevice = useIsTouchDevice();
22
- const { toggle, disableAutoOpen } = useBaseRangePickerContext();
22
+ const { toggle, disableAutoOpen, isOpen } = useBaseRangePickerContext();
23
23
  const { handleInputClick } = useDateRangeTriggerInputClick(disableAutoOpen ?? false, popoverOpenFunction);
24
24
  const { onClick: onClickClearButton, 'aria-label': clearAriaLabel } = clearButtonProps;
25
25
  const { onClick: onCalendarClick, 'aria-label': calendarAriaLabel } = calendarButtonProps;
@@ -41,14 +41,12 @@ export const BaseRangePickerTrigger = forwardRef(({ enableClearValue = false, in
41
41
  const finalEndPlaceholder = endInputPlaceholder ?? getBaseRangePickerTriggerLabel('END_DATE_PLACEHOLDER', calendarLocale);
42
42
  const shouldShowClearButton = enableClearValue && (enhancedStartInputProps.value || enhancedEndInputProps.value);
43
43
  const classes = baseRangePickerTriggerSlotRecipe({ invalid, disabled, size });
44
+ // Popover open: mark calendar icon active. Use `undefined` when closed — `data-active="false"` is still truthy in CSS.
45
+ const shouldCalendarIconActive = isOpen ? true : undefined;
44
46
  const handleClickClearButton = useCallback((event) => {
45
47
  handleClear();
46
48
  onClickClearButton?.(event);
47
49
  }, [handleClear, onClickClearButton]);
48
- return (_jsxs("div", { ...wrapperProps, ...props, ref: ref, className: cx(classes.root, 'mfui-BaseRangePickerTrigger__root', wrapperProps.className), children: [_jsx("div", { className: cx(classes.inputGroup, 'mfui-BaseRangePickerTrigger__inputGroup'), children: isTouchDevice ? (
49
- // Touch device: hidden inputs + display divs
50
- _jsxs(_Fragment, { children: [_jsx("input", { type: "hidden", value: enhancedStartInputProps.value, name: startInputProps.name }), _jsx("input", { type: "hidden", value: enhancedEndInputProps.value, name: endInputProps.name }), _jsx("div", { className: cx(classes.input, 'mfui-BaseRangePickerTrigger__input'), onClick: !disableAutoOpen && !disabled ? handleInputClick('start', startInputProps.onClick) : undefined, children: enhancedStartInputProps.value || finalStartPlaceholder }), _jsx(Typography, { variant: "controlLabel", className: cx(classes.separator, 'mfui-BaseRangePickerTrigger__separator'), children: SEPARATOR_CONTENT }), _jsx("div", { className: cx(classes.input, 'mfui-BaseRangePickerTrigger__input'), onClick: !disableAutoOpen && !disabled ? handleInputClick('end', endInputProps.onClick) : undefined, children: enhancedEndInputProps.value || finalEndPlaceholder })] })) : (
51
- // Desktop: Standard inputs with FocusIndicator
52
- _jsxs(_Fragment, { children: [_jsx(FocusIndicator, { position: "inside", children: _jsx("input", { ref: startInputRef, disabled: disabled, className: cx(classes.input, 'mfui-BaseRangePickerTrigger__input'), ...enhancedStartInputProps, placeholder: finalStartPlaceholder }) }), _jsx(Typography, { variant: "controlLabel", className: cx(classes.separator, 'mfui-BaseRangePickerTrigger__separator'), children: SEPARATOR_CONTENT }), _jsx(FocusIndicator, { position: "inside", children: _jsx("input", { ref: endInputRef, disabled: disabled, className: cx(classes.input, 'mfui-BaseRangePickerTrigger__input'), ...enhancedEndInputProps, placeholder: finalEndPlaceholder }) })] })) }), shouldShowClearButton ? (_jsx(ClearButton, { size: size === 'small' ? 'small' : 'default', "aria-label": clearAriaLabel ?? getBaseRangePickerTriggerLabel('CLEAR_BUTTON_LABEL', calendarLocale), className: cx(classes.actionIcon, 'mfui-BaseRangePickerTrigger__actionIcon'), disabled: disabled, onClick: handleClickClearButton })) : null, _jsx(IconButton, { size: size === 'small' ? 'small' : 'default', "aria-label": calendarAriaLabel ?? getBaseRangePickerTriggerLabel('CALENDAR_BUTTON_LABEL', calendarLocale), className: cx(classes.actionIcon, 'mfui-BaseRangePickerTrigger__actionIcon'), disabled: disabled, onClick: onCalendarClick ?? toggle, children: _jsx(Calendar, {}) })] }));
50
+ return (_jsxs("div", { ...wrapperProps, ...props, ref: ref, className: cx(classes.root, 'mfui-BaseRangePickerTrigger__root', wrapperProps.className), children: [_jsx("div", { className: cx(classes.inputGroup, 'mfui-BaseRangePickerTrigger__inputGroup'), children: isTouchDevice ? (_jsxs(_Fragment, { children: [_jsx("input", { type: "hidden", value: enhancedStartInputProps.value, name: startInputProps.name }), _jsx("input", { type: "hidden", value: enhancedEndInputProps.value, name: endInputProps.name }), _jsx("div", { className: cx(classes.input, 'mfui-BaseRangePickerTrigger__input'), onClick: !disableAutoOpen && !disabled ? handleInputClick('start', startInputProps.onClick) : undefined, children: enhancedStartInputProps.value || finalStartPlaceholder }), _jsx(Typography, { variant: "controlLabel", className: cx(classes.separator, 'mfui-BaseRangePickerTrigger__separator'), children: SEPARATOR_CONTENT }), _jsx("div", { className: cx(classes.input, 'mfui-BaseRangePickerTrigger__input'), onClick: !disableAutoOpen && !disabled ? handleInputClick('end', endInputProps.onClick) : undefined, children: enhancedEndInputProps.value || finalEndPlaceholder })] })) : (_jsxs(_Fragment, { children: [_jsx(FocusIndicator, { position: "inside", children: _jsx("input", { ref: startInputRef, disabled: disabled, className: cx(classes.input, 'mfui-BaseRangePickerTrigger__input'), ...enhancedStartInputProps, placeholder: finalStartPlaceholder }) }), _jsx(Typography, { variant: "controlLabel", className: cx(classes.separator, 'mfui-BaseRangePickerTrigger__separator'), children: SEPARATOR_CONTENT }), _jsx(FocusIndicator, { position: "inside", children: _jsx("input", { ref: endInputRef, disabled: disabled, className: cx(classes.input, 'mfui-BaseRangePickerTrigger__input'), ...enhancedEndInputProps, placeholder: finalEndPlaceholder }) })] })) }), shouldShowClearButton ? (_jsx(ClearButton, { size: size === 'small' ? 'small' : 'default', "aria-label": clearAriaLabel ?? getBaseRangePickerTriggerLabel('CLEAR_BUTTON_LABEL', calendarLocale), className: cx(classes.actionIcon, 'mfui-BaseRangePickerTrigger__actionIcon'), disabled: disabled, onClick: handleClickClearButton })) : null, _jsx(IconButton, { size: size === 'small' ? 'small' : 'default', "aria-label": calendarAriaLabel ?? getBaseRangePickerTriggerLabel('CALENDAR_BUTTON_LABEL', calendarLocale), className: cx(classes.actionIcon, 'mfui-BaseRangePickerTrigger__actionIcon'), "data-active": shouldCalendarIconActive, disabled: disabled, onClick: onCalendarClick ?? toggle, children: _jsx(Calendar, {}) })] }));
53
51
  });
54
52
  BaseRangePickerTrigger.displayName = 'BaseRangePicker.Trigger';
@@ -1,6 +1,7 @@
1
1
  import { useMemo } from 'react';
2
2
  import { dayjs } from '../../../../../utilities/date/dayjs';
3
3
  import { useBaseRangePickerContext } from '../../BaseRangePickerProvider';
4
+ import { parseInputDateSimple } from '../../../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
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import React, { useEffect } from 'react';
2
+ import React, { useLayoutEffect } from 'react';
3
3
  import { monthGridSlotRecipe } from '../../../../styled-system/recipes';
4
4
  import { cx } from '../../../../styled-system/css';
5
5
  /**
@@ -28,8 +28,8 @@ export function MonthGrid({ year, selectedMonth, minMonth, maxMonth, checkDisabl
28
28
  const firstTabbableIndex = monthStates.find(({ disabled }) => !disabled)?.month ?? -1;
29
29
  return { monthStates, firstTabbableIndex };
30
30
  }, [months, year, minMonth, maxMonth, checkDisabledMonth]);
31
- // Handle pending focus
32
- useEffect(() => {
31
+ // Handle pending focus before paint so the focused month cell matches keyboard / popover-open flows.
32
+ useLayoutEffect(() => {
33
33
  if (!pendingFocusMonth)
34
34
  return;
35
35
  const targetCell = document.querySelector(`[data-mfui-month="${pendingFocusMonth}"]`);
@@ -37,6 +37,17 @@ export function parseInputDate(inputString, format) {
37
37
  if (dayjsResult.isValid()) {
38
38
  return { isValid: true, result: dayjsResult.toDate() };
39
39
  }
40
+ // Fall back to single-digit month/day tokens when the format uses two-digit tokens (MM/DD).
41
+ // For example, "2026/6/6" fails with "YYYY/MM/DD" but succeeds with "YYYY/M/D".
42
+ // Strict mode is intentional here: it ensures YYYY matches exactly 4 digits, preventing
43
+ // format-mismatch inputs like "31/12/2023" from accidentally passing via native Date fallback.
44
+ const yyyymdFormat = format.replaceAll(/\bMM\b/g, 'M').replaceAll(/\bDD\b/g, 'D');
45
+ if (yyyymdFormat !== format) {
46
+ const yyyymdResult = dayjs(inputString, yyyymdFormat, true);
47
+ if (yyyymdResult.isValid()) {
48
+ return { isValid: true, result: yyyymdResult.toDate() };
49
+ }
50
+ }
40
51
  // Fall back to YYYYMMDD format (8-digit number without delimiters) if the specified format fails
41
52
  // This allows inputs like "20251215" to be parsed even when format is "YYYY-MM-DD"
42
53
  if (/^\d{8}$/.test(inputString)) {
@@ -1,2 +1,10 @@
1
- export { MultilineTextBox } from './MultilineTextBox';
2
- export { MultilineTextBoxProps } from './MultilineTextBox.types';
1
+ /**
2
+ * MultilineTextBox backward compatibility re-exports.
3
+ *
4
+ * MultilineTextBox has been renamed to Textarea.
5
+ * Please migrate to Textarea. MultilineTextBox will be removed in a future major version (v5.0.0).
6
+ *
7
+ * @see Textarea
8
+ */
9
+ export { Textarea as MultilineTextBox } from '../Textarea';
10
+ export type { TextareaProps as MultilineTextBoxProps } from '../Textarea';
@@ -1 +1,9 @@
1
- export { MultilineTextBox } from './MultilineTextBox';
1
+ /**
2
+ * MultilineTextBox backward compatibility re-exports.
3
+ *
4
+ * MultilineTextBox has been renamed to Textarea.
5
+ * Please migrate to Textarea. MultilineTextBox will be removed in a future major version (v5.0.0).
6
+ *
7
+ * @see Textarea
8
+ */
9
+ export { Textarea as MultilineTextBox } from '../Textarea';