Skip to content

Commit

Permalink
Try to make selection more performant
Browse files Browse the repository at this point in the history
  • Loading branch information
DustinBrett committed Jul 9, 2024
1 parent 27b348d commit b0ce679
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 54 deletions.
2 changes: 1 addition & 1 deletion components/system/Files/FileEntry/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,8 @@ const FileEntry: FC<FileEntryProps> = ({
}
} else if (
isFocused &&
focusedEntries.length === 1 &&
buttonRef.current !== document.activeElement &&
focusedEntries.length === 1 &&
!buttonRef.current.contains(document.activeElement)
) {
buttonRef.current.focus(PREVENT_SCROLL);
Expand Down
27 changes: 27 additions & 0 deletions components/system/Files/FileManager/Selection/functions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
import { type SelectionRect } from "components/system/Files/FileManager/Selection/useSelection";

type SelectionStyling = {
height?: string;
transform?: string;
width?: string;
};

export const createSelectionStyling = (
isSelecting: boolean,
h: number | string,
w: number | string,
x: number | string,
y: number | string
): SelectionStyling => {
if (!isSelecting) return Object.create(null) as SelectionStyling;

const height = Number(h);
const width = Number(w);

return {
height: `${Math.abs(height)}px`,
transform: `translate(
${Number(x) + (width < 0 ? width : 0)}px,
${Number(y) + (height < 0 ? height : 0)}px)`,
width: `${Math.abs(width)}px`,
};
};

export const isSelectionIntersecting = (
element: DOMRect,
containerElement: DOMRect,
Expand Down
102 changes: 49 additions & 53 deletions components/system/Files/FileManager/Selection/useSelection.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { type Position } from "react-rnd";
import { useRef, useState } from "react";
import { createSelectionStyling } from "components/system/Files/FileManager/Selection/functions";
import { type FocusEntryFunctions } from "components/system/Files/FileManager/useFocusableEntries";
import { type Size } from "components/system/Window/RndWindow/useResizable";
import { useMenu } from "contexts/menu";
import { type MenuState } from "contexts/menu/useMenuContextState";
import { MILLISECONDS_IN_SECOND } from "utils/constants";

export type SelectionRect = Partial<Position> & Partial<Size>;

Expand All @@ -20,9 +20,6 @@ type Selection = {
selectionStyling: React.CSSProperties;
};

const FPS = 60;
const DEBOUNCE_TIME = MILLISECONDS_IN_SECOND / FPS;

const useSelection = (
containerRef: React.MutableRefObject<HTMLElement | null>,
focusedEntries: string[],
Expand All @@ -34,81 +31,80 @@ const useSelection = (
const [size, setSize] = useState<Size>(() => Object.create(null) as Size);
const { x, y } = position;
const { height: h, width: w } = size;
const debounceTimer = useRef<number>();
const animationRequestId = useRef(0);
const onMouseMove: React.MouseEventHandler<HTMLElement> = ({
clientX,
clientY,
}) => {
if (animationRequestId.current) return;

const { scrollTop = 0 } = containerRef.current || {};
const { x: targetX = 0, y: targetY = 0 } =
containerRef.current?.getBoundingClientRect() || {};

if (!debounceTimer.current) {
setSize({
height: clientY - targetY - (y || 0) + scrollTop,
width: clientX - targetX - (x || 0),
});
debounceTimer.current = window.setTimeout(() => {
debounceTimer.current = undefined;
}, DEBOUNCE_TIME);
}
setSize({
height: clientY - targetY - (y || 0) + scrollTop,
width: clientX - targetX - (x || 0),
});

animationRequestId.current = window.requestAnimationFrame(() => {
animationRequestId.current = 0;
});
};
const { menu, setMenu } = useMenu();
const onMouseDown: React.MouseEventHandler<HTMLElement> = ({
clientX,
clientY,
target,
}) => {
if (target === containerRef.current) {
const { scrollTop } = containerRef.current;
const { x: targetX = 0, y: targetY = 0 } =
containerRef.current.getBoundingClientRect();
if (target !== containerRef.current) return;

setSize(Object.create(null) as Size);
setPosition({
x: clientX - targetX,
y: clientY - targetY + scrollTop,
});
const { scrollTop } = containerRef.current;
const { x: targetX = 0, y: targetY = 0 } =
containerRef.current.getBoundingClientRect();

setSize(Object.create(null) as Size);
setPosition({
x: clientX - targetX,
y: clientY - targetY + scrollTop,
});

if (menu && Object.keys(menu).length > 0) {
setMenu(Object.create(null) as MenuState);
}
if (focusedEntries.length > 0) blurEntry();
if (menu && Object.keys(menu).length > 0) {
setMenu(Object.create(null) as MenuState);
}
if (focusedEntries.length > 0) blurEntry();
};
const hasMenu = Object.keys(menu).length > 0;
const hasSize = typeof w === "number" && typeof h === "number";
const hasPosition = typeof x === "number" && typeof y === "number";
const resetSelection = (): void => {
setSize(Object.create(null) as Size);
setPosition(Object.create(null) as Position);
};
const isSelecting = !hasMenu && hasSize && hasPosition;
const selectionStyling = isSelecting
? {
height: `${Math.abs(Number(h))}px`,
transform: `translate(
${Number(x) + (Number(w) < 0 ? Number(w) : 0)}px,
${Number(y) + (Number(h) < 0 ? Number(h) : 0)}px)`,
width: `${Math.abs(Number(w))}px`,
}
: {};

return {
const isSelecting = hasSize && hasPosition && Object.keys(menu).length === 0;
const selection: Selection = {
isSelecting,
selectionEvents: {
onMouseDown,
...(hasPosition
? {
onMouseLeave: resetSelection,
onMouseMove,
onMouseUp: resetSelection,
}
: {}),
},
selectionRect: isSelecting ? { ...position, ...size } : undefined,
selectionStyling,
selectionStyling: createSelectionStyling(isSelecting, h, w, x, y),
};

if (hasPosition) {
const resetSelection = (): void => {
setSize(Object.create(null) as Size);
setPosition(Object.create(null) as Position);
};

selection.selectionEvents.onMouseLeave = resetSelection;
selection.selectionEvents.onMouseMove = onMouseMove;
selection.selectionEvents.onMouseUp = resetSelection;
}

if (isSelecting) {
selection.selectionRect = Object.assign(
Object.create(null) as SelectionRect,
position,
size
);
}

return selection;
};

export default useSelection;

0 comments on commit b0ce679

Please sign in to comment.