Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Dragndrop feature to move a file into a folder (#138) #148

Merged
merged 36 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3d52d34
add_and_test_of_dnd_kit
Greemty Jul 6, 2023
5a8fccf
make it more clear
Greemty Jul 6, 2023
cbfeec1
Add bassis, adaptation of draggables
Greemty Jul 7, 2023
08303b0
Event is able to link file wiwth folder
Greemty Jul 10, 2023
481a061
Moving file works
Greemty Jul 10, 2023
3a1d633
add a validation message for the action
Greemty Jul 10, 2023
6b16dfc
remover debug parts
Greemty Jul 10, 2023
f5334a4
add a validation message and remove node_modules
Greemty Jul 10, 2023
169bb14
Merge branch 'dragndrop-feature-to-move-files' of https://github.com/…
Greemty Jul 10, 2023
a2b365b
remove node_module
Greemty Jul 10, 2023
2d2ece4
disable dragndrop on mobile
Greemty Jul 12, 2023
06e2c6f
add_and_test_of_dnd_kit
Greemty Jul 6, 2023
4c7c80b
make it more clear
Greemty Jul 6, 2023
ca14f5c
Add bassis, adaptation of draggables
Greemty Jul 7, 2023
083d22e
Event is able to link file wiwth folder
Greemty Jul 10, 2023
3e00704
Moving file works
Greemty Jul 10, 2023
3c0d5b0
add a validation message and remove node_modules
Greemty Jul 10, 2023
e342e67
add a validation message for the action
Greemty Jul 10, 2023
b6f67b6
remover debug parts
Greemty Jul 10, 2023
3c029ac
remove node_module
Greemty Jul 10, 2023
3cd1248
disable dragndrop on mobile
Greemty Jul 12, 2023
3119120
Merge branch 'dragndrop-feature-to-move-files' of https://github.com/…
Greemty Jul 12, 2023
6582028
confirmation modal
Greemty Jul 12, 2023
27e7f57
add validation modal
Greemty Jul 13, 2023
e79e0f2
Merge branch 'main' into dragndrop-feature-to-move-files
Greemty Jul 25, 2023
a2f00c1
🚧code review
shepilov Jul 26, 2023
7e47563
add drag overlay
Greemty Jul 27, 2023
5b2ee86
correct rebase
Greemty Jul 27, 2023
4f4c0a3
Merge branch 'main' into dragndrop-feature-to-move-files
Greemty Jul 27, 2023
b1c63cd
Merge remote-tracking branch 'origin/main' into dragndrop-feature-to-…
Greemty Jul 28, 2023
cca9da8
fix hook problem
Greemty Jul 28, 2023
df9f9aa
add highlight for over folder
Greemty Jul 28, 2023
276a14b
code refactor
Greemty Jul 28, 2023
c2ab5da
ref: confirm modal
MontaGhanmy Sep 1, 2023
dc54a98
merge: resolve conflict
MontaGhanmy Sep 1, 2023
1e923ab
feat: merge conflict marker
MontaGhanmy Sep 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tdrive/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"@babel/preset-env": "^7.8.7",
"@babel/preset-es2017": "^7.0.0-beta.53",
"@craco/craco": "^6.4.3",
"@dnd-kit/core": "^6.0.8",
"@fortawesome/fontawesome-svg-core": "^6.1.2",
"@fortawesome/free-solid-svg-icons": "^6.1.2",
"@fortawesome/react-fontawesome": "^0.2.0",
Expand Down
1 change: 1 addition & 0 deletions tdrive/frontend/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@
"common.access-level_read":"Read",
"common.access-level_write":"Write",
"common.access-level_no_access":"No access",
"components.dragndrop_info_move_to":"move to",
"components.SelectorModalContent_move_to":"Move to",
"components.SelectorModalContent_no_items":"No item selected",
"components.SelectorModalContent_select":"Selected",
Expand Down
1 change: 1 addition & 0 deletions tdrive/frontend/public/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@
"common.access-level_read":"Lecture",
"common.access-level_write":"Ecriture",
"common.access-level_no_access":"Pas d'accès",
"components.dragndrop_info_move_to":"déplacé vers",
"components.SelectorModalContent_move_to":"Déplacer vers",
"components.SelectorModalContent_no_items":"Pas de fichier sélectionné",
"components.SelectorModalContent_select":"sélectionné(s)",
Expand Down
1 change: 1 addition & 0 deletions tdrive/frontend/public/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@
"common.access-level_read":"Read",
"common.access-level_write":"Write",
"common.access-level_no_access":"No access",
"components.dragndrop_info_move_to":"move to",
"components.SelectorModalContent_move_to":"Move to",
"components.SelectorModalContent_no_items":"No item selected",
"components.SelectorModalContent_select":"Selected",
Expand Down
22 changes: 22 additions & 0 deletions tdrive/frontend/src/app/features/dragndrop/hook/draggable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { MouseEventHandler } from 'react';
import {useDraggable} from '@dnd-kit/core';

type DraggableProps={
children: React.ReactNode
id: number
}

export function Draggable(props:DraggableProps) {
const {attributes, listeners, setNodeRef, transform} = useDraggable({
id: `draggable-${props.id+1}`,
data: {
child: props.children
},
});

return (
<div ref={setNodeRef} {...listeners} {...attributes}>
{props.children}
</div>
);
}
23 changes: 23 additions & 0 deletions tdrive/frontend/src/app/features/dragndrop/hook/droppable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import {useDroppable} from '@dnd-kit/core';

type DroppableProps={
children: React.ReactNode
id: any
}

export function Droppable(props:DroppableProps) {
const { isOver, setNodeRef } = useDroppable({
id: `droppable-${props.id}`,
data: {
child: props.children
},
});

return (
<div ref={setNodeRef} className={isOver ? "bg-sky-500/[.18] rounded-t-md rounded-b-md" : ""}>
{props.children}
</div>
);
}

209 changes: 145 additions & 64 deletions tdrive/frontend/src/app/views/client/body/drive/browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
useOnBuildPeopleContextMenu,
useOnBuildDateContextMenu,
} from './context-menu';
import { DocumentRow } from './documents/document-row';
import {DocumentRow, DocumentRowOverlay} from './documents/document-row';
import { FolderRow } from './documents/folder-row';
import HeaderPath from './header-path';
import { ConfirmDeleteModal } from './modals/confirm-delete';
Expand All @@ -36,11 +36,19 @@ import useRouteState from 'app/features/router/hooks/use-route-state';
import { SharedWithMeFilterState } from '@features/drive/state/shared-with-me-filter';
import MenusManager from '@components/menus/menus-manager.jsx';
import Languages from 'features/global/services/languages-service';
import {DndContext, useSensors, useSensor, PointerSensor, DragOverlay} from '@dnd-kit/core';
import { Droppable } from 'app/features/dragndrop/hook/droppable';
import { Draggable } from 'app/features/dragndrop/hook/draggable';
import { useDriveActions } from '@features/drive/hooks/use-drive-actions';
import { ConfirmModalAtom } from './modals/confirm-move/index';
import { useCurrentUser } from 'app/features/users/hooks/use-current-user';
import { ConfirmModal } from './modals/confirm-move';



