Co-authored-by: Anton SHEPILOV <ashepilov@linagora.com> Co-authored-by: montaghanmy <monta.ghanmy@gmail.com>
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
@@ -36,7 +36,15 @@ 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,
|
||||
@@ -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 />
|
||||
</>
|
||||
@@ -197,8 +270,9 @@ export default memo(
|
||||
<PropertiesModal />
|
||||
<ConfirmDeleteModal />
|
||||
<ConfirmTrashModal />
|
||||
<ConfirmModal />
|
||||
<Suspense fallback={<></>}>
|
||||
<DrivePreview items={documents} />
|
||||
<DrivePreview items={documents}/>
|
||||
</Suspense>
|
||||
<div
|
||||
className={
|
||||
@@ -297,12 +371,16 @@ export default memo(
|
||||
</Menu>
|
||||
</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>
|
||||
|
||||
{folders.map((child, index) => (
|
||||
<Droppable id={index} key={index}>
|
||||
<FolderRow
|
||||
key={index}
|
||||
className={
|
||||
@@ -319,6 +397,7 @@ export default memo(
|
||||
}
|
||||
onBuildContextMenu={() => onBuildContextMenu(details, child)}
|
||||
/>
|
||||
</Droppable>
|
||||
))}
|
||||
<div className="my-6" />
|
||||
</>
|
||||
@@ -344,22 +423,24 @@ export default memo(
|
||||
)}
|
||||
|
||||
{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)}
|
||||
/>
|
||||
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>
|
||||
)}
|
||||
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -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 = ({
|
||||
|
||||
@@ -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';
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button } from '@atoms/button/button';
|
||||
import { Modal, ModalContent } from '@atoms/modal';
|
||||
import { useDriveItem } from '@features/drive/hooks/use-drive-item';
|
||||
import { DriveItem } from '@features/drive/types';
|
||||
import { atom, useRecoilState } from 'recoil';
|
||||
import Languages from '@features/global/services/languages-service';
|
||||
|
||||
export type ConfirmModalType = {
|
||||
open: boolean;
|
||||
parent_id: string;
|
||||
mode: 'move' | 'select-file' | 'select-files';
|
||||
title: string;
|
||||
onSelected: (ids: string[]) => Promise<void>;
|
||||
};
|
||||
|
||||
export const ConfirmModalAtom = atom<ConfirmModalType>({
|
||||
key: 'ConfirmModalAtom',
|
||||
default: {
|
||||
open: false,
|
||||
parent_id: '',
|
||||
mode: 'move',
|
||||
title: '',
|
||||
onSelected: async () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const ConfirmModalContent = () => {
|
||||
const [state, setState] = useRecoilState(ConfirmModalAtom);
|
||||
const [selected, setSelected] = useState<DriveItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const parentId = state.parent_id;
|
||||
|
||||
const { item: parent } = useDriveItem(parentId);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.mode === 'select-file' && parent) setSelected([]);
|
||||
if (state.mode === 'move' && parent) setSelected([parent]);
|
||||
}, [parentId, parent?.id]);
|
||||
|
||||
const handleClose = async () => {
|
||||
setLoading(true);
|
||||
await state.onSelected(selected.map((i) => i.id));
|
||||
setState({ ...state, open: false });
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalContent title={state.title}>
|
||||
<Button
|
||||
disabled={selected.length === 0}
|
||||
loading={loading}
|
||||
theme="primary"
|
||||
className="float-right"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<>{Languages.t('components.SelectorModalContent_move_to')} '{selected[0]?.name}'</>
|
||||
</Button>
|
||||
</ModalContent>
|
||||
);
|
||||
};
|
||||
|
||||
export const ConfirmModal = () => {
|
||||
const [state, setState] = useRecoilState(ConfirmModalAtom);
|
||||
|
||||
const handleClose = () => {
|
||||
setState({ ...state, open: false });
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal open={state.open} onClose={handleClose}>
|
||||
<ConfirmModalContent key={state.parent_id} />
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -2030,6 +2030,29 @@
|
||||
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
|
||||
integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
|
||||
|
||||
"@dnd-kit/accessibility@^3.0.0":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@dnd-kit/accessibility/-/accessibility-3.0.1.tgz#3ccbefdfca595b0a23a5dc57d3de96bc6935641c"
|
||||
integrity sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@dnd-kit/core@^6.0.8":
|
||||
version "6.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@dnd-kit/core/-/core-6.0.8.tgz#040ae13fea9787ee078e5f0361f3b49b07f3f005"
|
||||
integrity sha512-lYaoP8yHTQSLlZe6Rr9qogouGUz9oRUj4AHhDQGQzq/hqaJRpFo65X+JKsdHf8oUFBzx5A+SJPUvxAwTF2OabA==
|
||||
dependencies:
|
||||
"@dnd-kit/accessibility" "^3.0.0"
|
||||
"@dnd-kit/utilities" "^3.2.1"
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@dnd-kit/utilities@^3.2.1":
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@dnd-kit/utilities/-/utilities-3.2.1.tgz#53f9e2016fd2506ec49e404c289392cfff30332a"
|
||||
integrity sha512-OOXqISfvBw/1REtkSK2N3Fi2EQiLMlWUlqnOK/UpOISqBZPWpE6TqL+jcPtMOkE8TqYGiURvRdPSI9hltNUjEA==
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@emotion/hash@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
||||
|
||||
Generated
+102
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"name": "tdrive",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.0.8",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/accessibility": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.0.1.tgz",
|
||||
"integrity": "sha512-HXRrwS9YUYQO9lFRc/49uO/VICbM+O+ZRpFDe9Pd1rwVv2PCNkRiTZRdxrDgng/UkvdC3Re9r2vwPpXXrWeFzg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/core": {
|
||||
"version": "6.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.0.8.tgz",
|
||||
"integrity": "sha512-lYaoP8yHTQSLlZe6Rr9qogouGUz9oRUj4AHhDQGQzq/hqaJRpFo65X+JKsdHf8oUFBzx5A+SJPUvxAwTF2OabA==",
|
||||
"dependencies": {
|
||||
"@dnd-kit/accessibility": "^3.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.1",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dnd-kit/utilities": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.1.tgz",
|
||||
"integrity": "sha512-OOXqISfvBw/1REtkSK2N3Fi2EQiLMlWUlqnOK/UpOISqBZPWpE6TqL+jcPtMOkE8TqYGiURvRdPSI9hltNUjEA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
|
||||
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.23.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
|
||||
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
|
||||
"integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.0.8",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user