mirror of
https://github.com/zadam/trilium.git
synced 2025-10-10 23:54:21 +08:00
Note Type Badges (#6229)
This commit is contained in:
commit
8efef6842d
9 changed files with 235 additions and 105 deletions
|
@ -17,11 +17,17 @@ interface MenuSeparatorItem {
|
||||||
title: "----";
|
title: "----";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MenuItemBadge {
|
||||||
|
title: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MenuCommandItem<T> {
|
export interface MenuCommandItem<T> {
|
||||||
title: string;
|
title: string;
|
||||||
command?: T;
|
command?: T;
|
||||||
type?: string;
|
type?: string;
|
||||||
uiIcon?: string;
|
uiIcon?: string;
|
||||||
|
badges?: MenuItemBadge[];
|
||||||
templateNoteId?: string;
|
templateNoteId?: string;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
handler?: MenuHandler<T>;
|
handler?: MenuHandler<T>;
|
||||||
|
@ -161,6 +167,18 @@ class ContextMenu {
|
||||||
.append(" ") // some space between icon and text
|
.append(" ") // some space between icon and text
|
||||||
.append(item.title);
|
.append(item.title);
|
||||||
|
|
||||||
|
if ("badges" in item && item.badges) {
|
||||||
|
for (let badge of item.badges) {
|
||||||
|
const badgeElement = $(`<span class="badge">`).text(badge.title);
|
||||||
|
|
||||||
|
if (badge.className) {
|
||||||
|
badgeElement.addClass(badge.className);
|
||||||
|
}
|
||||||
|
|
||||||
|
$link.append(badgeElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ("shortcut" in item && item.shortcut) {
|
if ("shortcut" in item && item.shortcut) {
|
||||||
$link.append($("<kbd>").text(item.shortcut));
|
$link.append($("<kbd>").text(item.shortcut));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +1,87 @@
|
||||||
import server from "./server.js";
|
|
||||||
import froca from "./froca.js";
|
|
||||||
import { t } from "./i18n.js";
|
import { t } from "./i18n.js";
|
||||||
import type { MenuItem } from "../menus/context_menu.js";
|
import froca from "./froca.js";
|
||||||
|
import server from "./server.js";
|
||||||
|
import type { MenuCommandItem, MenuItem, MenuItemBadge } from "../menus/context_menu.js";
|
||||||
|
import type { NoteType } from "../entities/fnote.js";
|
||||||
import type { TreeCommandNames } from "../menus/tree_context_menu.js";
|
import type { TreeCommandNames } from "../menus/tree_context_menu.js";
|
||||||
|
|
||||||
|
export interface NoteTypeMapping {
|
||||||
|
type: NoteType;
|
||||||
|
mime?: string;
|
||||||
|
title: string;
|
||||||
|
icon?: string;
|
||||||
|
/** Indicates whether this type should be marked as a newly introduced feature. */
|
||||||
|
isNew?: boolean;
|
||||||
|
/** Indicates that this note type is part of a beta feature. */
|
||||||
|
isBeta?: boolean;
|
||||||
|
/** Indicates that this note type cannot be created by the user. */
|
||||||
|
reserved?: boolean;
|
||||||
|
/** Indicates that once a note of this type is created, its type can no longer be changed. */
|
||||||
|
static?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NOTE_TYPES: NoteTypeMapping[] = [
|
||||||
|
// The suggested note type ordering method: insert the item into the corresponding group,
|
||||||
|
// then ensure the items within the group are ordered alphabetically.
|
||||||
|
|
||||||
|
// The default note type (always the first item)
|
||||||
|
{ type: "text", mime: "text/html", title: t("note_types.text"), icon: "bx-note" },
|
||||||
|
|
||||||
|
// Text notes group
|
||||||
|
{ type: "book", mime: "", title: t("note_types.book"), icon: "bx-book" },
|
||||||
|
|
||||||
|
// Graphic notes
|
||||||
|
{ type: "canvas", mime: "application/json", title: t("note_types.canvas"), icon: "bx-pen" },
|
||||||
|
{ type: "mermaid", mime: "text/mermaid", title: t("note_types.mermaid-diagram"), icon: "bx-selection" },
|
||||||
|
|
||||||
|
// Map notes
|
||||||
|
{ type: "geoMap", mime: "application/json", title: t("note_types.geo-map"), icon: "bx-map-alt", isBeta: true },
|
||||||
|
{ type: "mindMap", mime: "application/json", title: t("note_types.mind-map"), icon: "bx-sitemap" },
|
||||||
|
{ type: "noteMap", mime: "", title: t("note_types.note-map"), icon: "bxs-network-chart", static: true },
|
||||||
|
{ type: "relationMap", mime: "application/json", title: t("note_types.relation-map"), icon: "bxs-network-chart" },
|
||||||
|
|
||||||
|
// Misc note types
|
||||||
|
{ type: "render", mime: "", title: t("note_types.render-note"), icon: "bx-extension" },
|
||||||
|
{ type: "search", title: t("note_types.saved-search"), icon: "bx-file-find", static: true },
|
||||||
|
{ type: "webView", mime: "", title: t("note_types.web-view"), icon: "bx-globe-alt" },
|
||||||
|
|
||||||
|
// Code notes
|
||||||
|
{ type: "code", mime: "text/plain", title: t("note_types.code"), icon: "bx-code" },
|
||||||
|
|
||||||
|
// Reserved types (cannot be created by the user)
|
||||||
|
{ type: "contentWidget", mime: "", title: t("note_types.widget"), reserved: true },
|
||||||
|
{ type: "doc", mime: "", title: t("note_types.doc"), reserved: true },
|
||||||
|
{ type: "file", title: t("note_types.file"), reserved: true },
|
||||||
|
{ type: "image", title: t("note_types.image"), reserved: true },
|
||||||
|
{ type: "launcher", mime: "", title: t("note_types.launcher"), reserved: true },
|
||||||
|
{ type: "aiChat", mime: "application/json", title: t("note_types.ai-chat"), reserved: true }
|
||||||
|
];
|
||||||
|
|
||||||
|
/** The maximum age in days for a template to be marked with the "New" badge */
|
||||||
|
const NEW_TEMPLATE_MAX_AGE = 3;
|
||||||
|
|
||||||
|
/** The length of a day in milliseconds. */
|
||||||
|
const DAY_LENGTH = 1000 * 60 * 60 * 24;
|
||||||
|
|
||||||
|
/** The menu item badge used to mark new note types and templates */
|
||||||
|
const NEW_BADGE: MenuItemBadge = {
|
||||||
|
title: t("note_types.new-feature"),
|
||||||
|
className: "new-note-type-badge"
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The menu item badge used to mark note types that are part of a beta feature */
|
||||||
|
const BETA_BADGE = {
|
||||||
|
title: t("note_types.beta-feature")
|
||||||
|
};
|
||||||
|
|
||||||
const SEPARATOR = { title: "----" };
|
const SEPARATOR = { title: "----" };
|
||||||
|
|
||||||
|
const creationDateCache = new Map<string, Date>();
|
||||||
|
let rootCreationDate: Date | undefined;
|
||||||
|
|
||||||
async function getNoteTypeItems(command?: TreeCommandNames) {
|
async function getNoteTypeItems(command?: TreeCommandNames) {
|
||||||
const items: MenuItem<TreeCommandNames>[] = [
|
const items: MenuItem<TreeCommandNames>[] = [
|
||||||
// The suggested note type ordering method: insert the item into the corresponding group,
|
...getBlankNoteTypes(command),
|
||||||
// then ensure the items within the group are ordered alphabetically.
|
|
||||||
// Please keep the order synced with the listing found also in aps/client/src/widgets/note_types.ts.
|
|
||||||
|
|
||||||
// The default note type (always the first item)
|
|
||||||
{ title: t("note_types.text"), command, type: "text", uiIcon: "bx bx-note" },
|
|
||||||
|
|
||||||
// Text notes group
|
|
||||||
{ title: t("note_types.book"), command, type: "book", uiIcon: "bx bx-book" },
|
|
||||||
|
|
||||||
// Graphic notes
|
|
||||||
{ title: t("note_types.canvas"), command, type: "canvas", uiIcon: "bx bx-pen" },
|
|
||||||
{ title: t("note_types.mermaid-diagram"), command, type: "mermaid", uiIcon: "bx bx-selection" },
|
|
||||||
|
|
||||||
// Map notes
|
|
||||||
{ title: t("note_types.geo-map"), command, type: "geoMap", uiIcon: "bx bx-map-alt" },
|
|
||||||
{ title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" },
|
|
||||||
{ title: t("note_types.note-map"), command, type: "noteMap", uiIcon: "bx bxs-network-chart" },
|
|
||||||
{ title: t("note_types.relation-map"), command, type: "relationMap", uiIcon: "bx bxs-network-chart" },
|
|
||||||
|
|
||||||
// Misc note types
|
|
||||||
{ title: t("note_types.render-note"), command, type: "render", uiIcon: "bx bx-extension" },
|
|
||||||
{ title: t("note_types.saved-search"), command, type: "search", uiIcon: "bx bx-file-find" },
|
|
||||||
{ title: t("note_types.web-view"), command, type: "webView", uiIcon: "bx bx-globe-alt" },
|
|
||||||
|
|
||||||
// Code notes
|
|
||||||
{ title: t("note_types.code"), command, type: "code", uiIcon: "bx bx-code" },
|
|
||||||
|
|
||||||
// Templates
|
|
||||||
...await getBuiltInTemplates(command),
|
...await getBuiltInTemplates(command),
|
||||||
...await getUserTemplates(command)
|
...await getUserTemplates(command)
|
||||||
];
|
];
|
||||||
|
@ -44,6 +89,28 @@ async function getNoteTypeItems(command?: TreeCommandNames) {
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBlankNoteTypes(command): MenuItem<TreeCommandNames>[] {
|
||||||
|
return NOTE_TYPES.filter((nt) => !nt.reserved).map((nt) => {
|
||||||
|
const menuItem: MenuCommandItem<TreeCommandNames> = {
|
||||||
|
title: nt.title,
|
||||||
|
command,
|
||||||
|
type: nt.type,
|
||||||
|
uiIcon: "bx " + nt.icon,
|
||||||
|
badges: []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nt.isNew) {
|
||||||
|
menuItem.badges?.push(NEW_BADGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nt.isBeta) {
|
||||||
|
menuItem.badges?.push(BETA_BADGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return menuItem;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function getUserTemplates(command?: TreeCommandNames) {
|
async function getUserTemplates(command?: TreeCommandNames) {
|
||||||
const templateNoteIds = await server.get<string[]>("search-templates");
|
const templateNoteIds = await server.get<string[]>("search-templates");
|
||||||
const templateNotes = await froca.getNotes(templateNoteIds);
|
const templateNotes = await froca.getNotes(templateNoteIds);
|
||||||
|
@ -54,14 +121,21 @@ async function getUserTemplates(command?: TreeCommandNames) {
|
||||||
const items: MenuItem<TreeCommandNames>[] = [
|
const items: MenuItem<TreeCommandNames>[] = [
|
||||||
SEPARATOR
|
SEPARATOR
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const templateNote of templateNotes) {
|
for (const templateNote of templateNotes) {
|
||||||
items.push({
|
const item: MenuItem<TreeCommandNames> = {
|
||||||
title: templateNote.title,
|
title: templateNote.title,
|
||||||
uiIcon: templateNote.getIcon(),
|
uiIcon: templateNote.getIcon(),
|
||||||
command: command,
|
command: command,
|
||||||
type: templateNote.type,
|
type: templateNote.type,
|
||||||
templateNoteId: templateNote.noteId
|
templateNoteId: templateNote.noteId
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (await isNewTemplate(templateNote.noteId)) {
|
||||||
|
item.badges = [NEW_BADGE];
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push(item);
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
@ -81,18 +155,71 @@ async function getBuiltInTemplates(command?: TreeCommandNames) {
|
||||||
const items: MenuItem<TreeCommandNames>[] = [
|
const items: MenuItem<TreeCommandNames>[] = [
|
||||||
SEPARATOR
|
SEPARATOR
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const templateNote of childNotes) {
|
for (const templateNote of childNotes) {
|
||||||
items.push({
|
const item: MenuItem<TreeCommandNames> = {
|
||||||
title: templateNote.title,
|
title: templateNote.title,
|
||||||
uiIcon: templateNote.getIcon(),
|
uiIcon: templateNote.getIcon(),
|
||||||
command: command,
|
command: command,
|
||||||
type: templateNote.type,
|
type: templateNote.type,
|
||||||
templateNoteId: templateNote.noteId
|
templateNoteId: templateNote.noteId
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (await isNewTemplate(templateNote.noteId)) {
|
||||||
|
item.badges = [NEW_BADGE];
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push(item);
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function isNewTemplate(templateNoteId) {
|
||||||
|
if (rootCreationDate === undefined) {
|
||||||
|
// Retrieve the root note creation date
|
||||||
|
try {
|
||||||
|
let rootNoteInfo: any = await server.get("notes/root");
|
||||||
|
if ("dateCreated" in rootNoteInfo) {
|
||||||
|
rootCreationDate = new Date(rootNoteInfo.dateCreated);
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
console.error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to retrieve the template's creation date from the cache
|
||||||
|
let creationDate: Date | undefined = creationDateCache.get(templateNoteId);
|
||||||
|
|
||||||
|
if (creationDate === undefined) {
|
||||||
|
// The creation date isn't available in the cache, try to retrieve it from the server
|
||||||
|
try {
|
||||||
|
const noteInfo: any = await server.get("notes/" + templateNoteId);
|
||||||
|
if ("dateCreated" in noteInfo) {
|
||||||
|
creationDate = new Date(noteInfo.dateCreated);
|
||||||
|
creationDateCache.set(templateNoteId, creationDate);
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
console.error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (creationDate) {
|
||||||
|
if (rootCreationDate && creationDate.getTime() - rootCreationDate.getTime() < 30000) {
|
||||||
|
// Ignore templates created within 30 seconds after the root note is created.
|
||||||
|
// This is useful to prevent predefined templates from being marked
|
||||||
|
// as 'New' after setting up a new database.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the difference in days between now and the template's creation date
|
||||||
|
const age = (new Date().getTime() - creationDate.getTime()) / DAY_LENGTH;
|
||||||
|
// Return true if the template is at most NEW_TEMPLATE_MAX_AGE days old
|
||||||
|
return (age <= NEW_TEMPLATE_MAX_AGE);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getNoteTypeItems
|
getNoteTypeItems
|
||||||
};
|
};
|
||||||
|
|
|
@ -192,6 +192,13 @@ samp {
|
||||||
font-family: var(--monospace-font-family) !important;
|
font-family: var(--monospace-font-family) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
--bs-badge-color: var(--muted-text-color);
|
||||||
|
|
||||||
|
margin-left: 8px;
|
||||||
|
background: var(--accented-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
.input-group-text {
|
.input-group-text {
|
||||||
background-color: var(--accented-background-color) !important;
|
background-color: var(--accented-background-color) !important;
|
||||||
color: var(--muted-text-color) !important;
|
color: var(--muted-text-color) !important;
|
||||||
|
|
|
@ -178,6 +178,9 @@
|
||||||
|
|
||||||
--alert-bar-background: #6b6b6b3b;
|
--alert-bar-background: #6b6b6b3b;
|
||||||
|
|
||||||
|
--badge-background-color: #ffffff1a;
|
||||||
|
--badge-text-color: var(--muted-text-color);
|
||||||
|
|
||||||
--promoted-attribute-card-background-color: var(--card-background-color);
|
--promoted-attribute-card-background-color: var(--card-background-color);
|
||||||
--promoted-attribute-card-shadow-color: #000000b3;
|
--promoted-attribute-card-shadow-color: #000000b3;
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,9 @@
|
||||||
|
|
||||||
--alert-bar-background: #32637b29;
|
--alert-bar-background: #32637b29;
|
||||||
|
|
||||||
|
--badge-background-color: #00000011;
|
||||||
|
--badge-text-color: var(--muted-text-color);
|
||||||
|
|
||||||
--promoted-attribute-card-background-color: var(--card-background-color);
|
--promoted-attribute-card-background-color: var(--card-background-color);
|
||||||
--promoted-attribute-card-shadow-color: #00000033;
|
--promoted-attribute-card-shadow-color: #00000033;
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,16 @@ html body .dropdown-item[disabled] {
|
||||||
opacity: var(--menu-item-disabled-opacity);
|
opacity: var(--menu-item-disabled-opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Badges */
|
||||||
|
:root .badge {
|
||||||
|
--bs-badge-color: var(--badge-text-color);
|
||||||
|
--bs-badge-font-weight: 500;
|
||||||
|
|
||||||
|
background: var(--badge-background-color);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .2pt;
|
||||||
|
}
|
||||||
|
|
||||||
/* Menu item icon */
|
/* Menu item icon */
|
||||||
.dropdown-item .bx {
|
.dropdown-item .bx {
|
||||||
transform: translateY(var(--menu-item-icon-vert-offset));
|
transform: translateY(var(--menu-item-icon-vert-offset));
|
||||||
|
|
|
@ -1627,7 +1627,8 @@
|
||||||
"geo-map": "Geo Map",
|
"geo-map": "Geo Map",
|
||||||
"beta-feature": "Beta",
|
"beta-feature": "Beta",
|
||||||
"ai-chat": "AI Chat",
|
"ai-chat": "AI Chat",
|
||||||
"task-list": "Task List"
|
"task-list": "Task List",
|
||||||
|
"new-feature": "New"
|
||||||
},
|
},
|
||||||
"protect_note": {
|
"protect_note": {
|
||||||
"toggle-on": "Protect the note",
|
"toggle-on": "Protect the note",
|
||||||
|
|
|
@ -154,13 +154,21 @@ export default class NoteTypeChooserDialog extends BasicWidget {
|
||||||
this.$noteTypeDropdown.append($('<h6 class="dropdown-header">').append(t("note_type_chooser.templates")));
|
this.$noteTypeDropdown.append($('<h6 class="dropdown-header">').append(t("note_type_chooser.templates")));
|
||||||
} else {
|
} else {
|
||||||
const commandItem = noteType as MenuCommandItem<CommandNames>;
|
const commandItem = noteType as MenuCommandItem<CommandNames>;
|
||||||
this.$noteTypeDropdown.append(
|
const listItem = $('<a class="dropdown-item" tabindex="0">')
|
||||||
$('<a class="dropdown-item" tabindex="0">')
|
|
||||||
.attr("data-note-type", commandItem.type || "")
|
.attr("data-note-type", commandItem.type || "")
|
||||||
.attr("data-template-note-id", commandItem.templateNoteId || "")
|
.attr("data-template-note-id", commandItem.templateNoteId || "")
|
||||||
.append($("<span>").addClass(commandItem.uiIcon || ""))
|
.append($("<span>").addClass(commandItem.uiIcon || ""))
|
||||||
.append(` ${noteType.title}`)
|
.append(` ${noteType.title}`);
|
||||||
);
|
|
||||||
|
if (commandItem.badges) {
|
||||||
|
for (let badge of commandItem.badges) {
|
||||||
|
listItem.append($(`<span class="badge">`)
|
||||||
|
.addClass(badge.className || "")
|
||||||
|
.text(badge.title));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$noteTypeDropdown.append(listItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,60 +1,15 @@
|
||||||
import server from "../services/server.js";
|
import { Dropdown } from "bootstrap";
|
||||||
|
import { NOTE_TYPES } from "../services/note_types.js";
|
||||||
|
import { t } from "../services/i18n.js";
|
||||||
|
import dialogService from "../services/dialog.js";
|
||||||
import mimeTypesService from "../services/mime_types.js";
|
import mimeTypesService from "../services/mime_types.js";
|
||||||
import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
import NoteContextAwareWidget from "./note_context_aware_widget.js";
|
||||||
import dialogService from "../services/dialog.js";
|
import server from "../services/server.js";
|
||||||
import { t } from "../services/i18n.js";
|
|
||||||
import type FNote from "../entities/fnote.js";
|
|
||||||
import type { NoteType } from "../entities/fnote.js";
|
|
||||||
import type { EventData } from "../components/app_context.js";
|
import type { EventData } from "../components/app_context.js";
|
||||||
import { Dropdown } from "bootstrap";
|
import type { NoteType } from "../entities/fnote.js";
|
||||||
|
import type FNote from "../entities/fnote.js";
|
||||||
|
|
||||||
interface NoteTypeMapping {
|
const NOT_SELECTABLE_NOTE_TYPES = NOTE_TYPES.filter((nt) => nt.reserved || nt.static).map((nt) => nt.type);
|
||||||
type: NoteType;
|
|
||||||
mime?: string;
|
|
||||||
title: string;
|
|
||||||
isBeta?: boolean;
|
|
||||||
selectable: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NOTE_TYPES: NoteTypeMapping[] = [
|
|
||||||
// The suggested note type ordering method: insert the item into the corresponding group,
|
|
||||||
// then ensure the items within the group are ordered alphabetically.
|
|
||||||
// Please keep the order synced with the listing found also in apps/client/src/services/note_types.ts.
|
|
||||||
|
|
||||||
// The default note type (always the first item)
|
|
||||||
{ type: "text", mime: "text/html", title: t("note_types.text"), selectable: true },
|
|
||||||
|
|
||||||
// Text notes group
|
|
||||||
{ type: "book", mime: "", title: t("note_types.book"), selectable: true },
|
|
||||||
|
|
||||||
// Graphic notes
|
|
||||||
{ type: "canvas", mime: "application/json", title: t("note_types.canvas"), selectable: true },
|
|
||||||
{ type: "mermaid", mime: "text/mermaid", title: t("note_types.mermaid-diagram"), selectable: true },
|
|
||||||
|
|
||||||
// Map notes
|
|
||||||
{ type: "geoMap", mime: "application/json", title: t("note_types.geo-map"), isBeta: true, selectable: true },
|
|
||||||
{ type: "mindMap", mime: "application/json", title: t("note_types.mind-map"), selectable: true },
|
|
||||||
{ type: "relationMap", mime: "application/json", title: t("note_types.relation-map"), selectable: true },
|
|
||||||
|
|
||||||
// Misc note types
|
|
||||||
{ type: "render", mime: "", title: t("note_types.render-note"), selectable: true },
|
|
||||||
{ type: "webView", mime: "", title: t("note_types.web-view"), selectable: true },
|
|
||||||
|
|
||||||
// Code notes
|
|
||||||
{ type: "code", mime: "text/plain", title: t("note_types.code"), selectable: true },
|
|
||||||
|
|
||||||
// Reserved types (cannot be created by the user)
|
|
||||||
{ type: "contentWidget", mime: "", title: t("note_types.widget"), selectable: false },
|
|
||||||
{ type: "doc", mime: "", title: t("note_types.doc"), selectable: false },
|
|
||||||
{ type: "file", title: t("note_types.file"), selectable: false },
|
|
||||||
{ type: "image", title: t("note_types.image"), selectable: false },
|
|
||||||
{ type: "launcher", mime: "", title: t("note_types.launcher"), selectable: false },
|
|
||||||
{ type: "noteMap", mime: "", title: t("note_types.note-map"), selectable: false },
|
|
||||||
{ type: "search", title: t("note_types.saved-search"), selectable: false },
|
|
||||||
{ type: "aiChat", mime: "application/json", title: t("note_types.ai-chat"), selectable: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
const NOT_SELECTABLE_NOTE_TYPES = NOTE_TYPES.filter((nt) => !nt.selectable).map((nt) => nt.type);
|
|
||||||
|
|
||||||
const TPL = /*html*/`
|
const TPL = /*html*/`
|
||||||
<div class="dropdown note-type-widget">
|
<div class="dropdown note-type-widget">
|
||||||
|
@ -64,13 +19,6 @@ const TPL = /*html*/`
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-type-dropdown .badge {
|
|
||||||
margin-left: 8px;
|
|
||||||
background: var(--accented-background-color);
|
|
||||||
font-weight: normal;
|
|
||||||
color: var(--menu-text-color);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<button type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm dropdown-toggle select-button note-type-button">
|
<button type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="btn btn-sm dropdown-toggle select-button note-type-button">
|
||||||
<span class="note-type-desc"></span>
|
<span class="note-type-desc"></span>
|
||||||
|
@ -117,10 +65,15 @@ export default class NoteTypeWidget extends NoteContextAwareWidget {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const noteType of NOTE_TYPES.filter((nt) => nt.selectable)) {
|
for (const noteType of NOTE_TYPES.filter((nt) => !nt.reserved && !nt.static)) {
|
||||||
let $typeLink: JQuery<HTMLElement>;
|
let $typeLink: JQuery<HTMLElement>;
|
||||||
|
|
||||||
const $title = $("<span>").text(noteType.title);
|
const $title = $("<span>").text(noteType.title);
|
||||||
|
|
||||||
|
if (noteType.isNew) {
|
||||||
|
$title.append($(`<span class="badge new-note-type-badge">`).text(t("note_types.new-feature")));
|
||||||
|
}
|
||||||
|
|
||||||
if (noteType.isBeta) {
|
if (noteType.isBeta) {
|
||||||
$title.append($(`<span class="badge">`).text(t("note_types.beta-feature")));
|
$title.append($(`<span class="badge">`).text(t("note_types.beta-feature")));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue