Skip to content
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
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@types/node": "^24.5.2",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@umijs/fabric": "^2.0.9",
"@umijs/fabric": "^4.0.0",
"cheerio": "1.0.0-rc.12",
"coveralls": "^3.0.6",
"cross-env": "^7.0.2",
Expand All @@ -68,19 +68,20 @@
"rc-test": "^7.0.15",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"regenerator-runtime": "^0.14.0"
"regenerator-runtime": "^0.14.0",
"eslint": "8.x"
},
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
},
"resolutions": {
"@types/minimatch": "5.1.2"
},
"@types/minimatch": "5.1.2"
},
"cnpm": {
"mode": "npm"
},
"tnpm": {
"mode": "npm"
}
}
}
91 changes: 12 additions & 79 deletions src/SingleObserver/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { supportRef, useComposeRef, getNodeRef } from '@rc-component/util/lib/re
import * as React from 'react';
import type { ResizeObserverProps } from '..';
import { CollectionContext } from '../Collection';
import { observe, unobserve } from '../utils/observerUtil';
import useResizeObserver from '../useResizeObserver';

export interface SingleObserverProps extends ResizeObserverProps {
children: React.ReactElement | ((ref: React.RefObject<Element>) => React.ReactElement);
}

function SingleObserver(props: SingleObserverProps, ref: React.Ref<HTMLElement>) {
const { children, disabled } = props;
const { children, disabled, onResize, data } = props;
const elementRef = React.useRef<Element>(null);

const onCollectionResize = React.useContext(CollectionContext);
Expand All @@ -19,14 +19,6 @@ function SingleObserver(props: SingleObserverProps, ref: React.Ref<HTMLElement>)
const isRenderProps = typeof children === 'function';
const mergedChildren = isRenderProps ? children(elementRef) : children;

// ============================= Size =============================
const sizeRef = React.useRef({
width: -1,
height: -1,
offsetWidth: -1,
offsetHeight: -1,
});

// ============================= Ref ==============================
const canRef =
!isRenderProps && React.isValidElement(mergedChildren) && supportRef(mergedChildren);
Expand All @@ -35,81 +27,22 @@ function SingleObserver(props: SingleObserverProps, ref: React.Ref<HTMLElement>)
const mergedRef = useComposeRef(originRef, elementRef);

const getDomElement = () => {
return getDOM( elementRef.current ) as HTMLElement
}

return getDOM(elementRef.current) as HTMLElement;
};

React.useImperativeHandle(ref, () => getDomElement());

// =========================== Observe ============================
const propsRef = React.useRef<SingleObserverProps>(props);
propsRef.current = props;

// Handler
const onInternalResize = React.useCallback((target: HTMLElement) => {
const { onResize, data } = propsRef.current;

const { width, height } = target.getBoundingClientRect();
const { offsetWidth, offsetHeight } = target;

/**
* Resize observer trigger when content size changed.
* In most case we just care about element size,
* let's use `boundary` instead of `contentRect` here to avoid shaking.
*/
const fixedWidth = Math.floor(width);
const fixedHeight = Math.floor(height);

if (
sizeRef.current.width !== fixedWidth ||
sizeRef.current.height !== fixedHeight ||
sizeRef.current.offsetWidth !== offsetWidth ||
sizeRef.current.offsetHeight !== offsetHeight
) {
const size = { width: fixedWidth, height: fixedHeight, offsetWidth, offsetHeight };
sizeRef.current = size;

// IE is strange, right?
const mergedOffsetWidth = offsetWidth === Math.round(width) ? width : offsetWidth;
const mergedOffsetHeight = offsetHeight === Math.round(height) ? height : offsetHeight;

const sizeInfo = {
...size,
offsetWidth: mergedOffsetWidth,
offsetHeight: mergedOffsetHeight,
};

// Let collection know what happened
onCollectionResize?.(sizeInfo, target, data);

if (onResize) {
// defer the callback but not defer to next frame
Promise.resolve().then(() => {
onResize(sizeInfo, target);
});
}
}
}, []);

// Dynamic observe
React.useEffect(() => {
const currentElement: HTMLElement = getDomElement();

if (currentElement && !disabled) {
observe(currentElement, onInternalResize);
}

return () => unobserve(currentElement, onInternalResize);
}, [elementRef.current, disabled]);
useResizeObserver(!disabled, getDomElement, onResize, (sizeInfo, target) => {
onCollectionResize?.(sizeInfo, target, data);
});

// ============================ Render ============================
return (
canRef
? React.cloneElement(mergedChildren as any, {
ref: mergedRef,
})
: mergedChildren
);
return canRef
? React.cloneElement(mergedChildren as any, {
ref: mergedRef,
})
: mergedChildren;
}

const RefSingleObserver = React.forwardRef(SingleObserver);
Expand Down
2 changes: 2 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export {
_rs,
};

export { default as useResizeObserver } from './useResizeObserver';

export interface SizeInfo {
width: number;
height: number;
Expand Down
79 changes: 79 additions & 0 deletions src/useResizeObserver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import * as React from 'react';
import type { OnResize, SizeInfo } from '.';
import { observe, unobserve } from './utils/observerUtil';
import { useEvent } from '@rc-component/util';

export default function useResizeObserver(
enabled: boolean,
getTarget: () => HTMLElement,
onDelayResize?: OnResize,
onSyncResize?: OnResize,
) {
// ============================= Size =============================
const sizeRef = React.useRef<SizeInfo>({
width: -1,
height: -1,
offsetWidth: -1,
offsetHeight: -1,
});

// =========================== Observe ============================

// Handler
const onInternalResize = useEvent((target: HTMLElement) => {
const { width, height } = target.getBoundingClientRect();
const { offsetWidth, offsetHeight } = target;

/**
* Resize observer trigger when content size changed.
* In most case we just care about element size,
* let's use `boundary` instead of `contentRect` here to avoid shaking.
*/
const fixedWidth = Math.floor(width);
const fixedHeight = Math.floor(height);

if (
sizeRef.current.width !== fixedWidth ||
sizeRef.current.height !== fixedHeight ||
sizeRef.current.offsetWidth !== offsetWidth ||
sizeRef.current.offsetHeight !== offsetHeight
) {
const size = { width: fixedWidth, height: fixedHeight, offsetWidth, offsetHeight };
sizeRef.current = size;

// IE is strange, right?
const mergedOffsetWidth = offsetWidth === Math.round(width) ? width : offsetWidth;
const mergedOffsetHeight = offsetHeight === Math.round(height) ? height : offsetHeight;

const sizeInfo = {
...size,
offsetWidth: mergedOffsetWidth,
offsetHeight: mergedOffsetHeight,
};

// Call the callback immediately, let the caller decide whether to defer
// onResize(sizeInfo, target);
onSyncResize?.(sizeInfo, target);

// defer the callback but not defer to next frame
Promise.resolve().then(() => {
onDelayResize?.(sizeInfo, target);
});
}
});

// Dynamic observe
React.useEffect(() => {
const target = getTarget();

if (target && enabled) {
observe(target, onInternalResize);
}

return () => {
if (target) {
unobserve(target, onInternalResize);
}
};
}, [enabled]);
}