Skip to content

Commit

Permalink
2444 slow middle box resizing (#2471)
Browse files Browse the repository at this point in the history
* hook for callbackDebounce and resize observer

* use hook everywhere, remove external resize observer lib

* back to previous statement list debounce handling but with custom observer hook

* debounced resize for statement list except annotator, remove disableUserSelect from redux and css by js instead
  • Loading branch information
Ptrhnk authored Dec 11, 2024
1 parent e0dd941 commit 8fc7db4
Show file tree
Hide file tree
Showing 15 changed files with 130 additions and 75 deletions.
1 change: 0 additions & 1 deletion packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@
"ts-loader": "^9.5.1",
"typescript": "^5.5.4",
"typescript-plugin-styled-components": "^3.0.0",
"use-resize-observer": "^9.1.0",
"webpack": "^5.94.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4",
Expand Down
23 changes: 2 additions & 21 deletions packages/client/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions packages/client/src/Theme/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { ThemeType } from "./theme";

interface GlobalStyle {
theme: ThemeType;
disableUserSelect?: boolean;
}
const GlobalStyle = createGlobalStyle<GlobalStyle>`
html {
Expand All @@ -32,9 +31,8 @@ const GlobalStyle = createGlobalStyle<GlobalStyle>`
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
*:not(input,textarea) {
user-select: ${({ disableUserSelect }) =>
disableUserSelect ? "none" : "auto"};
.no-select {
user-select: none;
}
h1 {
font-size: ${({ theme }) => theme.fontSize["4xl"]};
Expand Down
8 changes: 1 addition & 7 deletions packages/client/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,6 @@ const queryClient = new QueryClient({
});
export const App: React.FC = () => {
const dispatch = useAppDispatch();
const disableUserSelect = useAppSelector(
(state) => state.layout.disableUserSelect
);
const selectedThemeId: InterfaceEnums.Theme = useAppSelector(
(state) => state.theme
);
Expand Down Expand Up @@ -179,10 +176,7 @@ export const App: React.FC = () => {
<link rel="stylesheet" type="text/css" href="/custom.css" />
</Helmet>
<ThemeProvider theme={themeConfig}>
<GlobalStyle
theme={themeConfig}
disableUserSelect={disableUserSelect}
/>
<GlobalStyle theme={themeConfig} />
<QueryClientProvider client={queryClient}>
<div style={{ fontSize: "16px" }}>
{/* fontSize zooms query devtools to normal size */}
Expand Down
5 changes: 2 additions & 3 deletions packages/client/src/components/advanced/Page/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import useKeypress from "hooks/useKeyPress";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useLocation, useNavigate } from "react-router";
import { Id, toast } from "react-toastify";
import { setDisableUserSelect } from "redux/features/layout/disableUserSelectSlice";
import { setPing } from "redux/features/pingSlice";
import { setLastClickedIndex } from "redux/features/statementList/lastClickedIndexSlice";
import { setUsername } from "redux/features/usernameSlice";
Expand Down Expand Up @@ -106,8 +105,8 @@ export const Page: React.FC<Page> = ({ children }) => {

const [tempLocation, setTempLocation] = useState<string | false>(false);

useKeypress("Shift", () => dispatch(setDisableUserSelect(true)));
useKeyLift("Shift", () => dispatch(setDisableUserSelect(false)));
useKeypress("Shift", () => document.body.classList.add("no-select"));
useKeyLift("Shift", () => document.body.classList.remove("no-select"));

useQuery({
queryKey: ["ping"],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect, useState } from "react";
import { useSpring } from "@react-spring/web";
import { setDisableUserSelect } from "redux/features/layout/disableUserSelectSlice";
import React, { useEffect, useState } from "react";
import { setPanelWidths } from "redux/features/layout/panelWidthsSlice";
import { setSeparatorXPosition } from "redux/features/layout/separatorXPositionSlice";
import { useAppDispatch, useAppSelector } from "redux/hooks";
Expand Down Expand Up @@ -71,7 +70,7 @@ export const PanelSeparator: React.FC<PanelSeparator> = ({}) => {
const onMouseDown = (e: React.MouseEvent) => {
setSeparatorXTempPosition(e.clientX);
setDragging(true);
dispatch(setDisableUserSelect(true));
document.body.classList.add("no-select");
};

const onMove = (clientX: number) => {
Expand All @@ -98,7 +97,7 @@ export const PanelSeparator: React.FC<PanelSeparator> = ({}) => {

const onMouseUp = () => {
setDragging(false);
dispatch(setDisableUserSelect(false));
document.body.classList.remove("no-select");
};

useEffect(() => {
Expand Down
16 changes: 15 additions & 1 deletion packages/client/src/hooks/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
import { useContainerDimensions } from "./useContainerDimensions";
import useDebounce from "./useDebounce";
import useDebouncedCallback from "./useDebouncedCallback";
import useKeyLift from "./useKeyLift";
import useKeyPress from "./useKeyPress";
import { useResizeObserver } from "./useResizeObserver";
import { useSearchParams } from "./useSearchParamsContext";
import { useWindowSize } from "./useWindowSize";

export { useDebounce, useKeyPress, useSearchParams };
export {
useDebounce,
useKeyPress,
useKeyLift,
useContainerDimensions,
useSearchParams,
useDebouncedCallback,
useResizeObserver,
useWindowSize,
};
33 changes: 33 additions & 0 deletions packages/client/src/hooks/useDebouncedCallback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useCallback, useRef } from "react";

const useDebouncedCallback = <T extends (...args: any[]) => void>(
callback: T,
delay: number
): T => {
const timeoutRef = useRef<number | undefined>();

const debouncedCallback = useCallback(
(...args: Parameters<T>) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}

timeoutRef.current = window.setTimeout(() => {
callback(...args);
}, delay);
},
[callback, delay]
);

const cleanup = useCallback(() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
}, []);

useCallback(() => cleanup, [cleanup]);

return debouncedCallback as T;
};

export default useDebouncedCallback;
60 changes: 60 additions & 0 deletions packages/client/src/hooks/useResizeObserver.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useDebouncedCallback } from "hooks";
import { useLayoutEffect, useState, useRef } from "react";

interface UseResizeObserverOptions {
debounceDelay?: number;
}

interface Size {
width: number | undefined;
height: number | undefined;
}

export const useResizeObserver = <T extends HTMLElement>({
debounceDelay = 0,
}: UseResizeObserverOptions = {}) => {
const ref = useRef<T | null>(null);
const animationFrameRef = useRef<number | null>(null);
const [size, setSize] = useState<Size>({
width: undefined,
height: undefined,
});

const debouncedCallback = useDebouncedCallback((value: Size) => {
setSize(value);
}, debounceDelay);

useLayoutEffect(() => {
const element = ref.current;
if (!element || typeof ResizeObserver === "undefined") {
return;
}

const resizeObserver = new ResizeObserver((entries) => {
animationFrameRef.current = window.requestAnimationFrame(() => {
if (!Array.isArray(entries) || entries.length === 0) {
return;
}

const entry = entries[0];
const { width, height } = entry.contentRect;

debouncedCallback({
width: Math.round(width),
height: Math.round(height),
});
});
});

resizeObserver.observe(element);

return () => {
if (animationFrameRef.current !== null) {
window.cancelAnimationFrame(animationFrameRef.current);
}
resizeObserver.disconnect();
};
}, [debouncedCallback]);

return { ref, ...size };
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import React, {
} from "react";
import { FaSave, FaTrash } from "react-icons/fa";
import { RiFileEditFill } from "react-icons/ri";
import useResizeObserver from "use-resize-observer";
import {
StyledCount,
StyledCountTag,
Expand All @@ -28,6 +27,7 @@ import {
} from "../DocumentsPageStyles";
import theme from "Theme/theme";
import { EntityColors } from "types";
import { useResizeObserver } from "hooks";

interface DocumentRow {
document: IResponseDocument;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ import Dropdown, {
EntitySuggester,
EntityTag,
} from "components/advanced";
import { useDebounce, useSearchParams } from "hooks";
import { useDebounce, useResizeObserver, useSearchParams } from "hooks";
import React, { useEffect, useMemo, useState } from "react";
import { CgOptions } from "react-icons/cg";
import { FaPlus } from "react-icons/fa";
import { IoMdArrowDropdownCircle } from "react-icons/io";
import { RiCloseFill } from "react-icons/ri";
import { DropdownItem } from "types";
import useResizeObserver from "use-resize-observer";
import {
StyledAdvancedOptions,
StyledAdvancedOptionsSign,
Expand Down Expand Up @@ -79,9 +78,8 @@ export const EntitySearchBox: React.FC = () => {
const [searchData, setSearchData] = useState<IRequestSearch>(initValues);
const debouncedValues = useDebounce<IRequestSearch>(searchData, debounceTime);

const { ref: resultRef, height = 0 } = useResizeObserver<HTMLDivElement>();

const debouncedResultsHeight = useDebounce(height, 20);
const { ref: resultRef, height: debouncedResultsHeight = 0 } =
useResizeObserver<HTMLDivElement>();

const statusOptionSelected: EntityEnums.Status = useMemo(() => {
if (!!searchData.status) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import api from "api";
import { CustomScrollbar, Loader, Submit, ToastWithLink } from "components";
import { CStatement, CTerritory } from "constructors";
import { useDebounce, useSearchParams } from "hooks";
import { useDebounce, useResizeObserver, useSearchParams } from "hooks";
import React, { useEffect, useMemo, useState } from "react";
import { BsInfoCircle } from "react-icons/bs";
import { toast } from "react-toastify";
Expand All @@ -22,7 +22,6 @@ import { setRowsExpanded } from "redux/features/statementList/rowsExpandedSlice"
import { useAppDispatch, useAppSelector } from "redux/hooks";
import { COLLAPSED_TABLE_WIDTH } from "Theme/constants";
import { EntitiesDeleteSuccessResponse, StatementListDisplayMode } from "types";
import useResizeObserver from "use-resize-observer";
import { StatementListHeader } from "./StatementListHeader/StatementListHeader";
import { StatementListTable } from "./StatementListTable/StatementListTable";
import { StatementListTextAnnotator } from "./StatementListTextAnnotator/StatementListTextAnnotator";
Expand Down Expand Up @@ -511,9 +510,9 @@ export const StatementListBox: React.FC = () => {
ref: contentRef,
height: contentHeight = 0,
width: contentWidth = 0,
} = useResizeObserver<HTMLDivElement>();

const debouncedWidth = useDebounce(contentWidth, 100);
} = useResizeObserver<HTMLDivElement>({
debounceDelay: displayMode === StatementListDisplayMode.LIST ? 50 : 0,
});

const [storedAnnotatorResourceId, setStoredAnnotatorResourceId] = useState<
string | false
Expand Down Expand Up @@ -548,9 +547,9 @@ export const StatementListBox: React.FC = () => {
const width = useMemo(
() =>
displayMode === StatementListDisplayMode.LIST
? debouncedWidth
? contentWidth
: COLLAPSED_TABLE_WIDTH,
[displayMode, debouncedWidth]
[displayMode, contentWidth]
);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@ import { TbAnchorOff } from "react-icons/tb";
import { TiDocumentText } from "react-icons/ti";
import { ThemeContext } from "styled-components";
import { COLLAPSED_TABLE_WIDTH } from "Theme/constants";
import useResizeObserver from "use-resize-observer";
import { StyledInfoText } from "../StatementListHeader/StatementListHeaderStyles";
import { entitiesDict } from "@shared/dictionaries/entity";
import { useDebounce } from "hooks";
import { useDebounce, useResizeObserver } from "hooks";

interface StatementListTextAnnotator {
statements: IResponseStatement[];
Expand Down Expand Up @@ -252,7 +251,7 @@ export const StatementListTextAnnotator: React.FC<
}, [selectedDocument, territoryId]);

const { ref: selectorRef, height: selectorHeight = 0 } =
useResizeObserver<HTMLDivElement>();
useResizeObserver<HTMLDivElement>({ debounceDelay: 0 });

const themeContext = useContext(ThemeContext);

Expand Down
Loading

0 comments on commit 8fc7db4

Please sign in to comment.