Skip to content

Commit

Permalink
DAO-533: Implement Popover for Sentiment component (#64)
Browse files Browse the repository at this point in the history
* show popover in sentiment component

* custom popover position, size and background

* place popover at the bottom depending of the index

* votes amount position

* add caret

* remove unused prop
  • Loading branch information
rodrigoncalves authored Jul 22, 2024
1 parent 4ff3921 commit 39c4b3c
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 37 deletions.
93 changes: 67 additions & 26 deletions src/app/proposals/LatestProposalsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { getEventArguments } from '@/app/proposals/shared/utils'
import { Header } from '@/components/Typography'
import { Table } from '@/components/Table'
import { useFetchLatestProposals } from '@/app/proposals/hooks/useFetchLatestProposals'
import { Link } from '@/components/Link'
import { useGetProposalVotes } from '@/app/proposals/hooks/useGetProposalVotes'
import { useMemo } from 'react'
import { getEventArguments } from '@/app/proposals/shared/utils'
import { ComparativeProgressBar } from '@/components/ComparativeProgressBar/ComparativeProgressBar'
import { StatusColumn } from '@/app/proposals/StatusColumn'
import { Link } from '@/components/Link'
import { Popover } from '@/components/Popover'
import { Table } from '@/components/Table'
import { Header, Paragraph } from '@/components/Typography'
import { useMemo } from 'react'

interface ProposalNameColumnProps {
name: string
Expand All @@ -19,45 +20,85 @@ const ProposalNameColumn = ({ name, proposalId }: ProposalNameColumnProps) => (

const VotesColumn = ({ proposalId }: Omit<ProposalNameColumnProps, 'name'>) => {
const data = useGetProposalVotes(proposalId)

const votes = useMemo(() => {
if (data?.length === 3) {
return data.reduce((prev, next) => Number(next) + prev, 0)
}
return 0n
}, [data])

const votes = data.reduce((prev, next) => Number(next) + prev, 0)
return <p>{votes.toString()}</p>
}

const SentimentColumn = ({ proposalId }: Omit<ProposalNameColumnProps, 'name'>) => {
const PopoverSentiment = ({ votes }: { votes: string[] }) => {
const [againstVotes, forVotes, abstainVotes] = votes
return (
<div className="text-black">
<Paragraph variant="semibold" className="text-[12px] font-bold">
Votes for
</Paragraph>
<div className="flex flex-row">
<Paragraph variant="semibold" className="text-[12px] w-1/2 text-st-success">
For
</Paragraph>
<Paragraph variant="semibold" className="text-[12px] w-1/2">
{forVotes}
</Paragraph>
</div>
<div className="flex flex-row">
<Paragraph variant="semibold" className="text-[12px] w-1/2 text-st-error">
Against
</Paragraph>
<Paragraph variant="semibold" className="text-[12px] w-1/2">
{againstVotes}
</Paragraph>
</div>
<div className="flex flex-row">
<Paragraph variant="semibold" className="text-[12px] w-1/2 text-st-info">
Abstain
</Paragraph>
<Paragraph variant="semibold" className="text-[12px] w-1/2">
{abstainVotes}
</Paragraph>
</div>
</div>
)
}

const SentimentColumn = ({
proposalId,
index,
}: Omit<ProposalNameColumnProps, 'name'> & { index: number }) => {
const data = useGetProposalVotes(proposalId)

const sentimentValues = useMemo(() => {
if (data?.length === 3) {
const [againstVotes, forVotes, abstainVotes] = data
return [
{ value: Number(forVotes), color: 'var(--st-success)' },
{ value: Number(againstVotes), color: 'var(--st-error)' },
{ value: Number(abstainVotes), color: 'var(--st-info)' },
]
}
return [{ value: 0, color: 'var(--st-info)' }]
const [againstVotes, forVotes, abstainVotes] = data
return [
{ value: Number(forVotes), color: 'var(--st-success)' },
{ value: Number(againstVotes), color: 'var(--st-error)' },
{ value: Number(abstainVotes), color: 'var(--st-info)' },
]
}, [data])

return <ComparativeProgressBar values={sentimentValues} />
const position = index === 0 ? 'bottom' : 'top'
return (
<Popover
content={<PopoverSentiment votes={data} />}
trigger="hover"
background="light"
position={position}
size="small"
hasCaret={true}
>
<ComparativeProgressBar values={sentimentValues} />
</Popover>
)
}

interface LatestProposalsTableProps {
latestProposals: ReturnType<typeof useFetchLatestProposals>['latestProposals']
}

const latestProposalsTransformer = (proposals: ReturnType<typeof getEventArguments>[]) =>
proposals.map(proposal => ({
proposals.map((proposal, i) => ({
'Proposal Name': <ProposalNameColumn {...proposal} />,
'Current Votes': <VotesColumn {...proposal} />,
Starts: proposal.Starts,
Sentiment: <SentimentColumn {...proposal} />,
Sentiment: <SentimentColumn {...proposal} index={i} />,
Status: <StatusColumn {...proposal} />,
}))

Expand Down
2 changes: 1 addition & 1 deletion src/app/proposals/hooks/useGetProposalVotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ export const useGetProposalVotes = (proposalId: string, shouldRefetch = false) =
if (data) {
return [formatUnits(data[0], 18), formatUnits(data[1], 18), formatUnits(data[2], 18)]
}
return [0n, 0n, 0n]
return ['0', '0', '0']
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { JSX, FC } from 'react'
const DEFAULT_CLASSES = 'w-100 rounded-[6px] bg-white h-[6px] rounded-[20px] relative flex overflow-hidden'
const DEFAULT_CLASSES = 'rounded-[6px] bg-white h-[6px] rounded-[20px] relative flex overflow-hidden'

type Value = {
value: number
Expand Down
57 changes: 48 additions & 9 deletions src/components/Popover/Popover.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
'use client'
import { cn } from '@/lib/utils'
import { ReactNode, useEffect, useRef, useState } from 'react'

interface Props {
children: ReactNode
content: ReactNode
trigger?: 'click' | 'hover'
background?: 'dark' | 'light'
position?: 'top' | 'bottom'
size?: 'small' | 'medium'
hasCaret?: boolean
}

export const Popover = ({ children, content, trigger = 'click' }: Props) => {
export const Popover = ({
children,
content,
trigger = 'click',
background = 'dark',
position = 'bottom',
size = 'medium',
hasCaret = false,
}: Props) => {
const [show, setShow] = useState(false)
const wrapperRef = useRef<any>(null)

Expand Down Expand Up @@ -39,18 +52,44 @@ export const Popover = ({ children, content, trigger = 'click' }: Props) => {
}, [show, wrapperRef])

return (
<div
ref={wrapperRef}
onMouseEnter={handleMouseOver}
onMouseLeave={handleMouseLeft}
className="w-fit h-fit relative flex justify-center"
>
<div ref={wrapperRef} onMouseEnter={handleMouseOver} onMouseLeave={handleMouseLeft} className="relative">
<div onClick={() => setShow(!show)}>{children}</div>
<div hidden={!show} className="min-w-fit w-96 h-fit absolute top-full z-50 transition-all">
<div className="rounded bg-zinc-900 p-3 shadow-[10px_30px_150px_rgba(46,38,92,0.25)] mb-[10px]">
<div
hidden={!show}
className={cn(
'absolute z-50 transition-all',
position === 'top' && 'bottom-full',
position === 'bottom' && 'top-full',
size === 'small' && 'w-36',
size === 'medium' && 'w-96',
)}
style={{ top: position === 'bottom' && hasCaret ? '15px' : '' }}
>
<div
className={cn(
'rounded bg-zinc-900 p-2 shadow-[10px_30px_150px_rgba(46,38,92,0.25)] mb-[10px]',
background === 'light' && 'bg-white',
)}
>
{content}
{hasCaret && <PopoverCaret position={position} />}
</div>
</div>
</div>
)
}

const PopoverCaret = ({ position }: { position: 'top' | 'bottom' }) => (
<>
{position === 'top' && (
<div className="absolute inset-x-0 flex justify-center" style={{ bottom: '2px' }}>
<div className="w-0 h-0 border-t-8 border-t-white border-x-8 border-x-transparent"></div>
</div>
)}
{position === 'bottom' && (
<div className="absolute inset-x-0 flex justify-center" style={{ top: '-8px' }}>
<div className="w-0 h-0 border-b-8 border-b-white border-x-8 border-x-transparent"></div>
</div>
)}
</>
)

0 comments on commit 39c4b3c

Please sign in to comment.