Skip to content

Commit

Permalink
feat: on move resize callback with metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
KatoakDR committed Jul 29, 2024
1 parent 9187544 commit d9b54c3
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 36 deletions.
75 changes: 51 additions & 24 deletions electron/renderer/components/grid/grid-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,21 @@ import { css } from '@emotion/react';
import { animated, useSpring } from '@react-spring/web';
import type { EventTypes, Handler, UserDragConfig } from '@use-gesture/react';
import { useDrag } from '@use-gesture/react';
import debounce from 'lodash-es/debounce.js';
import get from 'lodash-es/get';
import isNil from 'lodash-es/isNil';
import type { MouseEvent, ReactNode, RefObject } from 'react';
import type { ReactNode, RefObject } from 'react';
import { useCallback, useMemo, useRef } from 'react';

export interface GridItemMetadata {
itemId: string;
isFocused: boolean;
x: number;
y: number;
width: number;
height: number;
}

export interface GridItemProps {
/**
* The dimension for the grid where the item may be dragged and resized.
Expand Down Expand Up @@ -49,7 +59,7 @@ export interface GridItemProps {
* Handler when the user clicks the close button in the title bar.
* Passes the `itemId` of the grid item being closed.
*/
onClose?: (itemId: string) => void;
onClose?: (metadata: GridItemMetadata) => void;
/**
* Is this the focused grid item?
* When yes then it will be positioned above the other grid items.
Expand All @@ -60,7 +70,11 @@ export interface GridItemProps {
* The parent component has responsibility for managing the `isFocused`
* property for all of the grid items to reflect the change.
*/
onFocus?: (itemId: string) => void;
onFocus?: (metadata: GridItemMetadata) => void;
/**
* When the grid item is moved or resized then notify the parent component.
*/
onMoveResize?: (metadata: GridItemMetadata) => void;
/**
* This property contains any children nested within the grid item
* when you're constructing the grid layout.
Expand All @@ -72,29 +86,11 @@ export interface GridItemProps {
export const GridItem: React.FC<GridItemProps> = (
props: GridItemProps
): ReactNode => {
const { boundary, itemId, titleBarText } = props;
const { isFocused, onFocus, onClose, children } = props;
const { boundary, itemId, titleBarText, isFocused = false, children } = props;
const { onFocus, onClose, onMoveResize } = props;

const { euiTheme } = useEuiTheme();

// Handle when the user clicks the close button in the title bar.
const onCloseClick = useCallback(
(evt: MouseEvent<HTMLElement>) => {
evt.preventDefault();
if (onClose) {
onClose(itemId);
}
},
[onClose, itemId]
);

// Handle when the user clicks or focuses the grid item.
const onFocusClick = useCallback(() => {
if (onFocus) {
onFocus(itemId);
}
}, [onFocus, itemId]);

// Set default position and size for the grid item.
const [{ x, y, width, height }, sizeApi] = useSpring(() => ({
x: 0,
Expand All @@ -106,6 +102,35 @@ export const GridItem: React.FC<GridItemProps> = (
const dragHandleRef = useRef<HTMLDivElement>(null);
const resizeHandleRef = useRef<HTMLDivElement>(null);

const getItemMetadata = useCallback(() => {
return {
itemId,
isFocused,
x: x.get(),
y: y.get(),
width: width.get(),
height: height.get(),
};
}, [itemId, isFocused, x, y, width, height]);

// Handle when the user clicks the close button in the title bar.
const onCloseClick = useCallback(() => {
onClose?.(getItemMetadata());
}, [onClose, getItemMetadata]);

// Handle when the user clicks or focuses the grid item.
const onFocusClick = useCallback(() => {
onFocus?.(getItemMetadata());
}, [onFocus, getItemMetadata]);

// Handle when the user moves or resizes the grid item.
// Because this event triggers frequently, we debounce it for performance.
const onMoveResizeHandler = useMemo(() => {
return debounce(() => {
onMoveResize?.(getItemMetadata());
}, 300);
}, [onMoveResize, getItemMetadata]);

/**
* Is the event target the same element as the ref?
*/
Expand Down Expand Up @@ -169,13 +194,15 @@ export const GridItem: React.FC<GridItemProps> = (

if (isResizing(state.event)) {
sizeApi.set({ width: dx, height: dy });
onMoveResizeHandler();
}

if (isDragging(state.event)) {
sizeApi.set({ x: dx, y: dy });
onMoveResizeHandler();
}
},
[sizeApi, isResizing, isDragging]
[sizeApi, isResizing, isDragging, onMoveResizeHandler]
);

const dragOptions: UserDragConfig = useMemo(() => {
Expand Down
50 changes: 38 additions & 12 deletions electron/renderer/components/grid/grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
// https://www.youtube.com/watch?v=vDxZLN6FVqY

import type { ReactNode } from 'react';
import { useState } from 'react';
import { useCallback, useState } from 'react';
import { useLogger } from '../../hooks/logger.jsx';
import type { GridItemMetadata } from './grid-item.jsx';
import { GridItem } from './grid-item.jsx';

export interface GridProps {
Expand All @@ -26,24 +28,49 @@ export interface GridProps {
export const Grid: React.FC<GridProps> = (props: GridProps): ReactNode => {
const { boundary } = props;

const logger = useLogger('cmp:grid');

// TODO load layout from storage

// TODO determine the focused item from the layout, if none, use the first one
const [focusedItemId, setFocusedItemId] = useState<string>('');

// TODO when an item is closed then remove it from layout and save layout
const onItemFocus = useCallback((itemMeta: GridItemMetadata) => {
const { itemId } = itemMeta;
setFocusedItemId(itemId);
// TODO when an item is resized then save layout, including the focused item
}, []);

const onItemClose = useCallback(
(itemMeta: GridItemMetadata) => {
const { itemId } = itemMeta;
// TODO when an item is closed then remove it from layout and save layout
logger.debug(`closed item ${itemId}`);
},
[logger]
);

const onItemMoveResize = useCallback(
(itemMeta: GridItemMetadata) => {
const { itemId } = itemMeta;
// TODO when an item is dragged then save layout
logger.debug(`moved item ${itemId}`);
},
[logger]
);

// TODO when user adds an item to the grid then add it to layout and save layout
// - refer to UX of Genie client for adding items to the grid
// TODO when an item is dragged or resized then save layout
// - need to pass callback to grid items to know whey their position or size changes

const item1 = (
<GridItem
key="1"
itemId="1"
titleBarText="Item 1"
isFocused={focusedItemId === '1'}
onFocus={setFocusedItemId}
onClose={() => {
alert('Closed item 1');
}}
onFocus={onItemFocus}
onClose={onItemClose}
onMoveResize={onItemMoveResize}
boundary={boundary}
>
<div>Content1</div>
Expand All @@ -56,10 +83,9 @@ export const Grid: React.FC<GridProps> = (props: GridProps): ReactNode => {
itemId="2"
titleBarText="Experience"
isFocused={focusedItemId === '2'}
onFocus={setFocusedItemId}
onClose={() => {
alert('Closed item 2');
}}
onFocus={onItemFocus}
onClose={onItemClose}
onMoveResize={onItemMoveResize}
boundary={boundary}
>
<div>Content2a</div>
Expand Down

0 comments on commit d9b54c3

Please sign in to comment.