Skip to content

Commit 62f799c

Browse files
authored
File Explorer drag and drop to move files (#109)
- Add drag and drop styling - Get rid of SingleClickItemList and just used ItemList. This fixes Shift+Click selection - Fixed up stealing focus when opening preview notebook - Add isDoubleClickSelect to Itemlist - Fix the CSV overlay appearing when dragging over it - Allow dropping at the root and past the last element
1 parent 7bff013 commit 62f799c

32 files changed

+1558
-1603
lines changed

packages/code-studio/src/dashboard/panels/FileExplorerPanel.tsx

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import Log from '@deephaven/log';
22
import { getFileStorage } from '@deephaven/redux';
33
import FileExplorer, {
44
FileExplorerToolbar,
5-
FileListItem,
65
FileStorage,
6+
FileStorageItem,
77
NewItemModal,
8-
UpdateableComponent,
98
} from '@deephaven/file-explorer';
109
import GoldenLayout from 'golden-layout';
1110
import React, { ReactNode } from 'react';
@@ -65,7 +64,6 @@ export class FileExplorerPanel extends React.Component<
6564
);
6665
this.handleDelete = this.handleDelete.bind(this);
6766
this.handleRename = this.handleRename.bind(this);
68-
this.handleResize = this.handleResize.bind(this);
6967
this.handleSessionOpened = this.handleSessionOpened.bind(this);
7068
this.handleSessionClosed = this.handleSessionClosed.bind(this);
7169
this.handleShow = this.handleShow.bind(this);
@@ -85,8 +83,6 @@ export class FileExplorerPanel extends React.Component<
8583
}
8684
}
8785

88-
private fileExplorer = React.createRef<UpdateableComponent>();
89-
9086
handleCreateFile(): void {
9187
const { glEventHub } = this.props;
9288
const { session, language } = this.state;
@@ -121,7 +117,7 @@ export class FileExplorerPanel extends React.Component<
121117
fileStorage.createDirectory(path).catch(FileExplorerPanel.handleError);
122118
}
123119

