From 0ffdedcfa6dae53eef776c0037946456bfcf3d3d Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 13 Dec 2025 13:45:34 +0200 Subject: [PATCH] feat(layout/inline-title): dropdown for collections --- .../src/translations/en/translation.json | 3 +- .../client/src/widgets/layout/InlineTitle.tsx | 78 ++++++++++++++----- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 822e7ef92..986885a22 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1756,7 +1756,8 @@ "last_modified": "Last modified on ", "note_type_switcher_label": "Switch from {{type}} to:", "note_type_switcher_others": "More note types", - "note_type_switcher_templates": "Templates" + "note_type_switcher_templates": "Templates", + "note_type_switcher_collection": "Collections" }, "search_result": { "no_notes_found": "No notes have been found for given search parameters.", diff --git a/apps/client/src/widgets/layout/InlineTitle.tsx b/apps/client/src/widgets/layout/InlineTitle.tsx index de88b117c..ed5d9359a 100644 --- a/apps/client/src/widgets/layout/InlineTitle.tsx +++ b/apps/client/src/widgets/layout/InlineTitle.tsx @@ -144,7 +144,7 @@ function NoteTypeSwitcher() { const pinnedNoteTypes: NoteTypeMapping[] = []; const restNoteTypes: NoteTypeMapping[] = []; for (const noteType of NOTE_TYPES) { - if (noteType.reserved || noteType.static) continue; + if (noteType.reserved || noteType.static || noteType.type === "book") continue; if (SWITCHER_PINNED_NOTE_TYPES.has(noteType.type)) { pinnedNoteTypes.push(noteType); } else { @@ -154,6 +154,7 @@ function NoteTypeSwitcher() { return { pinnedNoteTypes, restNoteTypes }; }, []); const currentNoteTypeData = useMemo(() => NOTE_TYPES.find(t => t.type === currentNoteType), [ currentNoteType ]); + const { builtinTemplates, collectionTemplates } = useBuiltinTemplates(); return (note?.type === "text" &&
switchNoteType(note.noteId, noteType)} /> ))} + + - )}
@@ -196,9 +198,25 @@ function MoreNoteTypes({ noteId, restNoteTypes }: { noteId: string, restNoteType ); } -function TemplateNoteTypes({ noteId }: { noteId: string }) { +function CollectionNoteTypes({ noteId, collectionTemplates }: { noteId: string, collectionTemplates: FNote[] }) { + return ( + + {collectionTemplates.map(collectionTemplate => ( + setTemplate(noteId, collectionTemplate.noteId)} + >{collectionTemplate.title} + ))} + + ); +} + +function TemplateNoteTypes({ noteId, builtinTemplates }: { noteId: string, builtinTemplates: FNote[] }) { const [ userTemplates, setUserTemplates ] = useState([]); - const [ builtinTemplates, setBuiltinTemplates ] = useState([]); async function refreshTemplates() { const templateNoteIds = await server.get("search-templates"); @@ -206,22 +224,9 @@ function TemplateNoteTypes({ noteId }: { noteId: string }) { setUserTemplates(templateNotes); } - async function loadBuiltinTemplates() { - const templatesRoot = await froca.getNote("_templates"); - if (!templatesRoot) return; - const childNotes = await templatesRoot.getChildNotes(); - const builtinTemplates: FNote[] = []; - for (const childNote of childNotes) { - if (childNote.hasLabel("collection") || !childNote.hasLabel("template")) continue; - builtinTemplates.push(childNote); - } - setBuiltinTemplates(builtinTemplates); - } - // First load. useEffect(() => { refreshTemplates(); - loadBuiltinTemplates(); }, []); // React to external changes. @@ -247,7 +252,7 @@ function TemplateItem({ noteId, template }: { noteId: string, template: FNote }) return ( attributes.setRelation(noteId, "template", template.noteId)} + onClick={() => setTemplate(noteId, template.noteId)} >{template.title} ); } @@ -255,4 +260,41 @@ function TemplateItem({ noteId, template }: { noteId: string, template: FNote }) function switchNoteType(noteId: string, { type, mime }: NoteTypeMapping) { return server.put(`notes/${noteId}/type`, { type, mime }); } + +function setTemplate(noteId: string, templateId: string) { + return attributes.setRelation(noteId, "template", templateId); +} + +function useBuiltinTemplates() { + const [ templates, setTemplates ] = useState<{ + builtinTemplates: FNote[]; + collectionTemplates: FNote[]; + }>({ + builtinTemplates: [], + collectionTemplates: [] + }); + + async function loadBuiltinTemplates() { + const templatesRoot = await froca.getNote("_templates"); + if (!templatesRoot) return; + const childNotes = await templatesRoot.getChildNotes(); + const builtinTemplates: FNote[] = []; + const collectionTemplates: FNote[] = []; + for (const childNote of childNotes) { + if (!childNote.hasLabel("template")) continue; + if (childNote.hasLabel("collection")) { + collectionTemplates.push(childNote); + } else { + builtinTemplates.push(childNote); + } + } + setTemplates({ builtinTemplates, collectionTemplates }); + } + + useEffect(() => { + loadBuiltinTemplates(); + }, []); + + return templates; +} //#endregion