Browse Source

add draggable cells

master
jeffvli 3 years ago
parent
commit
5c57c689a8
  1. 3
      package.json
  2. 10
      src/components/player/NowPlayingView.tsx
  3. 8
      src/components/playlist/PlaylistList.tsx
  4. 10
      src/components/playlist/PlaylistView.tsx
  5. 20
      src/components/starred/StarredView.tsx
  6. 50
      src/components/table/DraggableCell.tsx
  7. 50
      src/components/table/DraggableHeaderCell.tsx
  8. 30
      src/components/viewtypes/ListViewType.tsx
  9. 6
      src/index.tsx
  10. 85
      yarn.lock

3
package.json

@ -185,6 +185,7 @@
"@types/node": "14.14.10",
"@types/randomstring": "^1.1.7",
"@types/react": "^16.9.44",
"@types/react-dnd": "^3.0.2",
"@types/react-dom": "^16.9.9",
"@types/react-redux": "^7.1.18",
"@types/react-router-dom": "^5.1.6",
@ -269,6 +270,8 @@
"nanoid": "^3.1.23",
"randomstring": "^1.2.1",
"react": "^17.0.1",
"react-dnd": "^14.0.2",
"react-dnd-html5-backend": "^14.0.0",
"react-dom": "^17.0.1",
"react-h5-audio-player": "^3.7.1",
"react-helmet-async": "^1.0.9",

10
src/components/player/NowPlayingView.tsx

