Beautify poll

This commit is contained in:
Lim Chee Aun 2023-03-30 11:11:35 +08:00
parent c2ee8c55d3
commit d2214c59be
2 changed files with 104 additions and 74 deletions

View file

@ -843,34 +843,48 @@ a.card:is(:hover, :focus) {
.poll.read-only { .poll.read-only {
pointer-events: none; pointer-events: none;
} }
.poll-options {
display: flex;
flex-direction: column;
gap: 4px;
padding: 4px;
border-radius: 16px;
border: 1px solid var(--outline-color);
background-color: var(--bg-faded-color);
}
.poll-option { .poll-option {
padding: 8px; padding: 4px;
display: flex; display: flex;
gap: 8px; gap: 8px;
justify-content: space-between; justify-content: space-between;
background-color: var(--bg-faded-color);
background-image: linear-gradient(
to right,
var(--link-faded-color),
var(--link-faded-color) var(--percentage),
transparent var(--percentage),
transparent
);
background-repeat: no-repeat;
/* border-radius: 8px; */
border: 1px solid var(--outline-color);
border-bottom: 0;
align-items: center; align-items: center;
text-shadow: 0 1px var(--bg-blur-color); position: relative;
} }
.poll-option:first-child { .poll-option:after {
border-top-left-radius: 8px; content: '';
border-top-right-radius: 8px; position: absolute;
inset: 0;
border-radius: 4px;
background-color: var(--link-faded-color);
opacity: 0;
pointer-events: none;
transition: all 0.2s ease-in-out;
mix-blend-mode: multiply;
} }
.poll-option:last-of-type { .poll-option:first-child:after {
border-bottom: 1px solid var(--outline-color); border-top-left-radius: 12px;
border-bottom-left-radius: 8px; border-top-right-radius: 12px;
border-bottom-right-radius: 8px; }
.poll-option:last-child:after {
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
}
.poll-option:hover:after {
opacity: 1;
}
.poll-option.poll-result:after {
width: var(--percentage);
opacity: 1;
} }
.poll-label { .poll-label {
width: 100%; width: 100%;
@ -897,6 +911,9 @@ a.card:is(:hover, :focus) {
margin: 8px 0; margin: 8px 0;
font-size: 90%; font-size: 90%;
} }
.poll-option-title {
text-shadow: 0 1px var(--bg-color);
}
.poll-option-title .icon { .poll-option-title .icon {
vertical-align: middle; vertical-align: middle;
} }

View file

@ -1309,53 +1309,64 @@ function Poll({
roundPrecision = 2; roundPrecision = 2;
} }
const [showResults, setShowResults] = useState(false);
const optionsHaveVoteCounts = options.every((o) => o.votesCount !== null);
return ( return (
<div <div
lang={lang} lang={lang}
class={`poll ${readOnly ? 'read-only' : ''} ${ class={`poll ${readOnly ? 'read-only' : ''} ${
uiState === 'loading' ? 'loading' : '' uiState === 'loading' ? 'loading' : ''
}`} }`}
onDblClick={() => {
setShowResults(!showResults);
}}
> >
{voted || expired ? ( {(showResults && optionsHaveVoteCounts) || voted || expired ? (
options.map((option, i) => { <div class="poll-options">
const { title, votesCount: optionVotesCount } = option; {options.map((option, i) => {
const percentage = pollVotesCount const { title, votesCount: optionVotesCount } = option;
? ((optionVotesCount / pollVotesCount) * 100).toFixed( const percentage = pollVotesCount
roundPrecision, ? ((optionVotesCount / pollVotesCount) * 100).toFixed(
) roundPrecision,
: 0; )
// check if current poll choice is the leading one : 0;
const isLeading = // check if current poll choice is the leading one
optionVotesCount > 0 && const isLeading =
optionVotesCount === Math.max(...options.map((o) => o.votesCount)); optionVotesCount > 0 &&
return ( optionVotesCount ===
<div Math.max(...options.map((o) => o.votesCount));
key={`${i}-${title}-${optionVotesCount}`} return (
class={`poll-option ${isLeading ? 'poll-option-leading' : ''}`}
style={{
'--percentage': `${percentage}%`,
}}
>
<div class="poll-option-title">
{title}
{voted && ownVotes.includes(i) && (
<>
{' '}
<Icon icon="check-circle" />
</>
)}
</div>
<div <div
class="poll-option-votes" key={`${i}-${title}-${optionVotesCount}`}
title={`${optionVotesCount} vote${ class={`poll-option poll-result ${
optionVotesCount === 1 ? '' : 's' isLeading ? 'poll-option-leading' : ''
}`} }`}
style={{
'--percentage': `${percentage}%`,
}}
> >
{percentage}% <div class="poll-option-title">
{title}
{voted && ownVotes.includes(i) && (
<>
{' '}
<Icon icon="check-circle" />
</>
)}
</div>
<div
class="poll-option-votes"
title={`${optionVotesCount} vote${
optionVotesCount === 1 ? '' : 's'
}`}
>
{percentage}%
</div>
</div> </div>
</div> );
); })}
}) </div>
) : ( ) : (
<form <form
onSubmit={async (e) => { onSubmit={async (e) => {
@ -1374,23 +1385,25 @@ function Poll({
setUIState('default'); setUIState('default');
}} }}
> >
{options.map((option, i) => { <div class="poll-options">
const { title } = option; {options.map((option, i) => {
return ( const { title } = option;
<div class="poll-option"> return (
<label class="poll-label"> <div class="poll-option">
<input <label class="poll-label">
type={multiple ? 'checkbox' : 'radio'} <input
name="poll" type={multiple ? 'checkbox' : 'radio'}
value={i} name="poll"
disabled={uiState === 'loading'} value={i}
readOnly={readOnly} disabled={uiState === 'loading'}
/> readOnly={readOnly}
<span class="poll-option-title">{title}</span> />
</label> <span class="poll-option-title">{title}</span>
</div> </label>
); </div>
})} );
})}
</div>
{!readOnly && ( {!readOnly && (
<button <button
class="poll-vote-button" class="poll-vote-button"