feat: enhance EventFightList with grouping and selection features
All checks were successful
SteamWarCI Build successful
All checks were successful
SteamWarCI Build successful
- Added grouping functionality to the EventFightList component, allowing fights to be grouped by their associated group. - Implemented row selection with checkboxes for both individual fights and groups, enabling bulk selection. - Updated columns definition to include a checkbox for selecting all rows and individual row selection checkboxes. - Modified the checkbox component to support indeterminate state and improved styling. - Enhanced date formatting for fight start times in the table.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -26,3 +26,4 @@ pnpm-debug.log*
|
||||
/src/env.d.ts
|
||||
/src/pages/en/
|
||||
/.idea
|
||||
pnpm-lock.yaml
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
{
|
||||
"$schema": "https://shadcn-svelte.com/schema.json",
|
||||
"style": "default",
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js",
|
||||
"css": "src\\styles\\app.css",
|
||||
"baseColor": "slate"
|
||||
},
|
||||
"aliases": {
|
||||
"components": "$lib/components",
|
||||
"utils": "$lib/components/utils",
|
||||
"ui": "$lib/components/ui",
|
||||
"hooks": "$lib/hooks"
|
||||
},
|
||||
"typescript": true,
|
||||
"registry": "https://next.shadcn-svelte.com/registry"
|
||||
"$schema": "https://shadcn-svelte.com/schema.json",
|
||||
"style": "default",
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js",
|
||||
"css": "src\\styles\\app.css",
|
||||
"baseColor": "slate"
|
||||
},
|
||||
"aliases": {
|
||||
"components": "$lib/components",
|
||||
"utils": "$lib/components/utils",
|
||||
"ui": "$lib/components/ui",
|
||||
"hooks": "$lib/hooks"
|
||||
},
|
||||
"typescript": true,
|
||||
"registry": "https://next.shadcn-svelte.com/registry"
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"@astrojs/tailwind": "^5.1.5",
|
||||
"@astropub/icons": "^0.2.0",
|
||||
"@internationalized/date": "^3.7.0",
|
||||
"@lucide/svelte": "^0.488.0",
|
||||
"@types/color": "^4.2.0",
|
||||
"@types/node": "^22.9.3",
|
||||
"@types/three": "^0.170.0",
|
||||
|
||||
9276
pnpm-lock.yaml
generated
9276
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -18,26 +18,36 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type {ExtendedEvent} from "@type/event";
|
||||
import {createSvelteTable, FlexRender} from "@components/ui/data-table";
|
||||
import type { ExtendedEvent } from "@type/event";
|
||||
import { createSvelteTable, FlexRender } from "@components/ui/data-table";
|
||||
import {
|
||||
type ColumnFiltersState,
|
||||
getCoreRowModel, getFilteredRowModel,
|
||||
getPaginationRowModel, getSortedRowModel,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getGroupedRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
type GroupingState,
|
||||
type RowSelectionState,
|
||||
type SortingState,
|
||||
} from "@tanstack/table-core";
|
||||
import { columns } from "./columns"
|
||||
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@components/ui/table";
|
||||
import { columns } from "./columns";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@components/ui/table";
|
||||
import { Checkbox } from "@components/ui/checkbox";
|
||||
|
||||
let { data }: { data: ExtendedEvent } = $props();
|
||||
|
||||
let sorting = $state<SortingState>([]);
|
||||
let columnFilters = $state<ColumnFiltersState>([]);
|
||||
let selection = $state<RowSelectionState>({});
|
||||
|
||||
const table = createSvelteTable({
|
||||
get data() {
|
||||
return data.fights;
|
||||
},
|
||||
initialState: {
|
||||
columnOrder: ["auswahl", "begegnung", "group"],
|
||||
},
|
||||
state: {
|
||||
get sorting() {
|
||||
return sorting;
|
||||
@ -45,6 +55,12 @@
|
||||
get columnFilters() {
|
||||
return columnFilters;
|
||||
},
|
||||
get grouping() {
|
||||
return ["group"];
|
||||
},
|
||||
get rowSelection() {
|
||||
return selection;
|
||||
},
|
||||
},
|
||||
onSortingChange: (updater) => {
|
||||
if (typeof updater === "function") {
|
||||
@ -60,10 +76,20 @@
|
||||
columnFilters = updater;
|
||||
}
|
||||
},
|
||||
onRowSelectionChange: (updater) => {
|
||||
if (typeof updater === "function") {
|
||||
selection = updater(selection);
|
||||
} else {
|
||||
selection = updater;
|
||||
}
|
||||
},
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getGroupedRowModel: getGroupedRowModel(),
|
||||
groupedColumnMode: "remove",
|
||||
getRowId: (row) => row.id.toString(),
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -74,10 +100,7 @@
|
||||
{#each headerGroup.headers as header (header.id)}
|
||||
<TableHead>
|
||||
{#if !header.isPlaceholder}
|
||||
<FlexRender
|
||||
content={header.column.columnDef.header}
|
||||
context={header.getContext()}
|
||||
/>
|
||||
<FlexRender content={header.column.columnDef.header} context={header.getContext()} />
|
||||
{/if}
|
||||
</TableHead>
|
||||
{/each}
|
||||
@ -85,23 +108,41 @@
|
||||
{/each}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#each table.getRowModel().rows as row (row.id)}
|
||||
<TableRow data-state={row.getIsSelected() && "selected"}>
|
||||
{#each row.getVisibleCells() as cell (cell.id)}
|
||||
<TableCell>
|
||||
<FlexRender
|
||||
content={cell.column.columnDef.cell}
|
||||
context={cell.getContext()}
|
||||
{#each table.getRowModel().rows as groupRow (groupRow.id)}
|
||||
{#if groupRow.getIsGrouped()}
|
||||
<TableRow class="bg-muted font-bold">
|
||||
<TableCell colspan={columns.length}>
|
||||
<Checkbox
|
||||
checked={groupRow.getIsSelected()}
|
||||
indeterminate={groupRow.getIsSomeSelected() && !groupRow.getIsSelected()}
|
||||
onCheckedChange={() => groupRow.toggleSelected()}
|
||||
class="mr-4"
|
||||
/>
|
||||
Gruppe: {groupRow.getValue("group") ?? "Keine"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{#each groupRow.subRows as row (row.id)}
|
||||
<TableRow data-state={row.getIsSelected() && "selected"}>
|
||||
{#each row.getVisibleCells() as cell (cell.id)}
|
||||
<TableCell>
|
||||
<FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} />
|
||||
</TableCell>
|
||||
{/each}
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableRow>
|
||||
{:else}
|
||||
<TableRow data-state={groupRow.getIsSelected() && "selected"}>
|
||||
{#each groupRow.getVisibleCells() as cell (cell.id)}
|
||||
<TableCell>
|
||||
<FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} />
|
||||
</TableCell>
|
||||
{/each}
|
||||
</TableRow>
|
||||
{/if}
|
||||
{:else}
|
||||
<TableRow>
|
||||
<TableCell colspan={columns.length} class="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
<TableCell colspan={columns.length} class="h-24 text-center">No results.</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Table>
|
||||
|
||||
@ -17,16 +17,64 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {ColumnDef} from "@tanstack/table-core";
|
||||
import type {EventFight} from "@type/event.ts";
|
||||
import { Checkbox } from "@components/ui/checkbox";
|
||||
import { renderComponent } from "@components/ui/data-table";
|
||||
import type { ColumnDef } from "@tanstack/table-core";
|
||||
import type { EventFight } from "@type/event.ts";
|
||||
|
||||
export const columns: ColumnDef<EventFight> = [
|
||||
{
|
||||
accessorFn: (r) => r.blueTeam.name,
|
||||
header: "Team Blue",
|
||||
id: "auswahl",
|
||||
header: ({ table }) => {
|
||||
return renderComponent(Checkbox, {
|
||||
checked: table.getIsAllRowsSelected(),
|
||||
indeterminate: table.getIsSomeRowsSelected(),
|
||||
onCheckedChange: () => {
|
||||
if (!table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()) {
|
||||
const now = new Date();
|
||||
const rows = table.getRowModel().rows.filter((row) => new Date(row.original.date) > now);
|
||||
|
||||
if (rows.length > 0) {
|
||||
rows.forEach((row) => {
|
||||
row.toggleSelected();
|
||||
});
|
||||
} else {
|
||||
table.toggleAllRowsSelected(true);
|
||||
}
|
||||
} else if (table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()) {
|
||||
table.toggleAllRowsSelected(true);
|
||||
} else {
|
||||
table.toggleAllRowsSelected(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
return renderComponent(Checkbox, {
|
||||
checked: row.getIsSelected(),
|
||||
onCheckedChange: row.getToggleSelectedHandler(),
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorFn: (r) => r.redTeam.name,
|
||||
header: "Team Red",
|
||||
accessorFn: (r) => r.blueTeam.name + " vs " + r.redTeam.name,
|
||||
id: "begegnung",
|
||||
header: "Begegnung",
|
||||
},
|
||||
];
|
||||
{
|
||||
header: "Gruppe",
|
||||
accessorKey: "group",
|
||||
id: "group",
|
||||
},
|
||||
{
|
||||
header: "Datum",
|
||||
accessorKey: "start",
|
||||
id: "start",
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue("start")).toLocaleString("de-DE", {
|
||||
dateStyle: "short",
|
||||
timeStyle: "medium",
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@ -1,35 +1,35 @@
|
||||
<script lang="ts">
|
||||
import { Checkbox as CheckboxPrimitive } from "bits-ui";
|
||||
import Check from "lucide-svelte/icons/check";
|
||||
import Minus from "lucide-svelte/icons/minus";
|
||||
import { Checkbox as CheckboxPrimitive, type WithoutChildrenOrChild } from "bits-ui";
|
||||
import Check from "@lucide/svelte/icons/check";
|
||||
import Minus from "@lucide/svelte/icons/minus";
|
||||
import { cn } from "$lib/components/utils.js";
|
||||
|
||||
type $$Props = CheckboxPrimitive.Props;
|
||||
type $$Events = CheckboxPrimitive.Events;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export let checked: $$Props["checked"] = false;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
checked = $bindable(false),
|
||||
indeterminate = $bindable(false),
|
||||
class: className,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
|
||||
</script>
|
||||
|
||||
<CheckboxPrimitive.Root
|
||||
bind:ref
|
||||
class={cn(
|
||||
"border-primary ring-offset-background focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground peer box-content h-4 w-4 shrink-0 rounded-sm border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50",
|
||||
"border-primary ring-offset-background focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground peer box-content size-4 shrink-0 rounded-sm border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50",
|
||||
className
|
||||
)}
|
||||
bind:checked
|
||||
{...$$restProps}
|
||||
on:click
|
||||
bind:indeterminate
|
||||
{...restProps}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
class={cn("flex h-4 w-4 items-center justify-center text-current")}
|
||||
let:isChecked
|
||||
let:isIndeterminate
|
||||
>
|
||||
{#if isChecked}
|
||||
<Check class="h-3.5 w-3.5" />
|
||||
{:else if isIndeterminate}
|
||||
<Minus class="h-3.5 w-3.5" />
|
||||
{/if}
|
||||
</CheckboxPrimitive.Indicator>
|
||||
{#snippet children({ checked, indeterminate })}
|
||||
<div class="flex size-4 items-center justify-center text-current">
|
||||
{#if indeterminate}
|
||||
<Minus class="size-3.5" />
|
||||
{:else}
|
||||
<Check class={cn("size-3.5", !checked && "text-transparent")} />
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
</CheckboxPrimitive.Root>
|
||||
|
||||
Reference in New Issue
Block a user