From c34a13b3e4beb9bc113b43c264e2815d09756f2b Mon Sep 17 00:00:00 2001 From: Danielv123 Date: Sat, 4 May 2024 20:31:36 +0200 Subject: [PATCH] Use global drag and drop hint overlay --- packages/web_ui/src/components/Dropzone.tsx | 42 ++++++ packages/web_ui/src/components/ModsPage.tsx | 67 +-------- packages/web_ui/src/components/SavesList.tsx | 57 +------- packages/web_ui/src/components/SiteLayout.tsx | 127 ++++++++++-------- .../web_ui/src/components/UploadButton.tsx | 20 +++ packages/web_ui/src/model/is_dragging.ts | 3 + 6 files changed, 149 insertions(+), 167 deletions(-) create mode 100644 packages/web_ui/src/components/Dropzone.tsx create mode 100644 packages/web_ui/src/components/UploadButton.tsx create mode 100644 packages/web_ui/src/model/is_dragging.ts diff --git a/packages/web_ui/src/components/Dropzone.tsx b/packages/web_ui/src/components/Dropzone.tsx new file mode 100644 index 00000000..0af48ff4 --- /dev/null +++ b/packages/web_ui/src/components/Dropzone.tsx @@ -0,0 +1,42 @@ +import { InboxOutlined } from "@ant-design/icons"; +import { DraggingContext } from "../model/is_dragging"; +import { useContext } from "react"; + +// This component is used to display a dropzone when a file is being dragged over the parent component +export function Dropzone() { + const isDroppingFile = useContext(DraggingContext); + + return
+
+ +

+ Drop to upload +

+
+
; +} diff --git a/packages/web_ui/src/components/ModsPage.tsx b/packages/web_ui/src/components/ModsPage.tsx index 7b2e85ed..2637d1cc 100644 --- a/packages/web_ui/src/components/ModsPage.tsx +++ b/packages/web_ui/src/components/ModsPage.tsx @@ -3,7 +3,6 @@ import { useNavigate } from "react-router-dom"; import { Button, Form, Input, Modal, Popconfirm, Space, Table, Typography, Upload } from "antd"; import ImportOutlined from "@ant-design/icons/ImportOutlined"; import PlusOutlined from "@ant-design/icons/PlusOutlined"; -import UploadOutlined from "@ant-design/icons/UploadOutlined"; import * as lib from "@clusterio/lib"; @@ -17,7 +16,8 @@ import PageLayout from "./PageLayout"; import PluginExtra from "./PluginExtra"; import SectionHeader from "./SectionHeader"; import ModDetails from "./ModDetails"; -import { InboxOutlined } from "@ant-design/icons"; +import { Dropzone } from "./Dropzone"; +import UploadButton from "./UploadButton"; const strcmp = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }).compare; @@ -134,7 +134,6 @@ export default function ModsPage() { let navigate = useNavigate(); let [mods] = useMods(); let [modPacks] = useModPacks(); - const [isDroppingFile, setIsDroppingFile] = useState(false); function actions(mod: lib.ModInfo) { return @@ -176,27 +175,13 @@ export default function ModsPage() { name="file" accept=".zip" multiple + showUploadList={false} headers={{ "X-Access-Token": control.connector.token || "", }} action={`${webRoot}api/upload-mod`} > - + ; } @@ -256,50 +241,8 @@ export default function ModsPage() { position: "relative", zIndex: "1010", }} - onDragEnter={e => { - setIsDroppingFile(true); - }} - onDragLeave={e => { - if ((e.currentTarget as Node).contains(e.relatedTarget as Node)) { - return; - } - setIsDroppingFile(false); - }} - onDrop={e => setIsDroppingFile(false)} > -
-
- -

- Drop to upload -

-
-
+ ([]); - const [isDroppingFile, setIsDroppingFile] = useState(false); let hostOffline = ["unassigned", "unknown"].includes(props.instance.status!); const saveTable =
{account.hasPermission("core.instance.save.upload") && - + + Upload save + } {account.hasPermission("core.instance.save.create") && } } /> @@ -360,55 +362,8 @@ export default function SavesList(props: { instance: lib.InstanceDetails }) { position: "relative", zIndex: "1010", }} - onDragEnter={e => { - setIsDroppingFile(true); - }} - onDragLeave={e => { - if ((e.currentTarget as Node).contains(e.relatedTarget as Node)) { - return; - } - setIsDroppingFile(false); - }} - onDrop={e => setIsDroppingFile(false)} > -
-
- -

