@box/blueprint-web 12.135.1 → 12.136.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/lib-esm/breadcrumb/breadcrumb-dropdown.d.ts +1 -1
- package/dist/lib-esm/breadcrumb/breadcrumb-dropdown.js +6 -4
- package/dist/lib-esm/breadcrumb/breadcrumb.js +58 -98
- package/dist/lib-esm/breadcrumb/breadcrumb.module.js +1 -1
- package/dist/lib-esm/breadcrumb/ellipsis-truncation-view.d.ts +2 -0
- package/dist/lib-esm/breadcrumb/ellipsis-truncation-view.js +42 -0
- package/dist/lib-esm/breadcrumb/folder-tree-truncation-view.d.ts +2 -0
- package/dist/lib-esm/breadcrumb/folder-tree-truncation-view.js +41 -0
- package/dist/lib-esm/breadcrumb/full-view.d.ts +2 -0
- package/dist/lib-esm/breadcrumb/full-view.js +19 -0
- package/dist/lib-esm/breadcrumb/mobile-view.d.ts +2 -0
- package/dist/lib-esm/breadcrumb/mobile-view.js +37 -0
- package/dist/lib-esm/breadcrumb/page-link.d.ts +2 -17
- package/dist/lib-esm/breadcrumb/page-link.js +13 -4
- package/dist/lib-esm/breadcrumb/types.d.ts +52 -1
- package/dist/lib-esm/breadcrumb/useFolderTreeTruncation.d.ts +8 -21
- package/dist/lib-esm/breadcrumb/useFolderTreeTruncation.js +105 -143
- package/dist/lib-esm/combobox/types.d.ts +4 -0
- package/dist/lib-esm/index.css +222 -219
- package/dist/lib-esm/primitives/dropdown-menu/dropdown-menu.module.js +1 -1
- package/dist/lib-esm/tooltip/tooltip.module.js +1 -1
- package/dist/lib-esm/util-components/base-grid-list-item/base-grid-list-item.module.js +1 -1
- package/package.json +3 -3
|
@@ -6,5 +6,5 @@ interface BreadcrumbDropdownProps {
|
|
|
6
6
|
onPageLinkClick: (id: string) => void;
|
|
7
7
|
size: 'xsmall' | 'small' | 'medium' | 'large';
|
|
8
8
|
}
|
|
9
|
-
export declare function BreadcrumbDropdown({ crumbsToRender, iconButton, onPageLinkClick, size,
|
|
9
|
+
export declare function BreadcrumbDropdown({ crumbsToRender, iconButton, listRef, onPageLinkClick, size, }: BreadcrumbDropdownProps): import("react/jsx-runtime").JSX.Element;
|
|
10
10
|
export {};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
2
|
import { useCallback } from 'react';
|
|
3
3
|
import { PointerRight } from '@box/blueprint-web-assets/icons/Fill';
|
|
4
|
+
import { EllipsizableText } from '../ellipsizable-text/ellipsizable-text.js';
|
|
4
5
|
import { DropdownMenu } from '../primitives/dropdown-menu/index.js';
|
|
5
|
-
import { Text } from '../text/text.js';
|
|
6
6
|
import { getSeparatorSize } from './utils.js';
|
|
7
7
|
import styles from './breadcrumb.module.js';
|
|
8
8
|
|
|
9
9
|
function BreadcrumbDropdown({
|
|
10
10
|
crumbsToRender,
|
|
11
11
|
iconButton,
|
|
12
|
+
listRef,
|
|
12
13
|
onPageLinkClick,
|
|
13
|
-
size
|
|
14
|
-
listRef
|
|
14
|
+
size
|
|
15
15
|
}) {
|
|
16
16
|
const handlePageLinkClick = useCallback(crumbId => () => {
|
|
17
17
|
onPageLinkClick(crumbId);
|
|
@@ -27,8 +27,10 @@ function BreadcrumbDropdown({
|
|
|
27
27
|
className: styles.dropdownContent,
|
|
28
28
|
children: crumbsToRender.map(crumb => jsx(DropdownMenu.Item, {
|
|
29
29
|
onSelect: handlePageLinkClick(crumb.id),
|
|
30
|
-
children: jsx(
|
|
30
|
+
children: jsx(EllipsizableText, {
|
|
31
31
|
as: "span",
|
|
32
|
+
lineClamp: 1,
|
|
33
|
+
tooltipSide: "bottom",
|
|
32
34
|
children: crumb.name
|
|
33
35
|
})
|
|
34
36
|
}, crumb.id))
|
|
@@ -1,50 +1,82 @@
|
|
|
1
|
-
import { jsx, jsxs
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import noop from 'lodash/noop';
|
|
2
3
|
import { forwardRef, useRef } from 'react';
|
|
3
4
|
import { FolderTree, PointerRight } from '@box/blueprint-web-assets/icons/Fill';
|
|
4
|
-
import { Ellipsis } from '@box/blueprint-web-assets/icons/Medium';
|
|
5
5
|
import { Home } from '@box/blueprint-web-assets/icons/MediumFilled';
|
|
6
|
-
import noop from 'lodash/noop';
|
|
7
|
-
import { IconButton } from '../primitives/icon-button/icon-button.js';
|
|
8
6
|
import { useBreakpoint, Breakpoint } from '../utils/useBreakpoint.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
7
|
+
import { EllipsisTruncationView } from './ellipsis-truncation-view.js';
|
|
8
|
+
import { FolderTreeTruncationView } from './folder-tree-truncation-view.js';
|
|
9
|
+
import { FullView } from './full-view.js';
|
|
10
|
+
import { MobileView } from './mobile-view.js';
|
|
11
11
|
import { useFolderTreeTruncation } from './useFolderTreeTruncation.js';
|
|
12
12
|
import { getSeparatorSize } from './utils.js';
|
|
13
13
|
import styles from './breadcrumb.module.js';
|
|
14
14
|
|
|
15
|
+
const ELLIPSIS_TRUNCATION_THRESHOLD = 7;
|
|
15
16
|
const Breadcrumb = /*#__PURE__*/forwardRef((props, forwardedRef) => {
|
|
16
17
|
const {
|
|
17
18
|
breadcrumbAriaLabel,
|
|
18
19
|
crumbs,
|
|
19
|
-
truncatedLinksIconAriaLabel,
|
|
20
|
-
rootIconAriaLabel,
|
|
21
|
-
rootIconVariant,
|
|
22
20
|
isInteractive = true,
|
|
23
21
|
isResponsiveEnabled,
|
|
22
|
+
onPageLinkClick = noop,
|
|
23
|
+
rootIconAriaLabel,
|
|
24
|
+
rootIconVariant,
|
|
24
25
|
size = 'medium',
|
|
26
|
+
truncatedLinksIconAriaLabel,
|
|
25
27
|
truncationMethod = 'ellipsis',
|
|
26
|
-
onPageLinkClick = noop,
|
|
27
28
|
...rest
|
|
28
29
|
} = props;
|
|
29
|
-
// Responsive detection: mobile/tablet takes priority over consumer-controlled truncationMethod
|
|
30
30
|
const breakpoint = useBreakpoint();
|
|
31
31
|
const isMobile = isResponsiveEnabled || breakpoint <= Breakpoint.Medium;
|
|
32
|
-
// If there are more than 7 crumbs, break up crumbs into first link, ellipsis icon button, and current page ancestor
|
|
33
|
-
const shouldUseEllipsisTruncation = !isMobile && truncationMethod === 'ellipsis' && crumbs && crumbs.length > 7;
|
|
34
|
-
// Get the current page (last crumb) and all ancestors (all crumbs except last)
|
|
35
|
-
const currentPage = crumbs[crumbs.length - 1];
|
|
36
|
-
const ancestorCrumbs = crumbs.slice(0, -1);
|
|
37
|
-
// Folder-tree truncation: detect overflow and show up to 3 visible crumbs
|
|
38
32
|
const breadcrumbListRef = useRef(null);
|
|
39
|
-
const isFolderTreeTruncationEnabled = !isMobile && truncationMethod === 'folder-tree';
|
|
40
33
|
const {
|
|
34
|
+
ellipsizeLastCrumb,
|
|
35
|
+
iconButtonRef,
|
|
41
36
|
isTruncationRequired,
|
|
42
|
-
visibleCrumbCount
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
visibleCrumbCount
|
|
38
|
+
} = useFolderTreeTruncation(breadcrumbListRef, crumbs, isMobile);
|
|
39
|
+
if (!crumbs || crumbs.length === 0) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
// Responsive detection: mobile/tablet takes priority over consumer-controlled truncationMethod
|
|
43
|
+
let breadcrumbList;
|
|
44
|
+
if (isMobile) {
|
|
45
|
+
breadcrumbList = jsx(MobileView, {
|
|
46
|
+
crumbs: crumbs,
|
|
47
|
+
isInteractive: isInteractive,
|
|
48
|
+
onPageLinkClick: onPageLinkClick,
|
|
49
|
+
size: size,
|
|
50
|
+
truncatedLinksIconAriaLabel: truncatedLinksIconAriaLabel
|
|
51
|
+
});
|
|
52
|
+
} else if (truncationMethod === 'ellipsis' && crumbs.length > ELLIPSIS_TRUNCATION_THRESHOLD) {
|
|
53
|
+
breadcrumbList = jsx(EllipsisTruncationView, {
|
|
54
|
+
crumbs: crumbs,
|
|
55
|
+
isInteractive: isInteractive,
|
|
56
|
+
onPageLinkClick: onPageLinkClick,
|
|
57
|
+
size: size,
|
|
58
|
+
truncatedLinksIconAriaLabel: truncatedLinksIconAriaLabel
|
|
59
|
+
});
|
|
60
|
+
} else if (truncationMethod === 'folder-tree' && isTruncationRequired) {
|
|
61
|
+
breadcrumbList = jsx(FolderTreeTruncationView, {
|
|
62
|
+
crumbs: crumbs,
|
|
63
|
+
ellipsizeLastCrumb: ellipsizeLastCrumb,
|
|
64
|
+
iconButtonRef: iconButtonRef,
|
|
65
|
+
isInteractive: isInteractive,
|
|
66
|
+
onPageLinkClick: onPageLinkClick,
|
|
67
|
+
size: size,
|
|
68
|
+
truncatedLinksIconAriaLabel: truncatedLinksIconAriaLabel,
|
|
69
|
+
visibleCrumbCount: visibleCrumbCount
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
breadcrumbList = jsx(FullView, {
|
|
73
|
+
crumbs: crumbs,
|
|
74
|
+
ellipsizeLastCrumb: ellipsizeLastCrumb,
|
|
75
|
+
isInteractive: isInteractive,
|
|
76
|
+
onPageLinkClick: onPageLinkClick,
|
|
77
|
+
size: size
|
|
78
|
+
});
|
|
79
|
+
}
|
|
48
80
|
return jsx("nav", {
|
|
49
81
|
ref: forwardedRef,
|
|
50
82
|
"aria-label": breadcrumbAriaLabel,
|
|
@@ -59,84 +91,12 @@ const Breadcrumb = /*#__PURE__*/forwardRef((props, forwardedRef) => {
|
|
|
59
91
|
"aria-label": rootIconAriaLabel
|
|
60
92
|
}) : jsx(FolderTree, {
|
|
61
93
|
"aria-label": rootIconAriaLabel
|
|
62
|
-
}),
|
|
94
|
+
}), jsx(PointerRight, {
|
|
63
95
|
height: getSeparatorSize(size),
|
|
64
96
|
role: "presentation",
|
|
65
97
|
width: getSeparatorSize(size)
|
|
66
98
|
})]
|
|
67
|
-
}),
|
|
68
|
-
children: [ancestorCrumbs.length > 0 && jsx(BreadcrumbDropdown, {
|
|
69
|
-
crumbsToRender: ancestorCrumbs,
|
|
70
|
-
iconButton: jsx(IconButton, {
|
|
71
|
-
"aria-label": truncatedLinksIconAriaLabel,
|
|
72
|
-
icon: FolderTree,
|
|
73
|
-
size: "small"
|
|
74
|
-
}),
|
|
75
|
-
onPageLinkClick: onPageLinkClick,
|
|
76
|
-
size: size
|
|
77
|
-
}), jsx(PageLink, {
|
|
78
|
-
crumb: currentPage,
|
|
79
|
-
isInteractive: isInteractive,
|
|
80
|
-
isLast: true,
|
|
81
|
-
onPageLinkClick: onPageLinkClick,
|
|
82
|
-
size: size
|
|
83
|
-
})]
|
|
84
|
-
}), shouldUseEllipsisTruncation && jsxs(Fragment, {
|
|
85
|
-
children: [jsx(PageLink, {
|
|
86
|
-
crumb: crumbs[0],
|
|
87
|
-
isInteractive: isInteractive,
|
|
88
|
-
isLast: false,
|
|
89
|
-
onPageLinkClick: onPageLinkClick,
|
|
90
|
-
size: size
|
|
91
|
-
}), jsx(BreadcrumbDropdown, {
|
|
92
|
-
crumbsToRender: crumbs.slice(1, crumbs.length - 2),
|
|
93
|
-
iconButton: jsx(IconButton, {
|
|
94
|
-
"aria-label": truncatedLinksIconAriaLabel,
|
|
95
|
-
icon: Ellipsis,
|
|
96
|
-
size: "small"
|
|
97
|
-
}),
|
|
98
|
-
onPageLinkClick: onPageLinkClick,
|
|
99
|
-
size: size
|
|
100
|
-
}), jsx(PageLink, {
|
|
101
|
-
crumb: crumbs[crumbs.length - 2],
|
|
102
|
-
isInteractive: isInteractive,
|
|
103
|
-
isLast: false,
|
|
104
|
-
onPageLinkClick: onPageLinkClick,
|
|
105
|
-
size: size
|
|
106
|
-
}), jsx(PageLink, {
|
|
107
|
-
crumb: currentPage,
|
|
108
|
-
isInteractive: isInteractive,
|
|
109
|
-
isLast: true,
|
|
110
|
-
onPageLinkClick: onPageLinkClick,
|
|
111
|
-
size: size
|
|
112
|
-
})]
|
|
113
|
-
}), shouldUseFolderTreeTruncation && jsxs(Fragment, {
|
|
114
|
-
children: [jsx(BreadcrumbDropdown, {
|
|
115
|
-
crumbsToRender: hiddenCrumbs,
|
|
116
|
-
iconButton: jsx(IconButton, {
|
|
117
|
-
"aria-label": truncatedLinksIconAriaLabel,
|
|
118
|
-
icon: FolderTree,
|
|
119
|
-
size: "small"
|
|
120
|
-
}),
|
|
121
|
-
listRef: iconButtonRef,
|
|
122
|
-
onPageLinkClick: onPageLinkClick,
|
|
123
|
-
size: size
|
|
124
|
-
}), visibleCrumbs.map((crumb, index) => jsx(PageLink, {
|
|
125
|
-
crumb: crumb,
|
|
126
|
-
isInteractive: isInteractive,
|
|
127
|
-
isLast: index === visibleCrumbs.length - 1,
|
|
128
|
-
onPageLinkClick: onPageLinkClick,
|
|
129
|
-
size: size
|
|
130
|
-
}, crumb.id))]
|
|
131
|
-
}), !isMobile && !shouldUseEllipsisTruncation && !shouldUseFolderTreeTruncation && crumbs?.map((crumb, index) => {
|
|
132
|
-
return jsx(PageLink, {
|
|
133
|
-
crumb: crumb,
|
|
134
|
-
isInteractive: isInteractive,
|
|
135
|
-
isLast: index === crumbs.length - 1,
|
|
136
|
-
onPageLinkClick: onPageLinkClick,
|
|
137
|
-
size: size
|
|
138
|
-
}, crumb.id);
|
|
139
|
-
})]
|
|
99
|
+
}), breadcrumbList]
|
|
140
100
|
})
|
|
141
101
|
});
|
|
142
102
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import '../index.css';
|
|
2
|
-
var styles = {"container":"bp_breadcrumb_module_container--
|
|
2
|
+
var styles = {"container":"bp_breadcrumb_module_container--335e0","breadcrumb":"bp_breadcrumb_module_breadcrumb--335e0","pageLink":"bp_breadcrumb_module_pageLink--335e0","linkWithHover":"bp_breadcrumb_module_linkWithHover--335e0","dropdownContent":"bp_breadcrumb_module_dropdownContent--335e0"};
|
|
3
3
|
|
|
4
4
|
export { styles as default };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Ellipsis } from '@box/blueprint-web-assets/icons/Medium';
|
|
3
|
+
import { IconButton } from '../primitives/icon-button/icon-button.js';
|
|
4
|
+
import { BreadcrumbDropdown } from './breadcrumb-dropdown.js';
|
|
5
|
+
import { PageLink } from './page-link.js';
|
|
6
|
+
|
|
7
|
+
const EllipsisTruncationView = ({
|
|
8
|
+
crumbs,
|
|
9
|
+
isInteractive,
|
|
10
|
+
onPageLinkClick,
|
|
11
|
+
size,
|
|
12
|
+
truncatedLinksIconAriaLabel
|
|
13
|
+
}) => {
|
|
14
|
+
const middleCrumbs = crumbs.slice(1, -2);
|
|
15
|
+
const lastTwoCrumbs = crumbs.slice(-2);
|
|
16
|
+
return jsxs(Fragment, {
|
|
17
|
+
children: [jsx(PageLink, {
|
|
18
|
+
crumb: crumbs[0],
|
|
19
|
+
isInteractive: isInteractive,
|
|
20
|
+
isLast: false,
|
|
21
|
+
onPageLinkClick: onPageLinkClick,
|
|
22
|
+
size: size
|
|
23
|
+
}), jsx(BreadcrumbDropdown, {
|
|
24
|
+
crumbsToRender: middleCrumbs,
|
|
25
|
+
iconButton: jsx(IconButton, {
|
|
26
|
+
"aria-label": truncatedLinksIconAriaLabel,
|
|
27
|
+
icon: Ellipsis,
|
|
28
|
+
size: "small"
|
|
29
|
+
}),
|
|
30
|
+
onPageLinkClick: onPageLinkClick,
|
|
31
|
+
size: size
|
|
32
|
+
}), lastTwoCrumbs.map((crumb, index) => jsx(PageLink, {
|
|
33
|
+
crumb: crumb,
|
|
34
|
+
isInteractive: isInteractive,
|
|
35
|
+
isLast: index === lastTwoCrumbs.length - 1,
|
|
36
|
+
onPageLinkClick: onPageLinkClick,
|
|
37
|
+
size: size
|
|
38
|
+
}, crumb.id))]
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export { EllipsisTruncationView };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { type FolderTreeTruncationViewProps } from './types';
|
|
2
|
+
export declare const FolderTreeTruncationView: ({ crumbs, ellipsizeLastCrumb, iconButtonRef, isInteractive, onPageLinkClick, size, truncatedLinksIconAriaLabel, visibleCrumbCount, }: FolderTreeTruncationViewProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { FolderTree } from '@box/blueprint-web-assets/icons/Fill';
|
|
3
|
+
import { IconButton } from '../primitives/icon-button/icon-button.js';
|
|
4
|
+
import { BreadcrumbDropdown } from './breadcrumb-dropdown.js';
|
|
5
|
+
import { PageLink } from './page-link.js';
|
|
6
|
+
|
|
7
|
+
const FolderTreeTruncationView = ({
|
|
8
|
+
crumbs,
|
|
9
|
+
ellipsizeLastCrumb,
|
|
10
|
+
iconButtonRef,
|
|
11
|
+
isInteractive,
|
|
12
|
+
onPageLinkClick,
|
|
13
|
+
size,
|
|
14
|
+
truncatedLinksIconAriaLabel,
|
|
15
|
+
visibleCrumbCount
|
|
16
|
+
}) => {
|
|
17
|
+
const visibleCrumbs = crumbs.slice(-visibleCrumbCount);
|
|
18
|
+
const hiddenCrumbs = crumbs.slice(0, -visibleCrumbCount);
|
|
19
|
+
return jsxs(Fragment, {
|
|
20
|
+
children: [jsx(BreadcrumbDropdown, {
|
|
21
|
+
crumbsToRender: hiddenCrumbs,
|
|
22
|
+
iconButton: jsx(IconButton, {
|
|
23
|
+
"aria-label": truncatedLinksIconAriaLabel,
|
|
24
|
+
icon: FolderTree,
|
|
25
|
+
size: "small"
|
|
26
|
+
}),
|
|
27
|
+
listRef: iconButtonRef,
|
|
28
|
+
onPageLinkClick: onPageLinkClick,
|
|
29
|
+
size: size
|
|
30
|
+
}), visibleCrumbs.map((crumb, index) => jsx(PageLink, {
|
|
31
|
+
crumb: crumb,
|
|
32
|
+
ellipsizeLastCrumb: ellipsizeLastCrumb,
|
|
33
|
+
isInteractive: isInteractive,
|
|
34
|
+
isLast: index === visibleCrumbs.length - 1,
|
|
35
|
+
onPageLinkClick: onPageLinkClick,
|
|
36
|
+
size: size
|
|
37
|
+
}, crumb.id))]
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export { FolderTreeTruncationView };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { PageLink } from './page-link.js';
|
|
3
|
+
|
|
4
|
+
const FullView = ({
|
|
5
|
+
crumbs,
|
|
6
|
+
ellipsizeLastCrumb,
|
|
7
|
+
isInteractive,
|
|
8
|
+
onPageLinkClick,
|
|
9
|
+
size
|
|
10
|
+
}) => crumbs.map((crumb, index) => jsx(PageLink, {
|
|
11
|
+
crumb: crumb,
|
|
12
|
+
ellipsizeLastCrumb: ellipsizeLastCrumb,
|
|
13
|
+
isInteractive: isInteractive,
|
|
14
|
+
isLast: index === crumbs.length - 1,
|
|
15
|
+
onPageLinkClick: onPageLinkClick,
|
|
16
|
+
size: size
|
|
17
|
+
}, crumb.id));
|
|
18
|
+
|
|
19
|
+
export { FullView };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { FolderTree } from '@box/blueprint-web-assets/icons/Fill';
|
|
3
|
+
import { IconButton } from '../primitives/icon-button/icon-button.js';
|
|
4
|
+
import { BreadcrumbDropdown } from './breadcrumb-dropdown.js';
|
|
5
|
+
import { PageLink } from './page-link.js';
|
|
6
|
+
|
|
7
|
+
const MobileView = ({
|
|
8
|
+
crumbs,
|
|
9
|
+
isInteractive,
|
|
10
|
+
onPageLinkClick,
|
|
11
|
+
size,
|
|
12
|
+
truncatedLinksIconAriaLabel
|
|
13
|
+
}) => {
|
|
14
|
+
const currentPage = crumbs[crumbs.length - 1];
|
|
15
|
+
const hiddenCrumbs = crumbs.slice(0, -1);
|
|
16
|
+
return jsxs(Fragment, {
|
|
17
|
+
children: [hiddenCrumbs.length > 0 && jsx(BreadcrumbDropdown, {
|
|
18
|
+
crumbsToRender: hiddenCrumbs,
|
|
19
|
+
iconButton: jsx(IconButton, {
|
|
20
|
+
"aria-label": truncatedLinksIconAriaLabel,
|
|
21
|
+
icon: FolderTree,
|
|
22
|
+
size: "small"
|
|
23
|
+
}),
|
|
24
|
+
onPageLinkClick: onPageLinkClick,
|
|
25
|
+
size: size
|
|
26
|
+
}), jsx(PageLink, {
|
|
27
|
+
crumb: currentPage,
|
|
28
|
+
ellipsizeLastCrumb: true,
|
|
29
|
+
isInteractive: isInteractive,
|
|
30
|
+
isLast: true,
|
|
31
|
+
onPageLinkClick: onPageLinkClick,
|
|
32
|
+
size: size
|
|
33
|
+
})]
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { MobileView };
|
|
@@ -1,17 +1,2 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
|
|
3
|
-
* A page link represents a combination of an optional folder icon, a single crumb, and a separator.
|
|
4
|
-
*/
|
|
5
|
-
export interface PageLinkProps {
|
|
6
|
-
/** The crumb to display in the page link. */
|
|
7
|
-
crumb: Crumb;
|
|
8
|
-
/** Whether the page link is the last crumb in the breadcrumb. */
|
|
9
|
-
isLast: boolean;
|
|
10
|
-
/** Whether the page link is interactive. */
|
|
11
|
-
isInteractive: boolean;
|
|
12
|
-
/** The callback to call when the page link is clicked. */
|
|
13
|
-
onPageLinkClick: (id: string) => void;
|
|
14
|
-
/** The text size of the page link. */
|
|
15
|
-
size: 'xsmall' | 'small' | 'medium' | 'large';
|
|
16
|
-
}
|
|
17
|
-
export declare const PageLink: ({ crumb, isLast, isInteractive, onPageLinkClick, size }: PageLinkProps) => import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
import { type PageLinkProps } from './types';
|
|
2
|
+
export declare const PageLink: ({ crumb, ellipsizeLastCrumb, isInteractive, isLast, onPageLinkClick, size, }: PageLinkProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,25 +1,34 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import { useCallback } from 'react';
|
|
3
3
|
import { PointerRight } from '@box/blueprint-web-assets/icons/Fill';
|
|
4
|
+
import { EllipsizableText } from '../ellipsizable-text/ellipsizable-text.js';
|
|
4
5
|
import { Link } from '../primitives/link/link.js';
|
|
5
6
|
import { Text } from '../text/text.js';
|
|
6
|
-
import styles from './breadcrumb.module.js';
|
|
7
7
|
import { getBoldTextVariantFromSize, getTextVariantFromSize, getSeparatorSize } from './utils.js';
|
|
8
|
+
import styles from './breadcrumb.module.js';
|
|
8
9
|
|
|
9
10
|
const PageLink = ({
|
|
10
11
|
crumb,
|
|
11
|
-
|
|
12
|
+
ellipsizeLastCrumb = false,
|
|
12
13
|
isInteractive,
|
|
14
|
+
isLast,
|
|
13
15
|
onPageLinkClick,
|
|
14
16
|
size
|
|
15
17
|
}) => {
|
|
16
18
|
const handlePageLinkClick = useCallback(() => {
|
|
17
19
|
onPageLinkClick(crumb.id);
|
|
18
|
-
}, [
|
|
20
|
+
}, [crumb.id, onPageLinkClick]);
|
|
19
21
|
if (isLast) {
|
|
20
22
|
return jsx("li", {
|
|
21
23
|
className: styles.pageLink,
|
|
22
|
-
children: jsx(
|
|
24
|
+
children: ellipsizeLastCrumb ? jsx(EllipsizableText, {
|
|
25
|
+
"aria-current": "page",
|
|
26
|
+
as: "span",
|
|
27
|
+
color: "textOnLightDefault",
|
|
28
|
+
lineClamp: 1,
|
|
29
|
+
variant: getBoldTextVariantFromSize(size),
|
|
30
|
+
children: crumb.name
|
|
31
|
+
}) : jsx(Text, {
|
|
23
32
|
"aria-current": "page",
|
|
24
33
|
as: "span",
|
|
25
34
|
color: "textOnLightDefault",
|
|
@@ -23,7 +23,8 @@ export type BreadcrumbProps = {
|
|
|
23
23
|
size?: 'xsmall' | 'small' | 'medium' | 'large';
|
|
24
24
|
/** Aria label for the icon button when breadcrumb is truncated. */
|
|
25
25
|
truncatedLinksIconAriaLabel: string;
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Controls behavior when there are too many crumbs to display.
|
|
27
28
|
* Ellipsis shows the crumbs at the beginning and end, with an ellipsis icon in between.
|
|
28
29
|
* Folder-tree dynamically detects container overflow and shows up to 3 visible crumbs with a folder tree icon dropdown.
|
|
29
30
|
*/
|
|
@@ -36,3 +37,53 @@ export interface rootIconProps {
|
|
|
36
37
|
/** Determines which icon is displayed at the root. */
|
|
37
38
|
rootIconVariant: 'home' | 'folder-tree';
|
|
38
39
|
}
|
|
40
|
+
export interface BreadcrumbViewProps {
|
|
41
|
+
crumbs: Crumb[];
|
|
42
|
+
ellipsizeLastCrumb?: boolean;
|
|
43
|
+
isInteractive: boolean;
|
|
44
|
+
onPageLinkClick: (id: string) => void;
|
|
45
|
+
size: NonNullable<BreadcrumbProps['size']>;
|
|
46
|
+
truncatedLinksIconAriaLabel: string;
|
|
47
|
+
}
|
|
48
|
+
export interface FolderTreeTruncationViewProps extends BreadcrumbViewProps {
|
|
49
|
+
iconButtonRef: React.RefObject<HTMLLIElement>;
|
|
50
|
+
visibleCrumbCount: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A page link represents a combination of an optional folder icon, a single crumb, and a separator.
|
|
54
|
+
*/
|
|
55
|
+
export interface PageLinkProps {
|
|
56
|
+
/** The crumb to display in the page link. */
|
|
57
|
+
crumb: Crumb;
|
|
58
|
+
/** Whether the last crumb should be ellipsized. */
|
|
59
|
+
ellipsizeLastCrumb?: boolean;
|
|
60
|
+
/** Whether the page link is interactive. */
|
|
61
|
+
isInteractive: boolean;
|
|
62
|
+
/** Whether the page link is the last crumb in the breadcrumb. */
|
|
63
|
+
isLast: boolean;
|
|
64
|
+
/** The callback to call when the page link is clicked. */
|
|
65
|
+
onPageLinkClick: (id: string) => void;
|
|
66
|
+
/** The text size of the page link. */
|
|
67
|
+
size: 'xsmall' | 'small' | 'medium' | 'large';
|
|
68
|
+
}
|
|
69
|
+
export interface TruncationState {
|
|
70
|
+
/** Whether the last crumb should be ellipsized (when it doesn't fit even with truncation) */
|
|
71
|
+
ellipsizeLastCrumb: boolean;
|
|
72
|
+
/** Whether the breadcrumb content would exceed the container width IF no truncation was being applied */
|
|
73
|
+
isTruncationRequired: boolean;
|
|
74
|
+
/** Number of visible crumbs (not in dropdown) to show (1-3) */
|
|
75
|
+
visibleCrumbCount: number;
|
|
76
|
+
}
|
|
77
|
+
export interface FolderTreeTruncationResult extends TruncationState {
|
|
78
|
+
/** Ref to attach to the icon button container for width measurement */
|
|
79
|
+
iconButtonRef: React.RefObject<HTMLLIElement>;
|
|
80
|
+
}
|
|
81
|
+
export interface PerformTruncationParams {
|
|
82
|
+
container: HTMLOListElement;
|
|
83
|
+
crumbCount: number;
|
|
84
|
+
iconButtonRef: React.RefObject<HTMLLIElement>;
|
|
85
|
+
isTruncationRequired: boolean;
|
|
86
|
+
measuredIconButtonWidth: React.MutableRefObject<number>;
|
|
87
|
+
setState: React.Dispatch<React.SetStateAction<TruncationState>>;
|
|
88
|
+
storedCrumbWidths: React.MutableRefObject<number[]>;
|
|
89
|
+
}
|
|
@@ -1,25 +1,12 @@
|
|
|
1
|
-
import { type Crumb } from './types';
|
|
2
|
-
interface FolderTreeTruncationState {
|
|
3
|
-
/** Whether the breadcrumb content would exceed the container width IF no truncation was being applied */
|
|
4
|
-
isTruncationRequired: boolean;
|
|
5
|
-
/** Number of visible crumbs (not in dropdown) to show (1-3) */
|
|
6
|
-
visibleCrumbCount: number;
|
|
7
|
-
}
|
|
8
|
-
interface FolderTreeTruncationResult extends FolderTreeTruncationState {
|
|
9
|
-
/** Ref to attach to the icon button container li element for width measurement */
|
|
10
|
-
iconButtonRef: React.RefObject<HTMLLIElement>;
|
|
11
|
-
}
|
|
1
|
+
import { type Crumb, type FolderTreeTruncationResult } from './types';
|
|
12
2
|
/**
|
|
13
|
-
* Hook
|
|
3
|
+
* Hook that calculates optimal breadcrumb truncation for folder-tree display.
|
|
14
4
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* - **>3 crumbs**: Render truncated state immediately, show icon button + up to 3 crumbs
|
|
5
|
+
* Uses a cascading fallback strategy to show as many crumbs as possible without
|
|
6
|
+
* text ellipsis, falling back to ellipsis only as a last resort.
|
|
18
7
|
*
|
|
19
|
-
* @param containerRef - Ref to the
|
|
20
|
-
* @param crumbs - Array of breadcrumb items
|
|
21
|
-
* @param
|
|
22
|
-
* @returns Object containing wouldCrumbsOverflow, visibleCrumbCount, and iconButtonRef
|
|
8
|
+
* @param containerRef - Ref to the breadcrumb list container (ol element)
|
|
9
|
+
* @param crumbs - Array of breadcrumb items to display
|
|
10
|
+
* @param isMobile - Whether the breadcrumb is on a mobile device
|
|
23
11
|
*/
|
|
24
|
-
export declare const useFolderTreeTruncation: (containerRef: React.RefObject<HTMLOListElement>, crumbs: Crumb[],
|
|
25
|
-
export {};
|
|
12
|
+
export declare const useFolderTreeTruncation: (containerRef: React.RefObject<HTMLOListElement>, crumbs: Crumb[], isMobile: boolean) => FolderTreeTruncationResult;
|