mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 01:18:05 +08:00
feat: multi select for file tree & clipboard support
This commit is contained in:
@ -0,0 +1,38 @@
|
||||
import type { StateCreator } from 'zustand'
|
||||
import type { ClipboardSliceShape, SkillEditorSliceShape } from './types'
|
||||
|
||||
export type { ClipboardSliceShape } from './types'
|
||||
|
||||
export const createClipboardSlice: StateCreator<
|
||||
SkillEditorSliceShape,
|
||||
[],
|
||||
[],
|
||||
ClipboardSliceShape
|
||||
> = (set, get) => ({
|
||||
clipboard: null,
|
||||
|
||||
copyNodes: (nodeIds) => {
|
||||
if (nodeIds.length === 0)
|
||||
return
|
||||
set({ clipboard: { operation: 'copy', nodeIds: new Set(nodeIds) } })
|
||||
},
|
||||
|
||||
cutNodes: (nodeIds) => {
|
||||
if (nodeIds.length === 0)
|
||||
return
|
||||
set({ clipboard: { operation: 'cut', nodeIds: new Set(nodeIds) } })
|
||||
},
|
||||
|
||||
clearClipboard: () => {
|
||||
set({ clipboard: null })
|
||||
},
|
||||
|
||||
isCutNode: (nodeId) => {
|
||||
const { clipboard } = get()
|
||||
return clipboard?.operation === 'cut' && clipboard.nodeIds.has(nodeId)
|
||||
},
|
||||
|
||||
hasClipboard: () => {
|
||||
return get().clipboard !== null
|
||||
},
|
||||
})
|
||||
@ -17,6 +17,7 @@ export const createFileTreeSlice: StateCreator<
|
||||
> = (set, get) => ({
|
||||
expandedFolderIds: new Set<string>(),
|
||||
selectedTreeNodeId: null,
|
||||
selectedNodeIds: new Set<string>(),
|
||||
pendingCreateNode: null,
|
||||
|
||||
setExpandedFolderIds: (ids: Set<string>) => {
|
||||
@ -61,6 +62,21 @@ export const createFileTreeSlice: StateCreator<
|
||||
set({ selectedTreeNodeId: nodeId })
|
||||
},
|
||||
|
||||
setSelectedNodeIds: (nodeIds) => {
|
||||
const lastId = nodeIds.length > 0 ? nodeIds[nodeIds.length - 1] : null
|
||||
set({
|
||||
selectedNodeIds: new Set(nodeIds),
|
||||
selectedTreeNodeId: lastId,
|
||||
})
|
||||
},
|
||||
|
||||
clearSelection: () => {
|
||||
set({
|
||||
selectedNodeIds: new Set<string>(),
|
||||
selectedTreeNodeId: null,
|
||||
})
|
||||
},
|
||||
|
||||
startCreateNode: (nodeType, parentId) => {
|
||||
set({
|
||||
pendingCreateNode: {
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import type { StateCreator } from 'zustand'
|
||||
import type { SkillEditorSliceShape } from './types'
|
||||
import { createClipboardSlice } from './clipboard-slice'
|
||||
import { createDirtySlice } from './dirty-slice'
|
||||
import { createFileOperationsMenuSlice } from './file-operations-menu-slice'
|
||||
import { createFileTreeSlice } from './file-tree-slice'
|
||||
import { createMetadataSlice } from './metadata-slice'
|
||||
import { createTabSlice } from './tab-slice'
|
||||
|
||||
export type { ClipboardSliceShape } from './clipboard-slice'
|
||||
export type { DirtySliceShape } from './dirty-slice'
|
||||
export type { FileOperationsMenuSliceShape } from './file-operations-menu-slice'
|
||||
export type { FileTreeSliceShape } from './file-tree-slice'
|
||||
@ -16,6 +18,7 @@ export type { SkillEditorSliceShape } from './types'
|
||||
export const createSkillEditorSlice: StateCreator<SkillEditorSliceShape> = (...args) => ({
|
||||
...createTabSlice(...args),
|
||||
...createFileTreeSlice(...args),
|
||||
...createClipboardSlice(...args),
|
||||
...createDirtySlice(...args),
|
||||
...createMetadataSlice(...args),
|
||||
...createFileOperationsMenuSlice(...args),
|
||||
@ -28,7 +31,9 @@ export const createSkillEditorSlice: StateCreator<SkillEditorSliceShape> = (...a
|
||||
previewTabId: null,
|
||||
expandedFolderIds: new Set<string>(),
|
||||
selectedTreeNodeId: null,
|
||||
selectedNodeIds: new Set<string>(),
|
||||
pendingCreateNode: null,
|
||||
clipboard: null,
|
||||
dirtyContents: new Map<string, string>(),
|
||||
fileMetadata: new Map<string, Record<string, unknown>>(),
|
||||
dirtyMetadataIds: new Set<string>(),
|
||||
|
||||
@ -32,6 +32,9 @@ export type FileTreeSliceShape = {
|
||||
getOpensObject: () => OpensObject
|
||||
selectedTreeNodeId: string | null
|
||||
setSelectedTreeNodeId: (nodeId: string | null) => void
|
||||
selectedNodeIds: Set<string>
|
||||
setSelectedNodeIds: (nodeIds: string[]) => void
|
||||
clearSelection: () => void
|
||||
pendingCreateNode: PendingCreateNode | null
|
||||
startCreateNode: (nodeType: PendingCreateNode['nodeType'], parentId: PendingCreateNode['parentId']) => void
|
||||
clearCreateNode: () => void
|
||||
@ -41,6 +44,22 @@ export type FileTreeSliceShape = {
|
||||
setFileTreeSearchTerm: (term: string) => void
|
||||
}
|
||||
|
||||
export type ClipboardOperation = 'copy' | 'cut'
|
||||
|
||||
export type ClipboardItem = {
|
||||
operation: ClipboardOperation
|
||||
nodeIds: Set<string>
|
||||
}
|
||||
|
||||
export type ClipboardSliceShape = {
|
||||
clipboard: ClipboardItem | null
|
||||
copyNodes: (nodeIds: string[]) => void
|
||||
cutNodes: (nodeIds: string[]) => void
|
||||
clearClipboard: () => void
|
||||
isCutNode: (nodeId: string) => boolean
|
||||
hasClipboard: () => boolean
|
||||
}
|
||||
|
||||
export type DirtySliceShape = {
|
||||
dirtyContents: Map<string, string>
|
||||
setDraftContent: (fileId: string, content: string) => void
|
||||
@ -76,6 +95,7 @@ export type FileOperationsMenuSliceShape = {
|
||||
export type SkillEditorSliceShape
|
||||
= TabSliceShape
|
||||
& FileTreeSliceShape
|
||||
& ClipboardSliceShape
|
||||
& DirtySliceShape
|
||||
& MetadataSliceShape
|
||||
& FileOperationsMenuSliceShape
|
||||
|
||||
Reference in New Issue
Block a user