fixes
This commit is contained in:
@@ -24,7 +24,8 @@ const normalizeEntries = (entries) =>
|
||||
|
||||
const ELLIPSIS = { id: '__breadcrumbs_ellipsis__', label: '…', onClick: null, raw: null };
|
||||
const WIDTH_TOLERANCE = 1;
|
||||
const WIDTH_BUFFER_RATIO = 0.99;
|
||||
const WIDTH_BUFFER_RATIO = 0.95;
|
||||
const WIDTH_CHANGE_TOLERANCE = 0.25;
|
||||
|
||||
const BreadcrumbTrail = ({
|
||||
entries = [],
|
||||
@@ -43,6 +44,7 @@ const BreadcrumbTrail = ({
|
||||
const ellipsisButtonRef = useRef(null);
|
||||
const [availableWidth, setAvailableWidth] = useState(null);
|
||||
const [startIndex, setStartIndex] = useState(0);
|
||||
const measureRafRef = useRef(null);
|
||||
|
||||
const {
|
||||
isOpen: ellipsisMenuOpen,
|
||||
@@ -58,18 +60,13 @@ const BreadcrumbTrail = ({
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setStartIndex(0);
|
||||
closeEllipsisMenu();
|
||||
}, [normalized, shouldTruncateFromStart, closeEllipsisMenu]);
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container || typeof ResizeObserver === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const updateWidth = () => {
|
||||
const host = containerRef.current;
|
||||
const resolveHost = () => containerRef.current?.parentElement || containerRef.current;
|
||||
const measure = () => {
|
||||
const host = resolveHost();
|
||||
if (!host) {
|
||||
return;
|
||||
}
|
||||
@@ -77,14 +74,56 @@ const BreadcrumbTrail = ({
|
||||
if (!nextWidth) {
|
||||
return;
|
||||
}
|
||||
setAvailableWidth((prev) => (prev && Math.abs(prev - nextWidth) < 0.5 ? prev : nextWidth));
|
||||
setAvailableWidth((prev) => (
|
||||
prev && Math.abs(prev - nextWidth) < WIDTH_CHANGE_TOLERANCE ? prev : nextWidth
|
||||
));
|
||||
};
|
||||
|
||||
updateWidth();
|
||||
const scheduleMeasure = () => {
|
||||
if (typeof window === 'undefined' || typeof window.requestAnimationFrame !== 'function') {
|
||||
measure();
|
||||
return;
|
||||
}
|
||||
if (measureRafRef.current) {
|
||||
cancelAnimationFrame(measureRafRef.current);
|
||||
}
|
||||
measureRafRef.current = window.requestAnimationFrame(() => {
|
||||
measureRafRef.current = null;
|
||||
measure();
|
||||
});
|
||||
};
|
||||
|
||||
const observer = new ResizeObserver(updateWidth);
|
||||
observer.observe(container.parentElement || container);
|
||||
return () => observer.disconnect();
|
||||
scheduleMeasure();
|
||||
|
||||
if (typeof ResizeObserver === 'undefined') {
|
||||
return () => {
|
||||
if (measureRafRef.current) {
|
||||
cancelAnimationFrame(measureRafRef.current);
|
||||
measureRafRef.current = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const host = resolveHost();
|
||||
if (!host) {
|
||||
return () => {
|
||||
if (measureRafRef.current) {
|
||||
cancelAnimationFrame(measureRafRef.current);
|
||||
measureRafRef.current = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const observer = new ResizeObserver(scheduleMeasure);
|
||||
observer.observe(host);
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
if (measureRafRef.current) {
|
||||
cancelAnimationFrame(measureRafRef.current);
|
||||
measureRafRef.current = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
@@ -94,7 +133,8 @@ const BreadcrumbTrail = ({
|
||||
|
||||
const container = containerRef.current;
|
||||
const measurement = measurementRef.current;
|
||||
if (!container || !measurement) {
|
||||
const host = container?.parentElement || container;
|
||||
if (!container || !measurement || !host) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -142,7 +182,7 @@ const BreadcrumbTrail = ({
|
||||
ellipsisNode.style.display = originalEllipsisDisplay ?? 'none';
|
||||
}
|
||||
|
||||
const available = availableWidth ?? container.getBoundingClientRect().width;
|
||||
const available = availableWidth ?? host.getBoundingClientRect().width;
|
||||
if (!available || !widths.length) {
|
||||
return;
|
||||
}
|
||||
@@ -283,33 +323,43 @@ const BreadcrumbTrail = ({
|
||||
className="breadcrumb-trail breadcrumb-trail--measure"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span
|
||||
<button
|
||||
type="button"
|
||||
className="breadcrumb-trail__link breadcrumb-trail__ellipsis-button"
|
||||
data-item-type="ellipsis"
|
||||
style={{ display: 'none' }}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{ELLIPSIS.label}
|
||||
</span>
|
||||
{measurementEntries.map((entry, index) => (
|
||||
<React.Fragment key={`measure-${entry.id || index}`}>
|
||||
{index > 0 ? (
|
||||
<span
|
||||
className="breadcrumb-trail__separator"
|
||||
data-item-type="separator"
|
||||
data-target-index={index}
|
||||
</button>
|
||||
{measurementEntries.map((entry, index) => {
|
||||
const isLast = index === measurementEntries.length - 1;
|
||||
const isInteractive = Boolean(entry.onClick) && !isLast;
|
||||
const MeasurementTag = isInteractive ? 'button' : 'span';
|
||||
|
||||
return (
|
||||
<React.Fragment key={`measure-${entry.id || index}`}>
|
||||
{index > 0 ? (
|
||||
<span
|
||||
className="breadcrumb-trail__separator"
|
||||
data-item-type="separator"
|
||||
data-target-index={index}
|
||||
>
|
||||
{separator}
|
||||
</span>
|
||||
) : null}
|
||||
<MeasurementTag
|
||||
type={isInteractive ? 'button' : undefined}
|
||||
className="breadcrumb-trail__link"
|
||||
data-item-type="entry"
|
||||
data-entry-index={index}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{separator}
|
||||
</span>
|
||||
) : null}
|
||||
<span
|
||||
className="breadcrumb-trail__link"
|
||||
data-item-type="entry"
|
||||
data-entry-index={index}
|
||||
>
|
||||
{entry.label}
|
||||
</span>
|
||||
</React.Fragment>
|
||||
))}
|
||||
{entry.label}
|
||||
</MeasurementTag>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</span>
|
||||
{ellipsisMenuOpen
|
||||
&& hasHiddenEntries
|
||||
@@ -321,6 +371,7 @@ const BreadcrumbTrail = ({
|
||||
role="menu"
|
||||
ref={ellipsisMenuRef}
|
||||
style={ellipsisMenuStyle}
|
||||
data-floating-position
|
||||
>
|
||||
<div className="menu__list">
|
||||
{hiddenEntries.map((hiddenEntry) => (
|
||||
|
||||
@@ -144,6 +144,23 @@ const QuickAddMenu = ({
|
||||
);
|
||||
|
||||
const canCreate = Boolean(onCreate);
|
||||
const isAnchoredMenu = positionStrategy === 'absolute' && align === 'start';
|
||||
const menuClassName = 'menu menu--floating';
|
||||
const anchoredMenuStyle = isAnchoredMenu && menuStyle
|
||||
? {
|
||||
top: menuStyle.top,
|
||||
left: menuStyle.left,
|
||||
...(menuStyle.width ? { width: menuStyle.width } : null),
|
||||
}
|
||||
: undefined;
|
||||
const menuInlineStyle = isAnchoredMenu ? anchoredMenuStyle : menuStyle || undefined;
|
||||
const hasFloatingWidthVar = Boolean(menuInlineStyle && Object.prototype.hasOwnProperty.call(menuInlineStyle, '--floating-min-width'));
|
||||
const menuStyleWithVar = hasFloatingWidthVar
|
||||
? menuInlineStyle
|
||||
: {
|
||||
...(menuInlineStyle || {}),
|
||||
'--floating-min-width': `${Math.max(menuMinWidth, 0)}px`,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className ? `quick-add ${className}` : 'quick-add'}>
|
||||
@@ -162,10 +179,11 @@ const QuickAddMenu = ({
|
||||
</button>
|
||||
{isOpen ? (
|
||||
<div
|
||||
className="menu menu--floating quick-add__menu"
|
||||
className={menuClassName}
|
||||
ref={menuRef}
|
||||
style={menuStyle || undefined}
|
||||
style={menuStyleWithVar}
|
||||
role="menu"
|
||||
data-floating-position
|
||||
>
|
||||
{canCreate ? (
|
||||
<form className="quick-add__form" onSubmit={handleCreate}>
|
||||
@@ -183,7 +201,7 @@ const QuickAddMenu = ({
|
||||
</button>
|
||||
</form>
|
||||
) : null}
|
||||
<div className="menu__list quick-add__list" role="presentation">
|
||||
<div className="menu__list" role="presentation">
|
||||
{filteredOptions.length ? (
|
||||
filteredOptions.map((option) => {
|
||||
const key = option.id ?? option.index;
|
||||
|
||||
@@ -28,8 +28,10 @@ const formatStyle = (metrics) => {
|
||||
position: metrics.strategy === 'absolute' ? 'absolute' : 'fixed',
|
||||
top: metrics.top,
|
||||
left: metrics.left,
|
||||
minWidth: metrics.minWidth,
|
||||
};
|
||||
if (typeof metrics.minWidth === 'number') {
|
||||
style['--floating-min-width'] = `${Math.max(metrics.minWidth, 0)}px`;
|
||||
}
|
||||
if (metrics.width) {
|
||||
style.width = metrics.width;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user