Skip to content

Commit 0ea9010

Browse files
authored
Fix: File disappear after dragging + add long press to enable dragging (#1159)
1 parent a5cd41c commit 0ea9010

10 files changed

Lines changed: 83 additions & 38 deletions

File tree

web/client/src/library/components/fileExplorer/Directory.tsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { type ModelArtifact } from '@models/artifact'
1212
import { getEmptyImage } from 'react-dnd-html5-backend'
1313
import { useFileExplorer } from './context'
1414
import FileExplorer from './FileExplorer'
15-
15+
import { useLongPress } from '@uidotdev/usehooks'
1616
const Directory = function Directory({
1717
directory,
1818
className,
@@ -27,6 +27,17 @@ const Directory = function Directory({
2727
const [newName, setNewName] = useState<string>()
2828
const [isOpen, setIsOpen] = useState<boolean>(directory.isOpened)
2929
const [isOpenContextMenu, setIsOpenContextMenu] = useState(false)
30+
const [isDraggable, setIsDraggable] = useState(false)
31+
32+
const attrs = useLongPress(() => setIsDraggable(true), {
33+
threshold: 500,
34+
onFinish() {
35+
setIsDraggable(false)
36+
},
37+
onCancel() {
38+
setIsDraggable(false)
39+
},
40+
})
3041

3142
const {
3243
activeRange,
@@ -60,13 +71,15 @@ const Directory = function Directory({
6071
return (
6172
monitor.isOver({ shallow: true }) &&
6273
isArrayNotEmpty(artifacts) &&
63-
artifacts.every(item => {
74+
artifacts.reduce((acc, item) => {
75+
if (isFalse(acc)) return false
76+
6477
if (item.parent === directory || item === directory) return false
6578
if (item instanceof ModelDirectory && item.hasDirectory(directory))
6679
return false
6780

6881
return true
69-
})
82+
}, true)
7083
)
7184
},
7285
collect(monitor) {
@@ -82,16 +95,19 @@ const Directory = function Directory({
8295
() => ({
8396
type: 'artifact',
8497
item: directory,
98+
end() {
99+
setIsDraggable(false)
100+
},
85101
canDrag() {
86-
return isStringEmptyOrNil(newName)
102+
return isStringEmptyOrNil(newName) && isDraggable
87103
},
88104
collect(monitor) {
89105
return {
90106
isDragging: monitor.isDragging(),
91107
}
92108
},
93109
}),
94-
[directory, newName],
110+
[directory, newName, isDraggable],
95111
)
96112

97113
const [isAllDirectories, shouldClose, shouldOpen, shouldToggle] =
@@ -183,11 +199,15 @@ const Directory = function Directory({
183199
className={clsx(isOver && isFalse(isDragging) && 'bg-brand-5')}
184200
>
185201
{directory.withParent && (
186-
<div ref={directory.withParent ? drag : undefined}>
202+
<div
203+
{...attrs}
204+
ref={directory.withParent ? drag : undefined}
205+
>
187206
<FileExplorer.Container
188207
artifact={directory}
189208
className={clsx(
190209
isFalse(isStringEmptyOrNil(newName)) && 'bg-primary-800',
210+
isDraggable && 'bg-primary-10 !cursor-grabbing',
191211
isOpenContextMenu && 'bg-primary-10',
192212
className,
193213
)}

web/client/src/library/components/fileExplorer/DragLayer.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,14 @@ export default function DragLayer(): JSX.Element {
3636
return (
3737
<>
3838
{isDragging && (
39-
<div style={layerStyles}>
40-
<div style={getItemStyles(currentOffset)}>
39+
<div
40+
style={layerStyles}
41+
className="!cursor-grabbing"
42+
>
43+
<div
44+
style={getItemStyles(currentOffset)}
45+
className="!cursor-grabbing"
46+
>
4147
{artifacts.map(artifact => (
4248
<span key={artifact.id}>
4349
{artifact instanceof ModelDirectory && (

web/client/src/library/components/fileExplorer/File.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { useFileExplorer } from './context'
99
import { type ModelFile } from '@models/file'
1010
import { useDrag } from 'react-dnd'
1111
import { getEmptyImage } from 'react-dnd-html5-backend'
12+
import { useLongPress } from '@uidotdev/usehooks'
1213

1314
function File({
1415
file,
@@ -22,6 +23,18 @@ function File({
2223
const selectedFile = useStoreProject(s => s.selectedFile)
2324
const setSelectedFile = useStoreProject(s => s.setSelectedFile)
2425

26+
const [isDraggable, setIsDraggable] = useState(false)
27+
28+
const attrs = useLongPress(() => setIsDraggable(true), {
29+
threshold: 500,
30+
onFinish() {
31+
setIsDraggable(false)
32+
},
33+
onCancel() {
34+
setIsDraggable(false)
35+
},
36+
})
37+
2538
const {
2639
activeRange,
2740
setActiveRange,
@@ -36,16 +49,19 @@ function File({
3649
() => ({
3750
type: 'artifact',
3851
item: file,
52+
end() {
53+
setIsDraggable(false)
54+
},
3955
canDrag() {
40-
return isStringEmptyOrNil(newName)
56+
return isStringEmptyOrNil(newName) && isDraggable
4157
},
4258
collect(monitor) {
4359
return {
4460
isDragging: monitor.isDragging(),
4561
}
4662
},
4763
}),
48-
[file, newName],
64+
[file, newName, isDraggable],
4965
)
5066

5167
useEffect(() => {
@@ -84,13 +100,18 @@ function File({
84100
const disabled = activeRange.size > 1 && activeRange.has(file)
85101

86102
return (
87-
<div ref={drag}>
103+
<div
104+
{...attrs}
105+
ref={drag}
106+
>
88107
<FileExplorer.Container
89108
artifact={file}
90109
isSelected={selectedFile === file}
91110
className={clsx(
92111
isFalse(isStringEmptyOrNil(newName)) && 'bg-primary-800',
93112
isOpenContextMenu && 'bg-primary-10',
113+
isDraggable &&
114+
'bg-primary-10 !cursor-grabbing outline-2 !outline-primary-500',
94115
isDragging && 'opacity-50',
95116
className,
96117
)}

web/client/src/library/components/fileExplorer/FileExplorer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { CheckCircleIcon, XCircleIcon } from '@heroicons/react/24/solid'
1414
import SearchList from '@components/search/SearchList'
1515
import { type ModelFile } from '@models/file'
1616
import { EnumSize } from '~/types/enum'
17-
1817
/* TODO:
1918
- add drag and drop files/directories from desktop
2019
- add copy and paste
@@ -75,6 +74,7 @@ const FileExplorer = function FileExplorer({
7574
onSelect={setSelectedFile}
7675
/>
7776
<FileExplorer.ContextMenu
77+
key={project.id}
7878
trigger={
7979
<FileExplorer.ContextMenuTrigger className="h-full pb-2">
8080
<DndProvider backend={HTML5Backend}>
@@ -285,7 +285,7 @@ function FileExplorerArtifactContainer({
285285
return (
286286
<span
287287
className={clsx(
288-
'w-full flex items-center overflow-hidden whitespace-nowrap group/file rounded-md px-2',
288+
'w-full flex items-center group/file rounded-md px-2',
289289
className,
290290
activeRange.has(artifact)
291291
? 'text-brand-100 !bg-brand-500 dark:bg-brand-700 dark:text-brand-100'

web/client/src/library/components/fileExplorer/context.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ import {
99
import { useStoreProject } from '@context/project'
1010
import { ModelArtifact } from '@models/artifact'
1111
import { ModelDirectory } from '@models/directory'
12-
import { getAllFilesInDirectory, toUniqueName } from './help'
12+
import { getAllFilesInDirectory } from './help'
1313
import {
1414
isArrayNotEmpty,
1515
isFalse,
1616
isNotNil,
1717
isStringEmptyOrNil,
18+
toUniqueName,
1819
} from '@utils/index'
1920
import { ModelFile } from '@models/file'
2021
import { useStoreEditor } from '@context/editor'

web/client/src/library/components/fileExplorer/help.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,3 @@ export function getAllFilesInDirectory(directory: ModelDirectory): ModelFile[] {
66

77
return [...files, ...directories.map(getAllFilesInDirectory).flat()]
88
}
9-
10-
export function toUniqueName(prefix?: string, suffix?: string): string {
11-
// Should be enough for now
12-
const hex = (Date.now() % 100000).toString(16)
13-
14-
return `${prefix == null ? '' : `${prefix}_`}${hex}${
15-
suffix ?? ''
16-
}`.toLowerCase()
17-
}

web/client/src/models/artifact.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import { isStringEmptyOrNil } from '@utils/index'
1+
import { isStringEmptyOrNil, toUniqueName } from '@utils/index'
22
import { type ModelDirectory } from './directory'
33
import { ModelInitial } from './initial'
4-
import { toUniqueName } from '@components/fileExplorer/help'
54

65
export interface InitialArtifact {
76
name: string

web/client/src/models/directory.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isFalse, isNil } from '@utils/index'
1+
import { isFalse } from '@utils/index'
22
import type { Directory, File } from '../api/client'
33
import { type InitialArtifact, ModelArtifact } from './artifact'
44
import { ModelFile } from './file'
@@ -30,21 +30,20 @@ export class ModelDirectory extends ModelArtifact<InitialDirectory> {
3030
parent,
3131
)
3232

33-
if (parent != null) {
34-
this.level = parent.level + 1
35-
}
36-
3733
if ((initial as ModelDirectory)?.isModel) {
3834
this.directories = (initial as ModelDirectory).directories
3935
this.files = (initial as ModelDirectory).files
4036
} else {
41-
this.directories = this.initial.directories?.map(
42-
d => new ModelDirectory(d, this),
43-
)
44-
this.files = this.initial.files?.map(f => new ModelFile(f, this))
37+
this.directories = []
38+
this.files = []
39+
40+
this.initial.directories.forEach(d => {
41+
this.addDirectory(new ModelDirectory(d))
42+
})
43+
this.initial.files.forEach(f => {
44+
this.addFile(new ModelFile(f))
45+
})
4546
}
46-
47-
this._isOpen = isNil(parent)
4847
}
4948

5049
get isChanged(): boolean {

web/client/src/models/file.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { type File, FileType } from '../api/client'
22
import { type ModelDirectory } from './directory'
33
import { type InitialArtifact, ModelArtifact } from './artifact'
4-
import { isStringEmptyOrNil } from '@utils/index'
5-
import { toUniqueName } from '@components/fileExplorer/help'
4+
import { isStringEmptyOrNil, toUniqueName } from '@utils/index'
65

76
export const EnumFileExtensions = {
87
SQL: '.sql',

web/client/src/utils/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,12 @@ export function uid(): string {
217217

218218
return time + random
219219
}
220+
221+
export function toUniqueName(prefix?: string, suffix?: string): string {
222+
// Should be enough for now
223+
const hex = (Date.now() % 100000).toString(16)
224+
225+
return `${prefix == null ? '' : `${prefix}_`}${hex}${
226+
suffix ?? ''
227+
}`.toLowerCase()
228+
}

0 commit comments

Comments
 (0)