From 0a319200aea159bb6f212608c20132fdedfacf43 Mon Sep 17 00:00:00 2001 From: "Kalle (Sendou)" <38327916+Sendouc@users.noreply.github.com> Date: Sat, 18 Dec 2021 13:34:01 +0200 Subject: [PATCH] Less jank dragging UI --- app/components/Draggable.tsx | 35 +++++++++ .../to/$organization.$tournament/seeds.tsx | 73 +++++++++++-------- app/styles/global.css | 1 + app/styles/tournament-seeds.css | 5 ++ 4 files changed, 82 insertions(+), 32 deletions(-) create mode 100644 app/components/Draggable.tsx diff --git a/app/components/Draggable.tsx b/app/components/Draggable.tsx new file mode 100644 index 000000000..0cffa773c --- /dev/null +++ b/app/components/Draggable.tsx @@ -0,0 +1,35 @@ +import { useSortable } from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; +import * as React from "react"; + +export function Draggable({ + id, + disabled, + liClassName, + children, +}: { + id: string; + disabled: boolean; + liClassName: string; + children: React.ReactNode; +}) { + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ id, disabled }); + + const style = { + transform: CSS.Transform.toString(transform), + transition, + }; + + return ( +
  • + {children} +
  • + ); +} diff --git a/app/routes/to/$organization.$tournament/seeds.tsx b/app/routes/to/$organization.$tournament/seeds.tsx index c65b3071e..9762ff6eb 100644 --- a/app/routes/to/$organization.$tournament/seeds.tsx +++ b/app/routes/to/$organization.$tournament/seeds.tsx @@ -1,6 +1,7 @@ import { closestCenter, DndContext, + DragOverlay, KeyboardSensor, PointerSensor, useSensor, @@ -10,28 +11,27 @@ import { arrayMove, SortableContext, sortableKeyboardCoordinates, - useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable"; -import { CSS } from "@dnd-kit/utilities"; import classNames from "classnames"; import * as React from "react"; -import { useFetcher, useMatches } from "remix"; import type { LinksFunction } from "remix"; +import { useFetcher, useMatches } from "remix"; +import invariant from "tiny-invariant"; import { Alert } from "~/components/Alert"; import { Button } from "~/components/Button"; +import { Draggable } from "~/components/Draggable"; import { TOURNAMENT_TEAM_ROSTER_MIN_SIZE } from "~/constants"; import { checkInHasStarted } from "~/core/tournament/utils"; import type { FindTournamentByNameForUrlI } from "~/services/tournament"; +import seedsStylesUrl from "~/styles/tournament-seeds.css"; import type { Unpacked } from "~/utils"; import { useTimeoutState } from "~/utils/hooks"; -import seedsStylesUrl from "~/styles/tournament-seeds.css"; export const links: LinksFunction = () => { return [{ rel: "stylesheet", href: seedsStylesUrl }]; }; -// TODO: https://docs.dndkit.com/presets/sortable#drag-overlay // TODO: what if returns error? check other APIs too -> add Cypress test // TODO: error if not admin export default function SeedsTab() { @@ -40,6 +40,9 @@ export default function SeedsTab() { const { id, teams, checkInStartTime } = parentRoute.data as FindTournamentByNameForUrlI; const [teamOrder, setTeamOrder] = React.useState(teams.map((t) => t.id)); + const [activeTeam, setActiveTeam] = React.useState | null>(null); const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { @@ -73,11 +76,18 @@ export default function SeedsTab() { id="team-seed-sorter" sensors={sensors} collisionDetection={closestCenter} - onDragStart={() => null} + onDragStart={(event) => { + const newActiveTeam = teamsSorted.find( + (t) => t.id === event.active.id + ); + invariant(newActiveTeam, "newActiveTeam is undefined"); + setActiveTeam(newActiveTeam); + }} onDragEnd={(event) => { const { active, over } = event; if (!over) return; + setActiveTeam(null); if (active.id !== over.id) { setTeamOrder((teamIds) => { const oldIndex = teamIds.indexOf(active.id); @@ -93,14 +103,31 @@ export default function SeedsTab() { strategy={verticalListSortingStrategy} > {teamsSorted.map((team, i) => ( - + liClassName={classNames( + "tournament__seeds__teams-list-row", + "sortable", + { + disabled: seedsFetcher.state !== "idle", + "visibility-hidden": activeTeam?.id === team.id, + } + )} + > + + ))} + + + {activeTeam && ( +
  • + +
  • + )} +
    @@ -164,36 +191,18 @@ function SeedAlert({ ); } -function SortableRow({ +function RowContents({ team, seed, - disabled, }: { team: Unpacked; - seed: number; - disabled: boolean; + seed?: number; }) { const [, parentRoute] = useMatches(); const { checkInStartTime } = parentRoute.data as FindTournamentByNameForUrlI; - const { attributes, listeners, setNodeRef, transform, transition } = - useSortable({ id: team.id, disabled }); - - const style = { - transform: CSS.Transform.toString(transform), - transition, - }; - return ( -
  • + <>
    {seed}
    {team.name}
    @@ -222,7 +231,7 @@ function SortableRow({ > {team.members.length}
    -
  • + ); } diff --git a/app/styles/global.css b/app/styles/global.css index e8e4e0a9f..b08b5d1fd 100644 --- a/app/styles/global.css +++ b/app/styles/global.css @@ -1,6 +1,7 @@ :root { --bg: hsl(237.3deg 42.3% 30.6%); --bg-lighter: hsl(237.3deg 42.3% 35.6%); + --bg-lighter-transparent: hsla(237.3deg 42.3% 35.6% / 50%); --border: hsl(237.3deg 42.3% 45.6%); --text: rgb(255 255 255 / 95%); --text-lighter: rgb(255 255 255 / 55%); diff --git a/app/styles/tournament-seeds.css b/app/styles/tournament-seeds.css index 20eb3cdd4..d11966fe5 100644 --- a/app/styles/tournament-seeds.css +++ b/app/styles/tournament-seeds.css @@ -33,7 +33,12 @@ } .tournament__seeds__teams-list-row.sortable:hover:not(.disabled) { + background-color: var(--bg-lighter-transparent); +} + +.tournament__seeds__teams-list-row.active { background-color: var(--bg-lighter); + cursor: grabbing; } .tournament__seeds__teams-list-row.sortable:active:not(.disabled) {