- Drop to upload -

-
-
+ {saveTable} diff --git a/packages/web_ui/src/components/SiteLayout.tsx b/packages/web_ui/src/components/SiteLayout.tsx index 07a995d1..31b0374f 100644 --- a/packages/web_ui/src/components/SiteLayout.tsx +++ b/packages/web_ui/src/components/SiteLayout.tsx @@ -9,16 +9,18 @@ import ErrorBoundary from "./ErrorBoundary"; import ErrorPage from "./ErrorPage"; import ControlContext from "./ControlContext"; import { pages } from "../pages"; +import { DraggingContext } from "../model/is_dragging"; const { Header, Sider } = Layout; export default function SiteLayout() { let navigate = useNavigate(); - let [currentSidebarPath, setCurrentSidebarPath] = useState(null); + let [currentSidebarPath, setCurrentSidebarPath] = useState(null); let account = useAccount(); let plugins = useContext(ControlContext).plugins; + const [dragging, setDragging] = useState(0); - function SetSidebar(props: { path: string|null }) { + function SetSidebar(props: { path: string | null }) { useEffect(() => { setCurrentSidebarPath(props.path); }); @@ -26,7 +28,7 @@ export default function SiteLayout() { } let accountMenuProps: MenuProps = { - onClick: ({ key }: {key:string}) => { + onClick: ({ key }: { key: string }) => { if (key === "user") { navigate(`/users/${account.name}/view`); } else if (key === "logOut") { @@ -72,59 +74,76 @@ export default function SiteLayout() { menuItems.push({ type: "group", label: name, children: group, key: name }); } - return -
-
- Clusterio - { webUiPackage.version } - - - - -
- - + // Prevent issues with event ordering + let dragChange = 0; + function setDraggingProxy(value: number) { + dragChange += value; + setDragging(dragging + dragChange); + } + + return setDraggingProxy(1)} + onDragLeave={() => setDraggingProxy(-1)} + // Reset dragging state when dropped + onDrop={() => setDragging(0)} + // Allow entire document to be a drag target + onDragOver={(e) => e.preventDefault()} + > + 0}> +
+
+ Clusterio + {webUiPackage.version} navigate(key)} - items={menuItems} + theme="dark" + mode="horizontal" + defaultSelectedKeys={["1"]} + items={[{ label: "Dashboard", key: "1" }]} /> - - - - {combinedPages.map(({path, sidebarPath, content}) => - - - {content} - - } - />)} - } /> - + + + +
+ + + navigate(key)} + items={menuItems} + /> + + + + {combinedPages.map(({ path, sidebarPath, content }) => + + + {content} + + } + />)} + } /> + + - + ; } diff --git a/packages/web_ui/src/components/UploadButton.tsx b/packages/web_ui/src/components/UploadButton.tsx new file mode 100644 index 00000000..c6c324a9 --- /dev/null +++ b/packages/web_ui/src/components/UploadButton.tsx @@ -0,0 +1,20 @@ +import { useContext } from "react"; +import { Button } from "antd"; +import { InboxOutlined, UploadOutlined } from "@ant-design/icons"; + +import { DraggingContext } from "../model/is_dragging"; + +export default function UploadButton(props: React.ComponentProps) { + const isDroppingFile = useContext(DraggingContext); + return ; +} diff --git a/packages/web_ui/src/model/is_dragging.ts b/packages/web_ui/src/model/is_dragging.ts new file mode 100644 index 00000000..652878e2 --- /dev/null +++ b/packages/web_ui/src/model/is_dragging.ts @@ -0,0 +1,3 @@ +import { createContext } from "react"; + +export const DraggingContext = createContext(false);