@moneyforward/mfui-components 3.31.1 → 3.32.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.
@@ -10,4 +10,5 @@ export declare const Breadcrumbs: import("react").ForwardRefExoticComponent<{
10
10
  items: import("./Breadcrumbs.types").BreadcrumbItem[];
11
11
  wrap?: boolean;
12
12
  customLinkComponent?: import("react").ElementType;
13
+ truncateButtonProps?: Pick<import("react").ComponentPropsWithoutRef<'button'>, 'aria-label'>;
13
14
  } & Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLElement>, HTMLElement>, "ref"> & import("react").RefAttributes<HTMLElement>>;
@@ -30,7 +30,7 @@ function Separator({ className }) {
30
30
  * when it has no `href`. If the last item has an `href`, it is rendered as a link and the
31
31
  * trail has no current-page item.
32
32
  */
33
- export const Breadcrumbs = forwardRef(({ items, wrap = false, customLinkComponent, className, 'aria-label': ariaLabel = '現在のページ位置', ...rest }, ref) => {
33
+ export const Breadcrumbs = forwardRef(({ items, wrap = false, customLinkComponent, className, 'aria-label': ariaLabel = '現在のページ位置', truncateButtonProps, ...rest }, ref) => {
34
34
  const classes = breadcrumbsSlotRecipe({ wrap });
35
35
  // Decompose items into logical groups:
36
36
  // - rootItem: items[0] (always shown, link)
@@ -159,7 +159,7 @@ export const Breadcrumbs = forwardRef(({ items, wrap = false, customLinkComponen
159
159
  setCurrentTruncated(!!(currentElement && currentElement.scrollWidth > currentElement.clientWidth));
160
160
  }, [containerWidth, collapsedCount, wrap]);
161
161
  const truncatedItems = middleItems.slice(0, collapsedCount);
162
- return (_jsx("nav", { ref: ref, className: cx(classes.root, className), "aria-label": ariaLabel, ...rest, children: _jsxs("ol", { ref: listRef, className: classes.list, "data-mfui-all-collapsed": allCollapsed ? true : undefined, children: [rootItem ? (_jsxs("li", { ref: rootItemLiRef, className: classes.item, style: allCollapsed && rootMinWidth !== undefined ? { minWidth: rootMinWidth } : undefined, children: [_jsx(Tooltip, { hideFromScreenReader: true, content: rootItem.label, disabled: !rootTruncated, placement: "bottom", style: TOOLTIP_TRIGGER_STYLE, children: _jsx(TextLink, { ref: rootLinkRef, href: rootItem.href, variant: "condensedBody", customLinkComponent: customLinkComponent, className: classes.linkText, children: rootItem.label }) }), _jsx(Separator, { className: classes.separator })] })) : null, collapsedCount > 0 ? (_jsxs("li", { className: classes.truncateItem, children: [_jsx(DropdownMenu, { label: "\u7701\u7565\u3055\u308C\u305F\u30A2\u30A4\u30C6\u30E0\u3092\u8868\u793A", icon: _jsx(MoreHorizontal, {}), triggerProps: {
162
+ return (_jsx("nav", { ref: ref, className: cx(classes.root, className), "aria-label": ariaLabel, ...rest, children: _jsxs("ol", { ref: listRef, className: classes.list, "data-mfui-all-collapsed": allCollapsed ? true : undefined, children: [rootItem ? (_jsxs("li", { ref: rootItemLiRef, className: classes.item, style: allCollapsed && rootMinWidth !== undefined ? { minWidth: rootMinWidth } : undefined, children: [_jsx(Tooltip, { hideFromScreenReader: true, content: rootItem.label, disabled: !rootTruncated, placement: "bottom", style: TOOLTIP_TRIGGER_STYLE, children: _jsx(TextLink, { ref: rootLinkRef, href: rootItem.href, variant: "condensedBody", customLinkComponent: customLinkComponent, className: classes.linkText, children: rootItem.label }) }), _jsx(Separator, { className: classes.separator })] })) : null, collapsedCount > 0 ? (_jsxs("li", { className: classes.truncateItem, children: [_jsx(DropdownMenu, { label: truncateButtonProps?.['aria-label'] ?? '省略されたアイテムを表示', icon: _jsx(MoreHorizontal, {}), triggerProps: {
163
163
  className: classes.truncateButton,
164
164
  }, children: truncatedItems.map((item, i) => item.href !== undefined ? (_jsx(DropdownMenu.Item, { href: item.href, customLinkComponent: customLinkComponent, children: item.label }, i)) : (_jsx(DropdownMenu.Item, { children: item.label }, i))) }), _jsx(Separator, { className: classes.separator })] })) : null, middleItems.map((item, i) => (_jsxs("li", { className: classes.item, style: i < collapsedCount ? { display: 'none' } : undefined, children: [_jsx(TextLink, { href: item.href, variant: "condensedBody", customLinkComponent: customLinkComponent, className: classes.linkText, children: item.label }), _jsx(Separator, { className: classes.separator })] }, i))), parentItem ? (_jsxs("li", { ref: parentItemLiRef, className: classes.item, style: allCollapsed && parentMinWidth !== undefined ? { minWidth: parentMinWidth } : undefined, children: [_jsx(Tooltip, { hideFromScreenReader: true, content: parentItem.label, disabled: !parentTruncated, placement: "bottom", style: TOOLTIP_TRIGGER_STYLE, children: _jsx(TextLink, { ref: parentLinkRef, href: parentItem.href, variant: "condensedBody", customLinkComponent: customLinkComponent, className: classes.linkText, children: parentItem.label }) }), _jsx(Separator, { className: classes.separator })] })) : null, currentItem ? (_jsx("li", { ref: currentItemLiRef, className: classes.item, "aria-current": lastItemIsCurrentPage ? 'page' : undefined, style: allCollapsed && currentMinWidth !== undefined ? { minWidth: currentMinWidth } : undefined, children: _jsx(Tooltip, { hideFromScreenReader: true, content: currentItem.label, disabled: !currentTruncated, placement: "bottom", style: TOOLTIP_TRIGGER_STYLE, children: lastItemIsCurrentPage ? (_jsx("span", { ref: currentItemRef, className: classes.currentItem, children: currentItem.label })) : (_jsx(TextLink, { ref: currentLinkRef, href: currentItem.href, variant: "condensedBody", customLinkComponent: customLinkComponent, className: classes.linkText, children: currentItem.label })) }) })) : null] }) }));
165
165
  });
@@ -32,4 +32,11 @@ export type BreadcrumbsProps = {
32
32
  * Also applied to truncated items in the DropdownMenu.
33
33
  */
34
34
  customLinkComponent?: ElementType;
35
+ /**
36
+ * The properties for the "…" button that opens the menu of truncated items.
37
+ * If this prop is not passed, the default values for each property will be applied.
38
+ *
39
+ * @property aria-label - The alternative text for the button. Override this to localize the label. default is '省略されたアイテムを表示'.
40
+ */
41
+ truncateButtonProps?: Pick<ComponentPropsWithoutRef<'button'>, 'aria-label'>;
35
42
  } & ComponentPropsWithoutRef<'nav'>;
@@ -71,6 +71,31 @@ export const usePopoverController = ({ placement, open, arrowRef }) => {
71
71
  });
72
72
  // Convert calculated placement back to original placement
73
73
  const originalPlacement = reversePlacementMapping[calculatedPlacement];
74
+ const isStartPlacement = originalPlacement.endsWith('Start');
75
+ const isEndPlacement = originalPlacement.endsWith('End');
76
+ const isVerticalPlacement = originalPlacement.startsWith('top') || originalPlacement.startsWith('bottom');
77
+ const arrowHorizontalPosition = (() => {
78
+ if (!isVerticalPlacement)
79
+ return;
80
+ if (isStartPlacement)
81
+ return { left: `${String(ARROW_PADDING)}px`, right: undefined };
82
+ if (isEndPlacement)
83
+ return { left: undefined, right: `${String(ARROW_PADDING)}px` };
84
+ return {
85
+ left: arrowData?.x ? `${String(arrowData.x)}px` : undefined,
86
+ right: undefined,
87
+ };
88
+ })();
89
+ const arrowVerticalPosition = (() => {
90
+ if (originalPlacement.startsWith('top'))
91
+ return { top: '100%', bottom: undefined };
92
+ if (originalPlacement.startsWith('bottom'))
93
+ return { top: undefined, bottom: '100%' };
94
+ return {
95
+ top: arrowData?.y ? `${String(arrowData.y)}px` : undefined,
96
+ bottom: undefined,
97
+ };
98
+ })();
74
99
  return {
75
100
  tooltip: {
76
101
  setReference: setFloating,
@@ -82,20 +107,10 @@ export const usePopoverController = ({ placement, open, arrowRef }) => {
82
107
  arrow: {
83
108
  style: {
84
109
  position: 'absolute',
85
- top: (() => {
86
- // When placement is top, the arrow should be at the bottom
87
- if (originalPlacement.startsWith('top'))
88
- return '100%';
89
- return arrowData?.y ? `${String(arrowData.y)}px` : undefined;
90
- })(),
91
- right: originalPlacement.startsWith('right') ? '100%' : undefined,
92
- left: (() => {
93
- // When placement is left, the arrow should be at the right
94
- if (originalPlacement.startsWith('left'))
95
- return '100%';
96
- return arrowData?.x ? `${String(arrowData.x)}px` : undefined;
97
- })(),
98
- bottom: originalPlacement.startsWith('bottom') ? '100%' : undefined,
110
+ top: arrowVerticalPosition.top,
111
+ bottom: arrowVerticalPosition.bottom,
112
+ left: arrowHorizontalPosition?.left ?? (originalPlacement.startsWith('left') ? '100%' : undefined),
113
+ right: arrowHorizontalPosition?.right ?? (originalPlacement.startsWith('right') ? '100%' : undefined),
99
114
  // Rotate the arrow if it's on the bottom or right
100
115
  transform: `rotate(${originalPlacement.startsWith('bottom') || originalPlacement.startsWith('right') ? '180deg' : '0'})`,
101
116
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moneyforward/mfui-components",
3
- "version": "3.31.1",
3
+ "version": "3.32.0",
4
4
  "description": "React UI Component Library for all Money Forward products",
5
5
  "repository": {
6
6
  "type": "git",
@@ -63,7 +63,7 @@
63
63
  "@floating-ui/react-dom": "^2.1.2",
64
64
  "@tanstack/react-virtual": "^3.13.18",
65
65
  "dayjs": "^1.11.13",
66
- "@moneyforward/mfui-design-tokens": "^3.3.0",
66
+ "@moneyforward/mfui-design-tokens": "^3.3.1",
67
67
  "@moneyforward/mfui-icons-react": "^3.6.0"
68
68
  },
69
69
  "scripts": {