Improves multiselect (#921)

* initial commit

* fixes open in new tab

---------

Co-authored-by: Christian Beutel <>
This commit is contained in:
Flomp
2026-04-20 21:04:00 +02:00
committed by GitHub
parent 19fd259129
commit 25c63ffa56
2 changed files with 55 additions and 25 deletions

View File

@@ -12,6 +12,7 @@
import { onMount, tick } from "svelte"; import { onMount, tick } from "svelte";
import type { Snippet } from "svelte"; import type { Snippet } from "svelte";
import TrailDropdown from "$lib/components/trail/trail_dropdown.svelte"; import TrailDropdown from "$lib/components/trail/trail_dropdown.svelte";
import { goto } from "$app/navigation";
interface Props { interface Props {
filter?: TrailFilter | null; filter?: TrailFilter | null;
@@ -313,6 +314,15 @@
handleHoverUpdate(trail); handleHoverUpdate(trail);
} }
function handleTrailClick(e: Event, trail: Trail) {
if (selection && selection.size > 0) {
e.stopPropagation();
e.preventDefault();
handleSelectionUpdate(trail);
}
return true
}
function setItemsPerPage() { function setItemsPerPage() {
localStorage.setItem("paginationItems", pagination.items.toString()); localStorage.setItem("paginationItems", pagination.items.toString());
onpagination?.(1, pagination.items); onpagination?.(1, pagination.items);
@@ -420,6 +430,7 @@
<a <a
class="max-w-full flex-1" class="max-w-full flex-1"
class:basis-full={selectedDisplayOption === "list"} class:basis-full={selectedDisplayOption === "list"}
onclick={(e) => handleTrailClick(e,trail)}
href="/trail/view/@{trail.author}{trail.domain href="/trail/view/@{trail.author}{trail.domain
? `@${trail.domain}` ? `@${trail.domain}`
: ''}/{trail.id}" : ''}/{trail.id}"

View File

@@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import { goto } from "$app/navigation";
import type { Trail, TrailFilter } from "$lib/models/trail"; import type { Trail, TrailFilter } from "$lib/models/trail";
import { import {
formatDistance, formatDistance,
@@ -7,8 +8,6 @@
} from "$lib/util/format_util"; } from "$lib/util/format_util";
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import type { SelectItem } from "../base/select.svelte"; import type { SelectItem } from "../base/select.svelte";
import { goto } from "$app/navigation";
import { getFileURL } from "$lib/util/file_util";
import ShareInfo from "../share_info.svelte"; import ShareInfo from "../share_info.svelte";
interface Props { interface Props {
@@ -17,11 +16,19 @@
selection: Set<Trail> | undefined; selection: Set<Trail> | undefined;
filter?: TrailFilter | null; filter?: TrailFilter | null;
items: number; items: number;
onsort?: (value: any) => void onsort?: (value: any) => void;
onTrailSelect?: (value: any) => void onTrailSelect?: (value: any) => void;
} }
let { tableHeader, trails = null, selection, filter = null, items, onsort, onTrailSelect: onselect }: Props = $props(); let {
tableHeader,
trails = null,
selection,
filter = null,
items,
onsort,
onTrailSelect: onselect,
}: Props = $props();
function getColumnWidth(columnValue: string): string { function getColumnWidth(columnValue: string): string {
switch (columnValue) { switch (columnValue) {
@@ -44,20 +51,19 @@
} }
function setSelectedTrail(e: Event, trail: Trail) { function setSelectedTrail(e: Event, trail: Trail) {
e.stopPropagation() e.stopPropagation();
if (trail !== undefined) { if (trail !== undefined) {
if (onselect !== undefined) { if (onselect !== undefined) {
onselect(trail) onselect(trail);
} } else {
else { console.error("undefined event handler");
console.error("undefined event handler")
} }
} }
} }
function setSelectedAllTrails(e: Event) { function setSelectedAllTrails(e: Event) {
onselect?.(undefined) onselect?.(undefined);
} }
function isSelected(trail: Trail): boolean { function isSelected(trail: Trail): boolean {
@@ -67,8 +73,7 @@
if (trail !== undefined) { if (trail !== undefined) {
for (const strail of selection) { for (const strail of selection) {
if (strail !== undefined && strail.id === trail.id) if (strail !== undefined && strail.id === trail.id) return true;
return true;
} }
} }
@@ -76,13 +81,26 @@
} }
function allSelected(): boolean { function allSelected(): boolean {
if (selection === undefined || trails === undefined || trails === null) { if (
selection === undefined ||
trails === undefined ||
trails === null
) {
return false; return false;
} }
return selection.size === trails.length; return selection.size === trails.length;
} }
function handleTrailClick(trail: Trail) {
if (selection && selection.size > 0) {
onselect?.(trail);
} else {
goto(
`/trail/view/@${trail.author}${trail.domain ? `@${trail.domain}` : ""}/${trail.id}`,
);
}
}
</script> </script>
<div <div
@@ -92,7 +110,9 @@
<thead> <thead>
<tr class="bg-secondary-hover"> <tr class="bg-secondary-hover">
<th <th
class="p-4 text-left text-sm font-medium {getColumnWidth("select")}" class="p-4 text-left text-sm font-medium {getColumnWidth(
'select',
)}"
> >
<div class="flex items-center"> <div class="flex items-center">
<input <input
@@ -130,12 +150,7 @@
{#each trails as trail} {#each trails as trail}
<tr <tr
class="border-t border-input-border cursor-pointer hover:bg-secondary-hover transition-colors" class="border-t border-input-border cursor-pointer hover:bg-secondary-hover transition-colors"
onclick={() => onclick={() => handleTrailClick(trail)}
goto(
`/trail/view/@${trail.author}${
trail.domain ? `@${trail.domain}` : ""
}/${trail.id}`,
)}
> >
<td class="p-4 text-sm"> <td class="p-4 text-sm">
<div class="flex items-center"> <div class="flex items-center">
@@ -143,14 +158,18 @@
type="checkbox" type="checkbox"
class="w-4 h-4 bg-input-background accent-primary border-input-border focus:ring-input-ring focus:ring-2" class="w-4 h-4 bg-input-background accent-primary border-input-border focus:ring-input-ring focus:ring-2"
checked={isSelected(trail)} checked={isSelected(trail)}
onclick={(e: Event) => setSelectedTrail(e, trail)} onclick={(e: Event) =>
setSelectedTrail(e, trail)}
/> />
</div> </div>
</td> </td>
<td <td
class="flex justify-between items-center text-sm relative" class="flex justify-between items-center text-sm relative"
> >
<div class="p-4 w-[75%] line-clamp-2 wrap-anywhere" title={trail.name}> <div
class="p-4 w-[75%] line-clamp-2 wrap-anywhere"
title={trail.name}
>
{trail.name} {trail.name}
</div> </div>
<div class="flex flex-col items-center"> <div class="flex flex-col items-center">