export const DriveCurrentFolderAtom = atomFamily<
string,
{ context?: string; initialFolderId: string }
string,
{ context?: string; initialFolderId: string }
>({
key: 'DriveCurrentFolderAtom',
default: options => options.initialFolderId || 'root',
Expand Down Expand Up @@ -158,13 +166,78 @@ export default memo(
const buildFileTypeContextMenu = useOnBuildFileTypeContextMenu();
const buildPeopleContextMen = useOnBuildPeopleContextMenu();
const buildDateContextMenu = useOnBuildDateContextMenu();
const setConfirmModalState = useSetRecoilState(ConfirmModalAtom);
const [activeIndex, setActiveIndex] = useState(null);
const [activeChild, setActiveChild] = useState(null);
const {update} = useDriveActions();
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
distance: 8,
},
})
);
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);

function handleDragStart(event:any) {
setActiveIndex(event.active.id);
setActiveChild(event.active.data.current.child.props.item);
}
function handleDragEnd(event:any) {
setActiveIndex(null);
setActiveChild(null);
if (event.over){
setConfirmModalState({
open: true,
parent_id: inTrash ? 'root' : event.over.data.current.child.props.item.id,
mode: 'move',
title:
Languages.t('components.item_context_menu.move.modal_header') +
` '${event.active.data.current.child.props.item.name}'`,
onSelected: async ids => {
await update(
{
parent_id: ids[0],
},
event.active.data.current.child.props.item.id,
event.active.data.current.child.props.item.parent_id,
);
},
})
}

}

function draggableMarkup(index: number, child: any) {
const commonProps = {
key: index,
className:
(index === 0 ? 'rounded-t-md ' : '') +
(index === documents.length - 1 ? 'rounded-b-md ' : ''),
item: child,
checked: checked[child.id] || false,
onCheck: (v: boolean) =>
setChecked(_.pickBy({ ...checked, [child.id]: v }, _.identity)),
onBuildContextMenu: () => onBuildContextMenu(details, child),
};
return (
isMobile ? (
<DocumentRow {...commonProps} />
) : (
<Draggable id={index}>
<DocumentRow {...commonProps} />
</Draggable>
)
);
}


