Skip to content

Commit 4fd96c8

Browse files
committed
chore(editor): added draggable node store
1 parent 0400875 commit 4fd96c8

2 files changed

Lines changed: 54 additions & 60 deletions

File tree

apps/blog/src/app/(public)/editor/atoms/file-tree.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,29 @@ export const useFileTreeStore = create<FileTreeState>((set, get) => ({
3131
setLastClickedItem: (v) => set({ lastClickedItem: v }),
3232
setHighlightedFolderPath: (v) => set({ highlightedFolderPath: v }),
3333
}))
34+
35+
// --- DraggableNode --------------------------------------
36+
37+
interface DraggableNode {
38+
name: string
39+
isDirectory: boolean
40+
path: string
41+
}
42+
43+
interface DraggableStore {
44+
name: string
45+
path: string
46+
active: boolean
47+
isDirectory: boolean
48+
set: (item: DraggableNode) => void
49+
reset: () => void
50+
}
51+
52+
export const useDraggableNode = create<DraggableStore>((set) => ({
53+
name: '',
54+
path: '',
55+
active: false,
56+
isDirectory: false,
57+
set: (item) => set({ ...item, active: true }),
58+
reset: () => set({ name: '', path: '', isDirectory: false, active: false }),
59+
}))

apps/blog/src/app/(public)/editor/components/file-tree.tsx

Lines changed: 28 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import type React from 'react'
3333
import { useCallback, useState } from 'react'
3434

3535
import { useCurrentFile } from '../atoms/current-file'
36-
import { useFileTreeStore } from '../atoms/file-tree'
36+
import { useDraggableNode, useFileTreeStore } from '../atoms/file-tree'
3737
import type { FileNode } from '../services/types'
3838
import { getWebContainerInstance, readAllFiles } from '../services/webcontainer'
3939

@@ -56,12 +56,6 @@ interface FileTreeProps {
5656
) => Promise<void>
5757
}
5858

