mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
select template when creating note, closes #2813
This commit is contained in:
parent
b204014a11
commit
93f0596b16
94
src/public/app/dialogs/note_type_chooser.js
Normal file
94
src/public/app/dialogs/note_type_chooser.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import noteTypesService from "../services/note_types.js";
|
||||||
|
|
||||||
|
const $dialog = $("#note-type-chooser-dialog");
|
||||||
|
const $noteTypeDropdown = $("#note-type-dropdown");
|
||||||
|
const $noteTypeDropdownTrigger = $("#note-type-dropdown-trigger");
|
||||||
|
$noteTypeDropdownTrigger.dropdown();
|
||||||
|
|
||||||
|
let resolve;
|
||||||
|
let $originalFocused; // element focused before the dialog was opened, so we can return to it afterwards
|
||||||
|
let $originalDialog;
|
||||||
|
|
||||||
|
export async function chooseNoteType() {
|
||||||
|
$originalFocused = $(':focus');
|
||||||
|
|
||||||
|
const noteTypes = await noteTypesService.getNoteTypeItems();
|
||||||
|
|
||||||
|
$noteTypeDropdown.empty();
|
||||||
|
|
||||||
|
for (const noteType of noteTypes) {
|
||||||
|
if (noteType.title === '----') {
|
||||||
|
$noteTypeDropdown.append($('<h6 class="dropdown-header">').append("Templates:"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$noteTypeDropdown.append(
|
||||||
|
$('<a class="dropdown-item" tabindex="0">')
|
||||||
|
.attr("data-note-type", noteType.type)
|
||||||
|
.attr("data-template-note-id", noteType.templateNoteId)
|
||||||
|
.append($("<span>").addClass(noteType.uiIcon))
|
||||||
|
.append(" " + noteType.title)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$noteTypeDropdownTrigger.dropdown('show');
|
||||||
|
|
||||||
|
$originalDialog = glob.activeDialog;
|
||||||
|
glob.activeDialog = $dialog;
|
||||||
|
$dialog.modal();
|
||||||
|
|
||||||
|
$noteTypeDropdown.find(".dropdown-item:first").focus();
|
||||||
|
|
||||||
|
return new Promise((res, rej) => { resolve = res; });
|
||||||
|
}
|
||||||
|
|
||||||
|
$dialog.on("hidden.bs.modal", () => {
|
||||||
|
if (resolve) {
|
||||||
|
resolve({success: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($originalFocused) {
|
||||||
|
$originalFocused.trigger('focus');
|
||||||
|
$originalFocused = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
glob.activeDialog = $originalDialog;
|
||||||
|
});
|
||||||
|
|
||||||
|
function doResolve(e) {
|
||||||
|
const $item = $(e.target).closest(".dropdown-item");
|
||||||
|
const noteType = $item.attr("data-note-type");
|
||||||
|
const templateNoteId = $item.attr("data-template-note-id");
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
success: true,
|
||||||
|
noteType,
|
||||||
|
templateNoteId
|
||||||
|
});
|
||||||
|
resolve = null;
|
||||||
|
|
||||||
|
$dialog.modal("hide");
|
||||||
|
}
|
||||||
|
|
||||||
|
$noteTypeDropdown.on('click', '.dropdown-item', e => doResolve(e));
|
||||||
|
|
||||||
|
$noteTypeDropdown.on('focus', '.dropdown-item', e => {
|
||||||
|
$noteTypeDropdown.find('.dropdown-item').each((i, el) => {
|
||||||
|
$(el).toggleClass('active', el === e.target);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$noteTypeDropdown.on('keydown', '.dropdown-item', e => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
doResolve(e);
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$noteTypeDropdown.parent().on('hide.bs.dropdown', e => {
|
||||||
|
// prevent closing dropdown by clicking outside
|
||||||
|
if (e.clickEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
@ -170,9 +170,18 @@ function initNoteAutocomplete($el, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (suggestion.action === 'create-note') {
|
if (suggestion.action === 'create-note') {
|
||||||
|
const noteTypeChooserDialog = await import('../dialogs/note_type_chooser.js');
|
||||||
|
const {success, noteType, templateNoteId} = await noteTypeChooserDialog.chooseNoteType();
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const {note} = await noteCreateService.createNote(suggestion.parentNoteId, {
|
const {note} = await noteCreateService.createNote(suggestion.parentNoteId, {
|
||||||
title: suggestion.noteTitle,
|
title: suggestion.noteTitle,
|
||||||
activate: false
|
activate: false,
|
||||||
|
type: noteType,
|
||||||
|
templateNoteId: templateNoteId
|
||||||
});
|
});
|
||||||
|
|
||||||
suggestion.notePath = treeService.getSomeNotePath(note);
|
suggestion.notePath = treeService.getSomeNotePath(note);
|
||||||
|
@ -75,6 +75,20 @@ async function createNote(parentNotePath, options = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createNoteWithTypePrompt(parentNotePath, options = {}) {
|
||||||
|
const noteTypeChooserDialog = await import('../dialogs/note_type_chooser.js');
|
||||||
|
const {success, noteType, templateNoteId} = await noteTypeChooserDialog.chooseNoteType();
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.type = noteType;
|
||||||
|
options.templateNoteId = templateNoteId;
|
||||||
|
|
||||||
|
return await createNote(parentNotePath, options);
|
||||||
|
}
|
||||||
|
|
||||||
/* If first element is heading, parse it out and use it as a new heading. */
|
/* If first element is heading, parse it out and use it as a new heading. */
|
||||||
function parseSelectedHtml(selectedHtml) {
|
function parseSelectedHtml(selectedHtml) {
|
||||||
const dom = $.parseHTML(selectedHtml);
|
const dom = $.parseHTML(selectedHtml);
|
||||||
@ -106,5 +120,6 @@ async function duplicateSubtree(noteId, parentNotePath) {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
createNote,
|
createNote,
|
||||||
|
createNoteWithTypePrompt,
|
||||||
duplicateSubtree
|
duplicateSubtree
|
||||||
};
|
};
|
||||||
|
40
src/public/app/services/note_types.js
Normal file
40
src/public/app/services/note_types.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import server from "./server.js";
|
||||||
|
import froca from "./froca.js";
|
||||||
|
|
||||||
|
async function getNoteTypeItems(command) {
|
||||||
|
const items = [
|
||||||
|
{ title: "Text", command: command, type: "text", uiIcon: "bx bx-note" },
|
||||||
|
{ title: "Code", command: command, type: "code", uiIcon: "bx bx-code" },
|
||||||
|
{ title: "Saved Search", command: command, type: "search", uiIcon: "bx bx-file-find" },
|
||||||
|
{ title: "Relation Map", command: command, type: "relation-map", uiIcon: "bx bx-map-alt" },
|
||||||
|
{ title: "Note Map", command: command, type: "note-map", uiIcon: "bx bx-map-alt" },
|
||||||
|
{ title: "Render Note", command: command, type: "render", uiIcon: "bx bx-extension" },
|
||||||
|
{ title: "Book", command: command, type: "book", uiIcon: "bx bx-book" },
|
||||||
|
{ title: "Mermaid Diagram", command: command, type: "mermaid", uiIcon: "bx bx-selection" },
|
||||||
|
{ title: "Canvas", command: command, type: "canvas", uiIcon: "bx bx-pen" },
|
||||||
|
{ title: "Web View", command: command, type: "iframe", uiIcon: "bx bx-globe-alt" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const templateNoteIds = await server.get("search-templates");
|
||||||
|
const templateNotes = await froca.getNotes(templateNoteIds);
|
||||||
|
|
||||||
|
if (items.length > 0) {
|
||||||
|
items.push({ title: "----" });
|
||||||
|
|
||||||
|
for (const templateNote of templateNotes) {
|
||||||
|
items.push({
|
||||||
|
title: templateNote.title,
|
||||||
|
uiIcon: templateNote.getIcon(),
|
||||||
|
command: command,
|
||||||
|
type: templateNote.type,
|
||||||
|
templateNoteId: templateNote.noteId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getNoteTypeItems
|
||||||
|
}
|
@ -4,7 +4,7 @@ import clipboard from './clipboard.js';
|
|||||||
import noteCreateService from "./note_create.js";
|
import noteCreateService from "./note_create.js";
|
||||||
import contextMenu from "./context_menu.js";
|
import contextMenu from "./context_menu.js";
|
||||||
import appContext from "./app_context.js";
|
import appContext from "./app_context.js";
|
||||||
import server from "./server.js";
|
import noteTypesService from "./note_types.js";
|
||||||
|
|
||||||
class TreeContextMenu {
|
class TreeContextMenu {
|
||||||
/**
|
/**
|
||||||
@ -25,40 +25,6 @@ class TreeContextMenu {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNoteTypeItems(command) {
|
|
||||||
const items = [
|
|
||||||
{ title: "Text", command: command, type: "text", uiIcon: "bx bx-note" },
|
|
||||||
{ title: "Code", command: command, type: "code", uiIcon: "bx bx-code" },
|
|
||||||
{ title: "Saved Search", command: command, type: "search", uiIcon: "bx bx-file-find" },
|
|
||||||
{ title: "Relation Map", command: command, type: "relation-map", uiIcon: "bx bx-map-alt" },
|
|
||||||
{ title: "Note Map", command: command, type: "note-map", uiIcon: "bx bx-map-alt" },
|
|
||||||
{ title: "Render Note", command: command, type: "render", uiIcon: "bx bx-extension" },
|
|
||||||
{ title: "Book", command: command, type: "book", uiIcon: "bx bx-book" },
|
|
||||||
{ title: "Mermaid Diagram", command: command, type: "mermaid", uiIcon: "bx bx-selection" },
|
|
||||||
{ title: "Canvas", command: command, type: "canvas", uiIcon: "bx bx-pen" },
|
|
||||||
{ title: "Web View", command: command, type: "iframe", uiIcon: "bx bx-globe-alt" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const templateNoteIds = await server.get("search-templates");
|
|
||||||
const templateNotes = await froca.getNotes(templateNoteIds);
|
|
||||||
|
|
||||||
if (items.length > 0) {
|
|
||||||
items.push({ title: "----" });
|
|
||||||
|
|
||||||
for (const templateNote of templateNotes) {
|
|
||||||
items.push({
|
|
||||||
title: templateNote.title,
|
|
||||||
uiIcon: templateNote.getIcon(),
|
|
||||||
command: command,
|
|
||||||
type: templateNote.type,
|
|
||||||
templateNoteId: templateNote.noteId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getMenuItems() {
|
async getMenuItems() {
|
||||||
const note = await froca.getNote(this.node.data.noteId);
|
const note = await froca.getNote(this.node.data.noteId);
|
||||||
const branch = froca.getBranch(this.node.data.branchId);
|
const branch = froca.getBranch(this.node.data.branchId);
|
||||||
@ -81,10 +47,10 @@ class TreeContextMenu {
|
|||||||
{ title: 'Open in a new tab <kbd>Ctrl+Click</kbd>', command: "openInTab", uiIcon: "bx bx-empty", enabled: noSelectedNotes },
|
{ title: 'Open in a new tab <kbd>Ctrl+Click</kbd>', command: "openInTab", uiIcon: "bx bx-empty", enabled: noSelectedNotes },
|
||||||
{ title: 'Open in a new split', command: "openNoteInSplit", uiIcon: "bx bx-dock-right", enabled: noSelectedNotes },
|
{ title: 'Open in a new split', command: "openNoteInSplit", uiIcon: "bx bx-dock-right", enabled: noSelectedNotes },
|
||||||
{ title: 'Insert note after <kbd data-command="createNoteAfter"></kbd>', command: "insertNoteAfter", uiIcon: "bx bx-plus",
|
{ title: 'Insert note after <kbd data-command="createNoteAfter"></kbd>', command: "insertNoteAfter", uiIcon: "bx bx-plus",
|
||||||
items: insertNoteAfterEnabled ? await this.getNoteTypeItems("insertNoteAfter") : null,
|
items: insertNoteAfterEnabled ? await noteTypesService.getNoteTypeItems("insertNoteAfter") : null,
|
||||||
enabled: insertNoteAfterEnabled && noSelectedNotes },
|
enabled: insertNoteAfterEnabled && noSelectedNotes },
|
||||||
{ title: 'Insert child note <kbd data-command="createNoteInto"></kbd>', command: "insertChildNote", uiIcon: "bx bx-plus",
|
{ title: 'Insert child note <kbd data-command="createNoteInto"></kbd>', command: "insertChildNote", uiIcon: "bx bx-plus",
|
||||||
items: notSearch ? await this.getNoteTypeItems("insertChildNote") : null,
|
items: notSearch ? await noteTypesService.getNoteTypeItems("insertChildNote") : null,
|
||||||
enabled: notSearch && noSelectedNotes },
|
enabled: notSearch && noSelectedNotes },
|
||||||
{ title: 'Delete <kbd data-command="deleteNotes"></kbd>', command: "deleteNotes", uiIcon: "bx bx-trash",
|
{ title: 'Delete <kbd data-command="deleteNotes"></kbd>', command: "deleteNotes", uiIcon: "bx bx-trash",
|
||||||
enabled: isNotRoot && !isHoisted && parentNotSearch },
|
enabled: isNotRoot && !isHoisted && parentNotSearch },
|
||||||
|
@ -485,7 +485,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createNoteForReferenceLink(title) {
|
async createNoteForReferenceLink(title) {
|
||||||
const {note} = await noteCreateService.createNote(this.notePath, {
|
const {note} = await noteCreateService.createNoteWithTypePrompt(this.notePath, {
|
||||||
activate: false,
|
activate: false,
|
||||||
title: title
|
title: title
|
||||||
});
|
});
|
||||||
|
@ -305,7 +305,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createNoteForReferenceLink(title) {
|
async createNoteForReferenceLink(title) {
|
||||||
const {note} = await noteCreateService.createNote(this.notePath, {
|
const {note} = await noteCreateService.createNoteWithTypePrompt(this.notePath, {
|
||||||
activate: false,
|
activate: false,
|
||||||
title: title
|
title: title
|
||||||
});
|
});
|
||||||
|
@ -192,6 +192,7 @@ div.ui-tooltip {
|
|||||||
.dropdown-menu a:hover:not(.disabled), .dropdown-item:hover:not(.disabled) {
|
.dropdown-menu a:hover:not(.disabled), .dropdown-item:hover:not(.disabled) {
|
||||||
color: var(--hover-item-text-color) !important;
|
color: var(--hover-item-text-color) !important;
|
||||||
background-color: var(--hover-item-background-color) !important;
|
background-color: var(--hover-item-background-color) !important;
|
||||||
|
border-color: var(--hover-item-border-color) !important;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,17 +211,20 @@ div.ui-tooltip {
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item, .dropdown-header {
|
||||||
color: var(--menu-text-color) !important;
|
color: var(--menu-text-color) !important;
|
||||||
|
border: 1px solid transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item.disabled, .dropdown-item.disabled kbd {
|
.dropdown-item.disabled, .dropdown-item.disabled kbd {
|
||||||
color: #aaa !important;
|
color: #aaa !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item.active {
|
.dropdown-item.active, .dropdown-item:focus {
|
||||||
color: var(--active-item-text-color) !important;
|
color: var(--active-item-text-color) !important;
|
||||||
background-color: var(--active-item-background-color) !important;
|
background-color: var(--active-item-background-color) !important;
|
||||||
|
border-color: var(--active-item-border-color) !important;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror {
|
.CodeMirror {
|
||||||
@ -473,8 +477,8 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th
|
|||||||
}
|
}
|
||||||
|
|
||||||
.algolia-autocomplete .aa-dropdown-menu .aa-suggestion.aa-cursor {
|
.algolia-autocomplete .aa-dropdown-menu .aa-suggestion.aa-cursor {
|
||||||
color: var(--hover-item-text-color);
|
color: var(--active-item-text-color);
|
||||||
background-color: var(--hover-item-background-color);
|
background-color: var(--active-item-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.help-button {
|
.help-button {
|
||||||
|
@ -35,11 +35,13 @@
|
|||||||
--input-text-color: #ccc;
|
--input-text-color: #ccc;
|
||||||
--input-background-color: #333;
|
--input-background-color: #333;
|
||||||
|
|
||||||
--hover-item-text-color: black;
|
--hover-item-text-color: #ccc;
|
||||||
--hover-item-background-color: #777;
|
--hover-item-background-color: transparent;
|
||||||
|
--hover-item-border-color: #aaa;
|
||||||
|
|
||||||
--active-item-text-color: black;
|
--active-item-text-color: black;
|
||||||
--active-item-background-color: #777;
|
--active-item-background-color: #777;
|
||||||
|
--active-item-border-color: transparent;
|
||||||
|
|
||||||
--menu-text-color: white;
|
--menu-text-color: white;
|
||||||
--menu-background-color: #222;
|
--menu-background-color: #222;
|
||||||
|
@ -40,10 +40,12 @@ html {
|
|||||||
--input-background-color: transparent;
|
--input-background-color: transparent;
|
||||||
|
|
||||||
--hover-item-text-color: black;
|
--hover-item-text-color: black;
|
||||||
--hover-item-background-color: #ddd;
|
--hover-item-background-color: transparent;
|
||||||
|
--hover-item-border-color: #ccc;
|
||||||
|
|
||||||
--active-item-text-color: black;
|
--active-item-text-color: black;
|
||||||
--active-item-background-color: #ddd;
|
--active-item-background-color: #ddd;
|
||||||
|
--active-item-border-color: transparent;
|
||||||
|
|
||||||
--menu-text-color: black;
|
--menu-text-color: black;
|
||||||
--menu-background-color: white;
|
--menu-background-color: white;
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
<%- include('dialogs/delete_notes.ejs') %>
|
<%- include('dialogs/delete_notes.ejs') %>
|
||||||
<%- include('dialogs/password_not_set.ejs') %>
|
<%- include('dialogs/password_not_set.ejs') %>
|
||||||
<%- include('dialogs/bulk_assign_attributes.ejs') %>
|
<%- include('dialogs/bulk_assign_attributes.ejs') %>
|
||||||
|
<%- include('dialogs/note_type_chooser.ejs') %>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */
|
global = globalThis; /* fixes https://github.com/webpack/webpack/issues/10035 */
|
||||||
|
34
src/views/dialogs/note_type_chooser.ejs
Normal file
34
src/views/dialogs/note_type_chooser.ejs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<style>
|
||||||
|
#note-type-dropdown {
|
||||||
|
position: relative;
|
||||||
|
font-size: large;
|
||||||
|
padding: 20px;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 15px;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="note-type-chooser-dialog" class="modal mx-auto" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog" style="max-width: 500px;" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title mr-auto">Choose note type</h5>
|
||||||
|
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close" style="margin-left: 0 !important;">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Choose note type / template of the new note:
|
||||||
|
|
||||||
|
<div class="dropdown">
|
||||||
|
<button id="note-type-dropdown-trigger" type="button" style="display: none;" data-toggle="dropdown">Dropdown trigger</button>
|
||||||
|
|
||||||
|
<div id="note-type-dropdown" class="dropdown-menu"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
Loading…
x
Reference in New Issue
Block a user