import { Check } from 'lucide-react';
import type { ReactNode } from 'react';
import { fs } from '../shared/DialogShell';
import { Tooltip } from '../shared/Tooltip';
/**
* The pieces a poll of the trip is drawn with, shared by the Collab tab and
* the assistant chat's poll cards: an option with its bar, its check, its
* voters and its share, a voter's face and a fact on the poll's head band.
* An option is a button where it can be voted for and a plain row where it
* only shows; a vote the assistant holds marks its option with a dashed
* frame until it is decided.
*/
/** A voter as an option shows them: a name and the picture TREK serves, if any. */
interface PollVoterFace {
key: string | number;
name: string;
src: string | null;
}
/** One voter's face; faces after the first overlap the one before. */
function VoterChip({ name, src, offset }: { name: string; src: string | null; offset: boolean }) {
return (
{src ? (
) : (
(name || '?').charAt(0).toUpperCase()
)}
);
}
/** The voters past the third, as a count on the end of the stack; their names on hover. */
function MoreVoters({ names }: { names: string[] }) {
return (
name || '?').join(', ')}>
+{names.length}
);
}
/** A fact about the poll on its head band, set like the type chip on a booking card. */
export function PollChip({
icon,
tone = 'muted',
children,
}: {
icon?: ReactNode;
tone?: 'muted' | 'warning' | 'info';
children: ReactNode;
}) {
const color = tone === 'warning' ? 'text-warning' : tone === 'info' ? 'text-info' : 'text-content-muted';
return (
{icon}
{children}
);
}
interface PollOptionRowProps {
text: string;
/** Its share of all votes, which the bar draws. */
pct: number;
/** The signed-in user voted for it. */
mine: boolean;
/** The poll ended and it has the most votes. */
winner: boolean;
/** Shares and voters show once the user voted or the poll ended. */
showResults: boolean;
voters: readonly PollVoterFace[];
/** A button to vote with, or a row that only shows. */
as?: 'button' | 'div';
/** A vote waiting for its decision: the frame is dashed, and says so to a screen reader. */
pending?: { label: string } | null;
disabled?: boolean;
onClick?: () => void;
}
/** One option of a poll, as the Collab tab draws it. */
export function PollOptionRow({
text,
pct,
mine,
winner,
showResults,
voters,
as = 'button',
pending = null,
disabled = false,
onClick,
}: PollOptionRowProps) {
const frame = pending
? 'border-dashed border-accent'
: mine
? 'border-accent'
: 'border-edge-faint enabled:hover:border-edge';
const body = (
<>
{/* The bar behind it: the user's choice in the accent, the winner in green. */}
{mine && }
{/* Plain text, but a long or multiline option wraps instead of being clipped (#2177). */}
{text}
{voters.length > 0 && showResults && (
{voters.slice(0, 3).map((voter, index) => (
0} />
))}
{voters.length > 3 && voter.name)} />}
)}
{pending && {pending.label}}
{showResults && (
{pct}%
)}
>
);
const look = `relative flex w-full items-start gap-2.5 overflow-hidden rounded-[10px] border bg-surface-secondary px-3 py-2.5 text-start transition-colors disabled:cursor-default ${frame}`;
if (as === 'div') return