59-
interface DragState {
60-
path: string
61-
isDirectory: boolean
62-
name: string
63-
}
64-
6559
export function FileTree({
6660
nodes,
6761
onFileSelect,
@@ -79,6 +73,8 @@ export function FileTree({
7973
setLastClickedItem,
8074
setHighlightedFolderPath,
8175
} = useFileTreeStore()
76+
const draggable = useDraggableNode()
77+
8278
const [expandedFolders, setExpandedFolders] = useState<Set<string>>(
8379
new Set(nodes.filter((n) => n.type === 'directory').map((n) => n.path))
8480
)
@@ -109,16 +105,15 @@ export function FileTree({
109105
e.preventDefault()
110106
e.stopPropagation()
111107

112-
if (dragState && dragState.path.includes('/')) {
113-
const hasCollision = nodes.some((n) => n.name === dragState.name)
108+
if (draggable.active && draggable.path.includes('/')) {
109+
const hasCollision = nodes.some((n) => n.name === draggable.name)
114110
if (hasCollision) {
115111
e.dataTransfer.dropEffect = 'none'
116112
return
117113
}
118114
}
119115

120116
e.dataTransfer.dropEffect = 'move'
121-
setIsRootDragOver(true)
122117
setHighlightedFolderPath('')
123118
}
124119

@@ -133,28 +128,26 @@ export function FileTree({
133128
y <= rect.top ||
134129
y >= rect.bottom
135130
) {
136-
setIsRootDragOver(false)
137131
setHighlightedFolderPath(null)
138132
}
139133
}
140134

141135
const handleRootDrop = async (e: React.DragEvent) => {
142136
e.preventDefault()
143137
e.stopPropagation()
144-
setIsRootDragOver(false)
145138
setHighlightedFolderPath(null)
146139

147-
if (!onMove || !dragState) return
140+
if (!onMove || !draggable.active) return
148141

149-
const hasCollision = nodes.some((n) => n.name === dragState.name)
142+
const hasCollision = nodes.some((n) => n.name === draggable.name)
150143
if (hasCollision) {
151144
print.warn('Cannot move to root: item with same name already exists')
152145
return
153146
}
154147

155148
try {
156-
const sourcePath = dragState.path
157-
const isDirectory = dragState.isDirectory
149+
const sourcePath = draggable.path
150+
const isDirectory = draggable.isDirectory
158151

159152
if (sourcePath.includes('/')) {
160153
const fileName = sourcePath.split('/').pop()
@@ -207,9 +200,8 @@ export function FileTree({
207200
}
208201

209202
const handleRootDragEnd = () => {
210-
setIsRootDragOver(false)
211203
setHighlightedFolderPath(null)
212-
setDragState(null)
204+
draggable.reset()
213205
}
214206

215207
const handleStartCreateItem = (type: 'file' | 'folder') => {
@@ -316,7 +308,7 @@ export function FileTree({
316308
</div>
317309

318310
<div
319-
className={`transition-colors min-h-[200px] ${isRootDragOver ? 'bg-primary/10' : ''}`}
311+
className="transition-colors min-h-[200px]"
320312
onDragOver={handleRootDragOver}
321313
onDragLeave={handleRootDragLeave}
322314
onDrop={handleRootDrop}
@@ -366,8 +358,6 @@ export function FileTree({
366358
level={0}
367359
expandedFolders={expandedFolders}
368360
setExpandedFolders={setExpandedFolders}
369-
dragState={dragState}
370-
setDragState={setDragState}
371361
allNodes={nodes}
372362
onCreateItem={handleCreateItem}
373363
onCancelCreateItem={handleCancelCreateItem}
@@ -415,8 +405,6 @@ export function FileTree({
415405
level={0}
416406
expandedFolders={expandedFolders}
417407
setExpandedFolders={setExpandedFolders}
418-
dragState={dragState}
419-
setDragState={setDragState}
420408
allNodes={nodes}
421409
onCreateItem={handleCreateItem}
422410
onCancelCreateItem={handleCancelCreateItem}
@@ -428,12 +416,6 @@ export function FileTree({
428416
</>
429417
)
430418
})()}
431-
432-
{isRootDragOver && (
433-
<div className="p-4 text-center text-sm text-muted-foreground border-2 border-dashed border-primary rounded m-2">
434-
Drop here to move to root folder
435-
</div>
436-
)}
437419
</div>
438420
</div>
439421
)
@@ -457,8 +439,6 @@ interface FileTreeNodeProps {
457439
level: number
458440
expandedFolders: Set<string>
459441
setExpandedFolders: React.Dispatch<React.SetStateAction<Set<string>>>
460-
dragState: DragState | null
461-
setDragState: React.Dispatch<React.SetStateAction<DragState | null>>
462442
allNodes?: FileNode[]
463443
onCreateItem?: () => Promise<void>
464444
onCancelCreateItem?: () => void
@@ -477,8 +457,6 @@ function FileTreeNode({
477457
level,
478458
expandedFolders,
479459
setExpandedFolders,
480-
dragState,
481-
setDragState,
482460
allNodes,
483461
onCreateItem,
484462
onCancelCreateItem,
@@ -496,10 +474,10 @@ function FileTreeNode({
496474
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
497475
const [isRenaming, setIsRenaming] = useState(false)
498476
const [renameValue, setRenameValue] = useState(node.name)
499-
const [isDragOver, setIsDragOver] = useState(false)
500477
const isSelected = selectedFile === node.path
501478
const isExpanded = expandedFolders.has(node.path)
502479
const isLastClickedItem = lastClickedItemPath === node.path
480+
const draggable = useDraggableNode()
503481

504482
const isHighlighted =
505483
highlightedFolderPath !== null &&
@@ -621,18 +599,18 @@ function FileTreeNode({
621599
isDirectory: node.type === 'directory',
622600
})
623601
)
624-
setDragState({
625-
path: node.path,
626-
isDirectory: node.type === 'directory',
602+
draggable.set({
627603
name: node.name,
604+
isDirectory: node.type === 'directory',
605+
path: node.path,
628606
})
629607
}
630608

631609
const handleDragOver = (e: React.DragEvent) => {
632610
e.preventDefault()
633611
e.stopPropagation()
634612

635-
if (!dragState) return
613+
if (!draggable.active) return
636614

637615
let targetFolder: string
638616
if (node.type === 'directory') {
@@ -643,30 +621,28 @@ function FileTreeNode({
643621
pathParts.length > 1 ? pathParts.slice(0, -1).join('/') : ''
644622
}
645623

646-
const sourceParent = dragState.path.includes('/')
647-
? dragState.path.substring(0, dragState.path.lastIndexOf('/'))
624+
const sourceParent = draggable.path.includes('/')
625+
? draggable.path.substring(0, draggable.path.lastIndexOf('/'))
648626
: ''
649627

650628
const isSameParent = sourceParent === targetFolder
651629
const isMovingIntoSelf =
652-
dragState.path === targetFolder ||
653-
node.path.startsWith(dragState.path + '/')
630+
draggable.path === targetFolder ||
631+
node.path.startsWith(draggable.path + '/')
654632

655633
const targetChildren =
656634
node.type === 'directory'
657635
? node.children || []
658636
: getNodesInFolder(targetFolder)
659637
const hasCollision = targetChildren.some(
660-
(child) => child.name === dragState.name
638+
(child) => child.name === draggable.name
661639
)
662640

663641
if (isSameParent || isMovingIntoSelf || hasCollision) {
664642
e.dataTransfer.dropEffect = 'none'
665-
setIsDragOver(false)
666643
setHighlightedFolderPath(null)
667644
} else {
668645
e.dataTransfer.dropEffect = 'move'
669-
setIsDragOver(true)
670646
setHighlightedFolderPath(targetFolder)
671647
}
672648
}
@@ -682,29 +658,26 @@ function FileTreeNode({
682658
y <= rect.top ||
683659
y >= rect.bottom
684660
) {
685-
setIsDragOver(false)
686661
setHighlightedFolderPath(null)
687662
}
688663
}
689664

690665
const handleDragEnd = () => {
691-
setIsDragOver(false)
692666
setHighlightedFolderPath(null)
693-
setDragState(null)
667+
draggable.reset()
694668
}
695669

696670
const handleDrop = async (e: React.DragEvent) => {
697671
e.preventDefault()
698672
e.stopPropagation()
699-
setIsDragOver(false)
700673
setHighlightedFolderPath(null)
701674

702-
if (!onMove || !dragState) return
675+
if (!onMove || !draggable.active) return
703676

704677
try {
705-
const sourcePath = dragState.path
706-
const isDirectory = dragState.isDirectory
707-
const sourceName = dragState.name
678+
const sourcePath = draggable.path
679+
const isDirectory = draggable.isDirectory
680+
const sourceName = draggable.name
708681

709682
let targetFolder: string
710683

@@ -773,11 +746,10 @@ function FileTreeNode({
773746
<div>
774747
<div
775748
className={cn(
776-
'flex items-center gap-1 px-2 py-1 cursor-pointer hover:bg-accent transition-colors group',
749+
'flex items-center gap-1 px-2 py-1 cursor-pointer hover:bg-accent transition-colors group border-l-2 border-transparent',
777750
isSelected && 'bg-accent text-accent-foreground',
778751
isLastClickedItem && 'bg-accent text-accent-foreground',
779-
isDragOver && 'border-l-2 border-primary',
780-
isHighlighted && 'bg-primary/10'
752+
isHighlighted && 'border-primary bg-primary/10'
781753
)}
782754
style={{ paddingLeft: `${level * 12 + 8}px` }}
783755
onClick={handleClick}
@@ -900,8 +872,6 @@ function FileTreeNode({
900872
level={level + 1}
901873
expandedFolders={expandedFolders}
902874
setExpandedFolders={setExpandedFolders}
903-
dragState={dragState}
904-
setDragState={setDragState}
905875
allNodes={allNodes}
906876
onCreateItem={onCreateItem}
907877
onCancelCreateItem={onCancelCreateItem}
@@ -949,8 +919,6 @@ function FileTreeNode({
949919
level={level + 1}
950920
expandedFolders={expandedFolders}
951921
setExpandedFolders={setExpandedFolders}
952-
dragState={dragState}
953-
setDragState={setDragState}
954922
allNodes={allNodes}
955923
onCreateItem={onCreateItem}
956924
onCancelCreateItem={onCancelCreateItem}

0 commit comments

Comments
 (0)