Skip to content

Commit b70e368

Browse files
committed
feat: dynamic set grid dimensions
1 parent 0f23942 commit b70e368

File tree

5 files changed

+367
-258
lines changed

5 files changed

+367
-258
lines changed

electron/renderer/components/grid/grid.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,12 @@ export const Grid: React.FC<GridProps> = (props: GridProps): ReactNode => {
239239
width={gridMaxWidth}
240240
rowHeight={gridRowHeight}
241241
maxRows={gridMaxRows}
242-
// Disable the grid from managing its own height.
243-
// We manage it explicitly in the `gridLayoutStyles` above.
244-
autoSize={false}
242+
// Enable the grid to grow and shrink to stay within
243+
// the max columns and rows. Note, the items within the
244+
// grid might not shrink to fit the grid, but the canvas will.
245+
// The grid then becomes scrollable and ensures elements outside the
246+
// grid position on page correctly rather than floating over the grid.
247+
autoSize={true}
245248
// Provide nominal spacing between grid items.
246249
// If this value changes then review the grid row height variables.
247250
margin={[1, 1]}

electron/renderer/hooks/measure.tsx

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Inspired by `useMeasure` by @streamich/react-use.
2+
// https://github.com/streamich/react-use/blob/master/src/useMeasure.ts
3+
// https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver
4+
5+
import { debounce } from 'lodash';
6+
import { useEffect, useMemo, useState } from 'react';
7+
8+
export interface UseMeasureProps {
9+
/**
10+
* The number of milliseconds to wait before updating the measured size.
11+
* When the user is actively resizing the window, the 'resize' event
12+
* fires rapidly. For performance, debounce so we react more efficiently.
13+
*
14+
* Default is 100.
15+
*/
16+
delay?: number;
17+
}
18+
19+
export type UseMeasureRef<E extends Element = Element> = (element: E) => void;
20+
21+
export type UseMeasureResult<E extends Element = Element> = [
22+
UseMeasureRef<E>,
23+
ElementSize,
24+
];
25+
26+
export interface ElementSize {
27+
height: number;
28+
width: number;
29+
}
30+
31+
const defaultSize: ElementSize = {
32+
height: 0,
33+
width: 0,
34+
};
35+
36+
/**
37+
* Usage:
38+
* ```
39+
* const [ref, { height, width }] = useMeasure();
40+
* ...
41+
* <div ref={ref}></div>
42+
* ```
43+
*/
44+
export function useMeasure<E extends Element = Element>(
45+
props?: UseMeasureProps
46+
): UseMeasureResult<E> {
47+
const { delay = 100 } = props ?? {};
48+
49+
const [element, ref] = useState<E | null>(null);
50+
const [size, setSize] = useState<ElementSize>(defaultSize);
51+
52+
const observer = useMemo(() => {
53+
const onResize = debounce<ResizeObserverCallback>((entries) => {
54+
// We are observing at most one element,
55+
// but the API supports observing multiple elements.
56+
// Grab the first one.
57+
if (entries[0]) {
58+
const height = entries[0].contentRect.height;
59+
const width = entries[0].contentRect.width;
60+
setSize({ height, width });
61+
}
62+
}, delay);
63+
64+
return new ResizeObserver(onResize);
65+
}, [delay]);
66+
67+
useEffect(() => {
68+
if (!element) {
69+
return;
70+
}
71+
observer.observe(element);
72+
return () => {
73+
observer.disconnect();
74+
};
75+
}, [observer, element]);
76+
77+
return [ref, size];
78+
}

electron/renderer/hooks/window-dimensions.tsx

-46
This file was deleted.
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Inspired by stackoverflow solution.
2+
// https://stackoverflow.com/questions/36862334/get-viewport-window-height-in-reactjs
3+
4+
import { debounce } from 'lodash';
5+
import { useEffect, useState } from 'react';
6+
7+
export interface UseWindowSizeProps {
8+
/**
9+
* The number of milliseconds to wait before updating the window size.
10+
* When the user is actively resizing the window, the 'resize' event
11+
* fires rapidly. For performance, debounce so we react more efficiently.
12+
*
13+
* Default is 100.
14+
*/
15+
delay?: number;
16+
}
17+
18+
export interface WindowSize {
19+
height: number;
20+
width: number;
21+
}
22+
23+
const defaultSize: WindowSize = {
24+
height: 0,
25+
width: 0,
26+
};
27+
28+
/**
29+
* Usage:
30+
* ```
31+
* const { height, width } = useWindowSize();
32+
* ```
33+
*/
34+
export function useWindowSize(props?: UseWindowSizeProps): WindowSize {
35+
const { delay = 100 } = props ?? {};
36+
37+
const [size, setSize] = useState<WindowSize>(defaultSize);
38+
39+
useEffect(() => {
40+
const onWindowResize = debounce(() => {
41+
setSize({
42+
height: window.innerHeight,
43+
width: window.innerWidth,
44+
});
45+
}, delay);
46+
47+
onWindowResize(); // capture initial size
48+
49+
window.addEventListener('resize', onWindowResize);
50+
51+
return () => {
52+
window.removeEventListener('resize', onWindowResize);
53+
};
54+
}, [delay]);
55+
56+
return size;
57+
}

0 commit comments

Comments
 (0)