@ -15,13 +15,13 @@ import Loader from '../loader/Loader';
const tableColumns = [
{
header: '#',
id: '#',
dataKey: 'index',
alignment: 'center',
width: 70,
},
{
header: 'Title',
id: 'Title',
dataKey: 'title',
alignment: 'left',
resizable: true,
@ -29,21 +29,21 @@ const tableColumns = [
},
{
header: 'Artist',
id: 'Artist',
dataKey: 'artist',
alignment: 'center',
resizable: true,
width: 300,
},
{
header: 'Album',
id: 'Album',
dataKey: 'album',
alignment: 'center',
resizable: true,
width: 300,
},
{
header: 'Duration',
id: 'Duration',
dataKey: 'duration',
alignment: 'center',
resizable: true,

8
src/components/playlist/PlaylistList.tsx

@ -10,28 +10,28 @@ import GenericPageHeader from '../layout/GenericPageHeader';
const tableColumns = [
{
header: 'Name',
id: 'Name',
dataKey: 'name',
alignment: 'left',
flexGrow: 2,
resizable: false,
},
{
header: 'Tracks',
id: 'Tracks',
dataKey: 'songCount',
alignment: 'center',
flexGrow: 1,
resizable: false,
},
{
header: 'Description',
id: 'Description',
dataKey: 'comment',
alignment: 'left',
flexGrow: 2,
resizable: false,
},
{
header: 'Created',
id: 'Created',
dataKey: 'created',
alignment: 'left',
flexGrow: 1,

10
src/components/playlist/PlaylistView.tsx

@ -22,14 +22,14 @@ interface PlaylistParams {
const tableColumns = [
{
header: '#',
id: '#',
dataKey: 'index',
alignment: 'center',
resizable: true,
width: 70,
},
{
header: 'Title',
id: 'Title',
dataKey: 'title',
alignment: 'left',
resizable: true,
@ -37,21 +37,21 @@ const tableColumns = [
},
{
header: 'Artist',
id: 'Artist',
dataKey: 'artist',
alignment: 'center',
resizable: true,
width: 300,
},
{
header: 'Album',
id: 'Album',
dataKey: 'album',
alignment: 'center',
resizable: true,
width: 300,
},
{
header: 'Duration',
id: 'Duration',
dataKey: 'duration',
alignment: 'center',
resizable: true,

20
src/components/starred/StarredView.tsx

@ -19,13 +19,13 @@ import ListViewType from '../viewtypes/ListViewType';
const trackTableColumns = [
{
header: '#',
id: '#',
dataKey: 'index',
alignment: 'center',
width: 70,
},
{
header: 'Title',
id: 'Title',
dataKey: 'title',
alignment: 'left',
resizable: true,
@ -33,21 +33,21 @@ const trackTableColumns = [
},
{
header: 'Artist',
id: 'Artist',
dataKey: 'artist',
alignment: 'center',
resizable: true,
width: 300,
},
{
header: 'Album',
id: 'Album',
dataKey: 'album',
alignment: 'center',
resizable: true,
width: 300,
},
{
header: 'Duration',
id: 'Duration',
dataKey: 'duration',
alignment: 'center',
resizable: true,
@ -57,13 +57,13 @@ const trackTableColumns = [
const albumTableColumns = [
{
header: '#',
id: '#',
dataKey: 'index',
alignment: 'center',
width: 70,
},
{
header: 'Title',
id: 'Title',
dataKey: 'name',
alignment: 'left',
resizable: true,
@ -71,21 +71,21 @@ const albumTableColumns = [
},
{
header: 'Artist',
id: 'Artist',
dataKey: 'artist',
alignment: 'center',
resizable: true,
width: 300,
},
{
header: 'Tracks',
id: 'Tracks',
dataKey: 'songCount',
alignment: 'center',
resizable: true,
width: 300,
},
{
header: 'Duration',
id: 'Duration',
dataKey: 'duration',
alignment: 'center',
resizable: true,

50
src/components/table/DraggableCell.tsx

@ -0,0 +1,50 @@
import React from 'react';
import { useDrag, useDrop } from 'react-dnd';
import { Table } from 'rsuite';
const ItemTypes = {
COLUMN: 'column',
ROW: 'row',
};
const DraggableCell = ({ children, onDrag, id, rowData, ...rest }: any) => {
const ref = React.useRef(null);
const [{ canDrop, isOver }, drop] = useDrop({
accept: ItemTypes.ROW,
collect: (monitor) => ({
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
}),
drop(item: any) {
onDrag(item.id, rowData.id);
},
});
const [{ isDragging }, drag] = useDrag({
type: ItemTypes.ROW,
item: { id: rowData.id, type: ItemTypes.ROW },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
const isActive = canDrop && isOver;
drag(drop(ref));
const styles = {
opacity: isDragging ? 0.5 : 1,
background: isActive ? '#ddd' : undefined,
};
return (
<Table.Cell {...rest} style={{ padding: 0 }}>
<div ref={ref} style={styles}>
{children}
</div>
</Table.Cell>
);
};
export default DraggableCell;

50
src/components/table/DraggableHeaderCell.tsx

@ -0,0 +1,50 @@
import React from 'react';
import { useDrag, useDrop } from 'react-dnd';
import { Table } from 'rsuite';
const ItemTypes = {
COLUMN: 'column',
ROW: 'row',
};
const DraggableHeaderCell = ({ children, onDrag, id, ...rest }: any) => {
const ref = React.useRef(null);
const [{ canDrop, isOver }, drop] = useDrop({
accept: ItemTypes.COLUMN,
collect: (monitor) => ({
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
}),
drop(item: any) {
onDrag(item.id, id);
},
});
const [{ isDragging }, drag] = useDrag({
type: ItemTypes.COLUMN,
item: { id, type: ItemTypes.COLUMN },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
const isActive = canDrop && isOver;
drag(drop(ref));
return (
<Table.HeaderCell {...rest} style={{ padding: 0 }}>
<div
ref={ref}
style={{
opacity: isDragging ? 0 : 1,
backgroundColor: isActive ? '#dddddd' : undefined,
}}
>
{children}
</div>
</Table.HeaderCell>
);
};
export default DraggableHeaderCell;

30
src/components/viewtypes/ListViewType.tsx

@ -15,9 +15,10 @@ import {
import { nanoid } from '@reduxjs/toolkit';
import '../../styles/ListView.global.css';
import { formatSongDuration } from '../../shared/utils';
import Loader from '../loader/Loader';
import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { clearSelected } from '../../redux/multiSelectSlice';
import DraggableHeaderCell from '../table/DraggableHeaderCell';
import Loader from '../loader/Loader';
declare global {
interface Window {
@ -25,23 +26,39 @@ declare global {
}
}
const sort = (source: any, sourceId: any, targetId: any) => {
const nextData = source.filter((item: any) => item.id !== sourceId);
const dragItem = source.find((item: any) => item.id === sourceId);
const index = nextData.findIndex((item: any) => item.id === targetId);
nextData.splice(index + 1, 0, dragItem);
return nextData;
};
const ListViewType = ({
data,
handleRowClick,
handleRowDoubleClick,
tableColumns,
hasDraggableColumns,
rowHeight,
virtualized,
children,
}: any) => {
const [height, setHeight] = useState(0);
const [show, setShow] = useState(false);
const [columns, setColumns] = useState(tableColumns);
const { getHeight } = DOMHelper;
const wrapperRef = useRef<HTMLDivElement>(null);
const multiSelect = useAppSelector((state: any) => state.multiSelect);
const playQueue = useAppSelector((state: any) => state.playQueue);
const dispatch = useAppDispatch();
const handleDragColumn = (sourceId: any, targetId: any) => {
setColumns(sort(columns, sourceId, targetId));
};
useEffect(() => {
function handleResize() {
setShow(false);
@ -128,7 +145,7 @@ const ListViewType = ({
affixHorizontalScrollbar
shouldUpdateScroll={false}
>
{tableColumns.map((column: any) => (
{columns.map((column: any) => (
<Table.Column
key={nanoid()}
align={column.alignment}
@ -138,7 +155,14 @@ const ListViewType = ({
fixed={column.fixed}
verticalAlign="middle"
>
<Table.HeaderCell>{column.header}</Table.HeaderCell>
{hasDraggableColumns ? (
<DraggableHeaderCell onDrag={handleDragColumn} id={column.id}>
{column.id}
</DraggableHeaderCell>
) : (
<Table.HeaderCell>{column.id}</Table.HeaderCell>
)}
{column.dataKey === 'index' ? (
<Table.Cell>
{(rowData: any, rowIndex: any) => {

6
src/index.tsx

@ -1,6 +1,8 @@
import React from 'react';
import { render } from 'react-dom';
import { HelmetProvider } from 'react-helmet-async';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { Provider } from 'react-redux';
import { store } from './redux/store';
import App from './App';
@ -8,7 +10,9 @@ import App from './App';
render(
<Provider store={store}>
<HelmetProvider>
<App />
<DndProvider backend={HTML5Backend}>
<App />
</DndProvider>
</HelmetProvider>
</Provider>,
document.getElementById('root')

85
yarn.lock

@ -1432,6 +1432,21 @@
schema-utils "^2.6.5"
source-map "^0.7.3"
"@react-dnd/asap@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-4.0.0.tgz#b300eeed83e9801f51bd66b0337c9a6f04548651"
integrity sha512-0XhqJSc6pPoNnf8DhdsPHtUhRzZALVzYMTzRwV4VI6DJNJ/5xxfL9OQUwb8IH5/2x7lSf7nAZrnzUD+16VyOVQ==
"@react-dnd/invariant@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-2.0.0.tgz#09d2e81cd39e0e767d7da62df9325860f24e517e"
integrity sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==
"@react-dnd/shallowequal@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz#a3031eb54129f2c66b2753f8404266ec7bf67f0a"
integrity sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==
"@reduxjs/toolkit@^1.6.1":
version "1.6.1"
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.6.1.tgz#7bc83b47352a663bf28db01e79d17ba54b98ade9"
@ -1729,16 +1744,16 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/node@*", "@types/node@14.14.10":
version "14.14.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.10.tgz#5958a82e41863cfc71f2307b3748e3491ba03785"
integrity sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==
"@types/node@^14.6.2":
"@types/node@*", "@types/node@^14.6.2":
version "14.17.9"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.9.tgz#b97c057e6138adb7b720df2bd0264b03c9f504fd"
integrity sha512-CMjgRNsks27IDwI785YMY0KLt3co/c0cQ5foxHYv/shC2w8oOnVwz5Ubq1QG5KzrcW+AXk6gzdnxIkDnTvzu3g==
"@types/node@14.14.10":
version "14.14.10"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.10.tgz#5958a82e41863cfc71f2307b3748e3491ba03785"
integrity sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
@ -1777,6 +1792,13 @@
resolved "https://registry.yarnpkg.com/@types/randomstring/-/randomstring-1.1.7.tgz#e214feeed53a14965d2865f6d8ae99a4ff3a9278"
integrity sha512-S6NRYPiH8VGcLW4m9KEMUPtGxXqToCOLLCutQh8sSMaZGrL6/PEQCZAPGBtMP6SKd43ep5eWuPFN732h23h15w==
"@types/react-dnd@^3.0.2":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/react-dnd/-/react-dnd-3.0.2.tgz#939e5a8ca5b83f847c3f64dabbe2f49a9fefb192"
integrity sha512-Z1BqHYGFtfSPfWs+kgX4b6wQmwwtqq4/pLo4zdO9xcDUB1ZQP8iWTAYNf3EJ2f0WiVQpSLN8UytP+ILzZHDLYw==
dependencies:
react-dnd "*"
"@types/react-dom@^16.9.9":
version "16.9.9"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.9.tgz#d2d0a6f720a0206369ccbefff752ba37b9583136"
@ -4506,6 +4528,15 @@ dmg-license@^1.0.8:
smart-buffer "^4.0.2"
verror "^1.10.0"
dnd-core@14.0.0:
version "14.0.0"
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-14.0.0.tgz#973ab3470d0a9ac5a0fa9021c4feba93ad12347d"
integrity sha512-wTDYKyjSqWuYw3ZG0GJ7k+UIfzxTNoZLjDrut37PbcPGNfwhlKYlPUqjAKUjOOv80izshUiqusaKgJPItXSevA==
dependencies:
"@react-dnd/asap" "^4.0.0"
"@react-dnd/invariant" "^2.0.0"
redux "^4.0.5"
dns-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
@ -5602,7 +5633,7 @@ fast-deep-equal@^1.0.0:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=
fast-deep-equal@^3.1.1:
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
@ -6174,12 +6205,7 @@ got@^9.6.0:
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4:
version "4.2.6"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.3, graceful-fs@^4.2.4:
version "4.2.8"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
@ -6709,16 +6735,11 @@ ini@2.0.0:
resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
ini@^1.3.4:
ini@^1.3.4, ini@~1.3.0:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
internal-ip@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907"
@ -10054,6 +10075,24 @@ rc@^1.2.7, rc@^1.2.8:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-dnd-html5-backend@^14.0.0:
version "14.0.0"
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-14.0.0.tgz#28d660a2ad1e07447c34a65cd25f7de8f1657194"
integrity sha512-2wAQqRFC1hbRGmk6+dKhOXsyQQOn3cN8PSZyOUeOun9J8t3tjZ7PS2+aFu7CVu2ujMDwTJR3VTwZh8pj2kCv7g==
dependencies:
dnd-core "14.0.0"
react-dnd@*, react-dnd@^14.0.2:
version "14.0.2"
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-14.0.2.tgz#57266baec92b887301f81fa3b77f87168d159733"
integrity sha512-JoEL78sBCg8SzjOKMlkR70GWaPORudhWuTNqJ56lb2P8Vq0eM2+er3ZrMGiSDhOmzaRPuA9SNBz46nHCrjn11A==
dependencies:
"@react-dnd/invariant" "^2.0.0"
"@react-dnd/shallowequal" "^2.0.0"
dnd-core "14.0.0"
fast-deep-equal "^3.1.3"
hoist-non-react-statics "^3.3.2"
react-dom@^17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.1.tgz#1de2560474ec9f0e334285662ede52dbc5426fc6"
@ -10355,10 +10394,10 @@ redux-thunk@^2.3.0:
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
redux@^4.0.0, redux@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.0.tgz#eb049679f2f523c379f1aff345c8612f294c88d4"
integrity sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g==
redux@^4.0.0, redux@^4.0.5, redux@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.1.tgz#76f1c439bb42043f985fbd9bf21990e60bd67f47"
integrity sha512-hZQZdDEM25UY2P493kPYuKqviVwZ58lEmGQNeQ+gXa+U0gYPUBf7NKYazbe3m+bs/DzM/ahN12DbF+NG8i0CWw==
dependencies:
"@babel/runtime" "^7.9.2"

Loading…
Cancel
Save