Skip to content

Commit

Permalink
[core] Add trackAnchor prop for anchor positioning (#519)
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks authored Aug 4, 2024
1 parent dfeef6e commit 79d3997
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 44 deletions.
104 changes: 61 additions & 43 deletions docs/pages/experiments/anchor-positioning.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';

import { useAnchorPositioning } from '../../../packages/mui-base/src/utils/useAnchorPositioning';

Expand Down Expand Up @@ -26,6 +27,7 @@ export default function AnchorPositioning() {
const [sticky, setSticky] = React.useState(false);
const [constrainSize, setConstrainSize] = React.useState(false);
const [visible, setVisible] = React.useState(false);
const [trackAnchor, setTrackAnchor] = React.useState(true);

const { refs, positionerStyles, arrowStyles, arrowRef, renderedSide, arrowUncentered } =
useAnchorPositioning({
Expand All @@ -37,6 +39,7 @@ export default function AnchorPositioning() {
hideWhenDetached,
sticky,
arrowPadding,
trackAnchor,
});

const handleInitialScroll = React.useCallback((node: HTMLDivElement | null) => {
Expand All @@ -55,6 +58,54 @@ export default function AnchorPositioning() {
xl: 250,
}[anchorSize];

const popup = (
<div
ref={refs.setFloating}
style={{
visibility: visible ? 'visible' : 'hidden',
...positionerStyles,
}}
>
<div
style={{
background: 'white',
boxSizing: 'border-box',
padding: 10,
...(constrainSize && {
maxWidth: 'var(--available-width)',
maxHeight: 'var(--available-height)',
overflow: 'auto',
}),
}}
>
{`Content `.repeat(
{
xs: 1,
s: 3,
m: 10,
l: 50,
xl: 200,
}[popupSize],
)}
</div>
{arrow && (
<div
ref={arrowRef as React.RefObject<HTMLDivElement>}
style={{
...arrowStyles,
background: 'rgba(0, 0, 255, 0.5)',
width: 20,
height: 20,
[oppositeSideMap[renderedSide]]: -10,
...(arrowUncentered && hideArrowWhenUncentered && { visibility: 'hidden' }),
}}
/>
)}
</div>
);

const popupNode = trackAnchor ? popup : ReactDOM.createPortal(popup, document.body);

return (
<div style={{ fontFamily: 'sans-serif', margin: 50 }}>
<h1>Anchor Positioning Playground</h1>
Expand Down Expand Up @@ -88,49 +139,7 @@ export default function AnchorPositioning() {
>
{anchorSize !== 'xs' ? 'A' : null}
</div>
<div
ref={refs.setFloating}
style={{
visibility: visible ? 'visible' : 'hidden',
...positionerStyles,
}}
>
<div
style={{
background: 'white',
boxSizing: 'border-box',
padding: 10,
...(constrainSize && {
maxWidth: 'var(--available-width)',
maxHeight: 'var(--available-height)',
overflow: 'auto',
}),
}}
>
{`Content `.repeat(
{
xs: 1,
s: 3,
m: 10,
l: 50,
xl: 200,
}[popupSize],
)}
</div>
{arrow && (
<div
ref={arrowRef as React.RefObject<HTMLDivElement>}
style={{
...arrowStyles,
background: 'rgba(0, 0, 255, 0.5)',
width: 20,
height: 20,
[oppositeSideMap[renderedSide]]: -10,
...(arrowUncentered && hideArrowWhenUncentered && { visibility: 'hidden' }),
}}
/>
)}
</div>
{popupNode}
<div style={{ width: 1000 + anchorLength / 2, height: 1000 }} />
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
Expand Down Expand Up @@ -273,6 +282,15 @@ export default function AnchorPositioning() {
<input type="checkbox" checked={sticky} onChange={() => setSticky((prev) => !prev)} />
Sticky
</label>

<label>
<input
type="checkbox"
checked={trackAnchor}
onChange={() => setTrackAnchor((prev) => !prev)}
/>
Track anchor
</label>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ export interface PopoverPositionerParameters {
* @default false
*/
keepMounted?: boolean;
/**
* Whether the popover popup continuously tracks its anchor after the initial positioning upon
* mount.
* @default true
*/
trackAnchor?: boolean;
}

export interface UsePopoverPositionerParameters extends PopoverPositionerParameters {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ export interface PreviewCardPositionerParameters {
* @default false
*/
keepMounted?: boolean;
/**
* Whether the preview card popup continuously tracks its anchor after the initial positioning
* upon mount.
* @default true
*/
trackAnchor?: boolean;
}

export interface UsePreviewCardPositionerParameters extends PreviewCardPositionerParameters {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ export interface TooltipPositionerParameters {
* @default false
*/
keepMounted?: boolean;
/**
* Whether the tooltip popup continuously tracks its anchor after the initial positioning upon
* mount.
* @default true
*/
trackAnchor?: boolean;
}

export interface UseTooltipPositionerParameters extends TooltipPositionerParameters {
Expand Down
4 changes: 3 additions & 1 deletion packages/mui-base/src/utils/useAnchorPositioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ interface UseAnchorPositioningParameters {
arrowPadding?: number;
floatingRootContext?: FloatingRootContext;
mounted?: boolean;
trackAnchor?: boolean;
nodeId?: string;
}

Expand Down Expand Up @@ -86,6 +87,7 @@ export function useAnchorPositioning(
keepMounted = false,
arrowPadding = 5,
mounted = true,
trackAnchor = true,
nodeId,
} = params;

Expand Down Expand Up @@ -199,7 +201,7 @@ export function useAnchorPositioning(
placement,
middleware,
strategy: positionStrategy,
whileElementsMounted: keepMounted ? undefined : autoUpdate,
whileElementsMounted: keepMounted || !trackAnchor ? undefined : autoUpdate,
nodeId,
});

Expand Down

0 comments on commit 79d3997

Please sign in to comment.