forked from mirror/crumbs-original
Remove the sort mode selector UI. Notes now sort by custom order first (drag to reorder), falling back to most-recently-updated for ties. Drag handles are always available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
128 lines
3.7 KiB
Svelte
128 lines
3.7 KiB
Svelte
<script lang="ts">
|
|
import NoteGrid from '$lib/components/NoteGrid.svelte';
|
|
import NoteEditor from '$lib/components/NoteEditor.svelte';
|
|
import TagFilter from '$lib/components/TagFilter.svelte';
|
|
import { pinnedNotes, unpinnedNotes, selectedTag, currentFilter, notes, notesLoaded, loadNotes, updateSortOrders } from '$lib/stores/notes.js';
|
|
import { onMount } from 'svelte';
|
|
import type { Note, NoteFilter } from '$lib/types/index.js';
|
|
import Plus from 'lucide-svelte/icons/plus';
|
|
|
|
interface Props {
|
|
filter: NoteFilter;
|
|
tag?: string | null;
|
|
}
|
|
|
|
let { filter, tag = null }: Props = $props();
|
|
|
|
let editingNote: Note | null = $state(null);
|
|
let showNewNote = $state(false);
|
|
|
|
// Sync store with route props and reload notes when filter changes
|
|
$effect(() => {
|
|
currentFilter.set(filter);
|
|
selectedTag.set(tag);
|
|
loadNotes(filter);
|
|
});
|
|
|
|
function openEditor(note: Note) {
|
|
editingNote = note;
|
|
history.replaceState(null, '', `#${note.id}`);
|
|
}
|
|
|
|
function closeEditor() {
|
|
editingNote = null;
|
|
showNewNote = false;
|
|
history.replaceState(null, '', location.pathname);
|
|
}
|
|
|
|
function handleReorder(noteIds: string[]) {
|
|
const orders = noteIds.map((id, index) => ({ id, sortOrder: index }));
|
|
updateSortOrders(orders);
|
|
}
|
|
|
|
onMount(() => {
|
|
const hash = location.hash.slice(1);
|
|
if (!hash) return;
|
|
|
|
const unsubscribe = notes.subscribe(($notes) => {
|
|
if ($notes.length === 0) return;
|
|
const note = $notes.find((n) => n.id === hash);
|
|
if (note) editingNote = note;
|
|
unsubscribe();
|
|
});
|
|
});
|
|
</script>
|
|
|
|
{#if filter === 'all' && !tag}
|
|
<div class="mx-auto mb-6 hidden max-w-xl md:block">
|
|
<button
|
|
onclick={() => (showNewNote = true)}
|
|
class="flex w-full items-center gap-3 rounded-sm border border-dashed border-[var(--border-subtle)] bg-[var(--bg-surface)] px-4 py-3 text-left text-sm text-[var(--text-muted)] transition-colors hover:border-[var(--primary)] hover:text-[var(--primary)]"
|
|
data-testid="new-note-btn"
|
|
>
|
|
<Plus class="h-4 w-4" />
|
|
Add a crumb...
|
|
</button>
|
|
</div>
|
|
<button
|
|
onclick={() => (showNewNote = true)}
|
|
class="fixed bottom-6 right-6 z-40 flex h-12 w-12 items-center justify-center rounded-sm border border-[var(--border)] bg-[var(--primary)] text-white shadow-[var(--card-shadow)] transition-colors hover:bg-[var(--primary-hover)] md:hidden"
|
|
aria-label="Add a crumb"
|
|
data-testid="new-note-fab"
|
|
>
|
|
<Plus class="h-5 w-5" />
|
|
</button>
|
|
{/if}
|
|
|
|
<div class="mb-4">
|
|
<TagFilter />
|
|
</div>
|
|
|
|
{#if filter === 'archived'}
|
|
<h2 class="mb-4 text-lg font-medium text-[var(--text-muted)]">Archive</h2>
|
|
{:else if filter === 'trashed'}
|
|
<h2 class="mb-4 text-lg font-medium text-[var(--text-muted)]">Trash</h2>
|
|
{:else if tag}
|
|
<h2 class="mb-4 text-lg font-medium text-[var(--text-muted)]">#{tag}</h2>
|
|
{/if}
|
|
|
|
{#if $pinnedNotes.length > 0}
|
|
<div class="mb-6">
|
|
<NoteGrid notes={$pinnedNotes} label="Pinned" onEdit={openEditor} draggable dndType="pinned-notes" onReorder={handleReorder} />
|
|
</div>
|
|
{/if}
|
|
|
|
<NoteGrid
|
|
notes={$unpinnedNotes}
|
|
label={$pinnedNotes.length > 0 ? 'Others' : ''}
|
|
onEdit={openEditor}
|
|
draggable
|
|
dndType="unpinned-notes"
|
|
onReorder={handleReorder}
|
|
/>
|
|
|
|
{#if $notesLoaded && $pinnedNotes.length === 0 && $unpinnedNotes.length === 0}
|
|
<div class="flex flex-col items-center justify-center py-20 text-[var(--text-muted)]">
|
|
<img src="/favicon.svg" alt="" class="mb-4 h-18 w-18" />
|
|
<p class="font-['Press_Start_2P'] text-xs">
|
|
{#if filter === 'trashed'}
|
|
No crumbs in trash
|
|
{:else if filter === 'archived'}
|
|
No archived crumbs
|
|
{:else if tag}
|
|
No crumbs with tag #{tag}
|
|
{:else}
|
|
No crumbs yet
|
|
{/if}
|
|
</p>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if showNewNote}
|
|
<NoteEditor note={null} isNew={true} onClose={closeEditor} />
|
|
{/if}
|
|
|
|
{#if editingNote}
|
|
<NoteEditor note={editingNote} onClose={closeEditor} />
|
|
{/if}
|