Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: grid view adaptation #223

Merged
merged 3 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/nextjs-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@
"@types/react": "18.2.28",
"@types/react-dom": "18.2.13",
"@types/react-grid-layout": "1.3.2",
"@types/react-hammerjs": "^1.0.6",
"@types/react-syntax-highlighter": "15.5.6",
"@types/scroller": "^0.1.4",
"@types/sharedb": "3.3.5",
"@vitejs/plugin-react": "2.2.0",
"autoprefixer": "10.4.13",
Expand Down Expand Up @@ -160,6 +162,7 @@
"react-dom": "18.2.0",
"react-error-boundary": "4.0.10",
"react-grid-layout": "1.3.4",
"react-hammerjs": "^1.0.1",
"react-hook-form": "7.45.4",
"react-i18next": "11.18.6",
"react-markdown": "8.0.6",
Expand All @@ -173,6 +176,7 @@
"reconnecting-websocket": "4.4.0",
"reflect-metadata": "0.1.13",
"remark-gfm": "3.0.1",
"scroller": "^0.0.3",
"sharedb": "4.1.0",
"sharp": "0.32.6",
"tailwind-scrollbar": "3.0.5",
Expand Down
105 changes: 69 additions & 36 deletions apps/nextjs-app/src/features/app/blocks/grid/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import type {
import type { ISpriteMap, CombinedSelection } from './managers';
import { CoordinateManager, SpriteManager, ImageManager } from './managers';
import type { ICell, IInnerCell } from './renderers';
import { TouchLayer } from './TouchLayer';

export interface IGridExternalProps {
theme?: Partial<IGridTheme>;
Expand Down Expand Up @@ -57,6 +58,7 @@ export interface IGridProps extends IGridExternalProps {
rowCount: number;
rowHeight?: number;
style?: CSSProperties;
isTouchDevice?: boolean;
columnStatistics?: IColumnStatistics;
getCellContent: (cell: ICellItem) => ICell;
}
Expand Down Expand Up @@ -88,6 +90,7 @@ const GridBase: ForwardRefRenderFunction<IGridRef, IGridProps> = (props, forward
rowHeight = defaultRowHeight,
rowControls = [{ type: RowControlType.Checkbox }],
theme: customTheme,
isTouchDevice,
smoothScrollX,
smoothScrollY,
style,
Expand Down Expand Up @@ -149,6 +152,7 @@ const GridBase: ForwardRefRenderFunction<IGridRef, IGridProps> = (props, forward
const containerRef = useRef<HTMLDivElement | null>(null);
const interactionLayerRef = useRef<IInteractionLayerRef | null>(null);
const { ref, width, height } = useResizeObserver<HTMLDivElement>();

const hasColumnStatistics = columnStatistics != null;
const containerHeight = hasColumnStatistics ? height - columnStatisticHeight : height;

Expand Down Expand Up @@ -256,42 +260,71 @@ const GridBase: ForwardRefRenderFunction<IGridRef, IGridProps> = (props, forward
return (
<div className="h-full w-full" style={style} ref={ref}>
<div ref={containerRef} tabIndex={0} className="relative outline-none">
<InteractionLayer
ref={interactionLayerRef}
width={width}
height={height}
theme={theme}
columns={columns}
rowControls={rowControls}
imageManager={imageManager}
spriteManager={spriteManager}
coordInstance={coordInstance}
columnStatistics={columnStatistics}
scrollState={scrollState}
mouseState={mouseState}
setMouseState={setMouseState}
getCellContent={getCellContent}
forceRenderFlag={forceRenderFlag}
scrollToItem={scrollToItem}
scrollBy={scrollBy}
onCopy={onCopy}
onPaste={onPaste}
onDelete={onDelete}
onRowAppend={onRowAppend}
onRowExpand={onRowExpand}
onRowOrdered={onRowOrdered}
onCellEdited={onCellEdited}
onCellActivated={onCellActivated}
onSelectionChanged={onSelectionChanged}
onContextMenu={onContextMenu}
onColumnAppend={onColumnAppend}
onColumnResize={onColumnResize}
onColumnOrdered={onColumnOrdered}
onColumnHeaderClick={onColumnHeaderClick}
onColumnHeaderDblClick={onColumnHeaderDblClick}
onColumnHeaderMenuClick={onColumnHeaderMenuClick}
onColumnStatisticClick={onColumnStatisticClick}
/>
{isTouchDevice ? (
<TouchLayer
width={width}
height={height}
theme={theme}
columns={columns}
mouseState={mouseState}
scrollState={scrollState}
rowControls={rowControls}
imageManager={imageManager}
spriteManager={spriteManager}
coordInstance={coordInstance}
columnStatistics={columnStatistics}
getCellContent={getCellContent}
forceRenderFlag={forceRenderFlag}
setMouseState={setMouseState}
onDelete={onDelete}
onRowAppend={onRowAppend}
onRowExpand={onRowExpand}
onCellEdited={onCellEdited}
onCellActivated={onCellActivated}
onSelectionChanged={onSelectionChanged}
onContextMenu={onContextMenu}
onColumnAppend={onColumnAppend}
onColumnHeaderClick={onColumnHeaderClick}
onColumnStatisticClick={onColumnStatisticClick}
/>
) : (
<InteractionLayer
ref={interactionLayerRef}
width={width}
height={height}
theme={theme}
columns={columns}
rowControls={rowControls}
imageManager={imageManager}
spriteManager={spriteManager}
coordInstance={coordInstance}
columnStatistics={columnStatistics}
scrollState={scrollState}
mouseState={mouseState}
setMouseState={setMouseState}
getCellContent={getCellContent}
forceRenderFlag={forceRenderFlag}
scrollToItem={scrollToItem}
scrollBy={scrollBy}
onCopy={onCopy}
onPaste={onPaste}
onDelete={onDelete}
onRowAppend={onRowAppend}
onRowExpand={onRowExpand}
onRowOrdered={onRowOrdered}
onCellEdited={onCellEdited}
onCellActivated={onCellActivated}
onSelectionChanged={onSelectionChanged}
onContextMenu={onContextMenu}
onColumnAppend={onColumnAppend}
onColumnResize={onColumnResize}
onColumnOrdered={onColumnOrdered}
onColumnHeaderClick={onColumnHeaderClick}
onColumnHeaderDblClick={onColumnHeaderDblClick}
onColumnHeaderMenuClick={onColumnHeaderMenuClick}
onColumnStatisticClick={onColumnStatisticClick}
/>
)}
</div>

<InfiniteScroller
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { useIsTouchDevice } from '@teable-group/sdk/hooks';
import type { ForwardRefRenderFunction, MutableRefObject, ReactNode, UIEvent } from 'react';
import { useMemo, useRef, useCallback, forwardRef, useImperativeHandle } from 'react';
import { useMemo, useRef, useCallback, forwardRef, useImperativeHandle, useEffect } from 'react';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { Scroller } from 'scroller';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can declare it as a module in in types.d folder

import type { IGridProps } from './Grid';
import { getHorizontalRangeInfo, getVerticalRangeInfo, useEventListener } from './hooks';
import type { IScrollState } from './interface';
Expand Down Expand Up @@ -60,6 +64,9 @@ const InfiniteScrollerBase: ForwardRefRenderFunction<ScrollerRef, ScrollerProps>
},
}));

const isTouchDevice = useIsTouchDevice();

const scrollerRef = useRef<Scroller | null>(null);
const horizontalScrollRef = useRef<HTMLDivElement | null>(null);
const verticalScrollRef = useRef<HTMLDivElement | null>(null);
const resetScrollingTimeoutID = useRef<ITimeoutID | null>(null);
Expand Down Expand Up @@ -156,6 +163,15 @@ const InfiniteScrollerBase: ForwardRefRenderFunction<ScrollerRef, ScrollerProps>
}
}, []);

