Skip to content

Commit

Permalink
feat: Add keepAlign support (#36)
Browse files Browse the repository at this point in the history
* feat: Add keepAlign support

* add test case
  • Loading branch information
zombieJ authored Jan 6, 2020
1 parent 21a8a99 commit 4f6ac65
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 7 deletions.
79 changes: 79 additions & 0 deletions examples/position.js
Original file line number Diff line number Diff line change
@@ -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 (
<div style={{ marginBottom: 170 }}>
<div
style={{
margin: 20,
border: '1px solid red',
padding: '100px 0',
textAlign: 'center',
position: 'relative',
}}
onClick={this.onClick}
>
<div
ref={this.eleRef}
style={{
width: 10,
height: 10,
background: 'red',
position: 'absolute',
left,
top,
}}
/>
</div>

<Align
ref={this.alignRef}
target={() => this.eleRef.current}
align={align}
keepingAlign
>
<div
style={{
position: 'absolute',
width: 100,
height: 100,
background: 'rgba(0, 255, 0, 0.5)',
pointerEvents: 'none',
}}
>
Align
</div>
</Align>

<button
type="button"
onClick={() => {
this.setState({
left: Math.random() * 500,
top: Math.random() * 200,
});
}}
>
Random Position
</button>
</div>
);
}
}

export default Demo;
53 changes: 48 additions & 5 deletions src/Align.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export interface AlignProps {
monitorWindowResize?: boolean;
disabled?: boolean;
children: React.ReactElement;
/** Always trigger align with each render */
keepAlign?: boolean;
}

interface MonitorRef {
Expand All @@ -43,11 +45,27 @@ function getPoint(point: TargetType) {
return point;
}

interface InternalTestProps {
INTERNAL_TRIGGER_ALIGN?: Function;
}

const Align: React.RefForwardingComponent<RefAlign, AlignProps> = (
{ 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);

Expand All @@ -63,7 +81,17 @@ const Align: React.RefForwardingComponent<RefAlign, AlignProps> = (
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;

Expand Down Expand Up @@ -112,10 +140,16 @@ const Align: React.RefForwardingComponent<RefAlign, AlignProps> = (
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
Expand All @@ -136,6 +170,15 @@ const Align: React.RefForwardingComponent<RefAlign, AlignProps> = (
}
}, [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(() => {
Expand Down
28 changes: 26 additions & 2 deletions tests/element.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ describe('element align', () => {
render() {
return (
<div style={{ paddingTop: 100 }}>
<div ref={this.targetRef} style={{ display: 'inline-block', width: 50, height: 50 }}>
<div
ref={this.targetRef}
style={{ display: 'inline-block', width: 50, height: 50 }}
>
target
</div>
<Align target={this.getTarget} align={align} {...this.props}>
Expand Down Expand Up @@ -73,12 +76,33 @@ describe('element align', () => {
it('disabled should trigger align', () => {
const onAlign = jest.fn();

const wrapper = mount(<Test monitorWindowResize onAlign={onAlign} disabled />);
const wrapper = mount(
<Test monitorWindowResize onAlign={onAlign} disabled />,
);
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 = () => <Test INTERNAL_TRIGGER_ALIGN={triggerAlign} keepAlign />;
}

const wrapper = mount(<TestAlign />);
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 */

1 comment on commit 4f6ac65

@vercel
Copy link

@vercel vercel bot commented on 4f6ac65 Jan 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.