feat(polls): let creators set max selections for multiple-choice

The poll creator only offered single (max_selections 1) or multiple = pick ALL
options — no way to run a "pick your top 2" poll, even though the display side
already enforces an arbitrary max_selections ("Select up to N"). Add a "Voters
can pick up to N of M options" control shown for multiple-choice polls. Defaults
to the option count (preserving the old select-all behavior) until lowered;
clamped to [2, filled option count] on submit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 16:06:32 -04:00
co-authored by Claude Opus 4.8
parent 85ac8de5d9
commit a4660a8163
2 changed files with 34 additions and 1 deletions
+33 -1
View File
@@ -34,6 +34,10 @@ export function PollCreator({ roomId, onClose }: PollCreatorProps) {
const [question, setQuestion] = useState('');
const [options, setOptions] = useState<string[]>(['', '']);
const [isMultiple, setIsMultiple] = useState(false);
// For multiple-choice polls: the most options a voter may pick. Defaults high
// so an untouched multiple poll means "select all that apply" (the previous
// behavior); the effective value is clamped to the current option count.
const [maxSelections, setMaxSelections] = useState(10);
// Results visibility: disclosed (live results, default) vs undisclosed (hidden
// until the poll is ended).
const [disclosed, setDisclosed] = useState(true);
@@ -85,7 +89,9 @@ export function PollCreator({ roomId, onClose }: PollCreatorProps) {
'm.poll': {
question: { 'm.text': trimmedQuestion },
answers: filledOptions.map((o, i) => ({ 'm.id': `${i}`, 'm.text': o })),
max_selections: isMultiple ? filledOptions.length : 1,
max_selections: isMultiple
? Math.min(Math.max(2, maxSelections), filledOptions.length)
: 1,
kind: disclosed ? 'm.poll.disclosed' : 'm.poll.undisclosed',
},
body: fallbackBody,
@@ -222,6 +228,32 @@ export function PollCreator({ roomId, onClose }: PollCreatorProps) {
);
})}
</Box>
{isMultiple && (
<Box alignItems="Center" gap="200" style={{ marginTop: config.space.S200 }}>
<Text as="label" htmlFor="poll-max-select" size="T200" priority="400">
Voters can pick up to
</Text>
<Input
id="poll-max-select"
variant="Background"
size="300"
type="number"
min={2}
max={options.length}
value={Math.min(maxSelections, options.length)}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setMaxSelections(
Math.min(options.length, Math.max(2, parseInt(e.target.value, 10) || 2)),
)
}
style={{ width: '4rem' }}
aria-label="Maximum selections per voter"
/>
<Text size="T200" priority="300">
of {options.length} options
</Text>
</Box>
)}
</Box>
{/* Results visibility */}