refactor(frontend): Svelte 5 mount API, router v5, dependency-free table
main.ts uses mount() instead of the legacy constructor. App.svelte follows svelte-spa-router v5: the removed loc store becomes router.location, while wrap/push/link keep their v4 call shapes. AnimeTable.svelte drops svelte-headless-table and reimplements the same 11 sortable columns (WebsiteLink id cells, asc/desc indicators, click toggles through asc/desc/off) with local $state/$derived - about 120 lines, no dependency, fully covered by svelte-check instead of the libs view model.
This commit is contained in:
@@ -21,13 +21,13 @@
|
||||
import { CheckIfMALLoggedInAndSetUser } from "./helperModules/CheckIfMyAnimeListLoggedIn.svelte";
|
||||
import { CheckIfSimklLoggedInAndSetUser } from "./helperModules/CheckIsSimklLoggedIn.svelte";
|
||||
import {App} from "../bindings/AniTrack";
|
||||
import { loc } from "svelte-spa-router";
|
||||
import { router } from "svelte-spa-router";
|
||||
import ErrorModal from "./helperComponents/ErrorModal.svelte";
|
||||
|
||||
onMount(async () => {
|
||||
let isAniListLoggedIn: boolean;
|
||||
let isMALLoggedIn: boolean;
|
||||
let isSimklLoggedIn: boolean;
|
||||
let isAniListLoggedIn!: boolean;
|
||||
let isMALLoggedIn!: boolean;
|
||||
let isSimklLoggedIn!: boolean;
|
||||
aniListLoggedIn.subscribe((value) => (isAniListLoggedIn = value));
|
||||
malLoggedIn.subscribe((value) => (isMALLoggedIn = value));
|
||||
simklLoggedIn.subscribe((value) => (isSimklLoggedIn = value));
|
||||
@@ -39,7 +39,7 @@
|
||||
!isSimklLoggedIn && (await CheckIfSimklLoggedInAndSetUser());
|
||||
});
|
||||
|
||||
$: if ($loc?.location === "/" && $watchlistNeedsRefresh) {
|
||||
$: if (router.location === "/" && $watchlistNeedsRefresh) {
|
||||
(async () => {
|
||||
if ($aniListLoggedIn && $aniListPrimary) {
|
||||
await CheckIfAniListLoggedInAndLoadWatchList();
|
||||
|
||||
@@ -1,118 +1,95 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
createRender,
|
||||
createTable,
|
||||
Render,
|
||||
Subscribe,
|
||||
} from "svelte-headless-table";
|
||||
// @ts-ignore
|
||||
import { addSortBy } from "svelte-headless-table/plugins";
|
||||
import { tableItems } from "../helperModules/GlobalVariablesAndHelperFunctions.svelte";
|
||||
import type { TableItem } from "../helperTypes/TableTypes";
|
||||
import WebsiteLink from "./WebsiteLink.svelte";
|
||||
|
||||
//when adding sort here is code { sort: addSortBy() }
|
||||
const table = createTable(tableItems, { sort: addSortBy() });
|
||||
type SortKey = keyof TableItem;
|
||||
type SortOrder = "asc" | "desc" | undefined;
|
||||
|
||||
const columns = table.createColumns([
|
||||
table.column({
|
||||
header: "Service Id",
|
||||
cell: ({ value }) => createRender(WebsiteLink, { id: value }),
|
||||
accessor: "id",
|
||||
}),
|
||||
table.column({
|
||||
header: "Anime Title",
|
||||
accessor: "title",
|
||||
}),
|
||||
table.column({
|
||||
header: "Service",
|
||||
accessor: "service",
|
||||
}),
|
||||
table.column({
|
||||
header: "Episode Progress",
|
||||
accessor: "progress",
|
||||
}),
|
||||
table.column({
|
||||
header: "Status",
|
||||
accessor: "status",
|
||||
}),
|
||||
table.column({
|
||||
header: "Started At",
|
||||
accessor: "startedAt",
|
||||
}),
|
||||
table.column({
|
||||
header: "Completed At",
|
||||
accessor: "completedAt",
|
||||
}),
|
||||
table.column({
|
||||
header: "Rating",
|
||||
accessor: "score",
|
||||
}),
|
||||
table.column({
|
||||
header: "Repeat",
|
||||
accessor: "repeat",
|
||||
}),
|
||||
table.column({
|
||||
header: "Notes",
|
||||
accessor: "notes",
|
||||
}),
|
||||
]);
|
||||
const columns: { key: SortKey; header: string }[] = [
|
||||
{ key: "id", header: "Service Id" },
|
||||
{ key: "title", header: "Anime Title" },
|
||||
{ key: "service", header: "Service" },
|
||||
{ key: "progress", header: "Episode Progress" },
|
||||
{ key: "status", header: "Status" },
|
||||
{ key: "startedAt", header: "Started At" },
|
||||
{ key: "completedAt", header: "Completed At" },
|
||||
{ key: "score", header: "Rating" },
|
||||
{ key: "repeat", header: "Repeat" },
|
||||
{ key: "notes", header: "Notes" },
|
||||
];
|
||||
|
||||
//add pluginStates when add sort back
|
||||
const { headerRows, rows, tableAttrs, tableBodyAttrs } =
|
||||
table.createViewModel(columns);
|
||||
let sortKey = $state<SortKey | undefined>(undefined);
|
||||
let sortOrder = $state<SortOrder>(undefined);
|
||||
|
||||
function toggleSort(key: SortKey) {
|
||||
if (sortKey !== key) {
|
||||
sortKey = key;
|
||||
sortOrder = "asc";
|
||||
} else if (sortOrder === "asc") {
|
||||
sortOrder = "desc";
|
||||
} else if (sortOrder === "desc") {
|
||||
sortKey = undefined;
|
||||
sortOrder = undefined;
|
||||
} else {
|
||||
sortOrder = "asc";
|
||||
}
|
||||
}
|
||||
|
||||
function compareItems(a: TableItem, b: TableItem): number {
|
||||
if (sortKey === undefined || sortOrder === undefined) return 0;
|
||||
const av = a[sortKey];
|
||||
const bv = b[sortKey];
|
||||
let result: number;
|
||||
if (typeof av === "number" && typeof bv === "number") {
|
||||
result = av - bv;
|
||||
} else {
|
||||
result = String(av ?? "").localeCompare(String(bv ?? ""));
|
||||
}
|
||||
return sortOrder === "asc" ? result : -result;
|
||||
}
|
||||
|
||||
let sortedRows = $derived(
|
||||
sortKey === undefined ? $tableItems : [...$tableItems].sort(compareItems),
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="relative overflow-x-auto rounded-lg mb-5">
|
||||
<table
|
||||
class="w-full text-sm text-left rtl:text-right text-gray-400"
|
||||
{...$tableAttrs}
|
||||
>
|
||||
<table class="w-full text-sm text-left rtl:text-right text-gray-400">
|
||||
<thead class="text-xs uppercase bg-gray-700 text-gray-400">
|
||||
{#each $headerRows as headerRow (headerRow.id)}
|
||||
<Subscribe attrs={headerRow.attrs()} let:attrs>
|
||||
<tr {...attrs}>
|
||||
{#each headerRow.cells as cell (cell.id)}
|
||||
<Subscribe
|
||||
attrs={cell.attrs()}
|
||||
let:attrs
|
||||
props={cell.props()}
|
||||
let:props
|
||||
>
|
||||
<tr>
|
||||
{#each columns as column (column.key)}
|
||||
<th
|
||||
{...attrs}
|
||||
on:click={props.sort.toggle}
|
||||
class:sorted={props.sort.order !==
|
||||
undefined}
|
||||
class="px-6 py-3"
|
||||
onclick={() => toggleSort(column.key)}
|
||||
class:sorted={sortKey === column.key &&
|
||||
sortOrder !== undefined}
|
||||
class="px-6 py-3 cursor-pointer"
|
||||
>
|
||||
<div>
|
||||
<Render of={cell.render()} />
|
||||
{#if props.sort.order === "asc"}
|
||||
{column.header}
|
||||
{#if sortKey === column.key && sortOrder === "asc"}
|
||||
⬇️
|
||||
{:else if props.sort.order === "desc"}
|
||||
{:else if sortKey === column.key && sortOrder === "desc"}
|
||||
⬆️
|
||||
{/if}
|
||||
</div>
|
||||
</th>
|
||||
</Subscribe>
|
||||
{/each}
|
||||
</tr>
|
||||
</Subscribe>
|
||||
{/each}
|
||||
</thead>
|
||||
<tbody {...$tableBodyAttrs}>
|
||||
{#each $rows as row (row.id)}
|
||||
<Subscribe attrs={row.attrs()} let:attrs>
|
||||
<tr {...attrs} class="bg-gray-800 border-gray-700">
|
||||
{#each row.cells as cell (cell.id)}
|
||||
<Subscribe attrs={cell.attrs()} let:attrs>
|
||||
<td {...attrs} class="px-6 py-4">
|
||||
<Render of={cell.render()} />
|
||||
<tbody>
|
||||
{#each sortedRows as row (row.id + row.service)}
|
||||
<tr class="bg-gray-800 border-gray-700">
|
||||
{#each columns as column (column.key)}
|
||||
<td class="px-6 py-4">
|
||||
{#if column.key === "id"}
|
||||
<WebsiteLink id={row.id} />
|
||||
{:else}
|
||||
{row[column.key]}
|
||||
{/if}
|
||||
</td>
|
||||
</Subscribe>
|
||||
{/each}
|
||||
</tr>
|
||||
</Subscribe>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import './style.css'
|
||||
import { mount } from 'svelte'
|
||||
import App from './App.svelte'
|
||||
|
||||
const app = new App({
|
||||
target: document.getElementById('app')
|
||||
const app = mount(App, {
|
||||
target: document.getElementById('app')!
|
||||
})
|
||||
|
||||
export default app
|
||||
|
||||
Reference in New Issue
Block a user