mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-03-22 21:59:22 +01:00
Beautify poll
This commit is contained in:
parent
c2ee8c55d3
commit
d2214c59be
2 changed files with 104 additions and 74 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue