diff --git a/examples/position.js b/examples/position.js new file mode 100644 index 0000000..fb170a3 --- /dev/null +++ b/examples/position.js @@ -0,0 +1,79 @@ +import React, { Component } from 'react'; +import Align from '../src'; + +const align = { + points: ['cc', 'cc'], +}; + +class Demo extends Component { + state = { + left: 0, + top: 0, + }; + + eleRef = React.createRef(); + + render() { + const { left, top } = this.state; + + return ( +
+
+
+
+ + this.eleRef.current} + align={align} + keepingAlign + > +
+ Align +
+
+ + +
+ ); + } +} + +export default Demo; diff --git a/src/Align.tsx b/src/Align.tsx index ce6b1ac..7433ffc 100644 --- a/src/Align.tsx +++ b/src/Align.tsx @@ -22,6 +22,8 @@ export interface AlignProps { monitorWindowResize?: boolean; disabled?: boolean; children: React.ReactElement; + /** Always trigger align with each render */ + keepAlign?: boolean; } interface MonitorRef { @@ -43,11 +45,27 @@ function getPoint(point: TargetType) { return point; } +interface InternalTestProps { + INTERNAL_TRIGGER_ALIGN?: Function; +} + const Align: React.RefForwardingComponent = ( - { children, disabled, target, align, onAlign, monitorWindowResize, monitorBufferTime = 0 }, + { + children, + disabled, + target, + align, + onAlign, + monitorWindowResize, + monitorBufferTime = 0, + keepAlign, + ...restProps + }, ref, ) => { - const cacheRef = React.useRef<{ element?: HTMLElement; point?: TargetPoint }>({}); + const cacheRef = React.useRef<{ element?: HTMLElement; point?: TargetPoint }>( + {}, + ); const nodeRef = React.useRef(); let childNode = React.Children.only(children); @@ -63,7 +81,17 @@ const Align: React.RefForwardingComponent = ( forceAlignPropsRef.current.onAlign = onAlign; const [forceAlign, cancelForceAlign] = useBuffer(() => { - const { disabled: latestDisabled, target: latestTarget } = forceAlignPropsRef.current; + if ( + process.env.NODE_ENV !== 'production' && + (restProps as InternalTestProps).INTERNAL_TRIGGER_ALIGN + ) { + (restProps as InternalTestProps).INTERNAL_TRIGGER_ALIGN(); + } + + const { + disabled: latestDisabled, + target: latestTarget, + } = forceAlignPropsRef.current; if (!latestDisabled && latestTarget) { const source = nodeRef.current; @@ -112,10 +140,16 @@ const Align: React.RefForwardingComponent = ( if (nodeRef.current !== sourceResizeMonitor.current.element) { sourceResizeMonitor.current.cancel(); sourceResizeMonitor.current.element = nodeRef.current; - sourceResizeMonitor.current.cancel = monitorResize(nodeRef.current, forceAlign); + sourceResizeMonitor.current.cancel = monitorResize( + nodeRef.current, + forceAlign, + ); } - if (cacheRef.current.element !== element || !isSamePoint(cacheRef.current.point, point)) { + if ( + cacheRef.current.element !== element || + !isSamePoint(cacheRef.current.point, point) + ) { forceAlign(); // Add resize observer @@ -136,6 +170,15 @@ const Align: React.RefForwardingComponent = ( } }, [disabled]); + /** + * [Legacy] Should keep re-algin since we don't know if target position changed. + */ + React.useEffect(() => { + if (keepAlign && !disabled) { + forceAlign(true); + } + }); + // Listen for window resize const winResizeRef = React.useRef<{ remove: Function }>(null); React.useEffect(() => { diff --git a/tests/element.test.js b/tests/element.test.js index 42d2344..b747632 100644 --- a/tests/element.test.js +++ b/tests/element.test.js @@ -26,7 +26,10 @@ describe('element align', () => { render() { return (
-
+
target
@@ -73,12 +76,33 @@ describe('element align', () => { it('disabled should trigger align', () => { const onAlign = jest.fn(); - const wrapper = mount(); + const wrapper = mount( + , + ); expect(onAlign).not.toHaveBeenCalled(); wrapper.setProps({ disabled: false }); jest.runAllTimers(); expect(onAlign).toHaveBeenCalled(); }); + + it('keepAlign', () => { + const triggerAlign = jest.fn(); + + class TestAlign extends React.Component { + state = {}; + + render = () => ; + } + + const wrapper = mount(); + const times = triggerAlign.mock.calls.length; + + for (let i = 0; i < 10; i += 1) { + wrapper.instance().forceUpdate(); + } + + expect(triggerAlign.mock.calls.length > times).toBeTruthy(); + }); }); /* eslint-enable */