Skip to content

Commit 432f089

Browse files
committed
feat(Space): use motion library to create smooth animation between layout changes
1 parent bea60da commit 432f089

File tree

5 files changed

+218
-108
lines changed

5 files changed

+218
-108
lines changed

Diff for: components/Space/sub/ProposalCards.tsx

+48-45
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import getVotedIcon from "./card/VoteIcon";
2020
import NewVoteButton from "@/components/Vote/NewVoteButton";
2121
import VotesBar from "./card/VotesBar";
2222
import ProposalRow from "./card/ProposalRow";
23+
import { AnimatePresence } from "motion/react";
2324

2425
const SortOptionsArr = ["status", "title", "approval", "participants", "voted"];
2526
const StatusValue: { [key: string]: number } = {
@@ -333,51 +334,53 @@ export default function ProposalCards({
333334
</th>
334335
</tr>
335336
</thead>
336-
<tbody>
337-
{isLoading && (
338-
<>
339-
<ProposalRowSkeleton isFirst />
340-
<ProposalRowSkeleton />
341-
<ProposalRowSkeleton />
342-
</>
343-
)}
344-
{!isLoading &&
345-
sortedProposals.map((proposal, proposalIdx) => (
346-
<ProposalRow
347-
key={proposal.uuid}
348-
proposal={proposal}
349-
snapshotProposal={snapshotProposalDict[proposal.voteURL!]}
350-
isFirst={proposalIdx === 0}
351-
proposalIdPrefix={
352-
proposalsPacket?.proposalInfo?.proposalIdPrefix || ""
353-
}
354-
votesBar={
355-
<VotesBar
356-
snapshotProposal={
357-
snapshotProposalDict[proposal.voteURL!]
358-
}
359-
proposal={proposal}
360-
threshold={
361-
proposalsPacket?.proposalInfo
362-
?.minTokenPassingAmount ?? 0
363-
}
364-
/>
365-
}
366-
voteActionOrStatus={
367-
<VoteActionOrLabel
368-
snapshotProposal={
369-
snapshotProposalDict[proposal.voteURL!]
370-
}
371-
snapshotSpace={
372-
proposalsPacket?.proposalInfo?.snapshotSpace || ""
373-
}
374-
votedData={votedData?.[proposal.voteURL!]}
375-
refetch={refetch}
376-
/>
377-
}
378-
/>
379-
))}
380-
</tbody>
337+
<AnimatePresence>
338+
<tbody>
339+
{isLoading && (
340+
<>
341+
<ProposalRowSkeleton isFirst />
342+
<ProposalRowSkeleton />
343+
<ProposalRowSkeleton />
344+
</>
345+
)}
346+
{!isLoading &&
347+
sortedProposals.map((proposal, proposalIdx) => (
348+
<ProposalRow
349+
key={proposal.uuid}
350+
proposal={proposal}
351+
snapshotProposal={snapshotProposalDict[proposal.voteURL!]}
352+
isFirst={proposalIdx === 0}
353+
proposalIdPrefix={
354+
proposalsPacket?.proposalInfo?.proposalIdPrefix || ""
355+
}
356+
votesBar={
357+
<VotesBar
358+
snapshotProposal={
359+
snapshotProposalDict[proposal.voteURL!]
360+
}
361+
proposal={proposal}
362+
threshold={
363+
proposalsPacket?.proposalInfo
364+
?.minTokenPassingAmount ?? 0
365+
}
366+
/>
367+
}
368+
voteActionOrStatus={
369+
<VoteActionOrLabel
370+
snapshotProposal={
371+
snapshotProposalDict[proposal.voteURL!]
372+
}
373+
snapshotSpace={
374+
proposalsPacket?.proposalInfo?.snapshotSpace || ""
375+
}
376+
votedData={votedData?.[proposal.voteURL!]}
377+
refetch={refetch}
378+
/>
379+
}
380+
/>
381+
))}
382+
</tbody>
383+
</AnimatePresence>
381384
</table>
382385
</div>
383386
</div>

Diff for: components/Space/sub/card/ProposalRow.tsx

+13-5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { formatNumber } from "@/utils/functions/NumberFormatter";
1818
import TokenSymbol from "@/components/AddressCard/TokenSymbol";
1919
import TooltipInfo from "@/components/common/TooltipInfo";
2020
import Image from "next/image";
21+
import { motion } from "motion/react";
2122

2223
export function RequestingTokensOfProposal({ actions }: { actions: Action[] }) {
2324
// we only parse Payout and Transfer actions here
@@ -115,7 +116,14 @@ export default function ProposalRow({
115116
}
116117

117118
return (
118-
<tr className="hover:bg-slate-100">
119+
<motion.tr
120+
layout
121+
initial={{ opacity: 0 }}
122+
animate={{ opacity: 1 }}
123+
exit={{ opacity: 0 }}
124+
transition={{ duration: 0.3 }}
125+
className="hover:bg-slate-100"
126+
>
119127
<td
120128
className={classNames(
121129
isFirst ? "" : "border-t border-transparent",
@@ -176,9 +184,9 @@ export default function ProposalRow({
176184
content={`The intended author does not have sufficient voting power to submit a proposal.\
177185
An address with atleast\
178186
${formatNumber(
179-
spaceInfo?.proposalSubmissionValidation
180-
?.minBalance || 0
181-
)}\
187+
spaceInfo?.proposalSubmissionValidation
188+
?.minBalance || 0
189+
)}\
182190
voting power must sponsor the proposal.`}
183191
/>
184192
</div>
@@ -262,6 +270,6 @@ export default function ProposalRow({
262270
>
263271
{voteActionOrStatus}
264272
</td>
265-
</tr>
273+
</motion.tr>
266274
);
267275
}

Diff for: components/Space/sub/card/ProposalRowSkeleton.tsx

+96-58
Original file line numberDiff line numberDiff line change
@@ -2,80 +2,118 @@ import { classNames } from "@/utils/functions/tailwind";
22
import ProposalBadgeLabel from "./ProposalBadgeLabel";
33
import ColorBar from "@/components/common/ColorBar";
44

5+
import { motion } from "framer-motion";
6+
57
export default function ProposalRowSkeleton({
68
isFirst = false,
79
}: {
810
isFirst?: boolean;
911
}) {
1012
return (
11-
<tr className="hover:cursor-pointer hover:bg-slate-100">
12-
<td
13-
className={classNames(
14-
isFirst ? "" : "border-t border-transparent",
15-
"relative hidden py-4 pl-6 pr-3 text-sm md:table-cell",
16-
)}
17-
>
18-
<ProposalBadgeLabel status="" />
19-
20-
{!isFirst ? (
21-
<div className="absolute -top-px left-6 right-0 h-px bg-gray-200" />
22-
) : null}
13+
<motion.tr
14+
initial={{ opacity: 0, y: 10 }}
15+
animate={{ opacity: 1, y: 0 }}
16+
transition={{ duration: 0.5 }}
17+
className={`${!isFirst ? "border-t border-transparent" : ""}`}
18+
>
19+
<td className="hidden py-4 pl-6 pr-3 md:table-cell">
20+
<motion.div
21+
animate={{
22+
opacity: [0.5, 1, 0.5],
23+
}}
24+
transition={{
25+
repeat: Infinity,
26+
duration: 1.5,
27+
ease: "easeInOut",
28+
}}
29+
className="h-6 w-20 rounded-full bg-gray-200"
30+
/>
2331
</td>
2432

25-
<td
26-
className={classNames(
27-
isFirst ? "" : "border-t border-gray-200",
28-
"px-3 py-3.5 text-sm text-gray-500",
29-
)}
30-
>
31-
<div className="flex flex-col space-y-1">
32-
<div className="block text-gray-900 md:hidden">
33-
<ProposalBadgeLabel status="" />
34-
</div>
35-
{/* cycle metadata */}
36-
<span className="h-4 w-20 animate-pulse rounded bg-slate-200 text-xs"></span>
37-
{/* proposal title */}
38-
<p className="h-6 w-32 animate-pulse break-words rounded bg-slate-200 text-base text-black"></p>
39-
40-
<div className="md:hidden">
41-
<ColorBar greenScore={0} redScore={0} />
33+
<td className="px-3 py-4">
34+
<div className="flex flex-col space-y-3">
35+
<motion.div
36+
animate={{
37+
opacity: [0.5, 1, 0.5],
38+
}}
39+
transition={{
40+
repeat: Infinity,
41+
duration: 1.5,
42+
ease: "easeInOut",
43+
}}
44+
className="h-4 w-full max-w-[300px] rounded bg-gray-200"
45+
/>
46+
<div className="flex space-x-4">
47+
<motion.div
48+
animate={{
49+
opacity: [0.5, 1, 0.5],
50+
}}
51+
transition={{
52+
repeat: Infinity,
53+
duration: 1.5,
54+
ease: "easeInOut",
55+
}}
56+
className="h-4 w-20 rounded bg-gray-200"
57+
/>
4258
</div>
4359
</div>
4460
</td>
4561

46-
{/* VotesBar */}
47-
<td
48-
className={classNames(
49-
isFirst ? "" : "border-t border-gray-200",
50-
"hidden px-3 py-3.5 text-sm text-gray-500 md:table-cell",
51-
)}
52-
>
53-
<ColorBar greenScore={0} redScore={0} noTooltip />
62+
<td className="hidden py-4 md:table-cell">
63+
<motion.div
64+
animate={{
65+
opacity: [0.5, 1, 0.5],
66+
}}
67+
transition={{
68+
repeat: Infinity,
69+
duration: 1.5,
70+
ease: "easeInOut",
71+
}}
72+
className="h-4 w-16 rounded bg-gray-200"
73+
/>
5474
</td>
5575

56-
{/* Votes */}
57-
<td
58-
className={classNames(
59-
isFirst ? "" : "border-t border-gray-200",
60-
"hidden px-3 py-3.5 text-center text-sm text-black md:table-cell",
61-
)}
62-
>
63-
<div className="flex justify-center">
64-
<p className="h-6 w-6 animate-pulse rounded-full bg-slate-200"></p>
65-
</div>
76+
<td className="hidden px-3 py-4 md:table-cell">
77+
<motion.div
78+
animate={{
79+
opacity: [0.5, 1, 0.5],
80+
}}
81+
transition={{
82+
repeat: Infinity,
83+
duration: 1.5,
84+
ease: "easeInOut",
85+
}}
86+
className="h-2 w-full rounded bg-gray-200"
87+
/>
6688
</td>
6789

68-
{/* VotedStatus or NewVoteButton */}
69-
<td
70-
className={classNames(
71-
isFirst ? "" : "border-t border-gray-200",
72-
"hidden px-3 py-3.5 text-center text-sm text-gray-500 md:table-cell",
73-
)}
74-
>
75-
<div className="flex justify-center">
76-
<p className="h-6 w-16 animate-pulse rounded bg-slate-200"></p>
77-
</div>
90+
<td className="hidden px-3 py-4 md:table-cell">
91+
<motion.div
92+
animate={{
93+
opacity: [0.5, 1, 0.5],
94+
}}
95+
transition={{
96+
repeat: Infinity,
97+
duration: 1.5,
98+
ease: "easeInOut",
99+
}}
100+
className="mx-auto h-4 w-12 rounded bg-gray-200"
101+
/>
102+
</td>
103+
104+
<td className="hidden px-3 py-4 md:table-cell">
105+
<motion.div
106+
animate={{
107+
opacity: [0.5, 1, 0.5],
108+
}}
109+
transition={{
110+
repeat: Infinity,
111+
duration: 1.5,
112+
ease: "easeInOut",
113+
}}
114+
className="mx-auto h-8 w-8 rounded bg-gray-200"
115+
/>
78116
</td>
79-
</tr>
117+
</motion.tr>
80118
);
81119
}

Diff for: package.json

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"gray-matter": "^4.0.3",
4545
"ioredis": "^5.3.2",
4646
"lightweight-charts": "^4.2.2",
47+
"motion": "^11.16.0",
4748
"next": "^14.2.5",
4849
"next-auth": "^4.24.7",
4950
"next-query-params": "^3.0.0",

0 commit comments

Comments
 (0)