Skip to content

Commit

Permalink
feat(component): helper function observeResize to observe size change…
Browse files Browse the repository at this point in the history
… via global ResizeObserver (#7241)

```ts
import { observeResize } from "@affine/component";

useEffect(() => {
  const dispose = observeResize(element entry => {
    console.log(entry.contentRect);
  });

  return () => dispose();
}, []);
```
  • Loading branch information
CatsJuice committed Jun 19, 2024
1 parent be36e03 commit 98e3538
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 15 deletions.
1 change: 1 addition & 0 deletions packages/frontend/component/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ export * from './ui/switch';
export * from './ui/table';
export * from './ui/toast';
export * from './ui/tooltip';
export * from './utils';
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
useState,
} from 'react';

import { observeResize } from '../../utils';
import { IconButton } from '../button';
import * as styles from './week-date-picker.css';

Expand Down Expand Up @@ -116,8 +117,7 @@ export const WeekDatePicker = memo(function WeekDatePicker({
const el = weekRef.current;
if (!el) return;

const resizeObserver = new ResizeObserver(entries => {
const rect = entries[0].contentRect;
return observeResize(el, ({ contentRect: rect }) => {
const width = rect.width;
if (!width) return;

Expand All @@ -127,11 +127,6 @@ export const WeekDatePicker = memo(function WeekDatePicker({
setViewPortSize(Math.max(1, Math.min(viewPortCount, 7)));
setDense(width < 300);
});
resizeObserver.observe(el);

return () => {
resizeObserver.disconnect();
};
}, []);

// when value changes, reset cursor
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/component/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './observe-resize';
77 changes: 77 additions & 0 deletions packages/frontend/component/src/utils/observe-resize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { ResizeObserverEntry } from '@juggle/resize-observer';

type ObserveResize = {
callback: (entity: ResizeObserverEntry) => void;
dispose: () => void;
};

let _resizeObserver: ResizeObserver | null = null;
const elementsMap = new WeakMap<Element, Array<ObserveResize>>();

// for debugging
(window as any)._resizeObserverElementsMap = elementsMap;

/**
* @internal get or initialize the ResizeObserver instance
*/
const getResizeObserver = () =>
(_resizeObserver ??= new ResizeObserver(entries => {
entries.forEach(entry => {
const listeners = elementsMap.get(entry.target) ?? [];
listeners.forEach(({ callback }) => callback(entry));
});
}));

/**
* @internal remove element's specific listener
*/
const removeListener = (element: Element, listener: ObserveResize) => {
if (!element) return;
const listeners = elementsMap.get(element) ?? [];
const observer = getResizeObserver();
// remove the listener from the element
if (listeners.includes(listener)) {
elementsMap.set(
element,
listeners.filter(l => l !== listener)
);
}
// if no more listeners, unobserve the element
if (elementsMap.get(element)?.length === 0) {
observer.unobserve(element);
elementsMap.delete(element);
}
};

/**
* A function to observe the resize of an element use global ResizeObserver.
*
* ```ts
* useEffect(() => {
* const dispose1 = observeResize(elRef1.current, (entry) => {});
* const dispose2 = observeResize(elRef2.current, (entry) => {});
*
* return () => {
* dispose1();
* dispose2();
* };
* }, [])
* ```
* @return A function to dispose the observer.
*/
export const observeResize = (
element: Element,
callback: ObserveResize['callback']
) => {
const observer = getResizeObserver();
if (!elementsMap.has(element)) {
observer.observe(element);
}
const prevListeners = elementsMap.get(element) ?? [];
const listener = { callback, dispose: () => {} };
listener.dispose = () => removeListener(element, listener);

elementsMap.set(element, [...prevListeners, listener]);

return listener.dispose;
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, IconButton } from '@affine/component';
import { Button, IconButton, observeResize } from '@affine/component';
import {
ArrowDownSmallIcon,
ArrowUpSmallIcon,
Expand Down Expand Up @@ -63,13 +63,7 @@ const CanvasText = ({
return;
}
drawText(canvas, text);
const resizeObserver = new ResizeObserver(() => {
drawText(canvas, text);
});
resizeObserver.observe(canvas);
return () => {
resizeObserver.disconnect();
};
return observeResize(canvas, () => drawText(canvas, text));
}, [text]);

return <canvas className={className} ref={ref} />;
Expand Down

0 comments on commit 98e3538

Please sign in to comment.