From f45da049b9243ad80bbd1c5ccf23bfbd38e6f421 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Thu, 21 Aug 2025 20:56:37 +0300 Subject: [PATCH] chore(react/ribbon): change note type --- apps/client/src/widgets/note_type.ts | 97 ------------------- apps/client/src/widgets/react/FormList.tsx | 9 +- .../src/widgets/ribbon/BasicPropertiesTab.tsx | 58 ++++++++--- .../ribbon_widgets/basic_properties.ts | 4 - 4 files changed, 51 insertions(+), 117 deletions(-) diff --git a/apps/client/src/widgets/note_type.ts b/apps/client/src/widgets/note_type.ts index fe45fa233..4231ac979 100644 --- a/apps/client/src/widgets/note_type.ts +++ b/apps/client/src/widgets/note_type.ts @@ -11,14 +11,6 @@ import type FNote from "../entities/fnote.js"; const NOT_SELECTABLE_NOTE_TYPES = NOTE_TYPES.filter((nt) => nt.reserved || nt.static).map((nt) => nt.type); -const TPL = /*html*/` - -`; - export default class NoteTypeWidget extends NoteContextAwareWidget { private dropdown!: Dropdown; @@ -47,93 +39,4 @@ export default class NoteTypeWidget extends NoteContextAwareWidget { this.dropdown.hide(); } - /** the actual body is rendered lazily on note-type button click */ - async renderDropdown() { - this.$noteTypeDropdown.empty(); - - if (!this.note) { - return; - } - - for (const noteType of ) { - let $typeLink: JQuery; - - if (noteType.type !== "code") { - $typeLink = $('') - .attr("data-note-type", noteType.type) - .append(' ') - .append($title) - .on("click", (e) => { - const type = $typeLink.attr("data-note-type"); - const noteType = NOTE_TYPES.find((nt) => nt.type === type); - - if (noteType) { - this.save(noteType.type, noteType.mime); - } - }); - } else { - this.$noteTypeDropdown.append(''); - $typeLink = $('').attr("data-note-type", noteType.type).append(' ').append($("").text()); - } - - if (this.note.type === noteType.type) { - $typeLink.addClass("selected"); - } - - this.$noteTypeDropdown.append($typeLink); - } - - for (const mimeType of ) { - const $mimeLink = $('') - .attr("data-mime-type", mimeType.mime) - .append(' ') - .on("click", (e) => { - const $link = $(e.target).closest(".dropdown-item"); - - this.save("code", $link.attr("data-mime-type") ?? ""); - }); - - if (this.note.type === "code" && this.note.mime === mimeType.mime) { - $mimeLink.addClass("selected"); - - this.$noteTypeDesc.text(mimeType.title); - } - - this.$noteTypeDropdown.append($mimeLink); - } - } - - - - async save(type: NoteType, mime?: string) { - if (type === this.note?.type && mime === this.note?.mime) { - return; - } - - if (type !== this.note?.type && !(await this.confirmChangeIfContent())) { - return; - } - - await server.put(`notes/${this.noteId}/type`, { type, mime }); - } - - async confirmChangeIfContent() { - if (!this.note) { - return; - } - - const blob = await this.note.getBlob(); - - if (!blob?.content || !blob.content.trim().length) { - return true; - } - - return await dialogService.confirm(t("note_types.confirm-change")); - } - - async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) { - if (loadResults.isNoteReloaded(this.noteId)) { - this.refresh(); - } - } } diff --git a/apps/client/src/widgets/react/FormList.tsx b/apps/client/src/widgets/react/FormList.tsx index 01f16264f..ff0da091a 100644 --- a/apps/client/src/widgets/react/FormList.tsx +++ b/apps/client/src/widgets/react/FormList.tsx @@ -76,14 +76,21 @@ interface FormListItemOpts { active?: boolean; badges?: FormListBadge[]; disabled?: boolean; + checked?: boolean; + onClick?: () => void; } -export function FormListItem({ children, icon, value, title, active, badges, disabled }: FormListItemOpts) { +export function FormListItem({ children, icon, value, title, active, badges, disabled, checked, onClick }: FormListItemOpts) { + if (checked) { + icon = "bx bx-check"; + } + return (   {children} diff --git a/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx b/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx index 7e18f49ba..dc16ba17b 100644 --- a/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx +++ b/apps/client/src/widgets/ribbon/BasicPropertiesTab.tsx @@ -1,4 +1,4 @@ -import { useMemo } from "preact/hooks"; +import { useCallback, useMemo } from "preact/hooks"; import Dropdown from "../react/Dropdown"; import { NOTE_TYPES } from "../../services/note_types"; import { FormDivider, FormListBadge, FormListItem } from "../react/FormList"; @@ -6,6 +6,8 @@ import { t } from "../../services/i18n"; import { useNoteContext, useNoteProperty, useTriliumOption } from "../react/hooks"; import mime_types from "../../services/mime_types"; import { NoteType } from "@triliumnext/commons"; +import server from "../../services/server"; +import dialog from "../../services/dialog"; export default function BasicPropertiesTab() { return ( @@ -21,53 +23,79 @@ function NoteTypeWidget() { const mimeTypes = useMemo(() => mime_types.getMimeTypes().filter(mimeType => mimeType.enabled), [ codeNotesMimeTypes ]); const { note } = useNoteContext(); - const type = useNoteProperty(note, "type") ?? undefined; - const mime = useNoteProperty(note, "mime"); + const currentNoteType = useNoteProperty(note, "type") ?? undefined; + const currentNoteMime = useNoteProperty(note, "mime"); + + const changeNoteType = useCallback(async (type: NoteType, mime?: string) => { + if (!note || (type === currentNoteType && mime === currentNoteMime)) { + return; + } + + // Confirm change if the note already has a content. + if (type !== currentNoteType) { + const blob = await note.getBlob(); + + if (blob?.content && blob.content.trim().length && + !await (dialog.confirm(t("note_types.confirm-change")))) { + return; + } + } + + await server.put(`notes/${note.noteId}/type`, { type, mime }); + }, [ note, currentNoteType, currentNoteMime ]); return ( - <> +
{t("basic_properties.note_type")}:   {findTypeTitle(type, mime)}} + text={{findTypeTitle(currentNoteType, currentNoteMime)}} > - {noteTypes.map(noteType => { + {noteTypes.map(({ isNew, isBeta, type, mime, title }) => { const badges: FormListBadge[] = []; - if (noteType.isNew) { + if (isNew) { badges.push({ className: "new-note-type-badge", text: t("note_types.new-feature") }); } - if (noteType.isBeta) { + if (isBeta) { badges.push({ text: t("note_types.beta-feature") }); } - if (noteType.type !== "code") { + const checked = (type === currentNoteType); + if (type !== "code") { return ( {noteType.title} + onClick={() => changeNoteType(type, mime)} + >{title} ); } else { return ( <> - - {noteType.title} + + {title} ) } })} - {mimeTypes.map(mimeType => ( - {mimeType.title} + {mimeTypes.map(({ title, mime }) => ( + changeNoteType("code", mime)}> + {title} + ))} - +
) } diff --git a/apps/client/src/widgets/ribbon_widgets/basic_properties.ts b/apps/client/src/widgets/ribbon_widgets/basic_properties.ts index e5f2ca8a2..1a59f3d48 100644 --- a/apps/client/src/widgets/ribbon_widgets/basic_properties.ts +++ b/apps/client/src/widgets/ribbon_widgets/basic_properties.ts @@ -10,10 +10,6 @@ import type FNote from "../../entities/fnote.js"; import NoteLanguageWidget from "../note_language.js"; const TPL = /*html*/` -
- -
-