detailpanel
This commit is contained in:
@@ -26,8 +26,18 @@ const ELLIPSIS = { id: '__breadcrumbs_ellipsis__', label: '…', onClick: null,
|
||||
const WIDTH_TOLERANCE = 1;
|
||||
const WIDTH_BUFFER_RATIO = 0.99;
|
||||
|
||||
const BreadcrumbTrail = ({ entries = [], className = '', separator = '/' }) => {
|
||||
const BreadcrumbTrail = ({
|
||||
entries = [],
|
||||
className = '',
|
||||
separator = '/',
|
||||
truncateFromStart = true,
|
||||
}) => {
|
||||
const normalized = useMemo(() => normalizeEntries(entries), [entries]);
|
||||
const shouldTruncateFromStart = truncateFromStart !== false;
|
||||
const measurementEntries = useMemo(
|
||||
() => (shouldTruncateFromStart ? normalized : normalized.slice().reverse()),
|
||||
[normalized, shouldTruncateFromStart],
|
||||
);
|
||||
const containerRef = useRef(null);
|
||||
const measurementRef = useRef(null);
|
||||
const ellipsisButtonRef = useRef(null);
|
||||
@@ -50,7 +60,7 @@ const BreadcrumbTrail = ({ entries = [], className = '', separator = '/' }) => {
|
||||
useEffect(() => {
|
||||
setStartIndex(0);
|
||||
closeEllipsisMenu();
|
||||
}, [normalized, closeEllipsisMenu]);
|
||||
}, [normalized, shouldTruncateFromStart, closeEllipsisMenu]);
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
@@ -58,10 +68,12 @@ const BreadcrumbTrail = ({ entries = [], className = '', separator = '/' }) => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const target = container.parentElement || container;
|
||||
|
||||
const updateWidth = () => {
|
||||
const nextWidth = target.getBoundingClientRect().width;
|
||||
const host = containerRef.current;
|
||||
if (!host) {
|
||||
return;
|
||||
}
|
||||
const nextWidth = host.getBoundingClientRect().width;
|
||||
if (!nextWidth) {
|
||||
return;
|
||||
}
|
||||
@@ -71,12 +83,12 @@ const BreadcrumbTrail = ({ entries = [], className = '', separator = '/' }) => {
|
||||
updateWidth();
|
||||
|
||||
const observer = new ResizeObserver(updateWidth);
|
||||
observer.observe(target);
|
||||
observer.observe(container.parentElement || container);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!normalized.length) {
|
||||
if (!measurementEntries.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -136,7 +148,7 @@ const BreadcrumbTrail = ({ entries = [], className = '', separator = '/' }) => {
|
||||
ellipsisNode.style.display = originalEllipsisDisplay ?? 'none';
|
||||
}
|
||||
|
||||
const available = availableWidth ?? container.clientWidth;
|
||||
const available = availableWidth ?? container.getBoundingClientRect().width;
|
||||
if (!available || !widths.length) {
|
||||
return;
|
||||
}
|
||||
@@ -156,13 +168,24 @@ const BreadcrumbTrail = ({ entries = [], className = '', separator = '/' }) => {
|
||||
if (nextStart !== startIndex) {
|
||||
setStartIndex(nextStart);
|
||||
}
|
||||
}, [normalized, separator, availableWidth, startIndex]);
|
||||
}, [measurementEntries, separator, availableWidth, startIndex]);
|
||||
|
||||
const sliceStart = Math.min(startIndex, Math.max(0, normalized.length - 1));
|
||||
const slice = normalized.slice(sliceStart);
|
||||
const visibleEntries = sliceStart > 0 ? [ELLIPSIS, ...slice] : slice;
|
||||
const hiddenEntries = sliceStart > 0 ? normalized.slice(0, sliceStart) : [];
|
||||
const trimmedCount = Math.min(startIndex, Math.max(0, normalized.length - 1));
|
||||
const visibleEntries = shouldTruncateFromStart
|
||||
? normalized.slice(trimmedCount)
|
||||
: normalized.slice(0, Math.max(normalized.length - trimmedCount, 1));
|
||||
const hiddenEntries = trimmedCount === 0
|
||||
? []
|
||||
: shouldTruncateFromStart
|
||||
? normalized.slice(0, trimmedCount)
|
||||
: normalized.slice(-trimmedCount);
|
||||
const hasHiddenEntries = hiddenEntries.length > 0;
|
||||
const ellipsisPlacement = shouldTruncateFromStart ? 'start' : 'end';
|
||||
const displayEntries = hasHiddenEntries
|
||||
? ellipsisPlacement === 'start'
|
||||
? [ELLIPSIS, ...visibleEntries]
|
||||
: [...visibleEntries, ELLIPSIS]
|
||||
: visibleEntries;
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasHiddenEntries) {
|
||||
@@ -191,9 +214,9 @@ const BreadcrumbTrail = ({ entries = [], className = '', separator = '/' }) => {
|
||||
return (
|
||||
<>
|
||||
<span ref={containerRef} className={wrapperClassName}>
|
||||
{visibleEntries.map((entry, index) => {
|
||||
{displayEntries.map((entry, index) => {
|
||||
const isEllipsis = entry.id === ELLIPSIS.id;
|
||||
const isLast = index === visibleEntries.length - 1;
|
||||
const isLast = index === displayEntries.length - 1;
|
||||
|
||||
if (isEllipsis) {
|
||||
return (
|
||||
@@ -273,7 +296,7 @@ const BreadcrumbTrail = ({ entries = [], className = '', separator = '/' }) => {
|
||||
>
|
||||
{ELLIPSIS.label}
|
||||
</span>
|
||||
{normalized.map((entry, index) => (
|
||||
{measurementEntries.map((entry, index) => (
|
||||
<React.Fragment key={`measure-${entry.id || index}`}>
|
||||
{index > 0 ? (
|
||||
<span
|
||||
@@ -342,6 +365,7 @@ BreadcrumbTrail.propTypes = {
|
||||
entries: PropTypes.arrayOf(breadcrumbEntryShape),
|
||||
className: PropTypes.string,
|
||||
separator: PropTypes.string,
|
||||
truncateFromStart: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default BreadcrumbTrail;
|
||||
|
||||
Reference in New Issue
Block a user