|
@ -1,18 +1,24 @@ |
|
|
import React, { useEffect, useMemo, useState } from 'react'; |
|
|
import React, { |
|
|
|
|
|
useCallback, useEffect, useMemo, useState, |
|
|
|
|
|
} from 'react'; |
|
|
import { |
|
|
import { |
|
|
Grid, Header, Statistic, Tab, |
|
|
Grid, |
|
|
|
|
|
Header, Statistic, Tab, |
|
|
} from 'semantic-ui-react'; |
|
|
} from 'semantic-ui-react'; |
|
|
import PropTypes from 'prop-types'; |
|
|
import PropTypes from 'prop-types'; |
|
|
import { useTranslation } from 'react-i18next'; |
|
|
import { useTranslation } from 'react-i18next'; |
|
|
import PollChartBar from './PollChartBar'; |
|
|
import PollChartBar from './PollChartBar'; |
|
|
import PollChartDonut from './PollChartDonut'; |
|
|
import PollChartDonut from './PollChartDonut'; |
|
|
import './styles.css'; |
|
|
import './styles.css'; |
|
|
|
|
|
import { CHART_TYPE_BAR, CHART_TYPE_DONUT } from '../../../constants/polls/PollGraph'; |
|
|
|
|
|
|
|
|
const PollGraph = (props) => { |
|
|
const PollGraph = (props) => { |
|
|
const { |
|
|
const { |
|
|
pollOptions, userVoteIndex, voteCounts, voterNames, |
|
|
pollOptions, userVoteIndex, voteCounts, voterNames, |
|
|
} = props; |
|
|
} = props; |
|
|
const [totalVotes, setTotalVotes] = useState(voteCounts.reduce((accumulator, voteCount) => accumulator + voteCount, 0)); |
|
|
const [totalVotes, setTotalVotes] = useState( |
|
|
|
|
|
voteCounts.reduce((accumulator, voteCount) => accumulator + voteCount, 0), |
|
|
|
|
|
); |
|
|
const { t } = useTranslation(); |
|
|
const { t } = useTranslation(); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
@ -20,7 +26,7 @@ const PollGraph = (props) => { |
|
|
}, [voteCounts]); |
|
|
}, [voteCounts]); |
|
|
|
|
|
|
|
|
const footer = useMemo(() => ( |
|
|
const footer = useMemo(() => ( |
|
|
<> |
|
|
<div> |
|
|
<Statistic size="mini"> |
|
|
<Statistic size="mini"> |
|
|
<Statistic.Value> |
|
|
<Statistic.Value> |
|
|
{ totalVotes } |
|
|
{ totalVotes } |
|
@ -36,84 +42,88 @@ const PollGraph = (props) => { |
|
|
<span className="poll-voted-option">{pollOptions[userVoteIndex]}</span> |
|
|
<span className="poll-voted-option">{pollOptions[userVoteIndex]}</span> |
|
|
</div> |
|
|
</div> |
|
|
)} |
|
|
)} |
|
|
</> |
|
|
</div> |
|
|
), [pollOptions, t, totalVotes, userVoteIndex]); |
|
|
), [pollOptions, t, totalVotes, userVoteIndex]); |
|
|
|
|
|
|
|
|
const panes = useMemo(() => { |
|
|
function calculateChartBarWidth(nOptions) { |
|
|
// TODO: tidy up duplicated code below using CHART_TYPE_BAR and CHART_TYPE_DONUT |
|
|
if (nOptions < 6) return 8; |
|
|
const chartBarPane = ( |
|
|
if (nOptions < 10) return 10; |
|
|
|
|
|
if (nOptions < 14) return 12; |
|
|
|
|
|
if (nOptions < 18) return 14; |
|
|
|
|
|
return 16; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function calculateChartDonutWidth(nOptions) { |
|
|
|
|
|
if (nOptions < 10) return 8; |
|
|
|
|
|
if (nOptions < 16) return 10; |
|
|
|
|
|
return 12; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const generatePane = useCallback( |
|
|
|
|
|
(type) => ( |
|
|
<Tab.Pane attached={false}> |
|
|
<Tab.Pane attached={false}> |
|
|
<Grid columns="equal"> |
|
|
<Grid columns="equal"> |
|
|
<Grid.Row> |
|
|
<Grid.Row> |
|
|
<Grid.Column /> |
|
|
<Grid.Column /> |
|
|
<Grid.Column width={8} textAlign="center"> |
|
|
<Grid.Column |
|
|
{totalVotes > 0 |
|
|
width={type === CHART_TYPE_BAR |
|
|
|
|
|
? calculateChartBarWidth(pollOptions.length) |
|
|
|
|
|
: calculateChartDonutWidth(pollOptions.length)} |
|
|
|
|
|
textAlign="center" |
|
|
|
|
|
> |
|
|
|
|
|
{type === CHART_TYPE_BAR |
|
|
? ( |
|
|
? ( |
|
|
<PollChartBar |
|
|
<PollChartBar |
|
|
pollOptions={pollOptions} |
|
|
pollOptions={pollOptions} |
|
|
voteCounts={voteCounts} |
|
|
voteCounts={voteCounts} |
|
|
voterNames={voterNames} |
|
|
voterNames={voterNames} |
|
|
/> |
|
|
/> |
|
|
) |
|
|
) : ( |
|
|
: ( |
|
|
|
|
|
<Header as="h4"> |
|
|
|
|
|
{t('topic.poll.tab.results.no.votes')} |
|
|
|
|
|
</Header> |
|
|
|
|
|
)} |
|
|
|
|
|
</Grid.Column> |
|
|
|
|
|
<Grid.Column /> |
|
|
|
|
|
</Grid.Row> |
|
|
|
|
|
<Grid.Row> |
|
|
|
|
|
<Grid.Column textAlign="center"> |
|
|
|
|
|
{totalVotes > 0 && footer} |
|
|
|
|
|
</Grid.Column> |
|
|
|
|
|
</Grid.Row> |
|
|
|
|
|
</Grid> |
|
|
|
|
|
</Tab.Pane> |
|
|
|
|
|
); |
|
|
|
|
|
const chartDonutPane = ( |
|
|
|
|
|
<Tab.Pane attached={false}> |
|
|
|
|
|
<Grid columns="equal"> |
|
|
|
|
|
<Grid.Row> |
|
|
|
|
|
<Grid.Column /> |
|
|
|
|
|
<Grid.Column width={8} textAlign="center"> |
|
|
|
|
|
{totalVotes > 0 |
|
|
|
|
|
? ( |
|
|
|
|
|
<PollChartDonut |
|
|
<PollChartDonut |
|
|
pollOptions={pollOptions} |
|
|
pollOptions={pollOptions} |
|
|
voteCounts={voteCounts} |
|
|
voteCounts={voteCounts} |
|
|
voterNames={voterNames} |
|
|
voterNames={voterNames} |
|
|
/> |
|
|
/> |
|
|
) |
|
|
|
|
|
: ( |
|
|
|
|
|
<Header as="h4"> |
|
|
|
|
|
{t('topic.poll.tab.results.no.votes')} |
|
|
|
|
|
</Header> |
|
|
|
|
|
)} |
|
|
)} |
|
|
</Grid.Column> |
|
|
</Grid.Column> |
|
|
<Grid.Column /> |
|
|
<Grid.Column /> |
|
|
</Grid.Row> |
|
|
</Grid.Row> |
|
|
<Grid.Row> |
|
|
<Grid.Row> |
|
|
<Grid.Column textAlign="center"> |
|
|
<Grid.Column textAlign="center"> |
|
|
{totalVotes > 0 && footer} |
|
|
{footer} |
|
|
</Grid.Column> |
|
|
</Grid.Column> |
|
|
</Grid.Row> |
|
|
</Grid.Row> |
|
|
</Grid> |
|
|
</Grid> |
|
|
</Tab.Pane> |
|
|
</Tab.Pane> |
|
|
|
|
|
), |
|
|
|
|
|
[footer, pollOptions, voteCounts, voterNames], |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
const panes = useMemo(() => { |
|
|
|
|
|
const chartBarPane = generatePane(CHART_TYPE_BAR); |
|
|
|
|
|
const chartDonutPane = generatePane(CHART_TYPE_DONUT); |
|
|
|
|
|
|
|
|
return ([ |
|
|
return ([ |
|
|
{ menuItem: { key: 'chart-bar', icon: 'chart bar' }, render: () => chartBarPane }, |
|
|
{ menuItem: { key: 'chart-bar', icon: 'chart bar' }, render: () => chartBarPane }, |
|
|
{ menuItem: { key: 'chart-donut', icon: 'chart pie' }, render: () => chartDonutPane }, |
|
|
{ menuItem: { key: 'chart-donut', icon: 'chart pie' }, render: () => chartDonutPane }, |
|
|
]); |
|
|
]); |
|
|
}, [footer, pollOptions, t, totalVotes, voteCounts, voterNames]); |
|
|
}, [generatePane]); |
|
|
|
|
|
return useMemo(() => ( |
|
|
return ( |
|
|
<> |
|
|
|
|
|
{totalVotes > 0 |
|
|
|
|
|
? ( |
|
|
<Tab |
|
|
<Tab |
|
|
menu={{ secondary: true }} |
|
|
menu={{ secondary: true }} |
|
|
panes={panes} |
|
|
panes={panes} |
|
|
/> |
|
|
/> |
|
|
); |
|
|
) |
|
|
|
|
|
: ( |
|
|
|
|
|
<Header as="h4" textAlign="center"> |
|
|
|
|
|
{t('topic.poll.tab.results.no.votes')} |
|
|
|
|
|
</Header> |
|
|
|
|
|
)} |
|
|
|
|
|
</> |
|
|
|
|
|
), [panes, t, totalVotes]); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
PollGraph.defaultProps = { |
|
|
PollGraph.defaultProps = { |
|
|