Skip to content

Commit 77c0c0e

Browse files
committed
feat(FileOrganizer): added gridRef prop to allow access to react-window grid
1 parent d6c7c91 commit 77c0c0e

File tree

6 files changed

+64
-41
lines changed

6 files changed

+64
-41
lines changed

src/components/ClickableDiv/ClickableDiv.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export interface ClickableDivProps extends Remove<HTMLAttributes<HTMLDivElement>
2222
export const ClickableDiv = forwardRef<HTMLDivElement, ClickableDivProps>(
2323
({ onClick, onKeyPress, disabled, noFocusStyle, usePointer, className, children, tabIndex, ...divProps }, ref) => {
2424
const clickableDivRef = useRef<HTMLDivElement>(null);
25-
useImperativeHandle(ref, () => clickableDivRef.current!);
25+
useImperativeHandle(ref, () => clickableDivRef.current as HTMLDivElement);
2626

2727
const handleOnClick = useOnClick(onClick, { disabled, stopPropagation: true });
2828

src/components/Draggable/Draggable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export const Draggable = forwardRef<HTMLDivElement, DraggableProps>(
8181
ref,
8282
) => {
8383
const draggableRef = useRef<HTMLDivElement>(null);
84-
useImperativeHandle(ref, () => draggableRef.current!);
84+
useImperativeHandle(ref, () => draggableRef.current as HTMLDivElement);
8585

8686
/* --- Drag and drop settings. --- */
8787

src/components/EditableText/EditableText.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export const EditableText = forwardRef<HTMLDivElement, EditableTextProps>(
8181
const inputRef = useRef<HTMLInputElement>(null);
8282

8383
const buttonRef = useRef<HTMLDivElement>(null);
84-
useImperativeHandle(ref, () => buttonRef.current!);
84+
useImperativeHandle(ref, () => buttonRef.current as HTMLDivElement);
8585

8686
// Use state if controlled edit mode not provided.
8787
const [stateEditMode, setStateEditMode] = useState(false);

src/components/FileOrganizer/FileOrganizer.stories.tsx

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { boolean, number } from '@storybook/addon-knobs';
2-
import React, { FC, useCallback, useEffect, useState } from 'react';
2+
import React, { FC, useCallback, useEffect, useState, useRef } from 'react';
33
import { useManagedFiles } from '../../hooks';
44
import { action } from '../../storybook-helpers/action/action';
55
import { createFile, FakeFile } from '../../storybook-helpers/data/files';
@@ -10,6 +10,8 @@ import { Icon } from '../Icon';
1010
import { Thumbnail } from '../Thumbnail';
1111
import { ThumbnailDragLayer } from '../ThumbnailDragLayer';
1212
import readme from './README.md';
13+
import { FixedSizeGrid } from 'react-window';
14+
import { Button } from '../Button';
1315

1416
export default {
1517
title: 'Components/FileOrganizer',
@@ -21,9 +23,10 @@ interface TemplateProps {
2123
onRenderDragLayer?: boolean;
2224
numFiles?: number;
2325
editable?: boolean;
26+
scrollToTop?: boolean;
2427
}
2528

26-
const Template: FC<TemplateProps> = ({ onRenderDragLayer, numFiles = 2, editable }) => {
29+
const Template: FC<TemplateProps> = ({ onRenderDragLayer, numFiles = 2, editable, scrollToTop }) => {
2730
// This is the index organizing function.
2831
const [files, setFiles] = useState<FakeFile[]>([]);
2932
const handleOnMove = useCallback<NonNullable<FileOrganizerProps<FakeFile>['onMove']>>((fromIndex, toIndex) => {
@@ -37,6 +40,11 @@ const Template: FC<TemplateProps> = ({ onRenderDragLayer, numFiles = 2, editable
3740
return true;
3841
}, []);
3942

43+
const gridRef = useRef<FixedSizeGrid>(null);
44+
const onScrollToTop = () => {
45+
gridRef.current?.scrollTo({ scrollTop: 0, scrollLeft: 0 });
46+
};
47+
4048
// This is just a helper for adding or removing files.
4149
useEffect(() => {
4250
setFiles((prev) => {
@@ -55,35 +63,39 @@ const Template: FC<TemplateProps> = ({ onRenderDragLayer, numFiles = 2, editable
5563
}, [numFiles]);
5664

5765
return (
58-
<FileOrganizer
59-
files={files}
60-
preventArrowsToMove={boolean('preventArrowsToMove', false)}
61-
preventClickAwayDeselect={boolean('preventClickAwayDeselect', false)}
62-
disableMove={boolean('disableMove', false)}
63-
padding={integer('padding', 0)}
64-
onMove={forwardAction('onMove', handleOnMove)}
65-
onDragChange={action('onDragChange')}
66-
onDeselectAll={action('onDeselectAll')}
67-
onSelectAll={action('onSelectAll')}
68-
onRenderDragLayer={onRenderDragLayer ? () => <ThumbnailDragLayer /> : undefined}
69-
onRenderThumbnail={({ onRenderThumbnailProps }) => (
70-
<Thumbnail
71-
{...onRenderThumbnailProps}
72-
onRename={editable ? () => {} : undefined}
73-
buttonProps={
74-
editable
75-
? [
76-
{
77-
children: <Icon icon="Close" />,
78-
onClick: () => {},
79-
key: 0,
80-
},
81-
]
82-
: undefined
83-
}
84-
/>
85-
)}
86-
/>
66+
<>
67+
<FileOrganizer
68+
files={files}
69+
gridRef={gridRef}
70+
preventArrowsToMove={boolean('preventArrowsToMove', false)}
71+
preventClickAwayDeselect={boolean('preventClickAwayDeselect', false)}
72+
disableMove={boolean('disableMove', false)}
73+
padding={integer('padding', 0)}
74+
onMove={forwardAction('onMove', handleOnMove)}
75+
onDragChange={action('onDragChange')}
76+
onDeselectAll={action('onDeselectAll')}
77+
onSelectAll={action('onSelectAll')}
78+
onRenderDragLayer={onRenderDragLayer ? () => <ThumbnailDragLayer /> : undefined}
79+
onRenderThumbnail={({ onRenderThumbnailProps }) => (
80+
<Thumbnail
81+
{...onRenderThumbnailProps}
82+
onRename={editable ? () => {} : undefined}
83+
buttonProps={
84+
editable
85+
? [
86+
{
87+
children: <Icon icon="Close" />,
88+
onClick: () => {},
89+
key: 0,
90+
},
91+
]
92+
: undefined
93+
}
94+
/>
95+
)}
96+
/>
97+
{scrollToTop && <Button onClick={onScrollToTop}>Scroll to top</Button>}
98+
</>
8799
);
88100
};
89101

@@ -99,6 +111,9 @@ export const WithCustomDragLayer = () => {
99111
const numFiles = number('number of files', 2, { min: 0, max: 16, step: 1, range: true });
100112
return <Template numFiles={numFiles} onRenderDragLayer />;
101113
};
114+
export const WithGridRef = () => {
115+
return <Template numFiles={100} scrollToTop />;
116+
};
102117

103118
export const UseManagedFilesHook = () => {
104119
const { fileOrganizerProps, getThumbnailSelectionProps, draggingIds } = useManagedFiles({

src/components/FileOrganizer/FileOrganizer.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import React, {
1313
useEffect,
1414
useRef,
1515
useState,
16+
Ref,
17+
useImperativeHandle,
1618
} from 'react';
17-
import { FixedSizeGrid as Grid } from 'react-window';
19+
import { FixedSizeGrid } from 'react-window';
1820
import {
1921
getRowAndColumnIndex,
2022
getSibling,
@@ -64,6 +66,10 @@ export interface FileOrganizerProps<F> extends HTMLAttributes<HTMLDivElement> {
6466
* ensure that the elements are positioned properly.
6567
*/
6668
padding?: number;
69+
/**
70+
* If provided, the ref is attached to the `react-window` FixedSizeGrid.
71+
*/
72+
gridRef?: Ref<FixedSizeGrid>;
6773
/**
6874
* On render function for generating the thumbnails for the page organizer.
6975
* If you do not want to build your own, try using the `Thumbnail` component.
@@ -133,14 +139,16 @@ export function FileOrganizer<F extends ObjectWithId>({
133139
preventClickAwayDeselect,
134140
draggingIds,
135141
padding,
142+
gridRef: _gridRef,
136143
className,
137144
onClick,
138145
onKeyDown,
139146
style,
140147
...divProps
141148
}: FileOrganizerProps<F>) {
142149
const fileOrganizerRef = useRef<HTMLDivElement>(null);
143-
const gridRef = useRef<Grid>(null);
150+
const gridRef = useRef<FixedSizeGrid>(null);
151+
useImperativeHandle(_gridRef, () => gridRef.current as FixedSizeGrid);
144152

145153
const [columnCount, setColumnCount] = useState(0);
146154

src/components/FileOrganizer/MemoAutoSizer.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { forwardRef, memo, useCallback, useEffect, useMemo } from 'react';
22
import AutoSizer, { Size } from 'react-virtualized-auto-sizer';
3-
import { FixedSizeGrid as Grid, GridChildComponentProps } from 'react-window';
3+
import { FixedSizeGrid, GridChildComponentProps } from 'react-window';
44
import { getItemIndex, ObjectWithId } from '../../utils';
55

66
interface VirtualizedProps {
@@ -19,7 +19,7 @@ const MemoGridItem = memo<GridChildComponentProps>(({ columnIndex, rowIndex, sty
1919
});
2020

2121
const MemoGrid = memo(
22-
forwardRef<Grid, Size & VirtualizedProps>(
22+
forwardRef<FixedSizeGrid, Size & VirtualizedProps>(
2323
({ width, height, files, padding, renderItem, onColumnCountChange, size }, ref) => {
2424
const modifiedHeight = height - 2 * padding;
2525
const modifiedWidth = width - 2 * padding;
@@ -35,7 +35,7 @@ const MemoGrid = memo(
3535
}, [onColumnCountChange, data.columnCount]);
3636

3737
return (
38-
<Grid
38+
<FixedSizeGrid
3939
ref={ref}
4040
columnWidth={size.width}
4141
rowHeight={size.height}
@@ -51,14 +51,14 @@ const MemoGrid = memo(
5151
}}
5252
>
5353
{MemoGridItem}
54-
</Grid>
54+
</FixedSizeGrid>
5555
);
5656
},
5757
),
5858
);
5959

6060
export const MemoAutoSizer = memo(
61-
forwardRef<Grid, VirtualizedProps>(({ files, padding, size, renderItem, onColumnCountChange }, ref) => {
61+
forwardRef<FixedSizeGrid, VirtualizedProps>(({ files, padding, size, renderItem, onColumnCountChange }, ref) => {
6262
const onRenderGrid = useCallback(
6363
({ width, height }: Size) => (
6464
<MemoGrid

0 commit comments

Comments
 (0)