NotesMode.tsx

2.9 KB · raw

1import { createSignal, onMount, Show } from "solid-js";2import type { VaultEntry } from "../../shared/types";3import { sendMessage } from "../../shared/bridge";4import { NotesTOC } from "./NotesTOC";5import { NotesEditor } from "./NotesEditor";67interface NotesModeProps {8  onBack: () => void;9  onNoteListChanged: () => void;10  initialNoteId?: string;11  notes: VaultEntry[];12}1314type Phase = "toc" | "editor" | "collapsing";1516const FADE_MS = 200;1718export function NotesMode(props: NotesModeProps) {19  const [phase, setPhase] = createSignal<Phase>("toc");20  const [activeNote, setActiveNote] = createSignal<VaultEntry | null>(null);21  const [isNewNote, setIsNewNote] = createSignal(false);2223  onMount(() => {24    if (props.initialNoteId) {25      const note = props.notes.find((n) => n.id === props.initialNoteId);26      if (note) {27        setActiveNote(note);28        setPhase("editor");29      }30    }31  });3233  function vibrate(ms: number) {34    try { navigator.vibrate?.(ms); } catch { /* unsupported */ }35  }3637  function handleNoteRowTap(note: VaultEntry, _rowEl: HTMLElement) {38    if (phase() !== "toc") return;39    vibrate(12);40    setActiveNote(note);41    setIsNewNote(false);42    requestAnimationFrame(() => setPhase("editor"));43  }4445  function handleNewNote() {46    if (phase() !== "toc") return;47    vibrate(12);48    setActiveNote(null);49    setIsNewNote(true);50    requestAnimationFrame(() => setPhase("editor"));51  }5253  function handleEditorBack() {54    if (phase() !== "editor") return;55    vibrate(12);56    setPhase("collapsing");57    setTimeout(() => {58      setActiveNote(null);59      setIsNewNote(false);60      setPhase("toc");61    }, FADE_MS);62  }6364  function handleSaved(note: VaultEntry) {65    if (!activeNote() || activeNote()!.id !== note.id) {66      setActiveNote(note);67    }68    props.onNoteListChanged();69  }7071  function handleDeleteNote(note: VaultEntry) {72    void sendMessage({ type: "DELETE_NOTE", payload: { id: note.id } });73    props.onNoteListChanged();74    handleEditorBack();75  }7677  return (78    <div class="peach-notes-mode" data-phase={phase()}>79      {/* TOC — fades out when editor opens */}80      <div81        class="peach-notes-toc-wrapper"82        classList={{ "is-hidden": phase() === "editor" }}83        aria-hidden={phase() !== "toc"}84      >85        <NotesTOC86          notes={props.notes}87          onBack={props.onBack}88          onOpenNote={handleNoteRowTap}89          onNewNote={handleNewNote}90          onRefresh={props.onNoteListChanged}91        />92      </div>9394      {/* Editor — fades in over the TOC */}95      <Show when={activeNote() !== null || isNewNote()}>96        <div97          class="peach-notes-editor-wrapper"98          classList={{ "is-visible": phase() === "editor" }}99        >100          <NotesEditor101            note={activeNote()}102            onBack={handleEditorBack}103            onSaved={handleSaved}104            onDelete={handleDeleteNote}105            autoFocusTitle={isNewNote()}106          />107        </div>108      </Show>109    </div>110  );111}