Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 39 additions & 20 deletions src/lib/components/SpeakerList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { Dialog } from "@skeletonlabs/skeleton-svelte";
import { tick, type Snippet } from "svelte";
import { flip } from "svelte/animate";
import { slide } from "svelte/transition";

import DelCombobox from "$lib/components/controls/DelCombobox.svelte";
import DelLabel from "$lib/components/del-label/DelLabel.svelte";
Expand All @@ -33,20 +34,26 @@
*/
delegates?: Delegate[];
/**
* The controls at the bottom of the speaker list
* which handle the addition of new speakers.
* Specifies what delegates can be input in the combobox.
*
* If this prop is defined, this overrides the default add delegate controls
* provided by this component.
* If not specified, this falls back to `delegates`.
*/
controls?: Snippet;
comboboxDelegates?: Delegate[];

/**
* Controls placed above the add speakers control.
*
* If `controls` is defined, neither this nor the default add speakers control
* will appear.
*/
subcontrols?: Snippet;
* If enabled, the controls at the bottom of the speakers list
* (the "add speakers", "clear speakers", and "set first/last speaker" options)
* are hidden.
*/
hideControls?: boolean;

/**
* If provided, this displays the "set first/last speaker" option,
* which allows you to select whether this delegate is placed first or last
* in the list.
*/
proposer?: DelegateID;

