diff --git a/package-lock.json b/package-lock.json
index 75d2d3437..f1550b8c7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,8 @@
"hasInstallScript": true,
"license": "GPL-3.0",
"dependencies": {
+ "@dnd-kit/core": "^6.0.5",
+ "@dnd-kit/modifiers": "^6.0.0",
"@emotion/react": "^11.9.3",
"@jellyfin/client-axios": "^10.7.8",
"@mantine/core": "^5.0.0",
@@ -790,6 +792,54 @@
"node": ">=10.0.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.5",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.0.5.tgz",
+ "integrity": "sha512-3nL+Zy5cT+1XwsWdlXIvGIFvbuocMyB4NBxTN74DeBaBqeWdH9JsnKwQv7buZQgAHmAH+eIENfS1ginkvW6bCw==",
+ "dependencies": {
+ "@dnd-kit/accessibility": "^3.0.0",
+ "@dnd-kit/utilities": "^3.2.0",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@dnd-kit/modifiers": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/modifiers/-/modifiers-6.0.0.tgz",
+ "integrity": "sha512-V3+JSo6/BTcgPRHiNUTSKgqVv/doKXg+T4Z0QvKiiXp+uIyJTUtPkQOBRQApUWi3ApBhnoWljyt/3xxY4fTd0Q==",
+ "dependencies": {
+ "@dnd-kit/utilities": "^3.2.0",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@dnd-kit/core": "^6.0.0"
+ }
+ },
+ "node_modules/@dnd-kit/utilities": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.0.tgz",
+ "integrity": "sha512-h65/pn2IPCCIWwdlR2BMLqRkDxpTEONA+HQW3n765HBijLYGyrnTCLa2YQt8VVjjSQD6EfFlTE6aS2Q/b6nb2g==",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/@electron/get": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz",
@@ -23630,6 +23680,41 @@
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
"dev": true
},
+ "@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==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@dnd-kit/core": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.0.5.tgz",
+ "integrity": "sha512-3nL+Zy5cT+1XwsWdlXIvGIFvbuocMyB4NBxTN74DeBaBqeWdH9JsnKwQv7buZQgAHmAH+eIENfS1ginkvW6bCw==",
+ "requires": {
+ "@dnd-kit/accessibility": "^3.0.0",
+ "@dnd-kit/utilities": "^3.2.0",
+ "tslib": "^2.0.0"
+ }
+ },
+ "@dnd-kit/modifiers": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/modifiers/-/modifiers-6.0.0.tgz",
+ "integrity": "sha512-V3+JSo6/BTcgPRHiNUTSKgqVv/doKXg+T4Z0QvKiiXp+uIyJTUtPkQOBRQApUWi3ApBhnoWljyt/3xxY4fTd0Q==",
+ "requires": {
+ "@dnd-kit/utilities": "^3.2.0",
+ "tslib": "^2.0.0"
+ }
+ },
+ "@dnd-kit/utilities": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.0.tgz",
+ "integrity": "sha512-h65/pn2IPCCIWwdlR2BMLqRkDxpTEONA+HQW3n765HBijLYGyrnTCLa2YQt8VVjjSQD6EfFlTE6aS2Q/b6nb2g==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
"@electron/get": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@electron/get/-/get-1.14.1.tgz",
diff --git a/package.json b/package.json
index 23c040455..b91d38dfc 100644
--- a/package.json
+++ b/package.json
@@ -245,6 +245,8 @@
"webpack-merge": "^5.8.0"
},
"dependencies": {
+ "@dnd-kit/core": "^6.0.5",
+ "@dnd-kit/modifiers": "^6.0.0",
"@emotion/react": "^11.9.3",
"@jellyfin/client-axios": "^10.7.8",
"@mantine/core": "^5.0.0",
diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx
index 8c8d92cb3..65f0e9086 100644
--- a/src/renderer/app.tsx
+++ b/src/renderer/app.tsx
@@ -1,4 +1,11 @@
import { ReactNode, useEffect } from 'react';
+import {
+ DndContext,
+ MouseSensor,
+ TouchSensor,
+ useSensor,
+ useSensors,
+} from '@dnd-kit/core';
import { MantineProvider } from '@mantine/core';
import { useLocalStorage } from '@mantine/hooks';
import isElectron from 'is-electron';
@@ -27,6 +34,21 @@ export const App = () => {
document.body.setAttribute('data-theme', theme);
}, [theme]);
+ const sensors = useSensors(
+ useSensor(MouseSensor, {
+ activationConstraint: {
+ delay: 200,
+ tolerance: 100,
+ },
+ }),
+ useSensor(TouchSensor, {
+ activationConstraint: {
+ delay: 500,
+ tolerance: 10,
+ },
+ })
+ );
+
return (
{
xl: 18,
xs: 10,
},
+
other: {},
spacing: {
xs: 2,
},
}}
>
-
-
-
+ console.log('drag end')}
+ onDragStart={() => console.log('drag start')}
+ >
+
+
+
+
);
};
diff --git a/src/renderer/components/drag-drop/Draggable.tsx b/src/renderer/components/drag-drop/Draggable.tsx
new file mode 100644
index 000000000..b5a754f9f
--- /dev/null
+++ b/src/renderer/components/drag-drop/Draggable.tsx
@@ -0,0 +1,14 @@
+import { useDraggable } from '@dnd-kit/core';
+
+export const Draggable = ({ element, id, children, key }: any) => {
+ const Element = element || 'div';
+ const { attributes, listeners, setNodeRef } = useDraggable({
+ id,
+ });
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/renderer/components/virtual-grid/GridCard.tsx b/src/renderer/components/virtual-grid/GridCard.tsx
index fa36732d1..3a304b7ad 100644
--- a/src/renderer/components/virtual-grid/GridCard.tsx
+++ b/src/renderer/components/virtual-grid/GridCard.tsx
@@ -1,7 +1,12 @@
+import React, { useState } from 'react';
+import { DragOverlay, useDndMonitor } from '@dnd-kit/core';
+import { snapCenterToCursor } from '@dnd-kit/modifiers';
import { Card, Skeleton } from '@mantine/core';
import { motion } from 'framer-motion';
+import { createPortal } from 'react-dom';
import styled from 'styled-components';
import { CardRow } from '../../types';
+import { Draggable } from '../drag-drop/Draggable';
import { Text } from '../text/Text';
import { GridCardControls } from './GridCardControls';
@@ -98,39 +103,56 @@ export const GridCard = ({ data, index, style }: any) => {
const stopIndex = Math.min(itemCount - 1, startIndex + columnCount - 1);
const cards = [];
+ const [isDragging, setIsDragging] = useState(false);
+
+ useDndMonitor({
+ onDragCancel: () => setIsDragging(false),
+ onDragEnd: () => setIsDragging(false),
+ onDragStart: () => setIsDragging(true),
+ });
+
for (let i = startIndex; i <= stopIndex; i += 1) {
cards.push(
-
-
-
-
-
-
-
-
-
-
-
- {cardRows.map((row: CardRow) => (
-
-
- {itemData[i] && itemData[i][row.prop]}
-
-
- ))}
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {cardRows.map((row: CardRow) => (
+
+
+ {itemData[i] && itemData[i][row.prop]}
+
+
+ ))}
+
+
+
+
+
+ {createPortal(
+
+ {isDragging ? OVERLAY
: null}
+ ,
+ document.body
+ )}
+
);
}