const mobileScrollHandler = useCallback((scrollLeft: number, scrollTop: number) => {
if (horizontalScrollRef.current) {
horizontalScrollRef.current.scrollLeft = scrollLeft;
}
if (verticalScrollRef.current) {
verticalScrollRef.current.scrollTop = scrollTop;
}
}, []);

const onWheel = useCallback(
(event: Event) => {
if (!scrollEnable) return;
Expand All @@ -168,6 +184,50 @@ const InfiniteScrollerBase: ForwardRefRenderFunction<ScrollerRef, ScrollerProps>
[scrollEnable, scrollHandler]
);

const onTouchStart = useCallback((e: TouchEvent) => {
if (scrollerRef.current) {
scrollerRef.current.doTouchStart(e.changedTouches, e.timeStamp);
}
}, []);

const onTouchMove = useCallback((e: TouchEvent) => {
e.preventDefault();
if (scrollerRef.current) {
scrollerRef.current.doTouchMove(e.changedTouches, e.timeStamp);
}
}, []);

const onTouchEnd = useCallback((e: TouchEvent) => {
if (scrollerRef.current) {
if (horizontalScrollRef.current && verticalScrollRef.current) {
scrollerRef.current?.scrollTo(
horizontalScrollRef.current.scrollLeft,
verticalScrollRef.current.scrollTop
);
}
scrollerRef.current.doTouchEnd(e.timeStamp);
}
}, []);

useEffect(() => {
if (!isTouchDevice) return;

const options = {
scrollingX: true,
scrollingY: true,
animationDuration: 200,
};

scrollerRef.current = new Scroller(mobileScrollHandler, options);
}, [mobileScrollHandler, isTouchDevice]);

useEffect(() => {
if (scrollerRef.current) {
scrollTo({});
scrollerRef.current.setDimensions(containerWidth, containerHeight, scrollWidth, scrollHeight);
}
}, [containerHeight, containerWidth, scrollWidth, scrollHeight]);

const placeholderElements: ReactNode[] = useMemo(() => {
let h = 0;
let key = 0;
Expand All @@ -182,6 +242,9 @@ const InfiniteScrollerBase: ForwardRefRenderFunction<ScrollerRef, ScrollerProps>
}, [scrollHeight]);

useEventListener('wheel', onWheel, containerRef.current, false);
useEventListener('touchstart', onTouchStart, containerRef.current, false);
useEventListener('touchmove', onTouchMove, containerRef.current, false);
useEventListener('touchend', onTouchEnd, containerRef.current, false);

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ export const InteractionLayerBase: ForwardRefRenderFunction<
return onColumnAppend?.();
case RegionType.RowHeaderExpandHandler:
return onRowExpand?.(rowIndex);
case RegionType.ColumnResizeHandler:
case RegionType.ColumnHeader:
return onColumnHeaderClick?.(columnIndex, {
x: coordInstance.getColumnRelativeOffset(columnIndex, scrollLeft),
y: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface IRenderLayerProps
| 'coordInstance'
| 'columnStatistics'
> {
isEditing: boolean;
isEditing?: boolean;
visibleRegion: IVisibleRegion;
activeCell: ICellItem | null;
dragState: IDragState;
Expand Down
Loading