@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.
- package/dist/src/CheckboxCard/CheckboxCard.js +1 -7
- package/dist/src/DateTimeSelection/DatePicker/DatePicker.js +2 -11
- package/dist/src/DateTimeSelection/DateRangePicker/DateRangePicker.d.ts +1 -1
- package/dist/src/DateTimeSelection/DateRangePicker/DateRangePicker.js +10 -2
- package/dist/src/DateTimeSelection/DateRangePicker/DateRangePicker.types.d.ts +14 -0
- package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerPopover/DateRangePickerPopover.js +13 -5
- package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerPopover/utilities/createDateRangePickerPopoverTestUtility.js +2 -2
- package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerProvider/DateRangePickerProvider.js +11 -12
- package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerTrigger/hooks/useDateRangeTriggerValueController.js +4 -3
- package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerTrigger/utilities/createDateRangePickerTriggerTestUtility.js +3 -1
- package/dist/src/DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.d.ts +6 -0
- package/dist/src/DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.js +33 -0
- package/dist/src/DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.types.d.ts +17 -0
- package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.d.ts +14 -0
- package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.js +42 -0
- package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.types.d.ts +78 -0
- package/dist/src/DateTimeSelection/FilterDateRangePicker/FilterDateRangePicker.types.js +1 -0
- package/dist/src/DateTimeSelection/FilterDateRangePicker/index.d.ts +2 -0
- package/dist/src/DateTimeSelection/FilterDateRangePicker/index.js +1 -0
- package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.d.ts +14 -0
- package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.js +75 -0
- package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.types.d.ts +83 -0
- package/dist/src/DateTimeSelection/FilterMonthPicker/FilterMonthPicker.types.js +1 -0
- package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.d.ts +6 -0
- package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.js +45 -0
- package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.types.d.ts +24 -0
- package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.types.js +1 -0
- package/dist/src/DateTimeSelection/FilterMonthPicker/index.d.ts +2 -0
- package/dist/src/DateTimeSelection/FilterMonthPicker/index.js +1 -0
- package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.d.ts +15 -0
- package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.js +89 -0
- package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.types.d.ts +75 -0
- package/dist/src/DateTimeSelection/FilterMonthRangePicker/FilterMonthRangePicker.types.js +1 -0
- package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.d.ts +5 -0
- package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.js +54 -0
- package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.types.d.ts +19 -0
- package/dist/src/DateTimeSelection/FilterMonthRangePicker/MonthRangePickerPopover/MonthRangePickerPopover.types.js +1 -0
- package/dist/src/DateTimeSelection/FilterMonthRangePicker/index.d.ts +2 -0
- package/dist/src/DateTimeSelection/FilterMonthRangePicker/index.js +1 -0
- package/dist/src/DateTimeSelection/MonthPicker/MonthPicker.d.ts +1 -1
- package/dist/src/DateTimeSelection/MonthPicker/MonthPicker.js +10 -2
- package/dist/src/DateTimeSelection/MonthPicker/MonthPicker.types.d.ts +14 -0
- package/dist/src/DateTimeSelection/MonthPicker/MonthPickerPanel/MonthCell/MonthCell.js +2 -25
- package/dist/src/DateTimeSelection/MonthPicker/MonthPickerPanel/MonthPickerPanel.js +1 -1
- package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePicker.d.ts +1 -1
- package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePicker.js +14 -5
- package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePicker.types.d.ts +21 -0
- package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerMonthCell/MonthRangePickerMonthCell.d.ts +1 -0
- package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerMonthCell/MonthRangePickerMonthCell.js +10 -3
- package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerNavigation/MonthRangePickerNavigation.js +16 -3
- package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerPanel/MonthRangePickerPanel.js +3 -1
- package/dist/src/DateTimeSelection/TimePicker/TimePicker.d.ts +43 -0
- package/dist/src/DateTimeSelection/TimePicker/TimePicker.js +85 -0
- package/dist/src/DateTimeSelection/TimePicker/TimePicker.types.d.ts +61 -0
- package/dist/src/DateTimeSelection/TimePicker/TimePicker.types.js +1 -0
- package/dist/src/DateTimeSelection/TimePicker/constants.d.ts +4 -0
- package/dist/src/DateTimeSelection/TimePicker/constants.js +12 -0
- package/dist/src/DateTimeSelection/TimePicker/index.d.ts +2 -0
- package/dist/src/DateTimeSelection/TimePicker/index.js +1 -0
- package/dist/src/DateTimeSelection/index.d.ts +4 -0
- package/dist/src/DateTimeSelection/index.js +4 -0
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.d.ts +1 -1
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.js +26 -10
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePicker.types.d.ts +19 -0
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerProvider/BaseRangePickerProvider.js +40 -19
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerProvider/BaseRangePickerProvider.types.d.ts +19 -0
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/BaseRangePickerTrigger.js +4 -6
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/hooks/useDateRangeTriggerValueController.js +4 -3
- package/dist/src/DateTimeSelection/shared/CalendarLocale/CalendarLocaleContext.d.ts +1 -1
- package/dist/src/DateTimeSelection/shared/CalendarLocale/CalendarLocaleContext.js +1 -1
- package/dist/src/DateTimeSelection/shared/DayCell/DayCell.js +2 -3
- package/dist/src/DateTimeSelection/shared/MonthGrid/MonthGrid.js +3 -3
- package/dist/src/DateTimeSelection/shared/YearSelector/YearSelector.js +1 -1
- package/dist/src/DateTimeSelection/shared/utilities/dateParsing.js +27 -9
- package/dist/src/DateTimeSelection/shared/utilities/japaneseCalendar.d.ts +36 -8
- package/dist/src/DateTimeSelection/shared/utilities/japaneseCalendar.js +82 -15
- package/dist/src/DateTimeSelection/shared/utilities/monthCellMonthFormat.d.ts +14 -0
- package/dist/src/DateTimeSelection/shared/utilities/monthCellMonthFormat.js +35 -0
- package/dist/src/MultilineTextBox/index.d.ts +10 -2
- package/dist/src/MultilineTextBox/index.js +9 -1
- package/dist/src/MultipleSelectBox/MultipleSelectBoxTrigger/MultipleSelectBoxTrigger.js +1 -3
- package/dist/src/RadioButtonCard/RadioButtonCard.js +1 -7
- package/dist/src/TextBox/TextBox.js +2 -6
- package/dist/src/{MultilineTextBox/MultilineTextBox.d.ts → Textarea/Textarea.d.ts} +5 -2
- package/dist/src/{MultilineTextBox/MultilineTextBox.js → Textarea/Textarea.js} +9 -5
- package/dist/src/{MultilineTextBox/MultilineTextBox.types.d.ts → Textarea/Textarea.types.d.ts} +2 -2
- package/dist/src/Textarea/Textarea.types.js +1 -0
- package/dist/src/Textarea/index.d.ts +2 -0
- package/dist/src/Textarea/index.js +1 -0
- package/dist/src/Tooltip/Tooltip.js +12 -3
- package/dist/src/Typography/Typography.js +1 -3
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/styled-system/recipes/filter-date-range-picker-slot-recipe.d.ts +33 -0
- package/dist/styled-system/recipes/filter-date-range-picker-slot-recipe.js +48 -0
- package/dist/styled-system/recipes/filter-month-picker-slot-recipe.d.ts +33 -0
- package/dist/styled-system/recipes/filter-month-picker-slot-recipe.js +44 -0
- package/dist/styled-system/recipes/filter-month-range-picker-slot-recipe.d.ts +33 -0
- package/dist/styled-system/recipes/filter-month-range-picker-slot-recipe.js +44 -0
- package/dist/styled-system/recipes/index.d.ts +5 -1
- package/dist/styled-system/recipes/index.js +5 -1
- package/dist/styled-system/recipes/textarea-slot-recipe.d.ts +52 -0
- package/dist/styled-system/recipes/textarea-slot-recipe.js +64 -0
- package/dist/styled-system/recipes/time-picker-slot-recipe.d.ts +44 -0
- package/dist/styled-system/recipes/time-picker-slot-recipe.js +62 -0
- package/dist/styles.css +601 -25
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +13 -11
- package/dist/src/DateTimeSelection/shared/BasePicker/YearSelector/YearSelector.d.ts +0 -18
- package/dist/src/DateTimeSelection/shared/BasePicker/YearSelector/YearSelector.js +0 -36
- package/dist/styled-system/recipes/multiline-text-box-slot-recipe.d.ts +0 -52
- package/dist/styled-system/recipes/multiline-text-box-slot-recipe.js +0 -64
- /package/dist/src/{MultilineTextBox/MultilineTextBox.types.js → DateTimeSelection/FilterDateRangePicker/DateRangePickerContent/DateRangePickerContent.types.js} +0 -0
package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type InternalMonthPickerPopoverProps } from './MonthPickerPopover.types';
|
|
2
|
+
/**
|
|
3
|
+
* Internal component that accesses BasePickerContext for viewing-month reset and focus trap.
|
|
4
|
+
* Must be rendered inside a BasePickerProvider.
|
|
5
|
+
*/
|
|
6
|
+
export declare function InternalMonthPickerPopover({ currentValue, defaultValue, isPickerOpen, togglePicker, handleValueChange, handleClear, disabled, className, classes, formattedValue, hasValue, label, clearButtonProps, checkDisabledMonth, minMonth, maxMonth, calendarLocale, triggerRef, targetDOMNode, dialogId, }: InternalMonthPickerPopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,45 @@
|
|
|
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 { MonthPickerPanel } from '../../MonthPicker/MonthPickerPanel/MonthPickerPanel';
|
|
12
|
+
import { CalendarLocaleProvider } from '../../shared/CalendarLocale/CalendarLocaleContext';
|
|
13
|
+
import { useBasePickerContext } from '../../shared/BasePicker/BasePickerProvider';
|
|
14
|
+
import { useFocusTrap } from '../../../utilities/dom/useFocusTrap';
|
|
15
|
+
import { getFocusableNodes } from '../../../utilities/dom/getFocusableNodes';
|
|
16
|
+
const MONTH_BASE_FORMAT = 'YYYY-MM';
|
|
17
|
+
/**
|
|
18
|
+
* Internal component that accesses BasePickerContext for viewing-month reset and focus trap.
|
|
19
|
+
* Must be rendered inside a BasePickerProvider.
|
|
20
|
+
*/
|
|
21
|
+
export function InternalMonthPickerPopover({ currentValue, defaultValue, isPickerOpen, togglePicker, handleValueChange, handleClear, disabled, className, classes, formattedValue, hasValue, label, clearButtonProps, checkDisabledMonth, minMonth, maxMonth, calendarLocale = 'ja', triggerRef, targetDOMNode, dialogId, }) {
|
|
22
|
+
const { viewingValue, setViewingValue, setPendingFocusDate } = useBasePickerContext();
|
|
23
|
+
const { handleOnKeyDown } = useFocusTrap({
|
|
24
|
+
onPressingTabOnNotFocusableElement: (event) => {
|
|
25
|
+
const nodes = getFocusableNodes(event.currentTarget);
|
|
26
|
+
const focusableNodeAfterFocusingNode = nodes.find((element) =>
|
|
27
|
+
// eslint-disable-next-line no-bitwise
|
|
28
|
+
element.compareDocumentPosition(event.target) & Node.DOCUMENT_POSITION_PRECEDING);
|
|
29
|
+
const firstFocusableNode = nodes.at(0);
|
|
30
|
+
const nextFocusableNode = focusableNodeAfterFocusingNode ?? firstFocusableNode;
|
|
31
|
+
nextFocusableNode?.focus();
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
const handlePopoverOpen = useCallback(() => {
|
|
36
|
+
const monthToFocus = dayjs(currentValue ?? defaultValue ?? new Date())
|
|
37
|
+
.startOf('month')
|
|
38
|
+
.toDate();
|
|
39
|
+
setViewingValue(monthToFocus);
|
|
40
|
+
setPendingFocusDate(dayjs(monthToFocus).format(MONTH_BASE_FORMAT));
|
|
41
|
+
}, [currentValue, defaultValue, setViewingValue, setPendingFocusDate]);
|
|
42
|
+
return (_jsx(Popover, { open: isPickerOpen, targetDOMNode: targetDOMNode, renderTrigger: ({ setTriggerRef, togglePopover, handleTriggerKeyDown, handleTriggerBlur }) => (_jsxs("div", { ref: setTriggerRef, "data-selected": hasValue, className: cx(classes.wrapper, 'mfui-FilterMonthPicker__wrapper'), children: [_jsx(FocusIndicator, { children: _jsxs("button", { ref: triggerRef, type: "button", disabled: disabled, className: cx(classes.trigger, 'mfui-FilterMonthPicker__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-FilterMonthPicker__content'), "data-mfui-content": "filter-month-picker-display-value", children: hasValue ? (_jsxs(Typography, { variant: "strongBody", children: [_jsx("span", { "data-mfui-content": "filter-month-picker-label", children: `${label}:` }), _jsx("span", { "data-mfui-content": "filter-month-picker-value", children: formattedValue })] })) : (_jsx(Typography, { variant: "controlLabel", children: _jsx("span", { "data-mfui-content": "filter-month-picker-label", children: label }) })) }), _jsx("span", { className: cx(classes.calendarIcon, 'mfui-FilterMonthPicker__calendarIcon'), children: _jsx(CalendarIcon, { "aria-hidden": true }) })] }) }), hasValue ? (_jsx("div", { className: cx(classes.clearButtonWrapper, 'mfui-FilterMonthPicker__clearButtonWrapper'), children: _jsx(ClearButton, { "aria-label": clearButtonProps?.['aria-label'] ?? '値をクリアする', "data-mfui-content": "filter-month-picker-clear-button", disabled: disabled, onClick: handleClear }) })) : null] })), renderContent: () => (_jsx("div", { id: dialogId, role: "dialog", "aria-label": label, className: "mfui-FilterMonthPicker__popoverWrapper", onKeyDown: handleOnKeyDown, children: _jsx(CalendarLocaleProvider, { value: calendarLocale, children: _jsx(MonthPickerPanel, { viewingValue: viewingValue, setViewingValue: setViewingValue, value: currentValue, minMonth: minMonth, maxMonth: maxMonth, checkDisabledMonth: checkDisabledMonth, onChange: (date) => {
|
|
43
|
+
handleValueChange(date);
|
|
44
|
+
} }) }) })), minWidth: "min-content", allowedPlacements: ['bottom-start', 'top-start'], onOpenStateChanged: togglePicker, onOpen: handlePopoverOpen }));
|
|
45
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type filterMonthPickerSlotRecipe } from '../../../../styled-system/recipes';
|
|
2
|
+
import { type FilterMonthPickerProps } from '../FilterMonthPicker.types';
|
|
3
|
+
export type InternalMonthPickerPopoverProps = {
|
|
4
|
+
currentValue: Date | undefined;
|
|
5
|
+
defaultValue: Date | undefined;
|
|
6
|
+
isPickerOpen: boolean;
|
|
7
|
+
togglePicker: () => void;
|
|
8
|
+
handleValueChange: (date: Date | undefined) => void;
|
|
9
|
+
handleClear: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
10
|
+
disabled: boolean;
|
|
11
|
+
className: string | undefined;
|
|
12
|
+
classes: ReturnType<typeof filterMonthPickerSlotRecipe>;
|
|
13
|
+
formattedValue: string | undefined;
|
|
14
|
+
hasValue: boolean;
|
|
15
|
+
label: string;
|
|
16
|
+
clearButtonProps: FilterMonthPickerProps['clearButtonProps'];
|
|
17
|
+
checkDisabledMonth: FilterMonthPickerProps['checkDisabledMonth'];
|
|
18
|
+
minMonth: Date | undefined;
|
|
19
|
+
maxMonth: Date | undefined;
|
|
20
|
+
calendarLocale: FilterMonthPickerProps['calendarLocale'];
|
|
21
|
+
triggerRef: React.RefObject<HTMLButtonElement | null>;
|
|
22
|
+
targetDOMNode: FilterMonthPickerProps['targetDOMNode'];
|
|
23
|
+
dialogId: string;
|
|
24
|
+
};
|
package/dist/src/DateTimeSelection/FilterMonthPicker/MonthPickerPopover/MonthPickerPopover.types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FilterMonthPicker } from './FilterMonthPicker';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type FilterMonthRangePickerProps } from './FilterMonthRangePicker.types';
|
|
2
|
+
/**
|
|
3
|
+
* FilterMonthRangePicker component.
|
|
4
|
+
*
|
|
5
|
+
* A filter trigger component that allows selecting a month range via the same panel as
|
|
6
|
+
* {@link MonthRangePicker}. Displays the label alone when no range is selected, and
|
|
7
|
+
* `label:start〜end` after both bounds are selected. Conforms to the FilterSelectBox / FilterDateBox
|
|
8
|
+
* visual style.
|
|
9
|
+
*
|
|
10
|
+
* Note: The trigger markup intentionally does not reuse FilterTrigger because
|
|
11
|
+
* FilterTrigger hardcodes the DropdownIcon and adds a hidden `<input>` element,
|
|
12
|
+
* neither of which are appropriate here. FilterMonthRangePicker uses CalendarIcon and
|
|
13
|
+
* has no form-field semantics.
|
|
14
|
+
*/
|
|
15
|
+
export declare function FilterMonthRangePicker({ label, value, defaultValue, onChange, open, onOpenStateChanged, format, initialDisplayedMonths, clearButtonProps, disabled, className, targetDOMNode, minMonth, maxMonth, }: FilterMonthRangePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -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 @@
|
|
|
1
|
+
export {};
|
|
@@ -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 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FilterMonthRangePicker } from './FilterMonthRangePicker';
|
|
@@ -5,4 +5,4 @@ import { type MonthPickerProps } from './MonthPicker.types';
|
|
|
5
5
|
*
|
|
6
6
|
* @param props - MonthPicker props including minMonth and maxMonth constraints
|
|
7
7
|
*/
|
|
8
|
-
export declare function MonthPicker({ format, minMonth, maxMonth, checkDisabledMonth, calendarLocale, ...restProps }: MonthPickerProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare function MonthPicker({ format, minMonth, maxMonth, checkDisabledMonth, calendarLocale, displayJapaneseCalendar, ...restProps }: MonthPickerProps): 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 BasePicker, except for the popover content and format.
|
|
7
7
|
* See BasePicker for implementation details.
|
|
8
8
|
*/
|
|
9
|
+
import { useMemo } from 'react';
|
|
9
10
|
import { BasePicker } from '../shared/BasePicker';
|
|
10
11
|
import { CalendarLocaleProvider } from '../shared/CalendarLocale/CalendarLocaleContext';
|
|
12
|
+
import { DisplayJapaneseCalendarProvider } from '../shared/CalendarLocale/DisplayJapaneseCalendarContext';
|
|
11
13
|
import { MonthPickerPanel } from './MonthPickerPanel/MonthPickerPanel';
|
|
14
|
+
import { dateToEnglishEraMonthShort, dateToJapaneseEraMonth } from '../shared/utilities/japaneseCalendar';
|
|
12
15
|
/**
|
|
13
16
|
* MonthPicker component for selecting months.
|
|
14
17
|
* Extends BasePicker with month-specific functionality.
|
|
15
18
|
*
|
|
16
19
|
* @param props - MonthPicker props including minMonth and maxMonth constraints
|
|
17
20
|
*/
|
|
18
|
-
export function MonthPicker({ format = 'YYYY/MM', minMonth, maxMonth, checkDisabledMonth, calendarLocale = 'ja', ...restProps }) {
|
|
19
|
-
|
|
21
|
+
export function MonthPicker({ format = 'YYYY/MM', minMonth, maxMonth, checkDisabledMonth, calendarLocale = 'ja', displayJapaneseCalendar = false, ...restProps }) {
|
|
22
|
+
const customFormatValue = useMemo(() => {
|
|
23
|
+
if (!displayJapaneseCalendar)
|
|
24
|
+
return;
|
|
25
|
+
return calendarLocale === 'en' ? dateToEnglishEraMonthShort : dateToJapaneseEraMonth;
|
|
26
|
+
}, [displayJapaneseCalendar, calendarLocale]);
|
|
27
|
+
return (_jsx(BasePicker, { ...restProps, format: format, baseFormat: "YYYY-MM", calendarLocale: calendarLocale, customFormatValue: customFormatValue, renderPopoverContent: ({ viewingValue, setViewingValue, value, onChange }) => (_jsx(DisplayJapaneseCalendarProvider, { value: displayJapaneseCalendar, children: _jsx(CalendarLocaleProvider, { value: calendarLocale, children: _jsx(MonthPickerPanel, { viewingValue: viewingValue, setViewingValue: setViewingValue, value: value, minMonth: minMonth, maxMonth: maxMonth, checkDisabledMonth: checkDisabledMonth, onChange: onChange }) }) })) }));
|
|
20
28
|
}
|
|
@@ -39,4 +39,18 @@ export type MonthPickerProps = Omit<BasePickerProps, 'renderPopoverContent' | 'b
|
|
|
39
39
|
* // Displays: "Jan", "Feb", etc. and English navigation labels
|
|
40
40
|
*/
|
|
41
41
|
calendarLocale?: BasePickerProps['calendarLocale'];
|
|
42
|
+
/**
|
|
43
|
+
* Whether to display the month in Japanese calendar format (e.g., "令和7年1月")
|
|
44
|
+
* When enabled, the text input will show months in Japanese calendar format
|
|
45
|
+
* while internal value handling remains unchanged.
|
|
46
|
+
*
|
|
47
|
+
* @default false
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* - When combined with `calendarLocale="en"`, months are romanized with
|
|
51
|
+
* a zero-padded month number (e.g., "Reiwa 7/01").
|
|
52
|
+
* - Pre-Meiji months (year < 1868) fall back to the Gregorian year
|
|
53
|
+
* (e.g., "1867/12").
|
|
54
|
+
*/
|
|
55
|
+
displayJapaneseCalendar?: boolean;
|
|
42
56
|
};
|
|
@@ -2,38 +2,15 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
3
|
import { MonthCell } from '../../../shared/MonthCell';
|
|
4
4
|
import { useCalendarLocale } from '../../../shared/CalendarLocale/CalendarLocaleContext';
|
|
5
|
+
import { getMonthCellMonthFormat } from '../../../shared/utilities/monthCellMonthFormat';
|
|
5
6
|
import { useMonthPickerKeyboardNavigation } from '../../utilities/useMonthPickerKeyboardNavigation';
|
|
6
|
-
// English month abbreviations wrapped in square brackets to escape them in dayjs format strings
|
|
7
|
-
const ENGLISH_MONTH_ABBREVIATIONS = [
|
|
8
|
-
'[Jan]',
|
|
9
|
-
'[Feb]',
|
|
10
|
-
'[Mar]',
|
|
11
|
-
'[Apr]',
|
|
12
|
-
'[May]',
|
|
13
|
-
'[Jun]',
|
|
14
|
-
'[Jul]',
|
|
15
|
-
'[Aug]',
|
|
16
|
-
'[Sep]',
|
|
17
|
-
'[Oct]',
|
|
18
|
-
'[Nov]',
|
|
19
|
-
'[Dec]',
|
|
20
|
-
];
|
|
21
7
|
/**
|
|
22
8
|
* MonthPickerMonthCell is a wrapper around the shared MonthCell component.
|
|
23
9
|
* It handles month selection and integrates with the MonthPicker context.
|
|
24
10
|
*/
|
|
25
11
|
export function MonthPickerMonthCell({ onKeyDown, minMonth, maxMonth, checkDisabledMonth, date, ...rest }) {
|
|
26
12
|
const locale = useCalendarLocale();
|
|
27
|
-
|
|
28
|
-
const monthFormat = useMemo(() => {
|
|
29
|
-
const monthIndex = date.getMonth();
|
|
30
|
-
if (locale === 'en') {
|
|
31
|
-
// Return English month abbreviation escaped for dayjs
|
|
32
|
-
return ENGLISH_MONTH_ABBREVIATIONS[monthIndex];
|
|
33
|
-
}
|
|
34
|
-
// Japanese format: "1月", "2月", etc. (using dayjs format)
|
|
35
|
-
return 'M月';
|
|
36
|
-
}, [locale, date]);
|
|
13
|
+
const monthFormat = useMemo(() => getMonthCellMonthFormat(locale, date), [locale, date]);
|
|
37
14
|
const handleKeyDown = useMonthPickerKeyboardNavigation(onKeyDown, minMonth, maxMonth, checkDisabledMonth);
|
|
38
15
|
return _jsx(MonthCell, { ...rest, date: date, monthFormat: monthFormat, onKeyDown: handleKeyDown });
|
|
39
16
|
}
|
|
@@ -109,7 +109,7 @@ export function MonthPickerPanel({ viewingValue, setViewingValue, value, onChang
|
|
|
109
109
|
setPendingFocusDate(null);
|
|
110
110
|
}, [viewingValue, pendingFocusDate, setPendingFocusDate, baseFormat]);
|
|
111
111
|
const classNames = monthPickerPanelSlotRecipe();
|
|
112
|
-
return (_jsxs("div", { ...props, className: cx(classNames.root, 'mfui-MonthPickerPanel__root', className), children: [_jsxs("div", { className: cx(classNames.navigation, 'mfui-MonthPickerPanel__navigation'), children: [!isPreviousYearDisabled ? (_jsx(IconButton, { "aria-label": getMonthPickerLabel(locale
|
|
112
|
+
return (_jsxs("div", { ...props, className: cx(classNames.root, 'mfui-MonthPickerPanel__root', className), children: [_jsxs("div", { className: cx(classNames.navigation, 'mfui-MonthPickerPanel__navigation'), children: [!isPreviousYearDisabled ? (_jsx(IconButton, { "aria-label": getMonthPickerLabel(locale, 'prevYear'), onClick: handlePreviousYear, children: _jsx(PickerBefore, {}) })) : (_jsx(YearNavigationPlaceholder, { className: cx(classNames.yearNavigationPlaceholder, 'mfui-MonthPickerPanel__yearNavigationPlaceholder') })), _jsx(YearSelector, { value: viewingValue, triggerWrapperProps: { className: cx(classNames.yearSelector, 'mfui-MonthPickerPanel__yearSelector') }, filterYear: shouldFilterYears ? filterYear : undefined, onChange: handleYearSelect }), !isNextYearDisabled ? (_jsx(IconButton, { "aria-label": getMonthPickerLabel(locale, 'nextYear'), onClick: handleNextYear, children: _jsx(PickerAfter, {}) })) : (_jsx(YearNavigationPlaceholder, { className: cx(classNames.yearNavigationPlaceholder, 'mfui-MonthPickerPanel__yearNavigationPlaceholder') }))] }), _jsx(MonthPickerMonthGrid, { viewingValue: viewingValue, value: value, minMonth: minMonth, maxMonth: maxMonth, checkDisabledMonth: checkDisabledMonth, onChange: onChange })] }));
|
|
113
113
|
}
|
|
114
114
|
// Create a placeholder div that matches IconButton dimensions to prevent layout shift
|
|
115
115
|
function YearNavigationPlaceholder({ className }) {
|
|
@@ -14,4 +14,4 @@ import { type MonthRangePickerProps } from './MonthRangePicker.types';
|
|
|
14
14
|
* />
|
|
15
15
|
* ```
|
|
16
16
|
*/
|
|
17
|
-
export declare function MonthRangePicker({ format, minMonth, maxMonth, startInputProps, endInputProps, initialDisplayedMonths, ...restProps }: MonthRangePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare function MonthRangePicker({ format, minMonth, maxMonth, startInputProps, endInputProps, initialDisplayedMonths, displayJapaneseCalendar, calendarLocale, ...restProps }: MonthRangePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -12,8 +12,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
12
12
|
* - Keyboard navigation support
|
|
13
13
|
* - Apply/Cancel workflow
|
|
14
14
|
*/
|
|
15
|
+
import { useMemo } from 'react';
|
|
15
16
|
import { BaseRangePicker } from '../shared/BaseRangePicker';
|
|
17
|
+
import { CalendarLocaleProvider } from '../shared/CalendarLocale/CalendarLocaleContext';
|
|
18
|
+
import { DisplayJapaneseCalendarProvider } from '../shared/CalendarLocale/DisplayJapaneseCalendarContext';
|
|
16
19
|
import { MonthRangePickerPanel } from './MonthRangePickerPanel';
|
|
20
|
+
import { dateToEnglishEraMonthShort, dateToJapaneseEraMonth } from '../shared/utilities/japaneseCalendar';
|
|
17
21
|
/**
|
|
18
22
|
* MonthRangePicker component for selecting month ranges.
|
|
19
23
|
*
|
|
@@ -29,7 +33,12 @@ import { MonthRangePickerPanel } from './MonthRangePickerPanel';
|
|
|
29
33
|
* />
|
|
30
34
|
* ```
|
|
31
35
|
*/
|
|
32
|
-
export function MonthRangePicker({ format = 'YYYY/MM', minMonth, maxMonth, startInputProps = {}, endInputProps = {}, initialDisplayedMonths, ...restProps }) {
|
|
36
|
+
export function MonthRangePicker({ format = 'YYYY/MM', minMonth, maxMonth, startInputProps = {}, endInputProps = {}, initialDisplayedMonths, displayJapaneseCalendar = false, calendarLocale = 'ja', ...restProps }) {
|
|
37
|
+
const customFormatValue = useMemo(() => {
|
|
38
|
+
if (!displayJapaneseCalendar)
|
|
39
|
+
return;
|
|
40
|
+
return calendarLocale === 'en' ? dateToEnglishEraMonthShort : dateToJapaneseEraMonth;
|
|
41
|
+
}, [displayJapaneseCalendar, calendarLocale]);
|
|
33
42
|
// MonthRangePicker panels are grouped by year (left = viewingYear, right = viewingYear + 1).
|
|
34
43
|
// When `previousAndCurrent` is requested, we shift by a full year so the left panel
|
|
35
44
|
// shows the previous year. This applies regardless of whether `value` is set.
|
|
@@ -51,11 +60,11 @@ export function MonthRangePicker({ format = 'YYYY/MM', minMonth, maxMonth, start
|
|
|
51
60
|
// Same-year value + previousAndCurrent: show previous year on the left
|
|
52
61
|
return new Date(startDate.getFullYear() - 1, 0, 1);
|
|
53
62
|
})();
|
|
54
|
-
return (_jsx(BaseRangePicker, { ...restProps, format: format, initialDisplayedMonths: initialDisplayedMonths, initialViewingDate: initialViewingDate, startInputProps: {
|
|
55
|
-
placeholder: '開始月',
|
|
63
|
+
return (_jsx(BaseRangePicker, { ...restProps, format: format, pendingFocusDayjsFormat: "YYYY-MM", initialDisplayedMonths: initialDisplayedMonths, initialViewingDate: initialViewingDate, customFormatValue: customFormatValue, startInputProps: {
|
|
64
|
+
placeholder: calendarLocale === 'en' ? 'Start month' : '開始月',
|
|
56
65
|
...startInputProps,
|
|
57
66
|
}, endInputProps: {
|
|
58
|
-
placeholder: '終了月',
|
|
67
|
+
placeholder: calendarLocale === 'en' ? 'End month' : '終了月',
|
|
59
68
|
...endInputProps,
|
|
60
|
-
}, minDate: minMonth, maxDate: maxMonth, minWidth: "min-content", renderPopoverContent: ({ viewingValue, setViewingValue, value }) => (_jsx(MonthRangePickerPanel, { viewingValue: viewingValue, setViewingValue: setViewingValue, value: value, minMonth: minMonth, maxMonth: maxMonth })) }));
|
|
69
|
+
}, minDate: minMonth, maxDate: maxMonth, minWidth: "min-content", calendarLocale: calendarLocale, renderPopoverContent: ({ viewingValue, setViewingValue, value }) => (_jsx(DisplayJapaneseCalendarProvider, { value: displayJapaneseCalendar, children: _jsx(CalendarLocaleProvider, { value: calendarLocale, children: _jsx(MonthRangePickerPanel, { viewingValue: viewingValue, setViewingValue: setViewingValue, value: value, minMonth: minMonth, maxMonth: maxMonth }) }) })) }));
|
|
61
70
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type BasePickerProps } from '../shared/BasePicker';
|
|
1
2
|
import { type BaseRangePickerProps } from '../shared/BaseRangePicker';
|
|
2
3
|
/**
|
|
3
4
|
* The props for the MonthRangePicker component
|
|
@@ -25,4 +26,24 @@ export type MonthRangePickerProps = BaseRangePickerProps & {
|
|
|
25
26
|
* ```
|
|
26
27
|
*/
|
|
27
28
|
maxMonth?: Date;
|
|
29
|
+
/**
|
|
30
|
+
* Whether to display the month in Japanese calendar format (e.g., "令和7年1月")
|
|
31
|
+
* When enabled, the text inputs will show months in Japanese calendar format
|
|
32
|
+
* while internal value handling remains unchanged.
|
|
33
|
+
*
|
|
34
|
+
* @default false
|
|
35
|
+
*
|
|
36
|
+
* @remarks
|
|
37
|
+
* - When combined with `calendarLocale="en"`, months are romanized with
|
|
38
|
+
* a zero-padded month number (e.g., "Reiwa 7/01").
|
|
39
|
+
* - Pre-Meiji months (year < 1868) fall back to the Gregorian year
|
|
40
|
+
* (e.g., "1867/12").
|
|
41
|
+
*/
|
|
42
|
+
displayJapaneseCalendar?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* The locale of the calendar.
|
|
45
|
+
*
|
|
46
|
+
* @default 'ja'
|
|
47
|
+
*/
|
|
48
|
+
calendarLocale?: BasePickerProps['calendarLocale'];
|
|
28
49
|
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
3
|
import { MonthCell } from '../../shared/MonthCell';
|
|
4
|
+
import { useCalendarLocale } from '../../shared/CalendarLocale/CalendarLocaleContext';
|
|
5
|
+
import { getMonthCellMonthFormat } from '../../shared/utilities/monthCellMonthFormat';
|
|
4
6
|
import { dayjs } from '../../../utilities/date/dayjs';
|
|
5
7
|
import { MONTH_RANGE_PICKER_A11Y_LABELS } from './constants';
|
|
8
|
+
import 'dayjs/locale/en';
|
|
6
9
|
/**
|
|
7
10
|
* MonthRangePickerMonthCell component that wraps MonthCell with range selection visual states.
|
|
8
11
|
*
|
|
@@ -17,11 +20,15 @@ import { MONTH_RANGE_PICKER_A11Y_LABELS } from './constants';
|
|
|
17
20
|
* @param props - MonthRangePickerMonthCell props
|
|
18
21
|
*/
|
|
19
22
|
export function MonthRangePickerMonthCell({ isInRange, isRangeStart, isRangeEnd, onMouseEnter, onMouseLeave, handleKeyboardNavigation, ...monthCellProps }) {
|
|
23
|
+
const calendarLocale = useCalendarLocale();
|
|
20
24
|
// Calculate if this month is selected (either start or end of range)
|
|
21
25
|
const selected = isRangeStart || isRangeEnd || monthCellProps.selected || false;
|
|
26
|
+
const monthFormat = useMemo(() => getMonthCellMonthFormat(calendarLocale, monthCellProps.date), [calendarLocale, monthCellProps.date]);
|
|
22
27
|
// Create accessible label for range selection
|
|
23
28
|
const accessibleLabel = useMemo(() => {
|
|
24
|
-
const monthText =
|
|
29
|
+
const monthText = calendarLocale === 'en'
|
|
30
|
+
? dayjs(monthCellProps.date).locale('en').format('MMMM YYYY')
|
|
31
|
+
: dayjs(monthCellProps.date).format('MMMM YYYY');
|
|
25
32
|
const statusParts = [];
|
|
26
33
|
if (monthCellProps.disabled) {
|
|
27
34
|
statusParts.push(MONTH_RANGE_PICKER_A11Y_LABELS.UNAVAILABLE);
|
|
@@ -41,6 +48,6 @@ export function MonthRangePickerMonthCell({ isInRange, isRangeStart, isRangeEnd,
|
|
|
41
48
|
statusParts.push(MONTH_RANGE_PICKER_A11Y_LABELS.IN_RANGE);
|
|
42
49
|
}
|
|
43
50
|
return statusParts.length > 0 ? `${monthText}, ${statusParts.join(', ')}` : monthText;
|
|
44
|
-
}, [monthCellProps.date, monthCellProps.disabled, selected, isInRange, isRangeStart, isRangeEnd]);
|
|
45
|
-
return (_jsx("div", { onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, children: _jsx(MonthCell, { ...monthCellProps, selected: selected, inRange: isInRange, "aria-label": accessibleLabel, "aria-pressed": selected ? 'true' : undefined, "aria-selected": selected ? 'true' : 'false', onKeyDown: handleKeyboardNavigation }) }));
|
|
51
|
+
}, [calendarLocale, monthCellProps.date, monthCellProps.disabled, selected, isInRange, isRangeStart, isRangeEnd]);
|
|
52
|
+
return (_jsx("div", { onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, children: _jsx(MonthCell, { ...monthCellProps, monthFormat: monthFormat, selected: selected, inRange: isInRange, "aria-label": accessibleLabel, "aria-pressed": selected ? 'true' : undefined, "aria-selected": selected ? 'true' : 'false', onKeyDown: handleKeyboardNavigation }) }));
|
|
46
53
|
}
|