feat: animate event list while reordering (#4611)
* fix: prevent waiting while invalidating * feat: add auto animate * static postion is added to animation container because auto animate adding a inline style of position relative, which breaks arrow buttons position. * fix: wait until query get cancelled * fix: set querydata correctly Co-authored-by: Alex van Andel <me@alexvanandel.com>pull/4633/head
parent
48ee7a956c
commit
0a2de203a6
|
@ -1,3 +1,4 @@
|
||||||
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||||
import { UserPlan } from "@prisma/client";
|
import { UserPlan } from "@prisma/client";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
@ -82,6 +83,7 @@ const MemoizedItem = React.memo(Item);
|
||||||
export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeListProps): JSX.Element => {
|
export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeListProps): JSX.Element => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const [parent] = useAutoAnimate<HTMLUListElement>();
|
||||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
const [deleteDialogTypeId, setDeleteDialogTypeId] = useState(0);
|
const [deleteDialogTypeId, setDeleteDialogTypeId] = useState(0);
|
||||||
const utils = trpc.useContext();
|
const utils = trpc.useContext();
|
||||||
|
@ -91,8 +93,8 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||||
await utils.cancelQuery(["viewer.eventTypes"]);
|
await utils.cancelQuery(["viewer.eventTypes"]);
|
||||||
await utils.invalidateQueries(["viewer.eventTypes"]);
|
await utils.invalidateQueries(["viewer.eventTypes"]);
|
||||||
},
|
},
|
||||||
onSettled: async () => {
|
onSettled: () => {
|
||||||
await utils.invalidateQueries(["viewer.eventTypes"]);
|
utils.invalidateQueries(["viewer.eventTypes"]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -107,7 +109,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function moveEventType(index: number, increment: 1 | -1) {
|
async function moveEventType(index: number, increment: 1 | -1) {
|
||||||
const newList = [...types];
|
const newList = [...types];
|
||||||
|
|
||||||
const type = types[index];
|
const type = types[index];
|
||||||
|
@ -117,24 +119,19 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||||
newList[index + increment] = type;
|
newList[index + increment] = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.cancelQuery(["viewer.eventTypes"]);
|
await utils.cancelQuery(["viewer.eventTypes"]);
|
||||||
utils.setQueryData(["viewer.eventTypes"], (data) => {
|
|
||||||
// tRPC is very strict with the return signature...
|
const previousValue = utils.getQueryData(["viewer.eventTypes"]);
|
||||||
if (!data)
|
if (previousValue) {
|
||||||
return {
|
utils.setQueryData(["viewer.eventTypes"], {
|
||||||
eventTypeGroups: [],
|
...previousValue,
|
||||||
profiles: [],
|
eventTypeGroups: [
|
||||||
viewer: { canAddEvents: true, plan: UserPlan.PRO },
|
...previousValue.eventTypeGroups.slice(0, groupIndex),
|
||||||
};
|
|
||||||
return {
|
|
||||||
...data,
|
|
||||||
eventTypesGroups: [
|
|
||||||
...data.eventTypeGroups.slice(0, groupIndex),
|
|
||||||
{ ...group, eventTypes: newList },
|
{ ...group, eventTypes: newList },
|
||||||
...data.eventTypeGroups.slice(groupIndex + 1),
|
...previousValue.eventTypeGroups.slice(groupIndex + 1),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
mutation.mutate({
|
mutation.mutate({
|
||||||
ids: newList.map((type) => type.id),
|
ids: newList.map((type) => type.id),
|
||||||
|
@ -201,7 +198,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||||
const lastItem = types[types.length - 1];
|
const lastItem = types[types.length - 1];
|
||||||
return (
|
return (
|
||||||
<div className="mb-16 flex overflow-hidden rounded-md border border-gray-200 bg-white">
|
<div className="mb-16 flex overflow-hidden rounded-md border border-gray-200 bg-white">
|
||||||
<ul className="w-full divide-y divide-neutral-200" data-testid="event-types">
|
<ul ref={parent} className="!static w-full divide-y divide-neutral-200" data-testid="event-types">
|
||||||
{types.map((type, index) => {
|
{types.map((type, index) => {
|
||||||
const embedLink = `${group.profile.slug}/${type.slug}`;
|
const embedLink = `${group.profile.slug}/${type.slug}`;
|
||||||
const calLink = `${CAL_URL}/${embedLink}`;
|
const calLink = `${CAL_URL}/${embedLink}`;
|
||||||
|
|
Loading…
Reference in New Issue