|
@ -52,7 +52,11 @@ export interface AffixState { |
|
|
prevTarget: Window | HTMLElement | null; |
|
|
prevTarget: Window | HTMLElement | null; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const Affix: React.FC<AffixProps> = (props) => { |
|
|
interface AffixRef { |
|
|
|
|
|
updatePosition: ReturnType<typeof throttleByAnimationFrame>; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const Affix = React.forwardRef<AffixRef, AffixProps>((props, ref) => { |
|
|
const { |
|
|
const { |
|
|
rootClassName: customizeRootClassName, |
|
|
rootClassName: customizeRootClassName, |
|
|
children, |
|
|
children, |
|
@ -134,9 +138,8 @@ const Affix: React.FC<AffixProps> = (props) => { |
|
|
}); |
|
|
}); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
const lazyUpdatePosition = throttleByAnimationFrame(() => { |
|
|
const updatePosition = throttleByAnimationFrame(() => { |
|
|
// Check position change before measure to make Safari smooth
|
|
|
// Check position change before measure to make Safari smooth
|
|
|
|
|
|
|
|
|
if (target && placeholderNodeRef.current) { |
|
|
if (target && placeholderNodeRef.current) { |
|
|
const targetRect = getTargetRect(target); |
|
|
const targetRect = getTargetRect(target); |
|
|
const placeholderRect = getTargetRect(placeholderNodeRef.current); |
|
|
const placeholderRect = getTargetRect(placeholderNodeRef.current); |
|
@ -152,10 +155,12 @@ const Affix: React.FC<AffixProps> = (props) => { |
|
|
measure(); |
|
|
measure(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
React.useImperativeHandle(ref, () => ({ updatePosition })); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
|
timerRef.current = setTimeout(() => { |
|
|
timerRef.current = setTimeout(() => { |
|
|
TRIGGER_EVENTS.forEach((eventName) => { |
|
|
TRIGGER_EVENTS.forEach((eventName) => { |
|
|
target?.addEventListener(eventName, lazyUpdatePosition); |
|
|
target?.addEventListener(eventName, updatePosition); |
|
|
}); |
|
|
}); |
|
|
measure(); |
|
|
measure(); |
|
|
}); |
|
|
}); |
|
@ -165,10 +170,10 @@ const Affix: React.FC<AffixProps> = (props) => { |
|
|
timerRef.current = null; |
|
|
timerRef.current = null; |
|
|
} |
|
|
} |
|
|
TRIGGER_EVENTS.forEach((eventName) => { |
|
|
TRIGGER_EVENTS.forEach((eventName) => { |
|
|
target?.removeEventListener(eventName, lazyUpdatePosition); |
|
|
target?.removeEventListener(eventName, updatePosition); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
lazyUpdatePosition.cancel(); |
|
|
updatePosition.cancel(); |
|
|
}; |
|
|
}; |
|
|
}, [offsetTop, offsetBottom, customizeTarget, getTargetContainer]); |
|
|
}, [offsetTop, offsetBottom, customizeTarget, getTargetContainer]); |
|
|
const [wrapSSR, hashId] = useStyle(affixPrefixCls); |
|
|
const [wrapSSR, hashId] = useStyle(affixPrefixCls); |
|
@ -188,16 +193,16 @@ const Affix: React.FC<AffixProps> = (props) => { |
|
|
]); |
|
|
]); |
|
|
|
|
|
|
|
|
return wrapSSR( |
|
|
return wrapSSR( |
|
|
<ResizeObserver onResize={lazyUpdatePosition}> |
|
|
<ResizeObserver onResize={updatePosition}> |
|
|
<div {...divProps} ref={placeholderNodeRef}> |
|
|
<div {...divProps} ref={placeholderNodeRef}> |
|
|
{affixStyle && <div style={placeholderStyle} aria-hidden="true" />} |
|
|
{affixStyle && <div style={placeholderStyle} aria-hidden="true" />} |
|
|
<div className={className} ref={fixedNodeRef} style={affixStyle}> |
|
|
<div className={className} ref={fixedNodeRef} style={affixStyle}> |
|
|
<ResizeObserver onResize={lazyUpdatePosition}>{children}</ResizeObserver> |
|
|
<ResizeObserver onResize={updatePosition}>{children}</ResizeObserver> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</ResizeObserver>, |
|
|
</ResizeObserver>, |
|
|
); |
|
|
); |
|
|
}; |
|
|
}); |
|
|
|
|
|
|
|
|
if (process.env.NODE_ENV !== 'production') { |
|
|
if (process.env.NODE_ENV !== 'production') { |
|
|
Affix.displayName = 'Affix'; |
|
|
Affix.displayName = 'Affix'; |
|
|