Skip to content
This repository was archived by the owner on Nov 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions examples/simple.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { Component } from 'react';
import Align from '../src';

const allPoints = ['tl', 'tc', 'tr', 'cl', 'cc', 'cr', 'bl', 'bc', 'br'];

class Test extends Component {
state = {
monitor: true,
Expand Down Expand Up @@ -62,6 +64,17 @@ class Test extends Component {
}));
};

randomAlign = () => {
const randomPoints = [];
randomPoints.push(allPoints[Math.floor(Math.random() * 100) % allPoints.length]);
randomPoints.push(allPoints[Math.floor(Math.random() * 100) % allPoints.length]);
this.setState({
align: {
points: randomPoints,
},
});
};

forceAlign = () => {
this.$align.forceAlign();
};
Expand Down Expand Up @@ -91,6 +104,10 @@ class Test extends Component {
Resize Source
</button>
&nbsp;&nbsp;&nbsp;
<button type="button" onClick={this.randomAlign}>
Random Align
</button>
&nbsp;&nbsp;&nbsp;
<label>
<input type="checkbox" checked={this.state.monitor} onChange={this.toggleMonitor} />
&nbsp; Monitor window resize
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"start": "cross-env NODE_ENV=development father doc dev --storybook",
"build": "father doc build --storybook",
"compile": "father build",
"tsc": "tsc",
"prepublishOnly": "npm run compile && np --yolo --no-publish",
"lint": "eslint src/ examples/ --ext .tsx,.ts,.jsx,.js",
"test": "father test",
Expand All @@ -42,6 +43,7 @@
"@babel/runtime": "^7.10.1",
"classnames": "2.x",
"dom-align": "^1.7.0",
"lodash": "^4.17.21",
"rc-util": "^5.3.0",
"resize-observer-polyfill": "^1.5.1"
},
Expand Down
23 changes: 17 additions & 6 deletions src/Align.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import { composeRef } from 'rc-util/lib/ref';
import isVisible from 'rc-util/lib/Dom/isVisible';
import { alignElement, alignPoint } from 'dom-align';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import isEqual from 'lodash/isEqual';

import { isSamePoint, restoreFocus, monitorResize } from './util';
import { AlignType, AlignResult, TargetType, TargetPoint } from './interface';
import type { AlignType, AlignResult, TargetType, TargetPoint } from './interface';
import useBuffer from './hooks/useBuffer';

type OnAlign = (source: HTMLElement, result: AlignResult) => void;
Expand Down Expand Up @@ -48,7 +49,9 @@ const Align: React.RefForwardingComponent<RefAlign, AlignProps> = (
{ children, disabled, target, align, onAlign, monitorWindowResize, monitorBufferTime = 0 },
ref,
) => {
const cacheRef = React.useRef<{ element?: HTMLElement; point?: TargetPoint }>({});
const cacheRef = React.useRef<{ element?: HTMLElement; point?: TargetPoint; align?: AlignType }>(
{},
);
const nodeRef = React.useRef();
let childNode = React.Children.only(children);

Expand All @@ -57,16 +60,19 @@ const Align: React.RefForwardingComponent<RefAlign, AlignProps> = (
const forceAlignPropsRef = React.useRef<{
disabled?: boolean;
target?: TargetType;
align?: AlignType;
onAlign?: OnAlign;
}>({});
forceAlignPropsRef.current.disabled = disabled;
forceAlignPropsRef.current.target = target;
forceAlignPropsRef.current.align = align;
forceAlignPropsRef.current.onAlign = onAlign;

const [forceAlign, cancelForceAlign] = useBuffer(() => {
const {
disabled: latestDisabled,
target: latestTarget,
align: latestAlign,
onAlign: latestOnAlign,
} = forceAlignPropsRef.current;
if (!latestDisabled && latestTarget) {
Expand All @@ -78,6 +84,7 @@ const Align: React.RefForwardingComponent<RefAlign, AlignProps> = (

cacheRef.current.element = element;
cacheRef.current.point = point;
cacheRef.current.align = latestAlign;

// IE lose focus after element realign
// We should record activeElement and restore later
Expand Down Expand Up @@ -121,7 +128,11 @@ const Align: React.RefForwardingComponent<RefAlign, AlignProps> = (
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) ||
!isEqual(cacheRef.current.align, align)
) {
forceAlign();

// Add resize observer
Expand Down Expand Up @@ -181,7 +192,7 @@ const Align: React.RefForwardingComponent<RefAlign, AlignProps> = (
return childNode;
};

const RefAlign = React.forwardRef(Align);
RefAlign.displayName = 'Align';
const RcAlign = React.forwardRef(Align);
RcAlign.displayName = 'Align';

export default RefAlign;
export default RcAlign;
2 changes: 1 addition & 1 deletion src/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ResizeObserver from 'resize-observer-polyfill';
import contains from 'rc-util/lib/Dom/contains';
import { TargetPoint } from './interface';
import type { TargetPoint } from './interface';

export function isSamePoint(prev: TargetPoint, next: TargetPoint) {
if (prev === next) return true;
Expand Down
18 changes: 18 additions & 0 deletions tests/element.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ describe('element align', () => {
expect(onAlign).toHaveBeenCalled();
});

// https://github.com/ant-design/ant-design/issues/31717
it('changing align should trigger onAlign', () => {
const onAlign = jest.fn();
const wrapper = mount(<Test align={{ points: ['cc', 'cc'] }} onAlign={onAlign} />);
expect(onAlign).toHaveBeenCalledTimes(1);
expect(onAlign).toHaveBeenLastCalledWith(
expect.any(HTMLElement),
expect.objectContaining({ points: ['cc', 'cc'] }),
);
wrapper.setProps({ align: { points: ['cc', 'tl'] } });
jest.runAllTimers();
expect(onAlign).toHaveBeenCalledTimes(2);
expect(onAlign).toHaveBeenLastCalledWith(
expect.any(HTMLElement),
expect.objectContaining({ points: ['cc', 'tl'] }),
);
});

it('should switch to the correct align callback after starting the timers', () => {
// This test case is tricky. An error occurs if the following things happen
// exactly in this order:
Expand Down
17 changes: 17 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "esnext",
"moduleResolution": "node",
"baseUrl": "./",
"jsx": "preserve",
"declaration": true,
"skipLibCheck": true,
"esModuleInterop": true,
"noEmit": true,
"paths": {
"@/*": ["src/*"],
"@@/*": ["src/.umi/*"],
"rc-dialog": ["src/index.ts"]
}
}
}