124-
handleDelete(files: FileListItem[]): void {
120+
handleDelete(files: FileStorageItem[]): void {
125121
const { glEventHub } = this.props;
126122
files.forEach(file => {
127123
glEventHub.emit(NotebookEvent.CLOSE_FILE, {
@@ -131,7 +127,7 @@ export class FileExplorerPanel extends React.Component<
131127
});
132128
}
133129

134-
handleFileSelect(file: FileListItem): void {
130+
handleFileSelect(file: FileStorageItem): void {
135131
log.debug('fileSelect', file);
136132
if (file.type === 'directory') {
137133
return;
@@ -159,10 +155,6 @@ export class FileExplorerPanel extends React.Component<
159155
glEventHub.emit(NotebookEvent.RENAME_FILE, oldName, newName);
160156
}
161157

162-
handleResize(): void {
163-
this.fileExplorer.current?.updateDimensions();
164-
}
165-
166158
handleSessionOpened(
167159
session: DhSession,
168160
{ language }: { language: string }
@@ -182,7 +174,6 @@ export class FileExplorerPanel extends React.Component<
182174

183175
handleShow(): void {
184176
this.setState({ isShown: true });
185-
this.fileExplorer.current?.updateDimensions();
186177
}
187178

188179
isHidden(): boolean {
@@ -202,7 +193,6 @@ export class FileExplorerPanel extends React.Component<
202193
glEventHub={glEventHub}
203194
onSessionOpen={this.handleSessionOpened}
204195
onSessionClose={this.handleSessionClosed}
205-
onResize={this.handleResize}
206196
onShow={this.handleShow}
207197
>
208198
<FileExplorerToolbar
@@ -212,7 +202,6 @@ export class FileExplorerPanel extends React.Component<
212202
{isShown && (
213203
<FileExplorer
214204
isMultiSelect
215-
ref={this.fileExplorer}
216205
storage={fileStorage}
217206
onDelete={this.handleDelete}
218207
onRename={this.handleRename}

packages/code-studio/src/dashboard/panels/NotebookPanel.jsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,7 @@ class NotebookPanel extends Component {
580580
const { fileMetadata } = this.state;
581581
if (fileMetadata.id === oldName) {
582582
this.setState({ fileMetadata: { id: newName, itemName: newName } });
583+
this.debouncedSavePanelState();
583584
}
584585
}
585586

@@ -715,13 +716,17 @@ class NotebookPanel extends Component {
715716
isDashboardActive,
716717
isLoaded,
717718
isLoading,
719+
isPreview,
718720
fileMetadata,
719721
session,
720722
sessionLanguage,
721723
settings: initialSettings,
722724
showCloseModal,
723725
showSaveAsModal,
724726
} = this.state;
727+
// We don't want to steal focus if this isn't shown or it's just a preview
728+
const focusOnMount =
729+
isDashboardActive && !glContainer.isHidden && !isPreview;
725730
const itemName = fileMetadata?.itemName ?? NotebookPanel.DEFAULT_NAME;
726731
const isExistingItem = fileMetadata?.id != null;
727732
const overflowActions = this.getOverflowActions();
@@ -859,7 +864,7 @@ class NotebookPanel extends Component {
859864
session={session}
860865
sessionLanguage={sessionLanguage}
861866
settings={settings}
862-
focusOnMount={isDashboardActive && !glContainer.isHidden}
867+
focusOnMount={focusOnMount}
863868
ref={notebook => {
864869
this.notebook = notebook;
865870
}}

packages/code-studio/src/styleguide/Grids.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { PureComponent } from 'react';
22
import { Grid, MockGridModel, MockTreeGridModel } from '@deephaven/grid';
3-
import { IrisGrid } from '@deephaven/iris-grid/dist/IrisGrid';
3+
import IrisGrid from '@deephaven/iris-grid/dist/IrisGrid';
44
import MockIrisGridTreeModel from './MockIrisGridTreeModel';
55

66
class Grids extends PureComponent {

packages/code-studio/src/styleguide/MockIrisGridTreeModel.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ class MockIrisGridTreeModel extends IrisGridModel {
4141
return true;
4242
}
4343

44+
get pendingRowCount() {
45+
return 0;
46+
}
47+
48+
set pendingRowCount(count) {}
49+
50+
get pendingDataMap() {
51+
return new Map();
52+
}
53+
54+
set pendingDataMap(value) {}
55+
4456
textForCell(column, row) {
4557
return (
4658
this.editedData[column]?.[row] ?? this.model.textForCell(column, row)

packages/code-studio/src/styleguide/StyleGuideInit.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
setWorkspace as setWorkspaceAction,
77
} from '@deephaven/redux';
88
import StyleGuide from './StyleGuide';
9-
import WorkspaceStorage from '../dashboard/WorkspaceStorage';
9+
import LocalWorkspaceStorage from '../dashboard/LocalWorkspaceStorage';
1010

1111
/**
1212
* Initialize data needed for the styleguide
@@ -15,7 +15,7 @@ const StyleGuideInit = props => {
1515
const { workspace, setWorkspace } = props;
1616

1717
useEffect(() => {
18-
setWorkspace(WorkspaceStorage.makeDefaultWorkspace());
18+
setWorkspace(LocalWorkspaceStorage.makeDefaultWorkspace());
1919
}, [setWorkspace]);
2020

2121
return <>{workspace && <StyleGuide />}</>;

packages/components/src/DraggableItemList.tsx

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { Draggable, Droppable, DraggableChildrenFn } from 'react-beautiful-dnd';
55
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
66
import { vsGripper } from '@deephaven/icons';
77
import { RangeUtils, Range } from '@deephaven/utils';
8-
import ItemList, { RenderItemProps, DefaultListItem } from './ItemList';
8+
import ItemList, {
9+
RenderItemProps,
10+
DefaultListItem,
11+
ItemListProps,
12+
} from './ItemList';
913
import { Tooltip } from './popper';
1014
import './DraggableItemList.scss';
1115

@@ -18,33 +22,19 @@ type DraggableRenderItemFn<T> = (
1822
props: DraggableRenderItemProps<T>
1923
) => React.ReactNode;
2024

21-
type DraggableItemListProps<T> = {
25+
type DraggableItemListProps<T> = Omit<
26+
ItemListProps<T>,
27+
'overscanCount' | 'focusSelector' | 'isDragSelect'
28+
> & {
2229
className: string;
2330
draggingItemClassName: string;
24-
// Total item count
25-
itemCount: number;
26-
rowHeight: number;
27-
// Offset of the top item in the items array
28-
offset: number;
29-
// Item object format expected by the default renderItem function
30-
// Can be anything as long as it's supported by the renderItem
31-
items: T[];
32-
// Whether to allow dropping items in this list
3331
isDropDisabled: boolean;
3432
// Whether to allow dragging items from this list
3533
isDragDisabled: boolean;
36-
// Whether to allow multiple selections in this item list
37-
isMultiSelect: boolean;
38-
// Set to true if you want the list to scroll when new items are added and it's already at the bottom
39-
isStickyBottom: boolean;
40-
// Fired when an item is clicked. With multiple selection, fired on double click.
41-
onSelect(index: number): void;
42-
onSelectionChange(ranges: Range[]): void;
43-
onViewportChange(): void;
44-
selectedRanges: Range[];
45-
disableSelect: boolean;
34+
4635
renderItem: DraggableRenderItemFn<T>;
4736
style: React.CSSProperties;
37+
4838
// The prefix to add to all draggable item IDs
4939
draggablePrefix: string;
5040
// The ID to give the droppable list
@@ -74,12 +64,16 @@ class DraggableItemList<T> extends PureComponent<
7464
offset: 0,
7565
items: [],
7666
rowHeight: DraggableItemList.DEFAULT_ROW_HEIGHT,
67+
isDoubleClickSelect: true,
7768
isDropDisabled: false,
7869
isDragDisabled: false,
7970
isMultiSelect: false,
8071
isStickyBottom: false,
8172
disableSelect: false,
8273
style: null,
74+
onFocusChange(): void {
75+
// no-op
76+
},
8377
onSelect(): void {
8478
// no-op
8579
},
@@ -165,16 +159,16 @@ class DraggableItemList<T> extends PureComponent<
165159

166160
itemList: React.RefObject<ItemList<T>>;
167161

162+
selectItem(itemIndex: number): void {
163+
this.itemList.current?.selectItem(itemIndex);
164+
}
165+
168166
focusItem(itemIndex: number): void {
169-
if (this.itemList.current) {
170-
this.itemList.current.focusItem(itemIndex);
171-
}
167+
this.itemList.current?.focusItem(itemIndex);
172168
}
173169

174170
scrollToItem(itemIndex: number): void {
175-
if (this.itemList.current) {
176-
this.itemList.current.scrollToItem(itemIndex);
177-
}
171+
this.itemList.current?.scrollToItem(itemIndex);
178172
}
179173

180174
getCachedDraggableItem = memoize(
@@ -183,7 +177,7 @@ class DraggableItemList<T> extends PureComponent<
183177
renderItem: DraggableRenderItemFn<T>,
184178
item: T,
185179
itemIndex: number,
186-
isKeyboardSelected: boolean,
180+
isFocused: boolean,
187181
isSelected: boolean,
188182
isDragDisabled: boolean,
189183
style: React.CSSProperties
@@ -211,7 +205,7 @@ class DraggableItemList<T> extends PureComponent<
211205
{renderItem({
212206
item,
213207
itemIndex,
214-
isKeyboardSelected,
208+
isFocused,
215209
isSelected,
216210
style,
217211
isClone: false,
@@ -238,7 +232,7 @@ class DraggableItemList<T> extends PureComponent<
238232
) => ({
239233
item,
240234
itemIndex,
241-
isKeyboardSelected,
235+
isFocused,
242236
isSelected,
243237
style,
244238
}: RenderItemProps<T>) =>
@@ -247,7 +241,7 @@ class DraggableItemList<T> extends PureComponent<
247241
renderItem,
248242
item,
249243
itemIndex,
250-
isKeyboardSelected,
244+
isFocused,
251245
isSelected,
252246
isDragDisabled,
253247
style
@@ -287,7 +281,7 @@ class DraggableItemList<T> extends PureComponent<
287281
{renderItem({
288282
item,
289283
itemIndex,
290-
isKeyboardSelected: false,
284+
isFocused: false,
291285
isSelected: true,
292286
style: {},
293287
isClone: true,
@@ -306,18 +300,20 @@ class DraggableItemList<T> extends PureComponent<
306300
draggablePrefix,
307301
draggingItemClassName,
308302
droppableId,
303+
isDoubleClickSelect,
309304
isDragDisabled,
310305
isDropDisabled,
311306
isMultiSelect,
312307
isStickyBottom,
313308
itemCount,
314309
items,
315-
onViewportChange,
316310
offset,
311+
onFocusChange,
312+
onSelect,
313+
onViewportChange,
317314
renderItem,
318315
rowHeight,
319316
selectedRanges,
320-
onSelect,
321317
style,
322318
} = this.props;
323319
return (
@@ -349,11 +345,13 @@ class DraggableItemList<T> extends PureComponent<
349345
>
350346
<ItemList
351347
focusSelector=".draggable-item-list-item"
348+
isDoubleClickSelect={isDoubleClickSelect}
352349
isDragSelect={false}
353350
isMultiSelect={isMultiSelect}
354351
isStickyBottom={isStickyBottom}
355352
itemCount={itemCount}
356353
items={items}
354+
onFocusChange={onFocusChange}
357355
onSelect={onSelect}
358356
onSelectionChange={this.handleSelectionChange}
359357
onViewportChange={onViewportChange}

0 commit comments

Comments
 (0)