2026-04-03 17:54:50 +00:00
|
|
|
<script lang="ts">
|
|
|
|
|
import {tweened} from "svelte/motion"
|
|
|
|
|
import type {TrustedEvent} from "@welshman/util"
|
2026-07-28 16:16:26 +00:00
|
|
|
import {noop, spec} from "@welshman/lib"
|
|
|
|
|
import {Poll} from "@welshman/domain"
|
2026-04-03 17:54:50 +00:00
|
|
|
import {stopPropagation} from "@lib/html"
|
2026-07-28 16:16:26 +00:00
|
|
|
import {reader} from "@app/core"
|
2026-04-03 17:54:50 +00:00
|
|
|
|
|
|
|
|
type Props = {
|
|
|
|
|
event: TrustedEvent
|
|
|
|
|
option: {id: string; label: string}
|
|
|
|
|
results: {voters: number; options: {id: string; votes: number}[]}
|
|
|
|
|
selectedIds: string[]
|
|
|
|
|
setSingleChoice: (id: string) => void
|
|
|
|
|
toggleMultipleChoice: (id: string) => void
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const {event, option, results, selectedIds, setSingleChoice, toggleMultipleChoice}: Props =
|
|
|
|
|
$props()
|
|
|
|
|
|
2026-07-28 16:16:26 +00:00
|
|
|
const poll = reader(Poll)(event)
|
|
|
|
|
const pollType = poll.pollType()
|
|
|
|
|
const closed = poll.isClosed()
|
2026-04-03 17:54:50 +00:00
|
|
|
|
|
|
|
|
const selected = $derived(
|
|
|
|
|
pollType === "singlechoice" ? selectedIds[0] === option.id : selectedIds.includes(option.id),
|
|
|
|
|
)
|
|
|
|
|
const onselect = () =>
|
|
|
|
|
pollType === "singlechoice" ? setSingleChoice(option.id) : toggleMultipleChoice(option.id)
|
|
|
|
|
|
2026-07-28 16:16:26 +00:00
|
|
|
const votes = $derived(results.options.find(spec({id: option.id}))?.votes || 0)
|
2026-04-03 17:54:50 +00:00
|
|
|
const maxVotes = $derived(Math.max(...results.options.map(r => r.votes), 1))
|
|
|
|
|
|
|
|
|
|
const tweenedVotes = tweened(votes, {duration: 300})
|
|
|
|
|
const tweenedMax = tweened(maxVotes, {duration: 300})
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
tweenedVotes.set(votes)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
tweenedMax.set(maxVotes)
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
2026-06-24 20:56:53 +00:00
|
|
|
<div class="flex flex-col gap-2 card card-sm">
|
2026-04-03 17:54:50 +00:00
|
|
|
<div class="flex items-center justify-between gap-2">
|
2026-04-07 21:53:19 +00:00
|
|
|
<label class="flex min-w-0 grow items-center gap-2">
|
2026-04-03 17:54:50 +00:00
|
|
|
{#if !closed}
|
|
|
|
|
{#if pollType === "singlechoice"}
|
|
|
|
|
<input
|
|
|
|
|
name={event.id}
|
|
|
|
|
type="radio"
|
2026-06-24 20:56:53 +00:00
|
|
|
class="radio"
|
2026-04-03 17:54:50 +00:00
|
|
|
checked={selected}
|
|
|
|
|
onclick={stopPropagation(noop)}
|
|
|
|
|
onchange={onselect} />
|
|
|
|
|
{:else}
|
|
|
|
|
<input
|
|
|
|
|
type="checkbox"
|
2026-06-24 20:56:53 +00:00
|
|
|
class="checkbox"
|
2026-04-03 17:54:50 +00:00
|
|
|
checked={selected}
|
|
|
|
|
onclick={stopPropagation(noop)}
|
|
|
|
|
onchange={onselect} />
|
|
|
|
|
{/if}
|
|
|
|
|
{/if}
|
|
|
|
|
<span class="truncate">{option.label}</span>
|
|
|
|
|
</label>
|
|
|
|
|
<span class="whitespace-nowrap text-xs opacity-75">{votes} vote{votes === 1 ? "" : "s"}</span>
|
|
|
|
|
</div>
|
2026-06-24 20:56:53 +00:00
|
|
|
<progress class="progress" value={$tweenedVotes} max={$tweenedMax}></progress>
|
2026-04-03 17:54:50 +00:00
|
|
|
</div>
|