mirror of
https://github.com/zadam/trilium.git
synced 2026-01-06 06:34:25 +01:00
chore(layout): relocate note type switcher right above content
This commit is contained in:
parent
a4f34ce6c5
commit
bfb3ed3ddf
@ -99,38 +99,3 @@ body.prefers-centered-content .inline-title {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes note-type-switcher-intro {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
} to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-type-switcher {
|
|
||||||
--badge-radius: 12px;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
top: 5px;
|
|
||||||
padding: .25em 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
overflow-x: auto;
|
|
||||||
min-width: 0;
|
|
||||||
gap: 5px;
|
|
||||||
min-height: 35px;
|
|
||||||
|
|
||||||
>* {
|
|
||||||
flex-shrink: 0;
|
|
||||||
animation: note-type-switcher-intro 200ms ease-in;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ext-badge {
|
|
||||||
--color: var(--input-background-color);
|
|
||||||
color: var(--main-text-color);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@ -3,25 +3,16 @@ import "./InlineTitle.css";
|
|||||||
import { NoteType } from "@triliumnext/commons";
|
import { NoteType } from "@triliumnext/commons";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { ComponentChild } from "preact";
|
import { ComponentChild } from "preact";
|
||||||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
|
import { useLayoutEffect, useRef, useState } from "preact/hooks";
|
||||||
import { Trans } from "react-i18next";
|
import { Trans } from "react-i18next";
|
||||||
|
|
||||||
import FNote from "../../entities/fnote";
|
|
||||||
import attributes from "../../services/attributes";
|
|
||||||
import froca from "../../services/froca";
|
|
||||||
import { t } from "../../services/i18n";
|
|
||||||
import { ViewScope } from "../../services/link";
|
import { ViewScope } from "../../services/link";
|
||||||
import { NOTE_TYPES, NoteTypeMapping } from "../../services/note_types";
|
|
||||||
import server from "../../services/server";
|
|
||||||
import { formatDateTime } from "../../utils/formatters";
|
import { formatDateTime } from "../../utils/formatters";
|
||||||
import NoteIcon from "../note_icon";
|
import NoteIcon from "../note_icon";
|
||||||
import NoteTitleWidget from "../note_title";
|
import NoteTitleWidget from "../note_title";
|
||||||
import { Badge, BadgeWithDropdown } from "../react/Badge";
|
import { useNoteContext, useNoteProperty, useStaticTooltip } from "../react/hooks";
|
||||||
import { FormDropdownDivider, FormListItem } from "../react/FormList";
|
|
||||||
import { useNoteBlob, useNoteContext, useNoteProperty, useStaticTooltip, useTriliumEvent } from "../react/hooks";
|
|
||||||
import { joinElements } from "../react/react_utils";
|
import { joinElements } from "../react/react_utils";
|
||||||
import { useNoteMetadata } from "../ribbon/NoteInfoTab";
|
import { useNoteMetadata } from "../ribbon/NoteInfoTab";
|
||||||
import { onWheelHorizontalScroll } from "../widget_utils";
|
|
||||||
|
|
||||||
const supportedNoteTypes = new Set<NoteType>([
|
const supportedNoteTypes = new Set<NoteType>([
|
||||||
"text", "code"
|
"text", "code"
|
||||||
@ -73,8 +64,6 @@ export default function InlineTitle() {
|
|||||||
<NoteTitleDetails />
|
<NoteTitleDetails />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NoteTypeSwitcher />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -138,168 +127,4 @@ function TextWithValue({ i18nKey, value, valueTooltip }: {
|
|||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Note type switcher
|
|
||||||
const SWITCHER_PINNED_NOTE_TYPES = new Set<NoteType>([ "text", "code", "book", "canvas" ]);
|
|
||||||
|
|
||||||
function NoteTypeSwitcher() {
|
|
||||||
const { note } = useNoteContext();
|
|
||||||
const blob = useNoteBlob(note);
|
|
||||||
const currentNoteType = useNoteProperty(note, "type");
|
|
||||||
const { pinnedNoteTypes, restNoteTypes } = useMemo(() => {
|
|
||||||
const pinnedNoteTypes: NoteTypeMapping[] = [];
|
|
||||||
const restNoteTypes: NoteTypeMapping[] = [];
|
|
||||||
for (const noteType of NOTE_TYPES) {
|
|
||||||
if (noteType.reserved || noteType.static || noteType.type === "book") continue;
|
|
||||||
if (SWITCHER_PINNED_NOTE_TYPES.has(noteType.type)) {
|
|
||||||
pinnedNoteTypes.push(noteType);
|
|
||||||
} else {
|
|
||||||
restNoteTypes.push(noteType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { pinnedNoteTypes, restNoteTypes };
|
|
||||||
}, []);
|
|
||||||
const currentNoteTypeData = useMemo(() => NOTE_TYPES.find(t => t.type === currentNoteType), [ currentNoteType ]);
|
|
||||||
const { builtinTemplates, collectionTemplates } = useBuiltinTemplates();
|
|
||||||
|
|
||||||
return (currentNoteType && supportedNoteTypes.has(currentNoteType) &&
|
|
||||||
<div
|
|
||||||
className="note-type-switcher"
|
|
||||||
onWheel={onWheelHorizontalScroll}
|
|
||||||
>
|
|
||||||
{note && blob?.contentLength === 0 && (
|
|
||||||
<>
|
|
||||||
<div className="intro">{t("note_title.note_type_switcher_label", { type: currentNoteTypeData?.title.toLocaleLowerCase() })}</div>
|
|
||||||
{pinnedNoteTypes.map(noteType => noteType.type !== currentNoteType && (
|
|
||||||
<Badge
|
|
||||||
key={noteType.type}
|
|
||||||
text={noteType.title}
|
|
||||||
icon={`bx ${noteType.icon}`}
|
|
||||||
onClick={() => switchNoteType(note.noteId, noteType)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{collectionTemplates.length > 0 && <CollectionNoteTypes noteId={note.noteId} collectionTemplates={collectionTemplates} />}
|
|
||||||
{builtinTemplates.length > 0 && <TemplateNoteTypes noteId={note.noteId} builtinTemplates={builtinTemplates} />}
|
|
||||||
{restNoteTypes.length > 0 && <MoreNoteTypes noteId={note.noteId} restNoteTypes={restNoteTypes} />}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function MoreNoteTypes({ noteId, restNoteTypes }: { noteId: string, restNoteTypes: NoteTypeMapping[] }) {
|
|
||||||
return (
|
|
||||||
<BadgeWithDropdown
|
|
||||||
text={t("note_title.note_type_switcher_others")}
|
|
||||||
icon="bx bx-dots-vertical-rounded"
|
|
||||||
>
|
|
||||||
{restNoteTypes.map(noteType => (
|
|
||||||
<FormListItem
|
|
||||||
key={noteType.type}
|
|
||||||
icon={`bx ${noteType.icon}`}
|
|
||||||
onClick={() => switchNoteType(noteId, noteType)}
|
|
||||||
>{noteType.title}</FormListItem>
|
|
||||||
))}
|
|
||||||
</BadgeWithDropdown>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function CollectionNoteTypes({ noteId, collectionTemplates }: { noteId: string, collectionTemplates: FNote[] }) {
|
|
||||||
return (
|
|
||||||
<BadgeWithDropdown
|
|
||||||
text={t("note_title.note_type_switcher_collection")}
|
|
||||||
icon="bx bx-book"
|
|
||||||
>
|
|
||||||
{collectionTemplates.map(collectionTemplate => (
|
|
||||||
<FormListItem
|
|
||||||
key={collectionTemplate.noteId}
|
|
||||||
icon={collectionTemplate.getIcon()}
|
|
||||||
onClick={() => setTemplate(noteId, collectionTemplate.noteId)}
|
|
||||||
>{collectionTemplate.title}</FormListItem>
|
|
||||||
))}
|
|
||||||
</BadgeWithDropdown>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function TemplateNoteTypes({ noteId, builtinTemplates }: { noteId: string, builtinTemplates: FNote[] }) {
|
|
||||||
const [ userTemplates, setUserTemplates ] = useState<FNote[]>([]);
|
|
||||||
|
|
||||||
async function refreshTemplates() {
|
|
||||||
const templateNoteIds = await server.get<string[]>("search-templates");
|
|
||||||
const templateNotes = await froca.getNotes(templateNoteIds);
|
|
||||||
setUserTemplates(templateNotes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// First load.
|
|
||||||
useEffect(() => {
|
|
||||||
refreshTemplates();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// React to external changes.
|
|
||||||
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
|
||||||
if (loadResults.getAttributeRows().some(attr => attr.type === "label" && attr.name === "template")) {
|
|
||||||
refreshTemplates();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<BadgeWithDropdown
|
|
||||||
text={t("note_title.note_type_switcher_templates")}
|
|
||||||
icon="bx bx-copy-alt"
|
|
||||||
>
|
|
||||||
{userTemplates.map(template => <TemplateItem key={template.noteId} noteId={noteId} template={template} />)}
|
|
||||||
{userTemplates.length > 0 && <FormDropdownDivider />}
|
|
||||||
{builtinTemplates.map(template => <TemplateItem key={template.noteId} noteId={noteId} template={template} />)}
|
|
||||||
</BadgeWithDropdown>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function TemplateItem({ noteId, template }: { noteId: string, template: FNote }) {
|
|
||||||
return (
|
|
||||||
<FormListItem
|
|
||||||
icon={template.getIcon()}
|
|
||||||
onClick={() => setTemplate(noteId, template.noteId)}
|
|
||||||
>{template.title}</FormListItem>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import { useNoteContext, useNoteLabel, useNoteProperty, useTriliumEvent, useTril
|
|||||||
import NoteLink from "../react/NoteLink";
|
import NoteLink from "../react/NoteLink";
|
||||||
import { useEditedNotes } from "../ribbon/EditedNotesTab";
|
import { useEditedNotes } from "../ribbon/EditedNotesTab";
|
||||||
import SearchDefinitionTab from "../ribbon/SearchDefinitionTab";
|
import SearchDefinitionTab from "../ribbon/SearchDefinitionTab";
|
||||||
|
import NoteTypeSwitcher from "./NoteTypeSwitcher";
|
||||||
|
|
||||||
export default function NoteTitleActions() {
|
export default function NoteTitleActions() {
|
||||||
const { note, ntxId, componentId, noteContext } = useNoteContext();
|
const { note, ntxId, componentId, noteContext } = useNoteContext();
|
||||||
@ -27,6 +28,7 @@ export default function NoteTitleActions() {
|
|||||||
{noteType === "search" && <SearchProperties note={note} ntxId={ntxId} />}
|
{noteType === "search" && <SearchProperties note={note} ntxId={ntxId} />}
|
||||||
{!isHiddenNote && note && noteType === "book" && <CollectionProperties note={note} />}
|
{!isHiddenNote && note && noteType === "book" && <CollectionProperties note={note} />}
|
||||||
<EditedNotes />
|
<EditedNotes />
|
||||||
|
<NoteTypeSwitcher />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
33
apps/client/src/widgets/layout/NoteTypeSwitcher.css
Normal file
33
apps/client/src/widgets/layout/NoteTypeSwitcher.css
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
@keyframes note-type-switcher-intro {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
} to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-type-switcher {
|
||||||
|
--badge-radius: 12px;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
top: 5px;
|
||||||
|
padding: .25em 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
overflow-x: auto;
|
||||||
|
min-width: 0;
|
||||||
|
gap: 5px;
|
||||||
|
min-height: 35px;
|
||||||
|
|
||||||
|
>* {
|
||||||
|
flex-shrink: 0;
|
||||||
|
animation: note-type-switcher-intro 200ms ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ext-badge {
|
||||||
|
--color: var(--input-background-color);
|
||||||
|
color: var(--main-text-color);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
182
apps/client/src/widgets/layout/NoteTypeSwitcher.tsx
Normal file
182
apps/client/src/widgets/layout/NoteTypeSwitcher.tsx
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import "./NoteTypeSwitcher.css";
|
||||||
|
|
||||||
|
import { NoteType } from "@triliumnext/commons";
|
||||||
|
import { useEffect, useMemo, useState } from "preact/hooks";
|
||||||
|
|
||||||
|
import FNote from "../../entities/fnote";
|
||||||
|
import attributes from "../../services/attributes";
|
||||||
|
import froca from "../../services/froca";
|
||||||
|
import { t } from "../../services/i18n";
|
||||||
|
import { NOTE_TYPES, NoteTypeMapping } from "../../services/note_types";
|
||||||
|
import server from "../../services/server";
|
||||||
|
import { Badge, BadgeWithDropdown } from "../react/Badge";
|
||||||
|
import { FormDropdownDivider, FormListItem } from "../react/FormList";
|
||||||
|
import { useNoteBlob, useNoteContext, useNoteProperty, useTriliumEvent } from "../react/hooks";
|
||||||
|
import { onWheelHorizontalScroll } from "../widget_utils";
|
||||||
|
|
||||||
|
const SWITCHER_PINNED_NOTE_TYPES = new Set<NoteType>([ "text", "code", "book", "canvas" ]);
|
||||||
|
const supportedNoteTypes = new Set<NoteType>([
|
||||||
|
"text", "code"
|
||||||
|
]);
|
||||||
|
|
||||||
|
export default function NoteTypeSwitcher() {
|
||||||
|
const { note } = useNoteContext();
|
||||||
|
const blob = useNoteBlob(note);
|
||||||
|
const currentNoteType = useNoteProperty(note, "type");
|
||||||
|
const { pinnedNoteTypes, restNoteTypes } = useMemo(() => {
|
||||||
|
const pinnedNoteTypes: NoteTypeMapping[] = [];
|
||||||
|
const restNoteTypes: NoteTypeMapping[] = [];
|
||||||
|
for (const noteType of NOTE_TYPES) {
|
||||||
|
if (noteType.reserved || noteType.static || noteType.type === "book") continue;
|
||||||
|
if (SWITCHER_PINNED_NOTE_TYPES.has(noteType.type)) {
|
||||||
|
pinnedNoteTypes.push(noteType);
|
||||||
|
} else {
|
||||||
|
restNoteTypes.push(noteType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { pinnedNoteTypes, restNoteTypes };
|
||||||
|
}, []);
|
||||||
|
const currentNoteTypeData = useMemo(() => NOTE_TYPES.find(t => t.type === currentNoteType), [ currentNoteType ]);
|
||||||
|
const { builtinTemplates, collectionTemplates } = useBuiltinTemplates();
|
||||||
|
|
||||||
|
return (currentNoteType && supportedNoteTypes.has(currentNoteType) &&
|
||||||
|
<div
|
||||||
|
className="note-type-switcher"
|
||||||
|
onWheel={onWheelHorizontalScroll}
|
||||||
|
>
|
||||||
|
{note && blob?.contentLength === 0 && (
|
||||||
|
<>
|
||||||
|
<div className="intro">{t("note_title.note_type_switcher_label", { type: currentNoteTypeData?.title.toLocaleLowerCase() })}</div>
|
||||||
|
{pinnedNoteTypes.map(noteType => noteType.type !== currentNoteType && (
|
||||||
|
<Badge
|
||||||
|
key={noteType.type}
|
||||||
|
text={noteType.title}
|
||||||
|
icon={`bx ${noteType.icon}`}
|
||||||
|
onClick={() => switchNoteType(note.noteId, noteType)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{collectionTemplates.length > 0 && <CollectionNoteTypes noteId={note.noteId} collectionTemplates={collectionTemplates} />}
|
||||||
|
{builtinTemplates.length > 0 && <TemplateNoteTypes noteId={note.noteId} builtinTemplates={builtinTemplates} />}
|
||||||
|
{restNoteTypes.length > 0 && <MoreNoteTypes noteId={note.noteId} restNoteTypes={restNoteTypes} />}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MoreNoteTypes({ noteId, restNoteTypes }: { noteId: string, restNoteTypes: NoteTypeMapping[] }) {
|
||||||
|
return (
|
||||||
|
<BadgeWithDropdown
|
||||||
|
text={t("note_title.note_type_switcher_others")}
|
||||||
|
icon="bx bx-dots-vertical-rounded"
|
||||||
|
>
|
||||||
|
{restNoteTypes.map(noteType => (
|
||||||
|
<FormListItem
|
||||||
|
key={noteType.type}
|
||||||
|
icon={`bx ${noteType.icon}`}
|
||||||
|
onClick={() => switchNoteType(noteId, noteType)}
|
||||||
|
>{noteType.title}</FormListItem>
|
||||||
|
))}
|
||||||
|
</BadgeWithDropdown>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CollectionNoteTypes({ noteId, collectionTemplates }: { noteId: string, collectionTemplates: FNote[] }) {
|
||||||
|
return (
|
||||||
|
<BadgeWithDropdown
|
||||||
|
text={t("note_title.note_type_switcher_collection")}
|
||||||
|
icon="bx bx-book"
|
||||||
|
>
|
||||||
|
{collectionTemplates.map(collectionTemplate => (
|
||||||
|
<FormListItem
|
||||||
|
key={collectionTemplate.noteId}
|
||||||
|
icon={collectionTemplate.getIcon()}
|
||||||
|
onClick={() => setTemplate(noteId, collectionTemplate.noteId)}
|
||||||
|
>{collectionTemplate.title}</FormListItem>
|
||||||
|
))}
|
||||||
|
</BadgeWithDropdown>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TemplateNoteTypes({ noteId, builtinTemplates }: { noteId: string, builtinTemplates: FNote[] }) {
|
||||||
|
const [ userTemplates, setUserTemplates ] = useState<FNote[]>([]);
|
||||||
|
|
||||||
|
async function refreshTemplates() {
|
||||||
|
const templateNoteIds = await server.get<string[]>("search-templates");
|
||||||
|
const templateNotes = await froca.getNotes(templateNoteIds);
|
||||||
|
setUserTemplates(templateNotes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// First load.
|
||||||
|
useEffect(() => {
|
||||||
|
refreshTemplates();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// React to external changes.
|
||||||
|
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||||
|
if (loadResults.getAttributeRows().some(attr => attr.type === "label" && attr.name === "template")) {
|
||||||
|
refreshTemplates();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BadgeWithDropdown
|
||||||
|
text={t("note_title.note_type_switcher_templates")}
|
||||||
|
icon="bx bx-copy-alt"
|
||||||
|
>
|
||||||
|
{userTemplates.map(template => <TemplateItem key={template.noteId} noteId={noteId} template={template} />)}
|
||||||
|
{userTemplates.length > 0 && <FormDropdownDivider />}
|
||||||
|
{builtinTemplates.map(template => <TemplateItem key={template.noteId} noteId={noteId} template={template} />)}
|
||||||
|
</BadgeWithDropdown>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TemplateItem({ noteId, template }: { noteId: string, template: FNote }) {
|
||||||
|
return (
|
||||||
|
<FormListItem
|
||||||
|
icon={template.getIcon()}
|
||||||
|
onClick={() => setTemplate(noteId, template.noteId)}
|
||||||
|
>{template.title}</FormListItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user