diff --git a/ui2/src/components/Commander/Commander.tsx b/ui2/src/components/Commander/Commander.tsx index f6545b6ab..cc2f9524e 100644 --- a/ui2/src/components/Commander/Commander.tsx +++ b/ui2/src/components/Commander/Commander.tsx @@ -3,7 +3,7 @@ import {Group} from "@mantine/core" import {useSelector, useDispatch} from "react-redux" import {useNavigate} from "react-router-dom" -import FolderNodeActions from "@/components/FolderNodeActions" +import FolderNodeActions from "@/components/Commander/FolderNodeActions" import Node from "@/components/Node" import { selectPanelNodes, @@ -111,7 +111,7 @@ export default function Commander({mode}: Args) { } const nodes = data.map((n: NodeType) => ( - + )) if (nodes.length > 0) { diff --git a/ui2/src/components/Commander/DeleteButton.tsx b/ui2/src/components/Commander/DeleteButton.tsx new file mode 100644 index 000000000..a8e9a7214 --- /dev/null +++ b/ui2/src/components/Commander/DeleteButton.tsx @@ -0,0 +1,12 @@ +import {Button} from "@mantine/core" +import {IconTrash} from "@tabler/icons-react" + +export default function DeleteButton() { + const onClick = () => {} + + return ( + + ) +} diff --git a/ui2/src/components/Commander/FolderNodeActions/FolderNodeActions.tsx b/ui2/src/components/Commander/FolderNodeActions/FolderNodeActions.tsx new file mode 100644 index 000000000..ee2dbabd7 --- /dev/null +++ b/ui2/src/components/Commander/FolderNodeActions/FolderNodeActions.tsx @@ -0,0 +1,33 @@ +import {Group} from "@mantine/core" +import {useSelector} from "react-redux" +import {selectSelectedNodeIds} from "@/slices/dualPanel" + +import type {RootState} from "@/app/types" +import type {PanelMode} from "@/types" +import ToggleSecondaryPanel from "@/components/DualPanel/ToggleSecondaryPanel" +import DeleteButton from "@/components/Commander/DeleteButton" +import NewFolderButton from "@/components/Commander/NewFolderButton" +import UploadButton from "@/components/Commander/UploadButton" + +type Args = { + mode: PanelMode +} + +export default function FolderNodeActions({mode}: Args) { + const selectedIds = useSelector((state: RootState) => + selectSelectedNodeIds(state, mode) + ) as Array + + return ( + + + + + {selectedIds.length > 0 && } + + + + + + ) +} diff --git a/ui2/src/components/FolderNodeActions/index.tsx b/ui2/src/components/Commander/FolderNodeActions/index.tsx similarity index 100% rename from ui2/src/components/FolderNodeActions/index.tsx rename to ui2/src/components/Commander/FolderNodeActions/index.tsx diff --git a/ui2/src/components/Commander/NewFolderButton.tsx b/ui2/src/components/Commander/NewFolderButton.tsx new file mode 100644 index 000000000..e0f1b6c94 --- /dev/null +++ b/ui2/src/components/Commander/NewFolderButton.tsx @@ -0,0 +1,35 @@ +import {ActionIcon} from "@mantine/core" +import {IconFolderPlus} from "@tabler/icons-react" +import {useSelector, useDispatch} from "react-redux" +import {selectCurrentFolderID, folderAdded} from "@/slices/dualPanel" +import create_new_folder from "@/components/modals/NewFolder" + +import type {RootState} from "@/app/types" +import type {NodeType, PanelMode} from "@/types" + +type Args = { + mode: PanelMode +} + +export default function NewFolderButton({mode}: Args) { + const dispatch = useDispatch() + const currentFolderId = useSelector((state: RootState) => + selectCurrentFolderID(state, mode) + ) + + const onNewFolder = () => { + if (!currentFolderId) { + console.error("Error: no current folder found") + return + } + create_new_folder(currentFolderId).then((new_folder: NodeType) => { + dispatch(folderAdded({node: new_folder, mode: mode})) + }) + } + + return ( + + + + ) +} diff --git a/ui2/src/components/Commander/UploadButton.tsx b/ui2/src/components/Commander/UploadButton.tsx new file mode 100644 index 000000000..1fb9bc345 --- /dev/null +++ b/ui2/src/components/Commander/UploadButton.tsx @@ -0,0 +1,16 @@ +import {ActionIcon, FileButton} from "@mantine/core" +import {IconUpload} from "@tabler/icons-react" + +export default function UploadButton() { + const onUpload = () => {} + + return ( + + {props => ( + + + + )} + + ) +} diff --git a/ui2/src/components/FolderNodeActions/FolderNodeActions.tsx b/ui2/src/components/FolderNodeActions/FolderNodeActions.tsx deleted file mode 100644 index 75837ffa9..000000000 --- a/ui2/src/components/FolderNodeActions/FolderNodeActions.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import {Group, ActionIcon, FileButton} from "@mantine/core" -import {IconFolderPlus, IconUpload} from "@tabler/icons-react" -import {useSelector, useDispatch} from "react-redux" -import {selectCurrentFolderID, folderAdded} from "@/slices/dualPanel" -import create_new_folder from "@/components/modals/NewFolder" - -import type {RootState} from "@/app/types" -import type {NodeType, PanelMode} from "@/types" -import ToggleSecondaryPanel from "../DualPanel/ToggleSecondaryPanel" - -type Args = { - mode: PanelMode -} - -export default function FolderNodeActions({mode}: Args) { - const dispatch = useDispatch() - const currentFolderId = useSelector((state: RootState) => - selectCurrentFolderID(state, mode) - ) - - const onNewFolder = () => { - if (!currentFolderId) { - console.error("Error: no current folder found") - return - } - create_new_folder(currentFolderId).then((new_folder: NodeType) => { - dispatch(folderAdded({node: new_folder, mode: mode})) - }) - } - - const onUpload = () => {} - - return ( - - - - {props => ( - - - - )} - - - - - - - - - - - ) -} diff --git a/ui2/src/components/Node/Document/Document.tsx b/ui2/src/components/Node/Document/Document.tsx index 6b53b1347..5f49a8717 100644 --- a/ui2/src/components/Node/Document/Document.tsx +++ b/ui2/src/components/Node/Document/Document.tsx @@ -1,17 +1,39 @@ +import {useDispatch, useSelector} from "react-redux" import {Checkbox} from "@mantine/core" -import type {NodeType} from "@/types" +import { + selectSelectedNodeIds, + selectionAddNode, + selectionRemoveNode +} from "@/slices/dualPanel" + +import type {NodeType, PanelMode} from "@/types" import classes from "./Document.module.css" +import {RootState} from "@/app/types" type Args = { node: NodeType + mode: PanelMode onClick: (node: NodeType) => void } -export default function Document({node, onClick}: Args) { +export default function Document({node, mode, onClick}: Args) { + const selectedIds = useSelector((state: RootState) => + selectSelectedNodeIds(state, mode) + ) as Array + const dispatch = useDispatch() + + const onCheck = (e: React.ChangeEvent) => { + if (e.currentTarget.checked) { + dispatch(selectionAddNode({selectionId: node.id, mode})) + } else { + dispatch(selectionRemoveNode({selectionId: node.id, mode})) + } + } + return (
- + onClick(node)}>
{node.title}
diff --git a/ui2/src/components/Node/Folder/Folder.tsx b/ui2/src/components/Node/Folder/Folder.tsx index 89d7b63fa..e431b36b7 100644 --- a/ui2/src/components/Node/Folder/Folder.tsx +++ b/ui2/src/components/Node/Folder/Folder.tsx @@ -1,16 +1,39 @@ +import {useDispatch, useSelector} from "react-redux" import {Checkbox} from "@mantine/core" -import type {NodeType} from "@/types" + +import { + selectSelectedNodeIds, + selectionAddNode, + selectionRemoveNode +} from "@/slices/dualPanel" + +import type {NodeType, PanelMode} from "@/types" import classes from "./Folder.module.css" +import {RootState} from "@/app/types" type Args = { node: NodeType + mode: PanelMode onClick: (node: NodeType) => void } -export default function Folder({node, onClick}: Args) { +export default function Folder({node, mode, onClick}: Args) { + const selectedIds = useSelector((state: RootState) => + selectSelectedNodeIds(state, mode) + ) as Array + const dispatch = useDispatch() + + const onCheck = (e: React.ChangeEvent) => { + if (e.currentTarget.checked) { + dispatch(selectionAddNode({selectionId: node.id, mode})) + } else { + dispatch(selectionRemoveNode({selectionId: node.id, mode})) + } + } + return (
- + onClick(node)}>
{node.title}
diff --git a/ui2/src/components/Node/Node.tsx b/ui2/src/components/Node/Node.tsx index 75a205f49..5fb5de817 100644 --- a/ui2/src/components/Node/Node.tsx +++ b/ui2/src/components/Node/Node.tsx @@ -1,16 +1,17 @@ -import type {NodeType} from "@/types" +import type {NodeType, PanelMode} from "@/types" import Document from "./Document/Document" import Folder from "./Folder/Folder" type Args = { node: NodeType + mode: PanelMode onClick: (node: NodeType) => void } -export default function Node({node, onClick}: Args) { +export default function Node({node, mode, onClick}: Args) { if (node.ctype == "folder") { - return + return } - return + return } diff --git a/ui2/src/slices/dualPanel.ts b/ui2/src/slices/dualPanel.ts index 8760c2e13..ceabfadc1 100644 --- a/ui2/src/slices/dualPanel.ts +++ b/ui2/src/slices/dualPanel.ts @@ -38,11 +38,22 @@ type FolderAddedArgs = { mode: PanelMode } +type SelectionNodePayload = { + selectionId: string + mode: PanelMode +} + +type SelectionNodesPayload = { + selectionIds: Array + mode: PanelMode +} + interface Commander { currentNode: CurrentNodeType | null pagination: PaginationType | null | undefined lastPageSize: number nodes: SliceState> + selectedIds: Array } interface Viewer {} @@ -67,7 +78,8 @@ function commanderInitialState(node: CurrentNodeType | null): Commander { status: "idle", error: null, data: null - } + }, + selectedIds: [] } } @@ -174,6 +186,68 @@ const dualPanelSlice = createSlice({ }, closeSecondaryPanel(state) { state.secondaryPanel = null + }, + selectionAddNode: (state, action: PayloadAction) => { + if (action.payload.mode == "main") { + if (state.mainPanel.commander) { + state.mainPanel.commander.selectedIds.push(action.payload.selectionId) + } + } else { + if (state.secondaryPanel?.commander) { + state.secondaryPanel.commander.selectedIds.push( + action.payload.selectionId + ) + } + } + }, + selectionAddNodes: ( + state, + action: PayloadAction + ) => { + if (action.payload.mode == "main") { + // main panel / commander + if (state.mainPanel.commander) { + state.mainPanel.commander.selectedIds = action.payload.selectionIds + } + } else { + // secondary panel / commander + if (state.secondaryPanel?.commander) { + state.secondaryPanel.commander.selectedIds = + action.payload.selectionIds + } + } + }, + selectionRemoveNode: ( + state, + action: PayloadAction + ) => { + if (action.payload.mode == "main") { + if (state.mainPanel.commander) { + const newSelectedIds = state.mainPanel.commander.selectedIds.filter( + i => i != action.payload.selectionId + ) + state.mainPanel.commander.selectedIds = newSelectedIds + } + } else { + if (state.secondaryPanel?.commander) { + const newSelectedIds = + state.secondaryPanel.commander.selectedIds.filter( + i => i != action.payload.selectionId + ) + state.secondaryPanel.commander.selectedIds = newSelectedIds + } + } + }, + clearNodesSelection: (state, action: PayloadAction) => { + if (action.payload == "main") { + if (state.mainPanel.commander) { + state.mainPanel.commander.selectedIds = [] + } + } else { + if (state.secondaryPanel?.commander) { + state.secondaryPanel.commander.selectedIds = [] + } + } } }, extraReducers(builder) { @@ -249,7 +323,11 @@ export const { setCurrentNode, folderAdded, openSecondaryPanel, - closeSecondaryPanel + closeSecondaryPanel, + selectionAddNode, + selectionAddNodes, + selectionRemoveNode, + clearNodesSelection } = dualPanelSlice.actions export default dualPanelSlice.reducer @@ -299,15 +377,25 @@ export const selectPanelNodes = ( const selectMainPanelNodes = ( state: RootState ): SliceState> => { - const nodeIds = state.dualPanel.mainPanel.commander!.nodes.data!.map( - (n: NodeWithSpinner) => n.id - ) - const output = { - status: "succeeded", - error: null, - data: state.dualPanel.nodes.filter((n: NodeType) => nodeIds.includes(n.id)) - } as SliceState> - return output + if (state.dualPanel.mainPanel!.commander!.nodes.data) { + const nodeIds = state.dualPanel.mainPanel.commander!.nodes.data!.map( + (n: NodeWithSpinner) => n.id + ) + const output = { + status: "succeeded", + error: null, + data: state.dualPanel.nodes.filter((n: NodeType) => + nodeIds.includes(n.id) + ) + } as SliceState> + return output + } + + return { + status: state.dualPanel.mainPanel!.commander!.nodes.status, + error: state.dualPanel.mainPanel!.commander!.nodes.error, + data: null + } } const selectSecondaryPanelNodes = ( @@ -415,3 +503,11 @@ export const selectCommanderPageNumber = ( return state.dualPanel.secondaryPanel?.commander?.pagination?.pageNumber } + +export const selectSelectedNodeIds = (state: RootState, mode: PanelMode) => { + if (mode == "main") { + return state.dualPanel.mainPanel.commander?.selectedIds + } + + return state.dualPanel.secondaryPanel?.commander?.selectedIds +}