diff --git a/package-lock.json b/package-lock.json index 0661b049..68a9121a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2694,6 +2694,42 @@ "@date-io/core": "^2.10.6" } }, + "@dnd-kit/accessibility": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-1.0.2.tgz", + "integrity": "sha512-aQq+B4But+369cYYtmXPUhqf26Fy9b/nR+uAP4cbhf3TXMlCqorT7bO7dT7cNe+me2DOf61GVr0mkrWtfsvR3A==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@dnd-kit/core": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-1.0.2.tgz", + "integrity": "sha512-etf7LmAye+Y3TDiHAivGPr4TXyDVtyV7jMxPPAVErdPoxKUcVjibnRhDeJlA7cRhioQE+rKmvs/EZ0XijuqrbQ==", + "requires": { + "@dnd-kit/accessibility": "^1.0.2", + "@dnd-kit/utilities": "^1.0.2", + "tslib": "^2.0.0" + } + }, + "@dnd-kit/sortable": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-1.0.2.tgz", + "integrity": "sha512-lma9+LBATJ5A7hdnDqtD4Nam1dEX4fQYxi8W+C6qNk3nhG1AT1fjZ+GwokvDCEbGCD6GNDHlWZybJdc7dqb7mQ==", + "requires": { + "@dnd-kit/core": "^1.0.2", + "@dnd-kit/utilities": "^1.0.2", + "tslib": "^2.0.0" + } + }, + "@dnd-kit/utilities": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-1.0.2.tgz", + "integrity": "sha512-RC3c9OVfr9Nx5acNwy278RCI0UOr8e08XQmws1w0lx+i+YIu74lQqat0MXR3zyQHimrduXRG/ctciuwj1JOgkQ==", + "requires": { + "tslib": "^2.0.0" + } + }, "@dsherret/to-absolute-glob": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@dsherret/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", @@ -4951,11 +4987,6 @@ "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=", "dev": true }, - "array-move": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/array-move/-/array-move-3.0.1.tgz", - "integrity": "sha512-H3Of6NIn2nNU1gsVDqDnYKY/LCdWvCMMOWifNGhKcVQgiZ6nOek39aESOvro6zmueP07exSl93YLvkN4fZOkSg==" - }, "array-reduce": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", @@ -12433,15 +12464,6 @@ "p-is-promise": "^1.1.0" } }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, "inversify": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/inversify/-/inversify-5.0.5.tgz", @@ -16148,17 +16170,6 @@ "react-is": "^16.13.1" } }, - "react-sortable-hoc": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-1.11.0.tgz", - "integrity": "sha512-v1CDCvdfoR3zLGNp6qsBa4J1BWMEVH25+UKxF/RvQRh+mrB+emqtVHMgZ+WreUiKJoEaiwYoScaueIKhMVBHUg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.2.0", - "invariant": "^2.2.4", - "prop-types": "^15.5.7" - } - }, "react-transition-group": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", @@ -18751,8 +18762,7 @@ "tslib": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", - "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==", - "dev": true + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" }, "tsutils": { "version": "3.21.0", diff --git a/package.json b/package.json index 56203c99..b1ac2895 100755 --- a/package.json +++ b/package.json @@ -68,10 +68,12 @@ } }, "dependencies": { + "@dnd-kit/core": "^1.0.2", + "@dnd-kit/sortable": "^1.0.2", + "@dnd-kit/utilities": "^1.0.2", "@material-ui/styled-engine-sc": "^5.0.0-alpha.25", "@rematch/core": "^2.0.0", "@tiddlygit/tiddlywiki": "5.1.24-prerelease.20210103", - "array-move": "^3.0.1", "beautiful-react-hooks": "^0.31.0", "bluebird": "^3.7.2", "chokidar": "^3.5.1", @@ -187,7 +189,6 @@ "react-dom": "17.0.1", "react-i18next": "^11.8.6", "react-redux": "7.2.2", - "react-sortable-hoc": "1.11.0", "redux": "4.0.5", "rimraf": "^3.0.2", "simplebar": "6.0.0-beta.4", diff --git a/src/pages/Main/SortableWorkspaceSelector.tsx b/src/pages/Main/SortableWorkspaceSelector.tsx index bf5fd6ae..8166187a 100644 --- a/src/pages/Main/SortableWorkspaceSelector.tsx +++ b/src/pages/Main/SortableWorkspaceSelector.tsx @@ -1,72 +1,76 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import { SortableContainer as sortableContainer, SortableElement as sortableElement } from 'react-sortable-hoc'; +import { useSortable } from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; import { WindowNames } from '@services/windows/WindowProperties'; import WorkspaceSelector from './WorkspaceSelector'; import { IWorkspace } from '@services/workspaces/interface'; import defaultIcon from '../../images/default-icon.png'; -export const SortableContainer = sortableContainer(({ children }: { children: React.ReactNode }) =>
{children}
); - export interface ISortableItemProps { index: number; workspace: IWorkspace; } -function SortableWorkspaceSelector({ index, workspace }: ISortableItemProps) { +export function SortableWorkspaceSelector({ index, workspace }: ISortableItemProps) { const { t } = useTranslation(); const { active, id, name, picturePath, hibernated, transparentBackground, isSubWiki, tagName } = workspace; + const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }); + const style = { + transform: CSS.Transform.toString(transform), + transition, + }; return ( - { - if (isSubWiki) { - await window.service.wiki.requestOpenTiddlerInWiki(tagName); - } else { - const activeWorkspace = await window.service.workspace.getActiveWorkspace(); - if (activeWorkspace?.id === id) { - await window.service.wiki.requestWikiSendActionMessage('tm-home'); +
+ { + if (isSubWiki) { + await window.service.wiki.requestOpenTiddlerInWiki(tagName); } else { - await window.service.workspaceView.setActiveWorkspaceView(id); + const activeWorkspace = await window.service.workspace.getActiveWorkspace(); + if (activeWorkspace?.id === id) { + await window.service.wiki.requestWikiSendActionMessage('tm-home'); + } else { + await window.service.workspaceView.setActiveWorkspaceView(id); + } } - } - }} - onContextMenu={() => { - const template = [ - { - label: t('WorkspaceSelector.EditWorkspace'), - click: async () => await window.service.window.open(WindowNames.editWorkspace, { workspaceID: id }), - }, - { - label: t('WorkspaceSelector.RemoveWorkspace'), - click: async () => await window.service.workspaceView.removeWorkspaceView(id), - }, - ]; - - if (!active && !isSubWiki) { - template.splice(1, 0, { - label: hibernated ? 'Wake Up Workspace' : 'Hibernate Workspace', - click: async () => { - if (hibernated) { - return await window.service.workspaceView.wakeUpWorkspaceView(id); - } - return await window.service.workspaceView.hibernateWorkspaceView(id); + }} + onContextMenu={() => { + const template = [ + { + label: t('WorkspaceSelector.EditWorkspace'), + click: async () => await window.service.window.open(WindowNames.editWorkspace, { workspaceID: id }), }, - }); - } + { + label: t('WorkspaceSelector.RemoveWorkspace'), + click: async () => await window.service.workspaceView.removeWorkspaceView(id), + }, + ]; - void window.service.menu.buildContextMenuAndPopup(template); - }} - /> + if (!active && !isSubWiki) { + template.splice(1, 0, { + label: hibernated ? 'Wake Up Workspace' : 'Hibernate Workspace', + click: async () => { + if (hibernated) { + return await window.service.workspaceView.wakeUpWorkspaceView(id); + } + return await window.service.workspaceView.hibernateWorkspaceView(id); + }, + }); + } + + void window.service.menu.buildContextMenuAndPopup(template); + }} + /> +
); } - -export default sortableElement(SortableWorkspaceSelector); diff --git a/src/pages/Main/WorkspaceSelector.tsx b/src/pages/Main/WorkspaceSelector.tsx index 60c94818..38abd491 100644 --- a/src/pages/Main/WorkspaceSelector.tsx +++ b/src/pages/Main/WorkspaceSelector.tsx @@ -1,11 +1,13 @@ +import Promise from 'bluebird'; import React, { useState, useEffect } from 'react'; -import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import BadgeRaw from '@material-ui/core/Badge'; import styled, { css } from 'styled-components'; import defaultIcon from '../../images/default-icon.png'; +Promise.config({ cancellation: true }); + // TODO: &:hover { background: theme.palette.action.hover; const Root = styled.div<{ hibernated?: boolean; active?: boolean }>` height: fit-content; @@ -125,10 +127,14 @@ export default function WorkspaceSelector({ const { t } = useTranslation(); const [shortWorkspaceName, shortWorkspaceNameSetter] = useState(t('Loading')); useEffect(() => { - void (async () => { - const baseName = await window.service.context.getBaseName(workspaceName); + const setBaseNamePromise = new Promise((resolve) => { + window.service.context.getBaseName(workspaceName).then((baseName) => resolve(baseName)); + }); + + setBaseNamePromise.then((baseName) => { shortWorkspaceNameSetter(baseName !== undefined ? baseName : t('WorkspaceSelector.BadWorkspacePath')); - })(); + }); + return () => setBaseNamePromise.cancel(); }, [workspaceName]); return ( diff --git a/src/pages/Main/index.tsx b/src/pages/Main/index.tsx index 699f207f..b63cb144 100644 --- a/src/pages/Main/index.tsx +++ b/src/pages/Main/index.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import arrayMove from 'array-move'; import styled, { css } from 'styled-components'; import { AsyncReturnType } from 'type-fest'; +import { DndContext } from '@dnd-kit/core'; +import { SortableContext, arrayMove, verticalListSortingStrategy } from '@dnd-kit/sortable'; import SimpleBar from 'simplebar-react'; import 'simplebar/dist/simplebar.min.css'; @@ -24,7 +25,7 @@ import DraggableRegion from './DraggableRegion'; import arrowWhite from '@/images/arrow-white.png'; import arrowBlack from '@/images/arrow-black.png'; -import SortableWorkspaceSelector, { SortableContainer } from './SortableWorkspaceSelector'; +import { SortableWorkspaceSelector } from './SortableWorkspaceSelector'; import { IWorkspace } from '@services/workspaces/interface'; import { IPreferences } from '@services/preferences/interface'; @@ -180,6 +181,7 @@ export default function Main(): JSX.Element { }, {} as AsyncReturnType); const requestReload = async (): Promise => await window.service.window.reload(window.meta.windowName); + const workspaceIDs = workspacesList.map((workspace) => workspace.id); return ( {workspacesList.length > 0 && } @@ -187,10 +189,11 @@ export default function Main(): JSX.Element { {sidebar === true && ( - { - if (oldIndex === newIndex) return; + { + if (over === null || active.id === over.id) return; + const oldIndex = workspaceIDs.indexOf(active.id); + const newIndex = workspaceIDs.indexOf(over.id); const newWorkspacesList = arrayMove(workspacesList, oldIndex, newIndex); const newWorkspaces: Record = {}; @@ -201,10 +204,12 @@ export default function Main(): JSX.Element { await window.service.workspace.setWorkspaces(newWorkspaces); }}> - {workspacesList.map((workspace, index) => ( - - ))} - + + {workspacesList.map((workspace, index) => ( + + ))} + + await window.service.window.open(WindowNames.addWorkspace)} />