Sorting

At the moment, things are sorted by whatever the database decides. We can do better.

In our <header>, add a <form>. This time we won’t be using remote functions:

<header>
	<form>
		<div class="sort">
			<label class="rounded">
				<input
					type="radio"
					name="sort-by"
					value="name"
					checked={page.url.searchParams.get('sort-by') !== 'date'}
				/>

				sort by name
			</label>

			<label class="rounded">
				<input
					type="radio"
					name="sort-by"
					value="date"
					checked={page.url.searchParams.get('sort-by') === 'date'}
				/>

				sort by date
			</label>

			<button class="rounded">apply</button>
		</div>
	</form>
</header>

Add some styles:

header {
	position: fixed;
	top: 0;
	width: 100%;
	padding: 1rem;
	z-index: 2;

	.sort {
		display: flex;
		gap: 1rem;

		label {
			padding: 1rem;
			background: var(--bg);
			display: flex;
			gap: 0.5rem;
		}
	}
}

Notice that when we click the button, the URL updates. We can use that elsewhere in the app. Over in Files.svelte, create a new $derived, along with a compare function that uses it:

let sortBy = $derived(page.url.searchParams.get('sort-by') ?? 'name');

function compare(a: typeof file.$inferSelect, b: typeof file.$inferSelect) {
	const aIsFolder = a.type === 'application/x-directory';
	const bIsFolder = b.type === 'application/x-directory';

	if (aIsFolder !== bIsFolder) {
		// folders always go first
		return aIsFolder ? -1 : 1;
	}

	if (sortBy === 'date') {
		// newest first
		return b.created_at.getTime() - a.created_at.getTime();
	}

	return a.name < b.name ? -1 : 1;
}

Then update the list snippet from files to files.sort(compare).

Because we’re using query params, this works for SSR, and it works without JavaScript. But we can make it work better with JavaScript. Add an oninput handler to the <form>:

<form
	oninput={(e) => {
		const data = new FormData(e.currentTarget);
		const params = new URLSearchParams(data as any);

		goto(`?${params}`, {
			replaceState: true,
			keepFocus: true
		});
	}}
>

The <button> is now surplus to requirements for most users — wrap it in <noscript>:

<noscript>
	<button class="rounded">apply</button>
</noscript>