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"