/**
* The title of the component, which appears at the top.
*
Expand All @@ -73,8 +80,9 @@
let {
order = $bindable([]),
delegates = [],
controls = undefined,
subcontrols = undefined,
comboboxDelegates,
hideControls = false,
proposer = undefined,
title = "Speakers List",
extra,
onBeforeSpeakerUpdate = undefined,
Expand Down Expand Up @@ -204,7 +212,7 @@
return true;
}

export function addSpeakerFirst(key: number): boolean {
function addSpeakerFirst(key: number): boolean {
if (!delegates.some(k => k.id === key)) return false;

// Successful, so add speakers:
Expand All @@ -214,7 +222,7 @@

return true;
}
export function addSpeakerLast(key: number): boolean {
function addSpeakerLast(key: number): boolean {
if (!delegates.some(k => k.id === key)) return false;

// Successful, so add speakers:
Expand Down Expand Up @@ -359,15 +367,26 @@
</ol>
</DragDropProvider>

{#if controls}
{@render controls()}
{:else}
{#if !hideControls}
<div class="flex flex-col items-stretch gap-1">
{@render subcontrols?.()}
<!-- First/Last Speaker display -->
{#if typeof proposer !== "undefined" && !order.some(s => s.key == proposer)}
<div
class="card card-filled p-2 flex justify-between items-center preset-filled-surface-200-800"
transition:slide
>
<DelLabel attrs={findDelegate(delegates, proposer)} inline />
<div>
<button class="btn preset-filled-primary-500" onclick={() => addSpeakerFirst(proposer)}>First</button>
<button class="btn preset-filled-primary-500" onclick={() => addSpeakerLast(proposer)}>Last</button>
</div>
</div>
{/if}
<!-- Add/Clear Speakers list -->
<div class="flex flex-row gap-1 items-center">
<!-- Delegate combobox -->
<DelCombobox
{delegates}
delegates={comboboxDelegates ?? delegates}
selectionBehavior="clear"
class="grow"
forgetSelected
Expand Down
20 changes: 2 additions & 18 deletions src/lib/components/motions/page/ModCaucus.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,14 @@
- An editable speakers list
-->
<script lang="ts">
import DelLabel from "$lib/components/del-label/DelLabel.svelte";
import { numSpeakersStr } from "$lib/components/motions/form/MotionForm.svelte";
import TimerPanel from "$lib/components/motions/TimerPanel.svelte";
import SpeakerList from "$lib/components/SpeakerList.svelte";
import type Timer from "$lib/components/Timer.svelte";
import { getSessionContext } from "$lib/context/index.svelte";
import { findDelegate } from "$lib/db/delegates";
import { db } from "$lib/db/index.svelte";
import type { Motion, Speaker } from "$lib/types";
import { lazyslide } from "$lib/util";


interface Props {
motion: Motion & { kind: "mod" };
order: Speaker[];
Expand Down Expand Up @@ -81,27 +78,14 @@
<SpeakerList
bind:order
delegates={$delegates}
proposer={motion.delegate}
bind:this={speakersList}
onBeforeSpeakerUpdate={resetDel}
onMarkComplete={(key, isRepeat) => { if (!isRepeat) db.updateDelegate(key, d => { d.stats.timesSpoken++; }) }}
>
{#snippet title()}
Speakers List (<span class="tabular-nums">{order.length}/{numSpeakersStr(motion.totalTime, motion.speakingTime)}</span>)
{/snippet}
{#snippet subcontrols()}
{#if !order.some(s => s.key == motion.delegate)}
<div
class="card card-filled p-2 flex justify-between items-center preset-filled-surface-200-800"
transition:lazyslide
>
<DelLabel attrs={findDelegate($delegates, motion.delegate)} inline />
<div>
<button class="btn preset-filled-primary-500" onclick={() => speakersList?.addSpeakerFirst(motion.delegate)}>First</button>
<button class="btn preset-filled-primary-500" onclick={() => speakersList?.addSpeakerLast(motion.delegate)}>Last</button>
</div>
</div>
{/if}
{/snippet}
</SpeakerList>
</div>
</div>
14 changes: 9 additions & 5 deletions src/lib/components/motions/page/RoundRobin.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import { getSessionContext } from "$lib/context/index.svelte";
import { db } from "$lib/db/index.svelte";
import type { Motion, Speaker } from "$lib/types";

interface Props {
motion: Motion & { kind: "rr" };
order: Speaker[]
Expand All @@ -22,7 +22,11 @@

let timerPanel = $state<TimerPanel>();
let speakersList = $state<SpeakerList>();

const comboboxDelegates = $derived.by(() => {
let addedDelegates = new Set(order.map(s => s.key));
return $delegates.filter(d => !addedDelegates.has(d.id));
});

function reset() {
timerPanel?.reset();
}
Expand Down Expand Up @@ -58,11 +62,11 @@
<SpeakerList
bind:order
delegates={$delegates}
proposer={motion.delegate}
{comboboxDelegates}
bind:this={speakersList}
onBeforeSpeakerUpdate={reset}
onMarkComplete={(key, isRepeat) => { if (!isRepeat) db.updateDelegate(key, d => { d.stats.timesSpoken++; }) }}
>
{#snippet controls()}{/snippet}
</SpeakerList>
/>
</div>
</div>
6 changes: 1 addition & 5 deletions src/routes/dashboard/points-motions/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,7 @@
async function acceptMotion(motion: Motion) {
// Update selected motion and initialize selected motion state:
$selectedMotion = motion;
if (motion.kind === "rr") {
$selectedMotionState = { speakersList: $delegates.filter(d => d.isPresent()).map(s => createSpeaker(s.id)) };
} else {
$selectedMotionState = { speakersList: [] };
}
$selectedMotionState = { speakersList: [] };

$motions = [];
await db.updateDelegate(motion.delegate, d => { d.stats.motionsAccepted++; });
Expand Down
5 changes: 2 additions & 3 deletions src/routes/dashboard/vp-roll-call/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,8 @@
bind:this={rightsSpeakersList}
onBeforeSpeakerUpdate={() => rightsTimerPanel?.reset()}
onMarkComplete={(key, isRepeat) => { if (!isRepeat) db.updateDelegate(key, d => { d.stats.timesSpoken++; }) }}
>
{#snippet controls()}{/snippet}
</SpeakerList>
hideControls
/>
<!-- Timer config -->
<div class="flex flex-row gap-5">
<form class="contents" onsubmit={setDuration}>
Expand Down