diff --git a/next.config.js b/next.config.js
index a5d9163bd..31fc7b641 100644
--- a/next.config.js
+++ b/next.config.js
@@ -5,7 +5,7 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
});
module.exports = withBundleAnalyzer({
- reactStrictMode: true,
+ reactStrictMode: false,
eslint: {
ignoreDuringBuilds: true,
},
diff --git a/package.json b/package.json
index 6bf7cda6d..59d5c5b6d 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,8 @@
"ci": "yarn test && yarn lint --fix && yarn typecheck && yarn prettier:write"
},
"dependencies": {
+ "@dnd-kit/core": "^6.0.1",
+ "@dnd-kit/sortable": "^7.0.0",
"@mantine/core": "^4.2.6",
"@mantine/dates": "^4.2.6",
"@mantine/dropzone": "^4.2.6",
diff --git a/src/components/AppShelf/AddAppShelfItem.tsx b/src/components/AppShelf/AddAppShelfItem.tsx
index 17f7d8598..c061930e9 100644
--- a/src/components/AppShelf/AddAppShelfItem.tsx
+++ b/src/components/AppShelf/AddAppShelfItem.tsx
@@ -6,22 +6,17 @@ import {
Image,
Button,
Select,
- AspectRatio,
- Text,
- Card,
LoadingOverlay,
ActionIcon,
Tooltip,
Title,
} from '@mantine/core';
import { useForm } from '@mantine/form';
-import { motion } from 'framer-motion';
import { useState } from 'react';
import { Apps } from 'tabler-icons-react';
import { v4 as uuidv4 } from 'uuid';
import { useConfig } from '../../tools/state';
import { ServiceTypeList } from '../../tools/types';
-import { AppShelfItemWrapper } from './AppShelfItemWrapper';
export function AddItemShelfButton(props: any) {
const [opened, setOpened] = useState(false);
@@ -52,56 +47,6 @@ export function AddItemShelfButton(props: any) {
);
}
-export default function AddItemShelfItem(props: any) {
- const [opened, setOpened] = useState(false);
- return (
- <>
- setOpened(false)}
- title="Add a service"
- >
-
-
-
-
-
-
- Add a service
-
-
-
-
-
-
- setOpened(true)} size={60} />
-
-
-
-
- >
- );
-}
-
function MatchIcon(name: string, form: any) {
fetch(
`https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/${name
diff --git a/src/components/AppShelf/AppShelf.story.tsx b/src/components/AppShelf/AppShelf.story.tsx
index 928510aac..c73a42f19 100644
--- a/src/components/AppShelf/AppShelf.story.tsx
+++ b/src/components/AppShelf/AppShelf.story.tsx
@@ -1,5 +1,6 @@
import { SimpleGrid } from '@mantine/core';
-import AppShelf, { AppShelfItem } from './AppShelf';
+import AppShelf from './AppShelf';
+import { AppShelfItem } from './AppShelfItem';
export default {
title: 'Item Shelf',
diff --git a/src/components/AppShelf/AppShelf.tsx b/src/components/AppShelf/AppShelf.tsx
index 9d689e157..2d4bf5768 100644
--- a/src/components/AppShelf/AppShelf.tsx
+++ b/src/components/AppShelf/AppShelf.tsx
@@ -1,112 +1,79 @@
import React, { useState } from 'react';
-import { motion } from 'framer-motion';
-import { Text, AspectRatio, Card, Image, Center, Grid, createStyles, Anchor } from '@mantine/core';
+import { Grid } from '@mantine/core';
+import {
+ closestCenter,
+ DndContext,
+ DragOverlay,
+ MouseSensor,
+ useSensor,
+ useSensors,
+} from '@dnd-kit/core';
+import { arrayMove, SortableContext } from '@dnd-kit/sortable';
import { useConfig } from '../../tools/state';
-import { serviceItem } from '../../tools/types';
-import AppShelfMenu from './AppShelfMenu';
-import PingComponent from '../modules/ping/PingModule';
-const useStyles = createStyles((theme) => ({
- item: {
- transition: 'box-shadow 150ms ease, transform 100ms ease',
-
- '&:hover': {
- boxShadow: `${theme.shadows.md} !important`,
- transform: 'scale(1.05)',
- },
- },
-}));
+import { SortableAppShelfItem, AppShelfItem } from './AppShelfItem';
const AppShelf = (props: any) => {
- const { config } = useConfig();
+ const [activeId, setActiveId] = useState(null);
+ const { config, setConfig } = useConfig();
+ const sensors = useSensors(
+ useSensor(MouseSensor, {
+ // Require the mouse to move by 10 pixels before activating
+ activationConstraint: {
+ delay: 250,
+ tolerance: 5,
+ },
+ })
+ );
+
+ function handleDragStart(event: any) {
+ const { active } = event;
+
+ setActiveId(active.id);
+ }
+
+ function handleDragEnd(event: any) {
+ const { active, over } = event;
+
+ if (active.id !== over.id) {
+ const newConfig = { ...config };
+ const activeIndex = newConfig.services.findIndex((e) => e.id === active.id);
+ const overIndex = newConfig.services.findIndex((e) => e.id === over.id);
+ newConfig.services = arrayMove(newConfig.services, activeIndex, overIndex);
+ setConfig(newConfig);
+ }
+
+ setActiveId(null);
+ }
+
return (
-
- {config.services.map((service) => (
-
-
-
- ))}
-
+
+
+
+ {config.services.map((service) => (
+
+
+
+ ))}
+
+
+
+ {activeId ? (
+ e.id === activeId)} id={activeId} />
+ ) : null}
+
+
);
};
-export function AppShelfItem(props: any) {
- const { service }: { service: serviceItem } = props;
- const [hovering, setHovering] = useState(false);
- const { classes, theme } = useStyles();
- return (
- {
- setHovering(true);
- }}
- onHoverEnd={() => {
- setHovering(false);
- }}
- >
-
-
-
-
- {service.name}
-
-
-
-
-
-
-
-
-
-
- {
- window.open(service.url);
- }}
- />
-
-
-
-
-
-
-
- );
-}
-
export default AppShelf;
diff --git a/src/components/AppShelf/AppShelfItem.tsx b/src/components/AppShelf/AppShelfItem.tsx
new file mode 100644
index 000000000..585f14bd1
--- /dev/null
+++ b/src/components/AppShelf/AppShelfItem.tsx
@@ -0,0 +1,123 @@
+import {
+ Text,
+ Card,
+ Anchor,
+ AspectRatio,
+ Image,
+ Center,
+ createStyles,
+} from '@mantine/core';
+import { motion } from 'framer-motion';
+import { useState } from 'react';
+import { useSortable } from '@dnd-kit/sortable';
+import { CSS } from '@dnd-kit/utilities';
+import { serviceItem } from '../../tools/types';
+import PingComponent from '../modules/ping/PingModule';
+import AppShelfMenu from './AppShelfMenu';
+
+const useStyles = createStyles((theme) => ({
+ item: {
+ transition: 'box-shadow 150ms ease, transform 100ms ease',
+
+ '&:hover': {
+ boxShadow: `${theme.shadows.md} !important`,
+ transform: 'scale(1.05)',
+ },
+ },
+}));
+
+export function SortableAppShelfItem(props: any) {
+ const { attributes, listeners, setNodeRef, transform, transition } = useSortable({
+ id: props.id,
+ });
+
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition,
+ };
+
+ return (
+
+ );
+}
+
+export function AppShelfItem(props: any) {
+ const { service }: { service: serviceItem } = props;
+ const [hovering, setHovering] = useState(false);
+ const { classes, theme } = useStyles();
+ return (
+ {
+ setHovering(true);
+ }}
+ onHoverEnd={() => {
+ setHovering(false);
+ }}
+ >
+
+
+
+
+ {service.name}
+
+
+
+
+
+
+
+
+
+
+ {
+ window.open(service.url);
+ }}
+ />
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/AppShelf/AppShelfItemWrapper.tsx b/src/components/AppShelf/AppShelfItemWrapper.tsx
deleted file mode 100644
index ffe409062..000000000
--- a/src/components/AppShelf/AppShelfItemWrapper.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { useMantineTheme, Card } from '@mantine/core';
-
-export function AppShelfItemWrapper(props: any) {
- const { children, hovering } = props;
- const theme = useMantineTheme();
- return (
-
- {children}
-
- );
-}
diff --git a/src/components/AppShelf/index.ts b/src/components/AppShelf/index.ts
new file mode 100644
index 000000000..fd496bd5b
--- /dev/null
+++ b/src/components/AppShelf/index.ts
@@ -0,0 +1,2 @@
+export { default as AppShelf } from './AppShelf';
+export * from './AppShelfItem';
diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx
index c32cc432b..abe7984ca 100644
--- a/src/pages/_document.tsx
+++ b/src/pages/_document.tsx
@@ -6,7 +6,6 @@ const stylesServer = createStylesServer();
export default class _Document extends Document {
static async getInitialProps(ctx: DocumentContext) {
const initialProps = await Document.getInitialProps(ctx);
-
// Add your app specific logic here
return {
diff --git a/yarn.lock b/yarn.lock
index 11c770987..589bcb2c0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1143,6 +1143,37 @@
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.0"
+ resolved "https://registry.yarnpkg.com/@dnd-kit/accessibility/-/accessibility-3.0.0.tgz#b56e3750414fd907b7d6972b3116aa8f96d07fde"
+ integrity sha512-QwaQ1IJHQHMMuAGOOYHQSx7h7vMZPfO97aDts8t5N/MY7n2QTDSnW+kF7uRQ1tVBkr6vJ+BqHWG5dlgGvwVjow==
+ dependencies:
+ tslib "^2.0.0"
+
+"@dnd-kit/core@^6.0.1":
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/@dnd-kit/core/-/core-6.0.1.tgz#fe1970e4490ae5c911b2837f9b5e55217e048ea7"
+ integrity sha512-ZyYmh6S1hvEkywdlwh1d0VW6UnkbP4zb0iZwBGHP4eePlPLDl0t18HaXcgbpVcFWQTUAQtEZVIJKUYBJdVQVsA==
+ dependencies:
+ "@dnd-kit/accessibility" "^3.0.0"
+ "@dnd-kit/utilities" "^3.2.0"
+ tslib "^2.0.0"
+
+"@dnd-kit/sortable@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/@dnd-kit/sortable/-/sortable-7.0.0.tgz#b094b288a8bd37cb74af0ed347b5ca4cfe6ce795"
+ integrity sha512-Em6d1n18lMmpRnNB9mmBWN/X7wNDnIw26tab+c7H0jCjW9UQ0+lRV+vatB1lLzFZlgQgIas/A/TXZDY16RQA5Q==
+ dependencies:
+ "@dnd-kit/utilities" "^3.2.0"
+ tslib "^2.0.0"
+
+"@dnd-kit/utilities@^3.2.0":
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/@dnd-kit/utilities/-/utilities-3.2.0.tgz#b3e956ea63a1347c9d0e1316b037ddcc6140acda"
+ integrity sha512-h65/pn2IPCCIWwdlR2BMLqRkDxpTEONA+HQW3n765HBijLYGyrnTCLa2YQt8VVjjSQD6EfFlTE6aS2Q/b6nb2g==
+ dependencies:
+ tslib "^2.0.0"
+
"@emotion/cache@11.7.1", "@emotion/cache@^11.7.1":
version "11.7.1"
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.7.1.tgz#08d080e396a42e0037848214e8aa7bf879065539"