return (
<>
{viewId == 'shared-with-me' ? (
<>
<Suspense fallback={<></>}>
<DrivePreview items={documents} />
<DrivePreview items={documents}/>
</Suspense>
<SharedFilesTable />
</>
Expand Down Expand Up @@ -197,8 +270,9 @@ export default memo(
<PropertiesModal />
<ConfirmDeleteModal />
<ConfirmTrashModal />
<ConfirmModal />
<Suspense fallback={<></>}>
<DrivePreview items={documents} />
<DrivePreview items={documents}/>
</Suspense>
<div
className={
Expand Down Expand Up @@ -297,69 +371,76 @@ export default memo(
</Menu>
</div>

<div className="grow overflow-auto">
{folders.length > 0 && (
<>
<Title className="mb-2 block">{Languages.t('scenes.app.drive.folders')}</Title>

{folders.map((child, index) => (
<FolderRow
key={index}
className={
(index === 0 ? 'rounded-t-md ' : '') +
(index === folders.length - 1 ? 'rounded-b-md ' : '')
}
item={child}
onClick={() => {
return setParentId(child.id);
}}
checked={checked[child.id] || false}
onCheck={v =>
setChecked(_.pickBy({ ...checked, [child.id]: v }, _.identity))
}
onBuildContextMenu={() => onBuildContextMenu(details, child)}
/>
))}
<div className="my-6" />
</>
)}

<Title className="mb-2 block">{Languages.t('scenes.app.drive.documents')}</Title>

{documents.length === 0 && !loading && (
<div className="mt-4 text-center border-2 border-dashed rounded-md p-8">
<Subtitle className="block mb-2">
{Languages.t('scenes.app.drive.nothing')}
</Subtitle>
{!inTrash && access != 'read' && (
<>
<Base>{Languages.t('scenes.app.drive.drag_and_drop')}</Base>
<br />
<Button onClick={() => openItemModal()} theme="primary" className="mt-4">
{Languages.t('scenes.app.drive.add_doc')}
</Button>
</>
)}
</div>
)}
<DndContext sensors={sensors} onDragEnd={handleDragEnd} onDragStart={handleDragStart}>
<div className="grow overflow-auto">

{folders.length > 0 && (
<>
<Title className="mb-2 block">{Languages.t('scenes.app.drive.folders')}</Title>

{documents.map((child, index) => (
<DocumentRow
key={index}
className={
(index === 0 ? 'rounded-t-md ' : '') +
(index === documents.length - 1 ? 'rounded-b-md ' : '')
}
item={child}
checked={checked[child.id] || false}
onCheck={v => setChecked(_.pickBy({ ...checked, [child.id]: v }, _.identity))}
onBuildContextMenu={() => onBuildContextMenu(details, child)}
/>
))}
</div>
</div>
{folders.map((child, index) => (
<Droppable id={index} key={index}>
<FolderRow
key={index}
className={
(index === 0 ? 'rounded-t-md ' : '') +
(index === folders.length - 1 ? 'rounded-b-md ' : '')
}
item={child}
onClick={() => {
return setParentId(child.id);
}}
checked={checked[child.id] || false}
onCheck={v =>
setChecked(_.pickBy({ ...checked, [child.id]: v }, _.identity))
}
onBuildContextMenu={() => onBuildContextMenu(details, child)}
/>
</Droppable>
))}
<div className="my-6" />
</>
)}

<Title className="mb-2 block">{Languages.t('scenes.app.drive.documents')}</Title>

{documents.length === 0 && !loading && (
<div className="mt-4 text-center border-2 border-dashed rounded-md p-8">
<Subtitle className="block mb-2">
{Languages.t('scenes.app.drive.nothing')}
</Subtitle>
{!inTrash && access != 'read' && (
<>
<Base>{Languages.t('scenes.app.drive.drag_and_drop')}</Base>
<br />
<Button onClick={() => openItemModal()} theme="primary" className="mt-4">
{Languages.t('scenes.app.drive.add_doc')}
</Button>
</>
)}
</div>
)}

{documents.map((child, index) => (
draggableMarkup(index, child)
))}
<DragOverlay>
{activeIndex ? (
<DocumentRowOverlay className={
(activeIndex === 0 ? 'rounded-t-md ' : '') +
(activeIndex === documents.length - 1 ? 'rounded-b-md ' : '')}
item={activeChild}
></DocumentRowOverlay>
): null}
</DragOverlay>

</div>
</DndContext>
</div>
</UploadZone>
)}

</>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export type DriveItemProps = {
onBuildContextMenu: () => Promise<any[]>;
};

export type DriveItemOverlayProps = {
item: DriveItem|null;
className: string;
};
export const menuBuilder = async () => {};

export const CheckableIcon = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import fileUploadApiClient from '@features/files/api/file-upload-api-client';
import { useEffect, useState } from 'react';
import Avatar from '../../../../../atoms/avatar';
import { PublicIcon } from '../components/public-icon';
import { CheckableIcon, DriveItemProps } from './common';
import {CheckableIcon, DriveItemOverlayProps, DriveItemProps} from './common';
import { getDevice } from '../../../../../features/global/utils/device';
import './style.scss';
import { useHistory } from 'react-router-dom';
Expand Down Expand Up @@ -143,3 +143,21 @@ export const DocumentRow = ({
</div>
);
};

export const DocumentRowOverlay = ({
item,
className,
}: DriveItemOverlayProps) => {
return (
<div
className={
'flex flex-row items-center border border-zinc-200 dark:border-zinc-800 -mt-px px-4 py-3 cursor-pointer ' +
(className || '')
}
>
<div className="grow text-ellipsis whitespace-nowrap overflow-hidden">
<Base>{item?.name}</Base>
</div>
</div>
)}

Loading