Private
Public Access
1
0

Replace bulk select button with hover checkboxes, remove board background override

- Cards now show a checkbox on hover (top-right) for bulk selection,
  removing the need for a separate "Select" toggle button
- Removed BoardBackgroundPicker from board header since per-board
  backgrounds were overriding tenant branding
- Removed inline background style from board area so branding page
  backgrounds apply correctly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Catherine Renelle
2026-02-27 10:58:46 -05:00
parent 8c41b33313
commit 986c88143a
+30 -24
View File
@@ -10,7 +10,6 @@
import CardModal from '$lib/components/CardModal.svelte'; import CardModal from '$lib/components/CardModal.svelte';
import ActivityFeed from '$lib/components/ActivityFeed.svelte'; import ActivityFeed from '$lib/components/ActivityFeed.svelte';
import BoardFilters from '$lib/components/BoardFilters.svelte'; import BoardFilters from '$lib/components/BoardFilters.svelte';
import BoardBackgroundPicker from '$lib/components/BoardBackgroundPicker.svelte';
import BulkToolbar from '$lib/components/BulkToolbar.svelte'; import BulkToolbar from '$lib/components/BulkToolbar.svelte';
import ColumnSortMenu from '$lib/components/ColumnSortMenu.svelte'; import ColumnSortMenu from '$lib/components/ColumnSortMenu.svelte';
import Avatar from '$lib/components/Avatar.svelte'; import Avatar from '$lib/components/Avatar.svelte';
@@ -38,8 +37,8 @@
let showArchivedCards = $state(false); let showArchivedCards = $state(false);
let archivedCards = $state<any[]>([]); let archivedCards = $state<any[]>([]);
let activeFilters = $state<FilterState>({ labelIds: [], assigneeIds: [], due: null }); let activeFilters = $state<FilterState>({ labelIds: [], assigneeIds: [], due: null });
let selectMode = $state(false);
let selectedCardIds = $state(new Set<string>()); let selectedCardIds = $state(new Set<string>());
let selectMode = $derived(selectedCardIds.size > 0);
let columnSorts = $state<Record<string, string | null>>({}); let columnSorts = $state<Record<string, string | null>>({});
const flipDurationMs = 200; const flipDurationMs = 200;
@@ -488,21 +487,6 @@
</div> </div>
{/if} {/if}
{#if data.canEdit}
<button
onclick={() => { selectMode = !selectMode; if (!selectMode) selectedCardIds = new Set(); }}
class="rounded px-2 py-1 text-sm text-gray-500 hover:bg-gray-200 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition flex items-center gap-1"
class:bg-gray-200={selectMode}
title="Select cards for bulk actions"
>
<svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
Select
</button>
<BoardBackgroundPicker boardId={data.board.id} currentBackground={data.board.background || null} />
{/if}
<button <button
onclick={() => toggleArchivedCards()} onclick={() => toggleArchivedCards()}
class="ml-auto rounded px-2 py-1 text-sm text-gray-500 hover:bg-gray-200 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition flex items-center gap-1" class="ml-auto rounded px-2 py-1 text-sm text-gray-500 hover:bg-gray-200 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition flex items-center gap-1"
@@ -554,7 +538,7 @@
</div> </div>
<!-- Main area: columns + optional activity sidebar --> <!-- Main area: columns + optional activity sidebar -->
<div class="flex-1 flex overflow-hidden" style="{data.board.background ? `background: ${data.board.background}; background-size: cover; background-position: center;` : ''}"> <div class="flex-1 flex overflow-hidden">
<!-- Columns container --> <!-- Columns container -->
<div class="flex-1 overflow-x-auto p-4"> <div class="flex-1 overflow-x-auto p-4">
<div <div
@@ -644,8 +628,8 @@
{#each visibleCards as card (card.id)} {#each visibleCards as card (card.id)}
{@const progress = getChecklistProgress(card)} {@const progress = getChecklistProgress(card)}
{@const dueUrgency = getDueUrgency(card.dueDate)} {@const dueUrgency = getDueUrgency(card.dueDate)}
<button <div
class="w-full text-left mb-2 p-3 shadow-sm hover:shadow-md transition cursor-pointer {selectMode && selectedCardIds.has(card.id) ? 'ring-2 ring-[var(--color-primary)]' : ''}" class="group/card relative w-full text-left mb-2 p-3 shadow-sm hover:shadow-md transition cursor-pointer {selectedCardIds.has(card.id) ? 'ring-2 ring-[var(--color-primary)]' : ''}"
style="background: var(--color-card-bg); border: 1px solid var(--color-card-border); color: var(--color-card-text); border-radius: var(--color-card-radius); backdrop-filter: var(--card-backdrop-blur, none);" style="background: var(--color-card-bg); border: 1px solid var(--color-card-border); color: var(--color-card-text); border-radius: var(--color-card-radius); backdrop-filter: var(--card-backdrop-blur, none);"
animate:flip={{ duration: flipDurationMs }} animate:flip={{ duration: flipDurationMs }}
onclick={() => { onclick={() => {
@@ -658,7 +642,32 @@
selectedCard = card; selectedCard = card;
} }
}} }}
role="button"
tabindex="0"
onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); selectedCard = card; } }}
> >
{#if data.canEdit}
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="absolute top-1.5 right-1.5 z-10 {selectedCardIds.has(card.id) ? 'opacity-100' : 'opacity-0 group-hover/card:opacity-100'} transition-opacity"
onclick={(e) => {
e.stopPropagation();
const next = new Set(selectedCardIds);
if (next.has(card.id)) next.delete(card.id);
else next.add(card.id);
selectedCardIds = next;
}}
>
<div class="w-5 h-5 rounded border-2 flex items-center justify-center cursor-pointer {selectedCardIds.has(card.id) ? 'bg-[var(--color-primary)] border-[var(--color-primary)]' : 'border-gray-300 dark:border-gray-500 bg-white dark:bg-gray-700 hover:border-[var(--color-primary)]'} transition">
{#if selectedCardIds.has(card.id)}
<svg class="w-3 h-3 text-white" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
{/if}
</div>
</div>
{/if}
{#if card.labels && card.labels.length > 0} {#if card.labels && card.labels.length > 0}
<div class="flex flex-wrap gap-1 mb-1.5"> <div class="flex flex-wrap gap-1 mb-1.5">
{#each card.labels as label} {#each card.labels as label}
@@ -722,7 +731,7 @@
{/if} {/if}
</div> </div>
{/if} {/if}
</button> </div>
{/each} {/each}
</div> </div>
@@ -863,13 +872,10 @@
{selectedCardIds} {selectedCardIds}
{columns} {columns}
onaction={() => { onaction={() => {
selectMode = false;
selectedCardIds = new Set(); selectedCardIds = new Set();
// Reload page data
window.location.reload(); window.location.reload();
}} }}
oncancel={() => { oncancel={() => {
selectMode = false;
selectedCardIds = new Set(); selectedCardIds = new Set();
}} }}
/> />