@moneyforward/mfui-components 3.29.1 → 3.31.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/Button/Button.d.ts +4 -4
- package/dist/src/CheckboxCard/CheckboxCard.d.ts +2 -2
- package/dist/src/CheckboxCard/CheckboxCard.js +1 -1
- package/dist/src/DataTable/DataTable.js +2 -2
- package/dist/src/DataTable/DataTable.types.d.ts +9 -0
- package/dist/src/DataTable/DataTableCell/DataTableCell.js +3 -2
- package/dist/src/DataTable/DataTableHeaderCell/DataTableHeaderCell.js +2 -1
- package/dist/src/DataTable/DataTableProvider.d.ts +2 -2
- package/dist/src/DataTable/DataTableProvider.js +4 -1
- package/dist/src/DataTable/DataTableRow/DataTableRow.js +34 -3
- package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerProvider/DateRangePickerProvider.d.ts +2 -2
- package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerTrigger/constants.d.ts +4 -4
- package/dist/src/DateTimeSelection/DateRangePicker/DateRangePickerTrigger/hooks/useDateRangeTriggerInputClick.d.ts +1 -1
- package/dist/src/DateTimeSelection/DateRangePicker/utilities/createDateRangePickerTestUtility.d.ts +1 -1
- package/dist/src/DateTimeSelection/MonthRangePicker/MonthRangePickerMonthCell/constants.d.ts +5 -5
- package/dist/src/DateTimeSelection/TimePicker/TimePicker.d.ts +9 -9
- package/dist/src/DateTimeSelection/shared/BasePicker/basePickerUtilities/useBasePickerValueController.d.ts +1 -1
- package/dist/src/DateTimeSelection/shared/BasePicker/constants.d.ts +1 -1
- package/dist/src/DateTimeSelection/shared/BasePicker/constants.js +2 -0
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerPopover/BaseRangePickerCalendar/constants.d.ts +6 -6
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerProvider/BaseRangePickerProvider.d.ts +2 -2
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/BaseRangePickerTrigger.js +1 -1
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/BaseRangePickerTrigger/hooks/useDateRangeTriggerInputClick.d.ts +1 -1
- package/dist/src/DateTimeSelection/shared/BaseRangePicker/utilities/createBaseRangePickerTestUtility.d.ts +1 -1
- package/dist/src/DateTimeSelection/shared/YearSelector/YearSelector.js +7 -1
- package/dist/src/DateTimeSelection/shared/accessibility/constants.d.ts +6 -6
- package/dist/src/Dialog/Dialog.d.ts +4 -4
- package/dist/src/Disclosure/Disclosure.d.ts +21 -10
- package/dist/src/DropdownMenu/DropdownMenu.d.ts +3 -3
- package/dist/src/DropdownMenu/DropdownMenu.js +26 -2
- package/dist/src/DropdownMenu/DropdownMenuButtonTrigger.d.ts +2 -2
- package/dist/src/DropdownMenu/DropdownMenuContainer/DropdownMenuContainer.js +28 -3
- package/dist/src/DropdownMenu/DropdownMenuContainer/DropdownMenuContainer.types.d.ts +5 -2
- package/dist/src/DropdownMenu/DropdownMenuItem/DropdownMenuItem.d.ts +1 -1
- package/dist/src/DropdownMenu/DropdownMenuItem/DropdownMenuItem.js +29 -6
- package/dist/src/DropdownMenu/DropdownMenuItem/DropdownMenuItem.types.d.ts +6 -0
- package/dist/src/EmbeddedSidePane/EmbeddedSidePane.d.ts +1 -1
- package/dist/src/FileBox/FileBox.d.ts +4 -4
- package/dist/src/FileBox/FileBox.js +1 -1
- package/dist/src/FileDropZone/FileDropZone.d.ts +4 -4
- package/dist/src/FileDropZone/utils/fileHelpers.d.ts +3 -3
- package/dist/src/FilterTrigger/FilterTrigger.d.ts +2 -2
- package/dist/src/Heading/Heading.d.ts +2 -2
- package/dist/src/HelpMessage/HelpMessage.d.ts +3 -3
- package/dist/src/IconButton/IconButton.d.ts +6 -6
- package/dist/src/KeyValue/KeyValue.d.ts +2 -2
- package/dist/src/MainNavigation/BaseMainNavigation.d.ts +1 -1
- package/dist/src/MainNavigation/BaseMainNavigation.js +4 -2
- package/dist/src/MainNavigation/MainNavigation.js +3 -3
- package/dist/src/MainNavigation/MainNavigation.types.d.ts +18 -0
- package/dist/src/MultipleSelectBox/MultipleSelectBox.js +1 -1
- package/dist/src/MultipleSelectBox/hooks/useApplyControls.d.ts +1 -1
- package/dist/src/MultipleSelectBox/hooks/useSelectedValues.d.ts +1 -1
- package/dist/src/NumberBox/NumberBox.d.ts +1 -1
- package/dist/src/NumberBox/NumberBox.js +68 -21
- package/dist/src/Pagination/ItemsCount/ItemsCount.js +1 -1
- package/dist/src/Popover/Popover.js +1 -1
- package/dist/src/RadioButton/RadioButton.d.ts +1 -1
- package/dist/src/RadioButtonCard/RadioButtonCard.d.ts +2 -2
- package/dist/src/RadioButtonCard/RadioButtonCard.js +1 -1
- package/dist/src/RadioGroup/RadioGroup.d.ts +1 -1
- package/dist/src/SearchBox/SearchBox.js +18 -4
- package/dist/src/SectionMessage/SectionMessage.d.ts +5 -5
- package/dist/src/SectionMessage/SectionMessage.js +11 -4
- package/dist/src/SectionMessage/SectionMessage.types.d.ts +4 -4
- package/dist/src/SelectBox/SelectBox.js +1 -1
- package/dist/src/SelectBox/SelectBoxOption.js +1 -1
- package/dist/src/SelectBox/SelectBoxOptionGroup.js +1 -1
- package/dist/src/SidePane/SidePane.d.ts +9 -7
- package/dist/src/SidePane/SidePane.js +8 -4
- package/dist/src/SidePane/hooks/useSidePaneStateController.d.ts +7 -2
- package/dist/src/SidePane/hooks/useSidePaneStateController.js +11 -5
- package/dist/src/Sidebar/Sidebar.d.ts +10 -1
- package/dist/src/Sidebar/Sidebar.js +40 -10
- package/dist/src/Sidebar/Sidebar.types.d.ts +39 -2
- package/dist/src/Sidebar/SidebarContext.d.ts +3 -0
- package/dist/src/Sidebar/SidebarContext.js +2 -0
- package/dist/src/Sidebar/SidebarMenuHeading/SidebarMenuHeading.d.ts +49 -0
- package/dist/src/Sidebar/SidebarMenuHeading/SidebarMenuHeading.js +113 -0
- package/dist/src/Sidebar/SidebarMenuHeading/SidebarMenuHeading.types.d.ts +36 -0
- package/dist/src/Sidebar/SidebarMenuHeading/SidebarMenuHeading.types.js +1 -0
- package/dist/src/Sidebar/SidebarMenuHeading/index.d.ts +2 -0
- package/dist/src/Sidebar/SidebarMenuHeading/index.js +1 -0
- package/dist/src/Sidebar/SidebarNavigationItem.js +6 -3
- package/dist/src/Sidebar/hooks/useSidebarResize.d.ts +2 -1
- package/dist/src/Sidebar/hooks/useSidebarResize.js +31 -3
- package/dist/src/Sidebar/index.d.ts +2 -1
- package/dist/src/Stack/Stack.d.ts +5 -5
- package/dist/src/StatusLabel/StatusLabel.d.ts +2 -2
- package/dist/src/Stepper/Step/Step.js +1 -1
- package/dist/src/SubNavigation/SubNavigation.js +1 -1
- package/dist/src/Tag/Tag.d.ts +1 -1
- package/dist/src/Tag/Tag.js +2 -2
- package/dist/src/Tag/Tag.types.d.ts +7 -0
- package/dist/src/TextBox/TextBox.d.ts +6 -6
- package/dist/src/TextLink/TextLink.d.ts +2 -2
- package/dist/src/Textarea/Textarea.d.ts +4 -4
- package/dist/src/Toast/Toast.d.ts +3 -3
- package/dist/src/Toast/Toast.js +9 -5
- package/dist/src/Toast/Toast.types.d.ts +3 -5
- package/dist/src/Toast/api/toast.d.ts +18 -2
- package/dist/src/Tooltip/Tooltip.d.ts +3 -3
- package/dist/src/Tooltip/Tooltip.js +1 -1
- package/dist/src/Tooltip/Tooltip.types.d.ts +2 -0
- package/dist/src/Tooltip/hooks/useTooltipDisplayController.js +14 -2
- package/dist/src/utilities/dom/dispatchNativeInputValueWithEvent.d.ts +1 -1
- package/dist/src/utilities/dom/useFocusTrap.d.ts +1 -1
- package/dist/src/utilities/react/useIsomorphicLayoutEffect.d.ts +2 -2
- package/dist/styled-system/css/conditions.d.ts +3 -3
- package/dist/styled-system/helpers.d.ts +36 -35
- package/dist/styled-system/jsx/factory-helper.d.ts +4 -4
- package/dist/styled-system/recipes/create-recipe.d.ts +2 -2
- package/dist/styled-system/recipes/data-table-cell-slot-recipe.d.ts +1 -0
- package/dist/styled-system/recipes/data-table-cell-slot-recipe.js +4 -0
- package/dist/styled-system/recipes/data-table-header-cell-slot-recipe.d.ts +1 -0
- package/dist/styled-system/recipes/data-table-header-cell-slot-recipe.js +4 -0
- package/dist/styled-system/recipes/dropdown-menu-item-slot-recipe.d.ts +1 -1
- package/dist/styled-system/recipes/dropdown-menu-item-slot-recipe.js +12 -0
- package/dist/styled-system/recipes/index.d.ts +2 -1
- package/dist/styled-system/recipes/index.js +1 -0
- package/dist/styled-system/recipes/section-message-slot-recipe.d.ts +1 -1
- package/dist/styled-system/recipes/section-message-slot-recipe.js +3 -1
- package/dist/styled-system/recipes/sidebar-menu-heading-slot-recipe.d.ts +33 -0
- package/dist/styled-system/recipes/sidebar-menu-heading-slot-recipe.js +44 -0
- package/dist/styled-system/recipes/sidebar-slot-recipe.d.ts +1 -1
- package/dist/styled-system/recipes/sidebar-slot-recipe.js +4 -0
- package/dist/styles.css +158 -14
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +11 -11
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FocusEventHandler, type KeyboardEventHandler, type
|
|
1
|
+
import { type FocusEventHandler, type KeyboardEventHandler, type MouseEventHandler } from 'react';
|
|
2
2
|
import { type ButtonProps } from '../Button';
|
|
3
3
|
import { type DropdownMenuButtonTriggerProps } from './DropdownMenu.types';
|
|
4
4
|
type DropdownMenuButtonTriggerComponentProps = DropdownMenuButtonTriggerProps & {
|
|
@@ -25,7 +25,7 @@ type DropdownMenuButtonTriggerComponentProps = DropdownMenuButtonTriggerProps &
|
|
|
25
25
|
/**
|
|
26
26
|
* Toggles the open state of the dropdown menu.
|
|
27
27
|
*/
|
|
28
|
-
togglePopover:
|
|
28
|
+
togglePopover: MouseEventHandler<HTMLElement>;
|
|
29
29
|
/**
|
|
30
30
|
* Keyboard handler for the trigger (ArrowUp / ArrowDown focus behavior already merged in).
|
|
31
31
|
*/
|
|
@@ -11,14 +11,21 @@ import { dropdownMenuContainerSlotRecipe } from '../../../styled-system/recipes'
|
|
|
11
11
|
export const DropdownMenuContainer = forwardRef(({ children, className, defaultFocusItem: defaultFocus = 'first', style, onClose }, ref) => {
|
|
12
12
|
const classes = dropdownMenuContainerSlotRecipe();
|
|
13
13
|
const menuContainerRef = useRef(null);
|
|
14
|
+
const getFocusableItems = useCallback(() => menuContainerRef.current?.querySelectorAll('[data-mfui-menu-item]:is([data-mfui-focusable=true])'), []);
|
|
14
15
|
useEffect(() => {
|
|
15
|
-
|
|
16
|
+
if (defaultFocus === 'none') {
|
|
17
|
+
// No item should appear focused (e.g. the menu was opened with the mouse). Focus the
|
|
18
|
+
// container itself so keyboard interactions (Escape / ArrowDown / ArrowUp) keep working.
|
|
19
|
+
menuContainerRef.current?.focus({ preventScroll: true });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const focusableItems = getFocusableItems();
|
|
16
23
|
if (!focusableItems) {
|
|
17
24
|
return;
|
|
18
25
|
}
|
|
19
26
|
const defaultFocusIndex = defaultFocus === 'first' ? 0 : focusableItems.length - 1;
|
|
20
27
|
focusableItems[defaultFocusIndex]?.focus({ preventScroll: true });
|
|
21
|
-
}, [defaultFocus]);
|
|
28
|
+
}, [defaultFocus, getFocusableItems]);
|
|
22
29
|
const handleKeyDown = useCallback((event) => {
|
|
23
30
|
switch (event.key) {
|
|
24
31
|
case 'Escape':
|
|
@@ -27,7 +34,25 @@ export const DropdownMenuContainer = forwardRef(({ children, className, defaultF
|
|
|
27
34
|
event.preventDefault();
|
|
28
35
|
onClose();
|
|
29
36
|
break;
|
|
37
|
+
case 'ArrowDown':
|
|
38
|
+
case 'ArrowUp': {
|
|
39
|
+
// Focused items handle arrow navigation themselves and stop propagation, so this
|
|
40
|
+
// branch only runs while the container itself is focused (defaultFocusItem 'none'):
|
|
41
|
+
// ArrowDown moves focus to the first item and ArrowUp to the last one.
|
|
42
|
+
if (event.target !== event.currentTarget) {
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
event.stopPropagation();
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
const focusableItems = getFocusableItems();
|
|
48
|
+
if (!focusableItems || focusableItems.length === 0) {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
const focusIndex = event.key === 'ArrowDown' ? 0 : focusableItems.length - 1;
|
|
52
|
+
focusableItems[focusIndex]?.focus();
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
30
55
|
}
|
|
31
|
-
}, [onClose]);
|
|
56
|
+
}, [onClose, getFocusableItems]);
|
|
32
57
|
return (_jsx("div", { ref: mergeRefs(ref, menuContainerRef), "data-mfui-menu-container": true, role: "menu", className: cx(classes.root, 'mfui-DropdownMenuContainer__root', className), tabIndex: -1, style: style, onKeyDown: handleKeyDown, children: children }));
|
|
33
58
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The default focus position of the menu.
|
|
3
3
|
*/
|
|
4
|
-
export type DropdownMenuDefaultFocusItem = 'first' | 'last';
|
|
4
|
+
export type DropdownMenuDefaultFocusItem = 'first' | 'last' | 'none';
|
|
5
5
|
export type DropdownMenuContainerProps = {
|
|
6
6
|
/**
|
|
7
7
|
* The content of the component.
|
|
@@ -16,10 +16,13 @@ export type DropdownMenuContainerProps = {
|
|
|
16
16
|
/**
|
|
17
17
|
* The default focus position of the dropdown menu.
|
|
18
18
|
*
|
|
19
|
-
* It should be either 'first' or '
|
|
19
|
+
* It should be either 'first', 'last' or 'none'.
|
|
20
20
|
*
|
|
21
21
|
* If 'first', the first focusable item will be focused.
|
|
22
22
|
* If 'last', the last focusable item will be focused.
|
|
23
|
+
* If 'none', no item will be focused; the menu container itself receives focus instead so that
|
|
24
|
+
* keyboard interactions (Escape / ArrowDown / ArrowUp) keep working. Use this when the menu is
|
|
25
|
+
* opened with a pointer, where visually focusing an item is not expected.
|
|
23
26
|
*
|
|
24
27
|
* @default 'first'
|
|
25
28
|
*/
|
|
@@ -12,4 +12,4 @@ import { type DropdownMenuItemProps } from './DropdownMenuItem.types';
|
|
|
12
12
|
*
|
|
13
13
|
* Otherwise, it will be rendered as a div element.
|
|
14
14
|
*/
|
|
15
|
-
export declare function DropdownMenuItem({ children, className, href, target, customLinkComponent, onClick, disabled, 'aria-labelledby': ariaLabelledby, ...rest }: DropdownMenuItemProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare function DropdownMenuItem({ children, className, href, target, customLinkComponent, onClick, disabled, disabledMessage, 'aria-labelledby': ariaLabelledby, 'aria-describedby': ariaDescribedby, ...rest }: DropdownMenuItemProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useId, useMemo, useRef } from 'react';
|
|
4
|
+
import { Info } from '@moneyforward/mfui-icons-react';
|
|
4
5
|
import { cx } from '../../../styled-system/css';
|
|
5
|
-
import { findSiblingElements } from '../../utilities/dom/findSiblingElements';
|
|
6
6
|
import { FocusIndicator } from '../../FocusIndicator';
|
|
7
7
|
import { dropdownMenuItemSlotRecipe } from '../../../styled-system/recipes';
|
|
8
8
|
import { Typography } from '../../Typography';
|
|
@@ -20,13 +20,18 @@ import { useDropdownMenuContext } from '../DropdownMenuContext';
|
|
|
20
20
|
*
|
|
21
21
|
* Otherwise, it will be rendered as a div element.
|
|
22
22
|
*/
|
|
23
|
-
export function DropdownMenuItem({ children, className, href, target, customLinkComponent, onClick, disabled, 'aria-labelledby': ariaLabelledby, ...rest }) {
|
|
23
|
+
export function DropdownMenuItem({ children, className, href, target, customLinkComponent, onClick, disabled, disabledMessage, 'aria-labelledby': ariaLabelledby, 'aria-describedby': ariaDescribedby, ...rest }) {
|
|
24
24
|
const classes = dropdownMenuItemSlotRecipe();
|
|
25
25
|
const { closeMenuPopover } = useDropdownMenuContext();
|
|
26
26
|
const id = useId();
|
|
27
27
|
const itemRef = useRef(null);
|
|
28
28
|
const handleKeyDown = useCallback((event) => {
|
|
29
|
-
|
|
29
|
+
// Search from the menu container (not the immediate parent) so arrow navigation still works
|
|
30
|
+
// when items are wrapped in another element such as a Tooltip.
|
|
31
|
+
const container = event.currentTarget.closest('[data-mfui-menu-container]');
|
|
32
|
+
const siblingElements = container
|
|
33
|
+
? [...container.querySelectorAll('[data-mfui-menu-item]:is([data-mfui-focusable=true])')]
|
|
34
|
+
: [];
|
|
30
35
|
const currentIndex = siblingElements.indexOf(event.currentTarget);
|
|
31
36
|
// If the current element is the last element, move to the first element.
|
|
32
37
|
const nextIndex = siblingElements.length - 1 === currentIndex ? 0 : currentIndex + 1;
|
|
@@ -52,7 +57,11 @@ export function DropdownMenuItem({ children, className, href, target, customLink
|
|
|
52
57
|
return;
|
|
53
58
|
if (onClick) {
|
|
54
59
|
onClick(event);
|
|
55
|
-
|
|
60
|
+
// Consumers can keep the menu open after a click by calling `event.preventDefault()`
|
|
61
|
+
// in their `onClick` handler (e.g. for in-menu actions like copy-to-clipboard).
|
|
62
|
+
if (!event.defaultPrevented) {
|
|
63
|
+
closeMenuPopover?.();
|
|
64
|
+
}
|
|
56
65
|
}
|
|
57
66
|
}, [disabled, onClick, closeMenuPopover]);
|
|
58
67
|
const isButton = useMemo(() => onClick && !href, [onClick, href]);
|
|
@@ -79,5 +88,19 @@ export function DropdownMenuItem({ children, className, href, target, customLink
|
|
|
79
88
|
: disabled
|
|
80
89
|
? { 'aria-disabled': true, 'data-disabled': '' }
|
|
81
90
|
: {};
|
|
82
|
-
|
|
91
|
+
const showMessage = Boolean(disabled && disabledMessage);
|
|
92
|
+
const labelId = `${id}-label`;
|
|
93
|
+
const messageId = `${id}-message`;
|
|
94
|
+
const label = typeof children === 'string' ? _jsx(Typography, { variant: "body", children: children }) : children;
|
|
95
|
+
// With a message, the accessible name must reference only the label span so the message
|
|
96
|
+
// stays a description; without one, keep the existing self-reference via the item's own id.
|
|
97
|
+
const resolvedAriaLabelledby = showMessage
|
|
98
|
+
? [ariaLabelledby, labelId].filter(Boolean).join(' ')
|
|
99
|
+
: ariaLabelledby
|
|
100
|
+
? `${ariaLabelledby} ${id}`
|
|
101
|
+
: undefined;
|
|
102
|
+
const resolvedAriaDescribedby = showMessage
|
|
103
|
+
? [ariaDescribedby, messageId].filter(Boolean).join(' ')
|
|
104
|
+
: ariaDescribedby;
|
|
105
|
+
return (_jsx(FocusIndicator, { position: "inside", children: _jsx(Tag, { ...rest, ref: itemRef, ...(ariaLabelledby && { id }), "data-mfui-menu-item": true, role: "menuitem", className: cx(classes.root, 'mfui-DropdownMenuItem__root', className), tabIndex: -1, href: resolvedHref, target: target, "data-mfui-focusable": isFocusable, type: onClick ? 'button' : undefined, "aria-labelledby": resolvedAriaLabelledby, "aria-describedby": resolvedAriaDescribedby, ...disabledProps, onKeyDown: handleKeyDown, onClick: isButton ? handleClick : undefined, children: showMessage ? (_jsxs("span", { className: cx(classes.content, 'mfui-DropdownMenuItem__content'), children: [_jsx("span", { id: labelId, children: label }), _jsxs("span", { id: messageId, className: cx(classes.disabledMessage, 'mfui-DropdownMenuItem__disabledMessage'), children: [_jsx(Info, { "aria-hidden": true, className: cx(classes.disabledMessageIcon, 'mfui-DropdownMenuItem__disabledMessageIcon') }), _jsx(Typography, { variant: "helpMessage", children: disabledMessage })] })] })) : (label) }) }));
|
|
83
106
|
}
|
|
@@ -77,6 +77,12 @@ export type DropdownMenuItemProps = {
|
|
|
77
77
|
* The class name to apply to the component.
|
|
78
78
|
*/
|
|
79
79
|
className?: string;
|
|
80
|
+
/**
|
|
81
|
+
* An explanatory message displayed inline below the item label when the item is disabled.
|
|
82
|
+
* Rendered with an info icon. Ignored unless `disabled` is true.
|
|
83
|
+
* Exposed to screen readers as the item's accessible description (aria-describedby).
|
|
84
|
+
*/
|
|
85
|
+
disabledMessage?: React.ReactNode;
|
|
80
86
|
/**
|
|
81
87
|
* The aria-labelledby attribute for accessibility.
|
|
82
88
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export declare const EmbeddedSidePane: import("react").ForwardRefExoticComponent<{
|
|
9
9
|
container?: HTMLElement | null;
|
|
10
|
-
} & Omit<import("..").SidePaneProps, "
|
|
10
|
+
} & Omit<import("..").SidePaneProps, "enableNonModal" | "targetDOMNode"> & import("react").RefAttributes<HTMLDivElement>> & {
|
|
11
11
|
/**
|
|
12
12
|
* The `EmbeddedSidePane.Container` component is used to define the container for the `EmbeddedSidePane`.
|
|
13
13
|
* It should be a parent element that has `position: relative` for proper positioning and `overflow: hidden/clip` for clipping.
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
|
|
7
7
|
*/
|
|
8
8
|
export declare const FileBox: import("react").ForwardRefExoticComponent<{
|
|
9
|
-
invalid?: import("../../styled-system/recipes").TextBoxSlotRecipeVariant[
|
|
9
|
+
invalid?: import("../../styled-system/recipes").TextBoxSlotRecipeVariant['invalid'];
|
|
10
10
|
disabled?: boolean;
|
|
11
|
-
multiple?: import("react").ComponentPropsWithoutRef<
|
|
12
|
-
accept?: import("react").ComponentPropsWithoutRef<
|
|
11
|
+
multiple?: import("react").ComponentPropsWithoutRef<'input'>['multiple'];
|
|
12
|
+
accept?: import("react").ComponentPropsWithoutRef<'input'>['accept'];
|
|
13
13
|
buttonLabel?: string;
|
|
14
14
|
placeholder?: string;
|
|
15
15
|
multipleFilesSuffixLabel?: string;
|
|
16
|
-
clearButtonProps?: Pick<import("..").IconButtonProps,
|
|
16
|
+
clearButtonProps?: Pick<import("..").IconButtonProps, 'aria-label'>;
|
|
17
17
|
} & Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { forwardRef, useRef, useState } from 'react';
|
|
4
4
|
import { cx } from '../../styled-system/css';
|
|
5
5
|
import { fileBoxSlotRecipe } from '../../styled-system/recipes';
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file
|
|
29
29
|
*/
|
|
30
30
|
export declare const FileDropZone: import("react").ForwardRefExoticComponent<{
|
|
31
|
-
invalid?: import("../../styled-system/recipes").TextBoxSlotRecipeVariant[
|
|
31
|
+
invalid?: import("../../styled-system/recipes").TextBoxSlotRecipeVariant['invalid'];
|
|
32
32
|
disabled?: boolean;
|
|
33
|
-
multiple?: import("react").ComponentPropsWithoutRef<
|
|
34
|
-
accept?: import("react").ComponentPropsWithoutRef<
|
|
35
|
-
clearButtonProps?: Pick<import("..").IconButtonProps,
|
|
33
|
+
multiple?: import("react").ComponentPropsWithoutRef<'input'>['multiple'];
|
|
34
|
+
accept?: import("react").ComponentPropsWithoutRef<'input'>['accept'];
|
|
35
|
+
clearButtonProps?: Pick<import("..").IconButtonProps, 'aria-label'>;
|
|
36
36
|
errorMessage?: string;
|
|
37
37
|
helperMessage?: string;
|
|
38
38
|
illustrationUrl?: string;
|
|
@@ -81,7 +81,7 @@ export declare function createValidationResult(isValid: boolean, error?: string)
|
|
|
81
81
|
* @property ARCHIVES - The archive file types
|
|
82
82
|
*/
|
|
83
83
|
export declare const FILE_TYPES: {
|
|
84
|
-
readonly IMAGES: readonly [
|
|
85
|
-
readonly DOCUMENTS: readonly [
|
|
86
|
-
readonly ARCHIVES: readonly [
|
|
84
|
+
readonly IMAGES: readonly ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'];
|
|
85
|
+
readonly DOCUMENTS: readonly ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'text/plain'];
|
|
86
|
+
readonly ARCHIVES: readonly ['application/zip', 'application/x-rar-compressed', 'application/x-tar'];
|
|
87
87
|
};
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
export declare const FilterTrigger: import("react").ForwardRefExoticComponent<{
|
|
6
6
|
label: string;
|
|
7
7
|
value?: string;
|
|
8
|
-
inputProps?: Omit<import("react").ComponentPropsWithoutRef<
|
|
8
|
+
inputProps?: Omit<import("react").ComponentPropsWithoutRef<'input'>, 'type'>;
|
|
9
9
|
className?: string;
|
|
10
10
|
enableClearButton?: boolean;
|
|
11
11
|
clearButtonProps?: {
|
|
12
|
-
|
|
12
|
+
'aria-label'?: string;
|
|
13
13
|
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
14
14
|
};
|
|
15
15
|
} & Omit<Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref">, "children"> & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements
|
|
12
12
|
*/
|
|
13
13
|
export declare const Heading: import("react").ForwardRefExoticComponent<{
|
|
14
|
-
variant:
|
|
15
|
-
tag:
|
|
14
|
+
variant: 'pageHeading1' | 'pageHeading2' | 'sectionHeading1' | 'sectionHeading2' | 'contentHeading';
|
|
15
|
+
tag: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
16
16
|
} & Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>, "ref"> & import("react").RefAttributes<HTMLHeadingElement>>;
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div
|
|
12
12
|
*/
|
|
13
13
|
export declare const HelpMessage: import("react").ForwardRefExoticComponent<{
|
|
14
|
-
messageType?: import("../../styled-system/recipes").HelpMessageRecipeVariant[
|
|
14
|
+
messageType?: import("../../styled-system/recipes").HelpMessageRecipeVariant['messageType'];
|
|
15
15
|
showIcon?: boolean;
|
|
16
|
-
messageAlign?:
|
|
17
|
-
children?: import("..").TypographyProps[
|
|
16
|
+
messageAlign?: 'left' | 'center';
|
|
17
|
+
children?: import("..").TypographyProps['children'];
|
|
18
18
|
} & Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
export declare const InternalIconButton: import("react").ForwardRefExoticComponent<{
|
|
2
|
-
outlined?: import("../../styled-system/recipes").IconButtonSlotRecipeVariant[
|
|
2
|
+
outlined?: import("../../styled-system/recipes").IconButtonSlotRecipeVariant['outlined'];
|
|
3
3
|
children: React.ReactNode;
|
|
4
4
|
disabled?: boolean;
|
|
5
5
|
href?: string;
|
|
6
6
|
customLinkComponent?: import("react").ElementType;
|
|
7
|
-
} & Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & Pick<Omit<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "ref">, "
|
|
8
|
-
size?: import("../../styled-system/recipes").IconButtonSlotRecipeVariant[
|
|
9
|
-
} & import("react").RefAttributes<
|
|
7
|
+
} & Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & Pick<Omit<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "ref">, "download" | "hrefLang" | "ping" | "referrerPolicy" | "rel" | "target" | "type"> & {
|
|
8
|
+
size?: import("../../styled-system/recipes").IconButtonSlotRecipeVariant['size'];
|
|
9
|
+
} & import("react").RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
|
|
10
10
|
export declare const IconButton: import("react").ForwardRefExoticComponent<{
|
|
11
|
-
outlined?: import("../../styled-system/recipes").IconButtonSlotRecipeVariant[
|
|
11
|
+
outlined?: import("../../styled-system/recipes").IconButtonSlotRecipeVariant['outlined'];
|
|
12
12
|
children: React.ReactNode;
|
|
13
13
|
disabled?: boolean;
|
|
14
14
|
href?: string;
|
|
15
15
|
customLinkComponent?: import("react").ElementType;
|
|
16
|
-
} & Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & Pick<Omit<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "ref">, "
|
|
16
|
+
} & Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & Pick<Omit<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "ref">, "download" | "hrefLang" | "ping" | "referrerPolicy" | "rel" | "target" | "type"> & import("react").RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Both the key and value areas accept any elements and are rendered with predefined layout styles.
|
|
4
4
|
*/
|
|
5
5
|
export declare const KeyValue: import("react").ForwardRefExoticComponent<{
|
|
6
|
-
size?: import("../../styled-system/recipes").KeyValueSlotRecipeVariant[
|
|
6
|
+
size?: import("../../styled-system/recipes").KeyValueSlotRecipeVariant['size'];
|
|
7
7
|
keyItem: React.ReactNode;
|
|
8
8
|
valueItem: React.ReactNode;
|
|
9
|
-
gridTemplateColumns?: import("react").CSSProperties[
|
|
9
|
+
gridTemplateColumns?: import("react").CSSProperties['gridTemplateColumns'];
|
|
10
10
|
} & Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -3,4 +3,4 @@ import { type MainNavigationProps } from './MainNavigation.types';
|
|
|
3
3
|
* The component for the main navigation on the normal viewport.
|
|
4
4
|
* This component must only be used in MainNavigation.tsx, do not export it.
|
|
5
5
|
*/
|
|
6
|
-
export declare function BaseMainNavigation({ className, featureShortcut, navigationItems, enableCollapsible, width, customLinkComponent, disableResponsive, isCollapsed: controlledIsCollapsed, onCollapsedChange, }: MainNavigationProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export declare function BaseMainNavigation({ className, featureShortcut, navigationItems, enableCollapsible, width, customLinkComponent, disableResponsive, isCollapsed: controlledIsCollapsed, onCollapsedChange, collapseButtonProps, expandButtonProps, }: MainNavigationProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -13,7 +13,7 @@ import { Popover } from '../Popover';
|
|
|
13
13
|
* The component for the main navigation on the normal viewport.
|
|
14
14
|
* This component must only be used in MainNavigation.tsx, do not export it.
|
|
15
15
|
*/
|
|
16
|
-
export function BaseMainNavigation({ className, featureShortcut, navigationItems, enableCollapsible = false, width = 'normal', customLinkComponent, disableResponsive, isCollapsed: controlledIsCollapsed, onCollapsedChange, }) {
|
|
16
|
+
export function BaseMainNavigation({ className, featureShortcut, navigationItems, enableCollapsible = false, width = 'normal', customLinkComponent, disableResponsive, isCollapsed: controlledIsCollapsed, onCollapsedChange, collapseButtonProps, expandButtonProps, }) {
|
|
17
17
|
const [localIsCollapsed, setLocalIsCollapsed] = useState(controlledIsCollapsed ?? false);
|
|
18
18
|
const [openPopoverIndex, setOpenPopoverIndex] = useState(null);
|
|
19
19
|
const isControlled = controlledIsCollapsed !== undefined;
|
|
@@ -28,7 +28,9 @@ export function BaseMainNavigation({ className, featureShortcut, navigationItems
|
|
|
28
28
|
};
|
|
29
29
|
const classes = baseMainNavigationSlotRecipe({ width, disableResponsive });
|
|
30
30
|
const InternalLinkTag = customLinkComponent ?? 'a';
|
|
31
|
-
const toggleButtonLabel = isCollapsed
|
|
31
|
+
const toggleButtonLabel = isCollapsed
|
|
32
|
+
? (expandButtonProps?.label ?? 'ナビゲーションを拡げる')
|
|
33
|
+
: (collapseButtonProps?.label ?? 'ナビゲーションを折りたたむ');
|
|
32
34
|
return (_jsxs("div", { "data-mfui-collapsed": isCollapsed, className: cx(classes.root, 'mfui-BaseMainNavigation__root', className), children: [featureShortcut ? (_jsx("div", { className: cx(classes.featureShortcut, 'mfui-BaseMainNavigation__featureShortcut'), children: _jsx(FocusIndicator, { children: _jsx(NavigationLink, { tag: InternalLinkTag, isExternal: featureShortcut.isExternal, href: featureShortcut.href, className: cx(classes.featureShortcutAnchor, 'mfui-BaseMainNavigation__featureShortcutAnchor'), children: _jsx(Typography, { variant: "controlLabel", children: featureShortcut.label }) }) }) })) : null, _jsxs("nav", { id: navId, className: cx(classes.nav, 'mfui-BaseMainNavigation__nav'), children: [_jsx("ul", { className: cx(classes.list, 'mfui-BaseMainNavigation__list'), children: navigationItems.map((navigation, index) => {
|
|
33
35
|
const isGroupLabel = navigation.children && 'isGroupLabel' in navigation && navigation.isGroupLabel;
|
|
34
36
|
const isGroupMenu = 'isGroupMenu' in navigation && navigation.isGroupMenu;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef } from 'react';
|
|
3
3
|
import { BaseMainNavigation } from './BaseMainNavigation';
|
|
4
4
|
import { NarrowViewportMainNavigation } from './NarrowViewportMainNavigation';
|
|
@@ -9,7 +9,7 @@ import { NarrowViewportMainNavigation } from './NarrowViewportMainNavigation';
|
|
|
9
9
|
* When the viewport is narrow, the navigation will be displayed as a drawer.
|
|
10
10
|
* When the viewport is not narrow, the navigation will be displayed as a sidebar.
|
|
11
11
|
*/
|
|
12
|
-
export const MainNavigation = forwardRef(({ className, featureShortcut, navigationItems, disableResponsive = false, width = 'normal', customLinkComponent, enableCollapsible, isCollapsed, onCollapsedChange, }, ref) => {
|
|
12
|
+
export const MainNavigation = forwardRef(({ className, featureShortcut, navigationItems, disableResponsive = false, width = 'normal', customLinkComponent, enableCollapsible, isCollapsed, onCollapsedChange, collapseButtonProps, expandButtonProps, }, ref) => {
|
|
13
13
|
const commonProps = {
|
|
14
14
|
className,
|
|
15
15
|
featureShortcut,
|
|
@@ -17,5 +17,5 @@ export const MainNavigation = forwardRef(({ className, featureShortcut, navigati
|
|
|
17
17
|
width,
|
|
18
18
|
customLinkComponent,
|
|
19
19
|
};
|
|
20
|
-
return (_jsxs(_Fragment, { children: [_jsx(BaseMainNavigation, { ...commonProps, enableCollapsible: enableCollapsible, disableResponsive: disableResponsive, isCollapsed: isCollapsed, onCollapsedChange: onCollapsedChange }), !disableResponsive && _jsx(NarrowViewportMainNavigation, { ...commonProps, ref: ref })] }));
|
|
20
|
+
return (_jsxs(_Fragment, { children: [_jsx(BaseMainNavigation, { ...commonProps, enableCollapsible: enableCollapsible, disableResponsive: disableResponsive, isCollapsed: isCollapsed, onCollapsedChange: onCollapsedChange, collapseButtonProps: collapseButtonProps, expandButtonProps: expandButtonProps }), !disableResponsive && _jsx(NarrowViewportMainNavigation, { ...commonProps, ref: ref })] }));
|
|
21
21
|
});
|
|
@@ -230,6 +230,24 @@ export type MainNavigationProps = {
|
|
|
230
230
|
* This prop is only effective when `enableCollapsible` is `true`.
|
|
231
231
|
*/
|
|
232
232
|
isCollapsed?: boolean;
|
|
233
|
+
/**
|
|
234
|
+
* The properties for the toggle button when the navigation is expanded (clicking will collapse).
|
|
235
|
+
* This prop is only effective when `enableCollapsible` is `true`.
|
|
236
|
+
*
|
|
237
|
+
* @property label - Accessible name for the toggle button. It is used for both the tooltip content and the `aria-label` of the toggle button. default is 'ナビゲーションを折りたたむ'.
|
|
238
|
+
*/
|
|
239
|
+
collapseButtonProps?: {
|
|
240
|
+
label?: string;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* The properties for the toggle button when the navigation is collapsed (clicking will expand).
|
|
244
|
+
* This prop is only effective when `enableCollapsible` is `true`.
|
|
245
|
+
*
|
|
246
|
+
* @property label - Accessible name for the toggle button. It is used for both the tooltip content and the `aria-label` of the toggle button. default is 'ナビゲーションを拡げる'.
|
|
247
|
+
*/
|
|
248
|
+
expandButtonProps?: {
|
|
249
|
+
label?: string;
|
|
250
|
+
};
|
|
233
251
|
/**
|
|
234
252
|
* Callback function called when the collapsed state changes.
|
|
235
253
|
* This is triggered when the toggle button is clicked.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useId, useMemo, useRef } from 'react';
|
|
4
4
|
import { Add, CheckboxChecked, CheckboxUnchecked, Error } from '@moneyforward/mfui-icons-react';
|
|
5
5
|
import { FocusIndicator } from '../FocusIndicator';
|
|
@@ -22,7 +22,7 @@ type UseApplyControlsProps<T extends AllowedValueTypes = string, AdditionalProps
|
|
|
22
22
|
*/
|
|
23
23
|
export declare function useApplyControls<T extends AllowedValueTypes = string, AdditionalProps extends Record<string, unknown> = Record<string, never>>({ options, onValuesChange, closeOptionPanel }: UseApplyControlsProps<T, AdditionalProps>): {
|
|
24
24
|
temporaryValues: Set<MultipleSelectBoxOption<T, AdditionalProps>["value"]>;
|
|
25
|
-
updateTemporaryValues: (values: MultipleSelectBoxOption<T, AdditionalProps>[
|
|
25
|
+
updateTemporaryValues: (values: MultipleSelectBoxOption<T, AdditionalProps>['value'][]) => void;
|
|
26
26
|
openWithSelectedOptions: (selectedOptions: MultipleSelectBoxOption<T, AdditionalProps>[]) => void;
|
|
27
27
|
initializeTemporaryValues: () => void;
|
|
28
28
|
handleCancelButtonClick: () => void;
|
|
@@ -28,7 +28,7 @@ type UseSelectedValuesProps<T extends AllowedValueTypes = string, AdditionalProp
|
|
|
28
28
|
export declare function useSelectedValues<T extends AllowedValueTypes = string, AdditionalProps extends Record<string, unknown> = Record<string, never>>({ options, defaultValue, value, onChange }: UseSelectedValuesProps<T, AdditionalProps>): {
|
|
29
29
|
localSelectedValuesSet: Set<MultipleSelectBoxOption<T, AdditionalProps>["value"]>;
|
|
30
30
|
localSelectedOptions: MultipleSelectBoxOption<T, AdditionalProps>[];
|
|
31
|
-
updateSelectedValues: (newValues: MultipleSelectBoxOption<T, AdditionalProps>[
|
|
31
|
+
updateSelectedValues: (newValues: MultipleSelectBoxOption<T, AdditionalProps>['value'][]) => void;
|
|
32
32
|
updateSelectedOptions: (newOptions: MultipleSelectBoxOption<T, AdditionalProps>[]) => void;
|
|
33
33
|
};
|
|
34
34
|
export {};
|
|
@@ -8,5 +8,5 @@ import React from 'react';
|
|
|
8
8
|
*/
|
|
9
9
|
export declare const NumberBox: React.ForwardRefExoticComponent<Omit<import("..").TextBoxProps, "onChange" | "prefixSlot" | "suffixSlot"> & {
|
|
10
10
|
onChange?: (value: string) => void;
|
|
11
|
-
textAlign?:
|
|
11
|
+
textAlign?: 'left' | 'right';
|
|
12
12
|
} & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { forwardRef, useCallback, useLayoutEffect, useRef, useState
|
|
3
|
+
import { forwardRef, useCallback, useEffect, useLayoutEffect, useReducer, useRef, useState } from 'react';
|
|
4
4
|
import { cx } from '../../styled-system/css';
|
|
5
5
|
import { mergeRefs } from '../utilities/dom/mergeRefs';
|
|
6
6
|
import { TextBox } from '../TextBox';
|
|
7
7
|
/**
|
|
8
|
-
* Convert full-width digits, minus
|
|
8
|
+
* Convert full-width digits, minus signs, and decimal points to half-width.
|
|
9
|
+
* Kana-mode IMEs can emit prolonged sound marks for the minus key.
|
|
9
10
|
*
|
|
10
11
|
* @param value - The string to convert.
|
|
11
12
|
*/
|
|
12
|
-
const toHalfWidth = (value) => value
|
|
13
|
+
const toHalfWidth = (value) => value
|
|
14
|
+
.replaceAll(/[\uFF10-\uFF19\uFF0D\uFF0E]/g, (char) => String.fromCodePoint((char.codePointAt(0) ?? 0) - 0xfe_e0))
|
|
15
|
+
.replaceAll(/[\u3002\uFF61]/g, '.')
|
|
16
|
+
.replaceAll(/[\u2212\u30FC\uFF70\u2015]/g, '-');
|
|
13
17
|
/**
|
|
14
18
|
* Sanitize input: convert full-width characters, then keep only valid numeric characters.
|
|
15
19
|
* Allows: digits 0-9, a single leading minus, a single decimal point after a digit.
|
|
@@ -69,6 +73,35 @@ const formatWithCommas = (raw) => {
|
|
|
69
73
|
* @param display - The formatted display string.
|
|
70
74
|
*/
|
|
71
75
|
const stripFormatting = (display) => display.replaceAll(',', '');
|
|
76
|
+
/**
|
|
77
|
+
* Map a display cursor position to the equivalent index in the raw numeric string.
|
|
78
|
+
*
|
|
79
|
+
* @param display - The formatted display string.
|
|
80
|
+
* @param displayCursor - The cursor position in the display string.
|
|
81
|
+
*/
|
|
82
|
+
const mapDisplayCursorToRaw = (display, displayCursor) => {
|
|
83
|
+
const commasBefore = (display.slice(0, displayCursor).match(/,/g) ?? []).length;
|
|
84
|
+
return displayCursor - commasBefore;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Map a raw numeric cursor position to the equivalent index in the formatted display string.
|
|
88
|
+
*
|
|
89
|
+
* @param formatted - The formatted display string.
|
|
90
|
+
* @param rawCursor - The cursor position in the raw numeric string.
|
|
91
|
+
*/
|
|
92
|
+
const mapRawCursorToDisplay = (formatted, rawCursor) => {
|
|
93
|
+
let rawCount = 0;
|
|
94
|
+
let index = 0;
|
|
95
|
+
for (const char of formatted) {
|
|
96
|
+
if (rawCount >= rawCursor) {
|
|
97
|
+
return index;
|
|
98
|
+
}
|
|
99
|
+
if (char !== ',')
|
|
100
|
+
rawCount += 1;
|
|
101
|
+
index += 1;
|
|
102
|
+
}
|
|
103
|
+
return formatted.length;
|
|
104
|
+
};
|
|
72
105
|
/**
|
|
73
106
|
* NumberBox is a numeric input component that wraps TextBox.
|
|
74
107
|
* It provides real-time comma formatting, full-width digit conversion,
|
|
@@ -79,6 +112,7 @@ const stripFormatting = (display) => display.replaceAll(',', '');
|
|
|
79
112
|
export const NumberBox = forwardRef(({ onChange, value, defaultValue, textAlign = 'right', style, enableClearButton, ...rest }, ref) => {
|
|
80
113
|
const inputRef = useRef(null);
|
|
81
114
|
const pendingCursorRef = useRef(null);
|
|
115
|
+
const [cursorRestoreToken, requestCursorRestore] = useReducer((count) => count + 1, 0);
|
|
82
116
|
const [displayValue, setDisplayValue] = useState(() => formatWithCommas(sanitizeInput(String(value ?? defaultValue ?? ''))));
|
|
83
117
|
// Sync with controlled value
|
|
84
118
|
useEffect(() => {
|
|
@@ -89,16 +123,22 @@ export const NumberBox = forwardRef(({ onChange, value, defaultValue, textAlign
|
|
|
89
123
|
// Restore cursor position after display value update
|
|
90
124
|
useLayoutEffect(() => {
|
|
91
125
|
if (pendingCursorRef.current !== null && inputRef.current) {
|
|
92
|
-
inputRef.current
|
|
126
|
+
const input = inputRef.current;
|
|
127
|
+
const cursor = pendingCursorRef.current;
|
|
93
128
|
pendingCursorRef.current = null;
|
|
129
|
+
if (input.value !== displayValue) {
|
|
130
|
+
input.value = displayValue;
|
|
131
|
+
}
|
|
132
|
+
input.setSelectionRange(cursor, cursor);
|
|
94
133
|
}
|
|
95
|
-
}, [displayValue]);
|
|
134
|
+
}, [displayValue, cursorRestoreToken]);
|
|
96
135
|
const handleChange = useCallback((event) => {
|
|
136
|
+
const inputDisplay = event.target.value;
|
|
97
137
|
const cursorPos = event.target.selectionStart ?? 0;
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
let rawCursor = cursorPos
|
|
138
|
+
const prevSanitized = stripFormatting(displayValue);
|
|
139
|
+
const rawInput = stripFormatting(toHalfWidth(inputDisplay));
|
|
140
|
+
// Use the current input value so comma positions match the cursor.
|
|
141
|
+
let rawCursor = mapDisplayCursorToRaw(inputDisplay, cursorPos);
|
|
102
142
|
// When two dots appear, the user is moving the decimal point.
|
|
103
143
|
// Keep the newly typed dot (at rawCursor - 1) and remove the old one.
|
|
104
144
|
let processedRawInput = rawInput;
|
|
@@ -114,22 +154,29 @@ export const NumberBox = forwardRef(({ onChange, value, defaultValue, textAlign
|
|
|
114
154
|
rawCursor -= 1;
|
|
115
155
|
}
|
|
116
156
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
break;
|
|
157
|
+
let sanitized = sanitizeInput(processedRawInput);
|
|
158
|
+
// Backspace right after a comma deletes the digit before the comma.
|
|
159
|
+
const isBackspaceOnComma = sanitized === prevSanitized && cursorPos < displayValue.length && displayValue[cursorPos] === ',';
|
|
160
|
+
if (isBackspaceOnComma) {
|
|
161
|
+
const rawPos = mapDisplayCursorToRaw(displayValue, cursorPos);
|
|
162
|
+
if (rawPos > 0) {
|
|
163
|
+
sanitized = prevSanitized.slice(0, rawPos - 1) + prevSanitized.slice(rawPos);
|
|
164
|
+
rawCursor = rawPos - 1;
|
|
126
165
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
166
|
+
}
|
|
167
|
+
const formatted = formatWithCommas(sanitized);
|
|
168
|
+
let newCursor = mapRawCursorToDisplay(formatted, rawCursor);
|
|
169
|
+
// Rejected characters keep the cursor before the ignored input.
|
|
170
|
+
const rejectedCharCount = rawInput.length - sanitized.length;
|
|
171
|
+
if (sanitized === prevSanitized && !isBackspaceOnComma && rejectedCharCount > 0) {
|
|
172
|
+
const rawBeforeCursor = mapDisplayCursorToRaw(inputDisplay, cursorPos);
|
|
173
|
+
newCursor = mapRawCursorToDisplay(formatted, rawBeforeCursor - rejectedCharCount);
|
|
130
174
|
}
|
|
131
175
|
pendingCursorRef.current = newCursor;
|
|
132
176
|
setDisplayValue(formatted);
|
|
177
|
+
if (formatted === displayValue) {
|
|
178
|
+
requestCursorRestore();
|
|
179
|
+
}
|
|
133
180
|
onChange?.(sanitized);
|
|
134
181
|
}, [displayValue, onChange]);
|
|
135
182
|
return (_jsx(TextBox, { ref: mergeRefs(ref, inputRef), ...rest, className: cx('mfui-NumberBox__root', rest.className), enableClearButton: Boolean(enableClearButton) && displayValue !== '', style: { textAlign, ...style }, value: displayValue, onChange: handleChange }));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { itemsCountSlotRecipe } from '../../../styled-system/recipes';
|
|
3
3
|
import { Typography } from '../../Typography';
|
|
4
4
|
import { cx } from '../../../styled-system/css';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useState, useRef, } from 'react';
|
|
4
4
|
import { getOverflowAncestors } from '@floating-ui/react-dom';
|
|
5
5
|
import { PopoverContextProvider, usePopoverContext } from './PopoverContext';
|
|
@@ -10,4 +10,4 @@ export declare const RadioButton: import("react").ForwardRefExoticComponent<{
|
|
|
10
10
|
label?: string;
|
|
11
11
|
checked?: boolean | undefined;
|
|
12
12
|
disabled?: boolean | undefined;
|
|
13
|
-
} & Omit<Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "
|
|
13
|
+
} & Omit<Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref">, "checked" | "disabled"> & import("react").RefAttributes<HTMLInputElement>>;
|