mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-28 21:19:37 +08:00
Merge pull request #15150 from ant-design/affox
Fix Affix updatePosition logic.
This commit is contained in:
commit
93820ba90e
@ -17,31 +17,21 @@ class AffixMounter extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: 100,
|
||||
overflowY: 'scroll',
|
||||
}}
|
||||
ref={node => {
|
||||
this.container = node;
|
||||
}}
|
||||
className="container"
|
||||
>
|
||||
<div
|
||||
className="background"
|
||||
style={{
|
||||
paddingTop: 60,
|
||||
height: 300,
|
||||
<Affix
|
||||
className="fixed"
|
||||
target={this.getTarget}
|
||||
ref={ele => {
|
||||
this.affix = ele;
|
||||
}}
|
||||
{...this.props}
|
||||
>
|
||||
<Affix
|
||||
target={() => this.container}
|
||||
ref={ele => {
|
||||
this.affix = ele;
|
||||
}}
|
||||
{...this.props}
|
||||
>
|
||||
<Button type="primary">Fixed at the top of container</Button>
|
||||
</Affix>
|
||||
</div>
|
||||
<Button type="primary">Fixed at the top of container</Button>
|
||||
</Affix>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -50,24 +40,36 @@ class AffixMounter extends React.Component {
|
||||
describe('Affix Render', () => {
|
||||
let wrapper;
|
||||
|
||||
const classRect = {
|
||||
container: {
|
||||
top: 0,
|
||||
bottom: 100,
|
||||
},
|
||||
};
|
||||
|
||||
const originGetBoundingClientRect = HTMLElement.prototype.getBoundingClientRect;
|
||||
HTMLElement.prototype.getBoundingClientRect = function() {
|
||||
return (
|
||||
classRect[this.className] || {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
HTMLElement.prototype.getBoundingClientRect = originGetBoundingClientRect;
|
||||
});
|
||||
|
||||
const scrollTo = top => {
|
||||
wrapper.instance().affix.fixedNode.parentNode.getBoundingClientRect = jest.fn(() => ({
|
||||
bottom: 100,
|
||||
height: 28,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 50 - top,
|
||||
width: 195,
|
||||
}));
|
||||
wrapper.instance().container.scrollTop = top;
|
||||
const movePlaceholder = top => {
|
||||
classRect.fixed = {
|
||||
top,
|
||||
bottom: top,
|
||||
};
|
||||
events.scroll({
|
||||
type: 'scroll',
|
||||
});
|
||||
@ -80,14 +82,14 @@ describe('Affix Render', () => {
|
||||
wrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
|
||||
jest.runAllTimers();
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBe(null);
|
||||
movePlaceholder(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
|
||||
scrollTo(100);
|
||||
expect(wrapper.instance().affix.state.affixStyle).not.toBe(null);
|
||||
movePlaceholder(-100);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBe(null);
|
||||
movePlaceholder(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
});
|
||||
|
||||
it('support offsetBottom', () => {
|
||||
@ -96,16 +98,17 @@ describe('Affix Render', () => {
|
||||
wrapper = mount(<AffixMounter offsetBottom={0} />, {
|
||||
attachTo: document.getElementById('mounter'),
|
||||
});
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).not.toBe(null);
|
||||
movePlaceholder(300);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
|
||||
scrollTo(100);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBe(null);
|
||||
movePlaceholder(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeFalsy();
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.instance().affix.state.affixStyle).not.toBe(null);
|
||||
movePlaceholder(300);
|
||||
expect(wrapper.instance().affix.state.affixStyle).toBeTruthy();
|
||||
});
|
||||
|
||||
it('updatePosition when offsetTop changed', () => {
|
||||
@ -116,7 +119,7 @@ describe('Affix Render', () => {
|
||||
});
|
||||
jest.runAllTimers();
|
||||
|
||||
scrollTo(100);
|
||||
movePlaceholder(-100);
|
||||
expect(wrapper.instance().affix.state.affixStyle.top).toBe(0);
|
||||
wrapper.setProps({
|
||||
offsetTop: 10,
|
||||
|
@ -1,40 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import { polyfill } from 'react-lifecycles-compat';
|
||||
import classNames from 'classnames';
|
||||
import shallowequal from 'shallowequal';
|
||||
import omit from 'omit.js';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import { throttleByAnimationFrameDecorator } from '../_util/throttleByAnimationFrame';
|
||||
|
||||
function getTargetRect(target: HTMLElement | Window | null): ClientRect {
|
||||
return target !== window
|
||||
? (target as HTMLElement).getBoundingClientRect()
|
||||
: ({ top: 0, left: 0, bottom: 0 } as ClientRect);
|
||||
}
|
||||
|
||||
function getOffset(element: HTMLElement, target: HTMLElement | Window | null) {
|
||||
const elemRect = element.getBoundingClientRect();
|
||||
const targetRect = getTargetRect(target);
|
||||
|
||||
const scrollTop = getScroll(target, true);
|
||||
const scrollLeft = getScroll(target, false);
|
||||
|
||||
const docElem = window.document.body;
|
||||
const clientTop = docElem.clientTop || 0;
|
||||
const clientLeft = docElem.clientLeft || 0;
|
||||
|
||||
return {
|
||||
top: elemRect.top - targetRect.top + scrollTop - clientTop,
|
||||
left: elemRect.left - targetRect.left + scrollLeft - clientLeft,
|
||||
width: elemRect.width,
|
||||
height: elemRect.height,
|
||||
};
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
import warning from '../_util/warning';
|
||||
import { addObserveTarget, removeObserveTarget, getTargetRect } from './utils';
|
||||
|
||||
function getDefaultTarget() {
|
||||
return typeof window !== 'undefined' ? window : null;
|
||||
@ -58,231 +30,168 @@ export interface AffixProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export interface AffixState {
|
||||
affixStyle: React.CSSProperties | undefined;
|
||||
placeholderStyle: React.CSSProperties | undefined;
|
||||
enum AffixStatus {
|
||||
None,
|
||||
Prepare,
|
||||
}
|
||||
|
||||
export default class Affix extends React.Component<AffixProps, AffixState> {
|
||||
static propTypes = {
|
||||
offsetTop: PropTypes.number,
|
||||
offsetBottom: PropTypes.number,
|
||||
target: PropTypes.func,
|
||||
export interface AffixState {
|
||||
affixStyle?: React.CSSProperties;
|
||||
placeholderStyle?: React.CSSProperties;
|
||||
status: AffixStatus;
|
||||
lastAffix: boolean;
|
||||
}
|
||||
|
||||
class Affix extends React.Component<AffixProps, AffixState> {
|
||||
static defaultProps = {
|
||||
target: getDefaultTarget,
|
||||
};
|
||||
|
||||
state: AffixState = {
|
||||
affixStyle: undefined,
|
||||
placeholderStyle: undefined,
|
||||
status: AffixStatus.None,
|
||||
lastAffix: false,
|
||||
};
|
||||
|
||||
placeholderNode: HTMLDivElement;
|
||||
fixedNode: HTMLDivElement;
|
||||
private timeout: number;
|
||||
private eventHandlers: Record<string, any> = {};
|
||||
private fixedNode: HTMLElement;
|
||||
private placeholderNode: HTMLElement;
|
||||
private readonly events = [
|
||||
'resize',
|
||||
'scroll',
|
||||
'touchstart',
|
||||
'touchmove',
|
||||
'touchend',
|
||||
'pageshow',
|
||||
'load',
|
||||
];
|
||||
|
||||
setAffixStyle(e: Event, affixStyle: React.CSSProperties | null) {
|
||||
const { onChange = noop, target = getDefaultTarget } = this.props;
|
||||
const originalAffixStyle = this.state.affixStyle;
|
||||
const isWindow = target() === window;
|
||||
if (e.type === 'scroll' && originalAffixStyle && affixStyle && isWindow) {
|
||||
return;
|
||||
}
|
||||
if (shallowequal(affixStyle, originalAffixStyle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ affixStyle: affixStyle as React.CSSProperties }, () => {
|
||||
const affixed = !!this.state.affixStyle;
|
||||
if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) {
|
||||
onChange(affixed);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setPlaceholderStyle(placeholderStyle: React.CSSProperties | null) {
|
||||
const originalPlaceholderStyle = this.state.placeholderStyle;
|
||||
if (shallowequal(placeholderStyle, originalPlaceholderStyle)) {
|
||||
return;
|
||||
}
|
||||
this.setState({ placeholderStyle: placeholderStyle as React.CSSProperties });
|
||||
}
|
||||
|
||||
syncPlaceholderStyle(e: Event) {
|
||||
const { affixStyle } = this.state;
|
||||
if (!affixStyle) {
|
||||
return;
|
||||
}
|
||||
this.placeholderNode.style.cssText = '';
|
||||
this.setAffixStyle(e, {
|
||||
...affixStyle,
|
||||
width: this.placeholderNode.offsetWidth,
|
||||
});
|
||||
this.setPlaceholderStyle({
|
||||
width: this.placeholderNode.offsetWidth,
|
||||
});
|
||||
}
|
||||
|
||||
@throttleByAnimationFrameDecorator()
|
||||
updatePosition(e: Event) {
|
||||
const { offsetBottom, offset, target = getDefaultTarget } = this.props;
|
||||
let { offsetTop } = this.props;
|
||||
const targetNode = target();
|
||||
|
||||
// Backwards support
|
||||
// Fix: if offsetTop === 0, it will get undefined,
|
||||
// if offsetBottom is type of number, offsetMode will be { top: false, ... }
|
||||
offsetTop = typeof offsetTop === 'undefined' ? offset : offsetTop;
|
||||
const scrollTop = getScroll(targetNode, true);
|
||||
const affixNode = ReactDOM.findDOMNode(this) as HTMLElement;
|
||||
const elemOffset = getOffset(affixNode, targetNode);
|
||||
const elemSize = {
|
||||
width: this.fixedNode.offsetWidth,
|
||||
height: this.fixedNode.offsetHeight,
|
||||
};
|
||||
|
||||
const offsetMode = {
|
||||
top: false,
|
||||
bottom: false,
|
||||
};
|
||||
// Default to `offsetTop=0`.
|
||||
if (typeof offsetTop !== 'number' && typeof offsetBottom !== 'number') {
|
||||
offsetMode.top = true;
|
||||
offsetTop = 0;
|
||||
} else {
|
||||
offsetMode.top = typeof offsetTop === 'number';
|
||||
offsetMode.bottom = typeof offsetBottom === 'number';
|
||||
}
|
||||
|
||||
const targetRect = getTargetRect(targetNode);
|
||||
const targetInnerHeight =
|
||||
(targetNode as Window).innerHeight || (targetNode as HTMLElement).clientHeight;
|
||||
// ref: https://github.com/ant-design/ant-design/issues/13662
|
||||
if (scrollTop >= elemOffset.top - (offsetTop as number) && offsetMode.top) {
|
||||
// Fixed Top
|
||||
const width = elemOffset.width;
|
||||
const top = targetRect.top + (offsetTop as number);
|
||||
this.setAffixStyle(e, {
|
||||
position: 'fixed',
|
||||
top,
|
||||
left: targetRect.left + elemOffset.left,
|
||||
width,
|
||||
});
|
||||
this.setPlaceholderStyle({
|
||||
width,
|
||||
height: elemSize.height,
|
||||
});
|
||||
} else if (
|
||||
scrollTop <=
|
||||
elemOffset.top + elemSize.height + (offsetBottom as number) - targetInnerHeight &&
|
||||
offsetMode.bottom
|
||||
) {
|
||||
// Fixed Bottom
|
||||
const targetBottomOffet = targetNode === window ? 0 : window.innerHeight - targetRect.bottom;
|
||||
const width = elemOffset.width;
|
||||
this.setAffixStyle(e, {
|
||||
position: 'fixed',
|
||||
bottom: targetBottomOffet + (offsetBottom as number),
|
||||
left: targetRect.left + elemOffset.left,
|
||||
width,
|
||||
});
|
||||
this.setPlaceholderStyle({
|
||||
width,
|
||||
height: elemOffset.height,
|
||||
});
|
||||
} else {
|
||||
const { affixStyle } = this.state;
|
||||
if (
|
||||
e.type === 'resize' &&
|
||||
affixStyle &&
|
||||
affixStyle.position === 'fixed' &&
|
||||
affixNode.offsetWidth
|
||||
) {
|
||||
this.setAffixStyle(e, { ...affixStyle, width: affixNode.offsetWidth });
|
||||
} else {
|
||||
this.setAffixStyle(e, null);
|
||||
}
|
||||
this.setPlaceholderStyle(null);
|
||||
}
|
||||
|
||||
if (e.type === 'resize') {
|
||||
this.syncPlaceholderStyle(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Event handler
|
||||
componentDidMount() {
|
||||
const target = this.props.target || getDefaultTarget;
|
||||
// Wait for parent component ref has its value
|
||||
this.timeout = setTimeout(() => {
|
||||
this.setTargetEventListeners(target);
|
||||
// Mock Event object.
|
||||
this.updatePosition({} as Event);
|
||||
});
|
||||
const { target } = this.props;
|
||||
if (target) {
|
||||
// [Legacy] Wait for parent component ref has its value.
|
||||
// We should use target as directly element instead of function which makes element check hard.
|
||||
this.timeout = setTimeout(() => {
|
||||
addObserveTarget(target(), this);
|
||||
// Mock Event object.
|
||||
this.updatePosition({} as Event);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: AffixProps) {
|
||||
if (this.props.target !== nextProps.target) {
|
||||
this.clearEventListeners();
|
||||
this.setTargetEventListeners(nextProps.target!);
|
||||
|
||||
// Mock Event object.
|
||||
this.updatePosition({} as Event);
|
||||
componentDidUpdate(prevProps: AffixProps) {
|
||||
const { target } = this.props;
|
||||
if (prevProps.target !== target) {
|
||||
removeObserveTarget(this);
|
||||
if (target) {
|
||||
addObserveTarget(target(), this);
|
||||
// Mock Event object.
|
||||
this.updatePosition({} as Event);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.props.offsetTop !== nextProps.offsetTop ||
|
||||
this.props.offsetBottom !== nextProps.offsetBottom
|
||||
prevProps.offsetTop !== this.props.offsetTop ||
|
||||
prevProps.offsetBottom !== this.props.offsetBottom
|
||||
) {
|
||||
this.updatePosition({} as Event);
|
||||
}
|
||||
|
||||
this.measure();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.clearEventListeners();
|
||||
clearTimeout(this.timeout);
|
||||
removeObserveTarget(this);
|
||||
(this.updatePosition as any).cancel();
|
||||
}
|
||||
|
||||
setTargetEventListeners(getTarget: () => HTMLElement | Window | null) {
|
||||
const target = getTarget();
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
this.clearEventListeners();
|
||||
|
||||
this.events.forEach(eventName => {
|
||||
this.eventHandlers[eventName] = addEventListener(target, eventName, this.updatePosition);
|
||||
});
|
||||
}
|
||||
|
||||
clearEventListeners() {
|
||||
this.events.forEach(eventName => {
|
||||
const handler = this.eventHandlers[eventName];
|
||||
if (handler && handler.remove) {
|
||||
handler.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
saveFixedNode = (node: HTMLDivElement) => {
|
||||
this.fixedNode = node;
|
||||
};
|
||||
|
||||
savePlaceholderNode = (node: HTMLDivElement) => {
|
||||
this.placeholderNode = node;
|
||||
};
|
||||
|
||||
saveFixedNode = (node: HTMLDivElement) => {
|
||||
this.fixedNode = node;
|
||||
};
|
||||
|
||||
// =================== Measure ===================
|
||||
// Handle realign logic
|
||||
@throttleByAnimationFrameDecorator()
|
||||
// @ts-ignore TS6133
|
||||
updatePosition(e: Event) {
|
||||
// event param is used before. Keep compatible ts define here.
|
||||
this.setState({
|
||||
status: AffixStatus.Prepare,
|
||||
affixStyle: undefined,
|
||||
placeholderStyle: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
measure = () => {
|
||||
const { status, lastAffix } = this.state;
|
||||
const { target, offset, offsetBottom, onChange } = this.props;
|
||||
if (status !== AffixStatus.Prepare || !this.fixedNode || !this.placeholderNode || !target) {
|
||||
return;
|
||||
}
|
||||
|
||||
let { offsetTop } = this.props;
|
||||
if (typeof offsetTop === 'undefined') {
|
||||
offsetTop = offset;
|
||||
warning(
|
||||
typeof offset === 'undefined',
|
||||
'Affix',
|
||||
'`offset` is deprecated. Please use `offsetTop` instead.',
|
||||
);
|
||||
}
|
||||
|
||||
if (offsetBottom === undefined && offsetTop === undefined) {
|
||||
offsetTop = 0;
|
||||
}
|
||||
|
||||
const targetNode = target();
|
||||
if (!targetNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newState: Partial<AffixState> = {
|
||||
status: AffixStatus.None,
|
||||
};
|
||||
const targetRect = getTargetRect(targetNode);
|
||||
const placeholderReact = getTargetRect(this.placeholderNode);
|
||||
|
||||
if (offsetTop !== undefined && targetRect.top > placeholderReact.top - offsetTop) {
|
||||
newState.affixStyle = {
|
||||
position: 'fixed',
|
||||
top: offsetTop + targetRect.top,
|
||||
width: placeholderReact.width,
|
||||
height: placeholderReact.height,
|
||||
};
|
||||
newState.placeholderStyle = {
|
||||
width: placeholderReact.width,
|
||||
height: placeholderReact.height,
|
||||
};
|
||||
} else if (
|
||||
offsetBottom !== undefined &&
|
||||
targetRect.bottom < placeholderReact.bottom + offsetBottom
|
||||
) {
|
||||
const targetBottomOffset = targetNode === window ? 0 : window.innerHeight - targetRect.bottom;
|
||||
newState.affixStyle = {
|
||||
position: 'fixed',
|
||||
bottom: offsetBottom + targetBottomOffset,
|
||||
width: placeholderReact.width,
|
||||
height: placeholderReact.height,
|
||||
};
|
||||
newState.placeholderStyle = {
|
||||
width: placeholderReact.width,
|
||||
height: placeholderReact.height,
|
||||
};
|
||||
}
|
||||
|
||||
newState.lastAffix = !!newState.affixStyle;
|
||||
if (onChange && lastAffix !== newState.lastAffix) {
|
||||
onChange(newState.lastAffix);
|
||||
}
|
||||
|
||||
this.setState(newState as AffixState);
|
||||
};
|
||||
|
||||
// =================== Render ===================
|
||||
renderAffix = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const { prefixCls } = this.props;
|
||||
const { affixStyle, placeholderStyle, status } = this.state;
|
||||
const { prefixCls, style, children } = this.props;
|
||||
const className = classNames({
|
||||
[getPrefixCls('affix', prefixCls)]: this.state.affixStyle,
|
||||
[getPrefixCls('affix', prefixCls)]: affixStyle,
|
||||
});
|
||||
|
||||
const props = omit(this.props, [
|
||||
@ -292,11 +201,14 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
|
||||
'target',
|
||||
'onChange',
|
||||
]);
|
||||
const placeholderStyle = { ...this.state.placeholderStyle, ...this.props.style };
|
||||
const mergedPlaceholderStyle = {
|
||||
...(status === AffixStatus.None ? placeholderStyle : null),
|
||||
...style,
|
||||
};
|
||||
return (
|
||||
<div {...props} style={placeholderStyle} ref={this.savePlaceholderNode}>
|
||||
<div {...props} style={mergedPlaceholderStyle} ref={this.savePlaceholderNode}>
|
||||
<div className={className} ref={this.saveFixedNode} style={this.state.affixStyle}>
|
||||
{this.props.children}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -306,3 +218,7 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
|
||||
return <ConfigConsumer>{this.renderAffix}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
|
||||
polyfill(Affix);
|
||||
|
||||
export default Affix;
|
||||
|
75
components/affix/utils.ts
Normal file
75
components/affix/utils.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import Affix from './';
|
||||
|
||||
// ======================== Observer ========================
|
||||
const TRIGGER_EVENTS = [
|
||||
'resize',
|
||||
'scroll',
|
||||
'touchstart',
|
||||
'touchmove',
|
||||
'touchend',
|
||||
'pageshow',
|
||||
'load',
|
||||
];
|
||||
|
||||
interface ObserverEntity {
|
||||
target: HTMLElement | Window;
|
||||
affixList: Affix[];
|
||||
eventHandlers: { [eventName: string]: any };
|
||||
}
|
||||
|
||||
let observerEntities: ObserverEntity[] = [];
|
||||
|
||||
export function addObserveTarget(target: HTMLElement | Window | null, affix: Affix): void {
|
||||
if (!target) return;
|
||||
|
||||
let entity: ObserverEntity | undefined = observerEntities.find(item => item.target === target);
|
||||
|
||||
if (entity) {
|
||||
entity.affixList.push(affix);
|
||||
} else {
|
||||
entity = {
|
||||
target,
|
||||
affixList: [affix],
|
||||
eventHandlers: {},
|
||||
};
|
||||
observerEntities.push(entity);
|
||||
|
||||
// Add listener
|
||||
TRIGGER_EVENTS.forEach(eventName => {
|
||||
entity!.eventHandlers[eventName] = addEventListener(target, eventName, (event: Event) => {
|
||||
entity!.affixList.forEach(affix => {
|
||||
affix.updatePosition(event);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function removeObserveTarget(affix: Affix): void {
|
||||
const observerEntity = observerEntities.find(oriObserverEntity => {
|
||||
const hasAffix = oriObserverEntity.affixList.some(item => item === affix);
|
||||
if (hasAffix) {
|
||||
oriObserverEntity.affixList = oriObserverEntity.affixList.filter(item => item !== affix);
|
||||
}
|
||||
return hasAffix;
|
||||
});
|
||||
|
||||
if (observerEntity && observerEntity.affixList.length === 0) {
|
||||
observerEntities = observerEntities.filter(item => item !== observerEntity);
|
||||
|
||||
// Remove listener
|
||||
TRIGGER_EVENTS.forEach(eventName => {
|
||||
const handler = observerEntity.eventHandlers[eventName];
|
||||
if (handler && handler.remove) {
|
||||
handler.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function getTargetRect(target: HTMLElement | Window | null): ClientRect {
|
||||
return target !== window
|
||||
? (target as HTMLElement).getBoundingClientRect()
|
||||
: ({ top: 0, bottom: window.innerHeight } as ClientRect);
|
||||
}
|
Loading…
Reference in New Issue
Block a user