add drag and drop ordering for categoriers

This commit is contained in:
41666 2021-07-06 10:37:45 -05:00
parent 2d589b988f
commit 098a4133df
3 changed files with 152 additions and 30 deletions

View file

@ -8,6 +8,7 @@ import { Category, CategoryType, PresentableGuild, Role } from '@roleypoly/types
import KSUID from 'ksuid'; import KSUID from 'ksuid';
import { sortBy } from 'lodash'; import { sortBy } from 'lodash';
import React from 'react'; import React from 'react';
import { DragDropContext, Draggable, Droppable, DropResult } from 'react-beautiful-dnd';
import { CgReorder } from 'react-icons/cg'; import { CgReorder } from 'react-icons/cg';
import { GoArrowDown, GoArrowUp, GoCheck, GoGrabber, GoPlus } from 'react-icons/go'; import { GoArrowDown, GoArrowUp, GoCheck, GoGrabber, GoPlus } from 'react-icons/go';
import { import {
@ -45,7 +46,7 @@ export const ServerCategoryEditor = (props: Props) => {
const categories = resetOrder(props.guild.data.categories); const categories = resetOrder(props.guild.data.categories);
const newCategory: Category = { const newCategory: Category = {
id: KSUID.randomSync().toString(), id: KSUID.randomSync().string,
name: 'New Category', name: 'New Category',
type: CategoryType.Multi, type: CategoryType.Multi,
position: categories.length, position: categories.length,
@ -56,8 +57,12 @@ export const ServerCategoryEditor = (props: Props) => {
props.onChange([...categories, newCategory]); props.onChange([...categories, newCategory]);
}; };
const onReorder = (categories: Category[]) => { const onReorder = (categories: Category[] | null) => {
setReorderMode(false); setReorderMode(false);
if (categories === null) {
return;
}
props.onChange(resetOrder(categories)); props.onChange(resetOrder(categories));
}; };
@ -96,7 +101,9 @@ export const ServerCategoryEditor = (props: Props) => {
); );
}; };
const ReorderMode = (props: Props & { exitReorderMode: (final: Category[]) => void }) => { const ReorderMode = (
props: Props & { exitReorderMode: (final: Category[] | null) => void }
) => {
const [categories, setCategories] = React.useState(props.guild.data.categories); const [categories, setCategories] = React.useState(props.guild.data.categories);
React.useEffect(() => { React.useEffect(() => {
@ -117,15 +124,24 @@ const ReorderMode = (props: Props & { exitReorderMode: (final: Category[]) => vo
setCategories(forceOrder(newCategories)); setCategories(forceOrder(newCategories));
}; };
const handleDrop = (dropEvent: DropResult) => {
const newCategories = [...categories];
const { source, destination } = dropEvent;
if (!destination || source.index === destination.index) {
return;
}
newCategories.splice(source.index, 1);
newCategories.splice(destination.index, 0, categories[source.index]);
setCategories(forceOrder(newCategories));
};
return ( return (
<div> <div>
<CategoryActions right> <CategoryActions right>
<Button <Button color="muted" size="small" onClick={() => props.exitReorderMode(null)}>
color="muted" Cancel
size="small"
onClick={() => props.exitReorderMode(props.guild.data.categories)}
>
Reset
</Button> </Button>
<Button <Button
color="primary" color="primary"
@ -135,24 +151,50 @@ const ReorderMode = (props: Props & { exitReorderMode: (final: Category[]) => vo
<BreakpointText small="Save" large="Save Order" /> <GoCheck /> <BreakpointText small="Save" large="Save Order" /> <GoCheck />
</Button> </Button>
</CategoryActions> </CategoryActions>
{sortBy(categories, ['position', 'id']).map((category, idx, array) => ( <DragDropContext onDragEnd={handleDrop}>
<ReorderCategoryContainer key={idx}> <Droppable droppableId="categories">
<ReorderButton data-tip="Drag to reorder" style={{ cursor: 'grab' }}> {(provided, snapshot) => (
<GoGrabber /> <div ref={provided.innerRef} {...provided.droppableProps}>
</ReorderButton> {sortBy(categories, ['position', 'id']).map((category, idx) => (
<FaderOpacity isVisible={idx !== 0}> <Draggable key={category.id} index={idx} draggableId={category.id}>
<ReorderButton onClick={handleReorder(category, 'up')} data-tip="Move up"> {(provided, snapshot) => (
<GoArrowUp /> <ReorderCategoryContainer
</ReorderButton> ref={provided.innerRef}
</FaderOpacity> {...provided.draggableProps}
<FaderOpacity isVisible={categories.length - 1 !== idx}> >
<ReorderButton onClick={handleReorder(category, 'down')} data-tip="Move down"> <ReorderButton
<GoArrowDown /> data-tip="Drag to reorder"
</ReorderButton> style={{ cursor: 'grab' }}
</FaderOpacity> {...provided.dragHandleProps}
<LargeText>{category.name}</LargeText> >
</ReorderCategoryContainer> <GoGrabber />
))} </ReorderButton>
<FaderOpacity isVisible={idx !== 0}>
<ReorderButton
onClick={handleReorder(category, 'up')}
data-tip="Move up"
>
<GoArrowUp />
</ReorderButton>
</FaderOpacity>
<FaderOpacity isVisible={categories.length - 1 !== idx}>
<ReorderButton
onClick={handleReorder(category, 'down')}
data-tip="Move down"
>
<GoArrowDown />
</ReorderButton>
</FaderOpacity>
<LargeText>{category.name}</LargeText>
</ReorderCategoryContainer>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</DragDropContext>
</div> </div>
); );
}; };

View file

@ -13,6 +13,7 @@
"ksuid": "^2.0.0", "ksuid": "^2.0.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "^17.0.2", "react": "^17.0.2",
"react-beautiful-dnd": "^13.1.0",
"react-custom-scrollbars": "^4.2.1", "react-custom-scrollbars": "^4.2.1",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
@ -34,6 +35,7 @@
"@types/deep-equal": "^1.0.1", "@types/deep-equal": "^1.0.1",
"@types/node": "^15.12.5", "@types/node": "^15.12.5",
"@types/react": "^17.0.11", "@types/react": "^17.0.11",
"@types/react-beautiful-dnd": "^13.1.0",
"@types/react-custom-scrollbars": "^4.0.7", "@types/react-custom-scrollbars": "^4.0.7",
"@types/react-dom": "^17.0.8", "@types/react-dom": "^17.0.8",
"@types/react-helmet": "^6.1.1", "@types/react-helmet": "^6.1.1",

View file

@ -1503,6 +1503,13 @@
dependencies: dependencies:
regenerator-runtime "^0.13.4" regenerator-runtime "^0.13.4"
"@babel/runtime@^7.12.1":
version "7.14.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.13.10": "@babel/runtime@^7.13.10":
version "7.13.10" version "7.13.10"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
@ -3448,7 +3455,7 @@
dependencies: dependencies:
"@types/unist" "*" "@types/unist" "*"
"@types/hoist-non-react-statics@*": "@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0":
version "3.3.1" version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
@ -3635,6 +3642,13 @@
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/react-beautiful-dnd@^13.1.0":
version "13.1.0"
resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz#8a256efe7b457c0eb9c508e600e53090b9547aac"
integrity sha512-RZUwL3gSkA1m3m553ZZkDk89KBRABMWVE3LK7AoSohmx/APGt8U2GApCNj4pHdPwL/vxzZjwuqf3NtlWquuhVw==
dependencies:
"@types/react" "*"
"@types/react-custom-scrollbars@^4.0.7": "@types/react-custom-scrollbars@^4.0.7":
version "4.0.7" version "4.0.7"
resolved "https://registry.yarnpkg.com/@types/react-custom-scrollbars/-/react-custom-scrollbars-4.0.7.tgz#b1312ec749fcf4a01fee7466508501e072ede7ea" resolved "https://registry.yarnpkg.com/@types/react-custom-scrollbars/-/react-custom-scrollbars-4.0.7.tgz#b1312ec749fcf4a01fee7466508501e072ede7ea"
@ -3656,6 +3670,16 @@
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/react-redux@^7.1.16":
version "7.1.16"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.16.tgz#0fbd04c2500c12105494c83d4a3e45c084e3cb21"
integrity sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw==
dependencies:
"@types/hoist-non-react-statics" "^3.3.0"
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
redux "^4.0.0"
"@types/react-syntax-highlighter@11.0.5": "@types/react-syntax-highlighter@11.0.5":
version "11.0.5" version "11.0.5"
resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087"
@ -6234,6 +6258,13 @@ css-blank-pseudo@^0.1.4:
dependencies: dependencies:
postcss "^7.0.5" postcss "^7.0.5"
css-box-model@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1"
integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==
dependencies:
tiny-invariant "^1.0.6"
css-color-keywords@^1.0.0: css-color-keywords@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
@ -8890,7 +8921,7 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0" minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1" minimalistic-crypto-utils "^1.0.1"
hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0: hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2" version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@ -11394,7 +11425,7 @@ memfs@^3.1.2:
dependencies: dependencies:
fs-monkey "1.0.1" fs-monkey "1.0.1"
memoize-one@^5.2.1: memoize-one@^5.1.1, memoize-one@^5.2.1:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
@ -13748,6 +13779,11 @@ quick-lru@^4.0.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
raf-schd@^4.0.2:
version "4.0.3"
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a"
integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==
raf@^3.1.0, raf@^3.4.1: raf@^3.1.0, raf@^3.4.1:
version "3.4.1" version "3.4.1"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
@ -13823,6 +13859,19 @@ react-app-polyfill@^2.0.0:
regenerator-runtime "^0.13.7" regenerator-runtime "^0.13.7"
whatwg-fetch "^3.4.1" whatwg-fetch "^3.4.1"
react-beautiful-dnd@^13.1.0:
version "13.1.0"
resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz#ec97c81093593526454b0de69852ae433783844d"
integrity sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA==
dependencies:
"@babel/runtime" "^7.9.2"
css-box-model "^1.2.0"
memoize-one "^5.1.1"
raf-schd "^4.0.2"
react-redux "^7.2.0"
redux "^4.0.4"
use-memo-one "^1.1.1"
react-colorful@^5.1.2: react-colorful@^5.1.2:
version "5.2.2" version "5.2.2"
resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.2.2.tgz#0a69d0648db47e51359d343854d83d250a742243" resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.2.2.tgz#0a69d0648db47e51359d343854d83d250a742243"
@ -13988,6 +14037,18 @@ react-popper@^2.2.4:
react-fast-compare "^3.0.1" react-fast-compare "^3.0.1"
warning "^4.0.2" warning "^4.0.2"
react-redux@^7.2.0:
version "7.2.4"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.4.tgz#1ebb474032b72d806de2e0519cd07761e222e225"
integrity sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA==
dependencies:
"@babel/runtime" "^7.12.1"
"@types/react-redux" "^7.1.16"
hoist-non-react-statics "^3.3.2"
loose-envify "^1.4.0"
prop-types "^15.7.2"
react-is "^16.13.1"
react-refresh@^0.8.3: react-refresh@^0.8.3:
version "0.8.3" version "0.8.3"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
@ -14275,6 +14336,13 @@ redent@^3.0.0:
indent-string "^4.0.0" indent-string "^4.0.0"
strip-indent "^3.0.0" strip-indent "^3.0.0"
redux@^4.0.0, redux@^4.0.4:
version "4.1.0"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.0.tgz#eb049679f2f523c379f1aff345c8612f294c88d4"
integrity sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g==
dependencies:
"@babel/runtime" "^7.9.2"
reflect.ownkeys@^0.2.0: reflect.ownkeys@^0.2.0:
version "0.2.0" version "0.2.0"
resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"
@ -16068,6 +16136,11 @@ tiny-emitter@^2.0.0:
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
tiny-invariant@^1.0.6:
version "1.1.0"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
tmpl@1.0.x: tmpl@1.0.x:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
@ -16659,6 +16732,11 @@ use-latest@^1.0.0:
dependencies: dependencies:
use-isomorphic-layout-effect "^1.0.0" use-isomorphic-layout-effect "^1.0.0"
use-memo-one@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20"
integrity sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==
use@^3.1.0: use@^3.1.0:
version "3.1.1" version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"