diff --git a/docs/backend_api/Attribute.html b/docs/backend_api/Attribute.html
index 204e91870..378b1db05 100644
--- a/docs/backend_api/Attribute.html
+++ b/docs/backend_api/Attribute.html
@@ -298,29 +298,6 @@
-
-
- utcDateModified |
+ utcDateCreated |
@@ -214,7 +191,7 @@
Source:
diff --git a/docs/backend_api/entities_attribute.js.html b/docs/backend_api/entities_attribute.js.html
index abf4502c9..0e833e283 100644
--- a/docs/backend_api/entities_attribute.js.html
+++ b/docs/backend_api/entities_attribute.js.html
@@ -46,7 +46,6 @@ const promotedAttributeDefinitionParser = require("../services/promoted_attribut
* @property {boolean} isInheritable - immutable
* @property {boolean} isDeleted
* @property {string|null} deleteId - ID identifying delete transaction
- * @property {string} utcDateCreated
* @property {string} utcDateModified
*
* @extends Entity
@@ -54,7 +53,7 @@ const promotedAttributeDefinitionParser = require("../services/promoted_attribut
class Attribute extends Entity {
static get entityName() { return "attributes"; }
static get primaryKeyName() { return "attributeId"; }
- static get hashedProperties() { return ["attributeId", "noteId", "type", "name", "value", "isInheritable", "isDeleted", "utcDateCreated"]; }
+ static get hashedProperties() { return ["attributeId", "noteId", "type", "name", "value", "isInheritable", "isDeleted"]; }
constructor(row) {
super(row);
@@ -62,6 +61,10 @@ class Attribute extends Entity {
this.isInheritable = !!this.isInheritable;
}
+ isAutoLink() {
+ return this.type === 'relation' && ['internalLink', 'imageLink', 'relationMapLink', 'includeNoteLink'].includes(this.name);
+ }
+
/**
* @returns {Note|null}
*/
@@ -127,15 +130,9 @@ class Attribute extends Entity {
this.isDeleted = false;
}
- if (!this.utcDateCreated) {
- this.utcDateCreated = dateUtils.utcNowDateTime();
- }
-
super.beforeSaving();
- if (this.isChanged) {
- this.utcDateModified = dateUtils.utcNowDateTime();
- }
+ this.utcDateModified = dateUtils.utcNowDateTime();
}
createClone(type, name, value, isInheritable) {
@@ -147,7 +144,6 @@ class Attribute extends Entity {
position: this.position,
isInheritable: isInheritable,
isDeleted: false,
- utcDateCreated: this.utcDateCreated,
utcDateModified: this.utcDateModified
});
}
diff --git a/docs/backend_api/entities_branch.js.html b/docs/backend_api/entities_branch.js.html
index 2dca97bcb..c71894b9d 100644
--- a/docs/backend_api/entities_branch.js.html
+++ b/docs/backend_api/entities_branch.js.html
@@ -66,7 +66,7 @@ class Branch extends Entity {
}
beforeSaving() {
- if (this.notePosition === undefined) {
+ if (this.notePosition === undefined || this.notePosition === null) {
const maxNotePos = sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [this.parentNoteId]);
this.notePosition = maxNotePos === null ? 0 : maxNotePos + 10;
}
@@ -85,9 +85,7 @@ class Branch extends Entity {
super.beforeSaving();
- if (this.isChanged) {
- this.utcDateModified = dateUtils.utcNowDateTime();
- }
+ this.utcDateModified = dateUtils.utcNowDateTime();
}
createClone(parentNoteId, notePosition) {
diff --git a/docs/backend_api/entities_entity.js.html b/docs/backend_api/entities_entity.js.html
index d398acf49..78a82ea00 100644
--- a/docs/backend_api/entities_entity.js.html
+++ b/docs/backend_api/entities_entity.js.html
@@ -43,18 +43,13 @@ class Entity {
}
}
- if ('isDeleted' in this) {
+ if ('isDeleted' in this && this.constructor.entityName !== 'recent_notes') {
this.isDeleted = !!this.isDeleted;
}
}
beforeSaving() {
this.generateIdIfNecessary();
-
- const origHash = this.hash;
-
- this.hash = this.generateHash();
- this.isChanged = origHash !== this.hash;
}
generateIdIfNecessary() {
@@ -73,6 +68,10 @@ class Entity {
return utils.hash(contentToHash).substr(0, 10);
}
+ getUtcDateChanged() {
+ return this.utcDateModified;
+ }
+
get repository() {
if (!repo) {
repo = require('../services/repository');
diff --git a/docs/backend_api/entities_note.js.html b/docs/backend_api/entities_note.js.html
index bfd2d1246..778234c83 100644
--- a/docs/backend_api/entities_note.js.html
+++ b/docs/backend_api/entities_note.js.html
@@ -49,7 +49,6 @@ const RELATION = 'relation';
* @property {boolean} isProtected - true if note is protected
* @property {boolean} isDeleted - true if note is deleted
* @property {string|null} deleteId - ID identifying delete transaction
- * @property {boolean} isErased - true if note's content is erased after it has been deleted
* @property {string} dateCreated - local date time (with offset)
* @property {string} dateModified - local date time (with offset)
* @property {string} utcDateCreated
@@ -98,9 +97,9 @@ class Note extends Entity {
/** @returns {*} */
getContent(silentNotFoundError = false) {
if (this.content === undefined) {
- const res = sql.getRow(`SELECT content, hash FROM note_contents WHERE noteId = ?`, [this.noteId]);
+ const row = sql.getRow(`SELECT content FROM note_contents WHERE noteId = ?`, [this.noteId]);
- if (!res) {
+ if (!row) {
if (silentNotFoundError) {
return undefined;
}
@@ -109,7 +108,7 @@ class Note extends Entity {
}
}
- this.content = res.content;
+ this.content = row.content;
if (this.isProtected) {
if (this.isContentAvailable) {
@@ -171,8 +170,7 @@ class Note extends Entity {
noteId: this.noteId,
content: content,
dateModified: dateUtils.localNowDateTime(),
- utcDateModified: dateUtils.utcNowDateTime(),
- hash: utils.hash(this.noteId + "|" + content.toString())
+ utcDateModified: dateUtils.utcNowDateTime()
};
if (this.isProtected) {
@@ -186,7 +184,15 @@ class Note extends Entity {
sql.upsert("note_contents", "noteId", pojo);
- entityChangesService.addNoteContentEntityChange(this.noteId);
+ const hash = utils.hash(this.noteId + "|" + content.toString());
+
+ entityChangesService.addEntityChange({
+ entityName: 'note_contents',
+ entityId: this.noteId,
+ hash: hash,
+ isErased: false,
+ utcDateChanged: this.getUtcDateChanged()
+ }, null);
}
setJsonContent(content) {
@@ -834,7 +840,7 @@ class Note extends Entity {
* @returns {boolean} - true if note has children
*/
hasChildren() {
- return (this.getChildNotes()).length > 0;
+ return this.getChildNotes().length > 0;
}
/**
@@ -932,10 +938,8 @@ class Note extends Entity {
super.beforeSaving();
- if (this.isChanged) {
- this.dateModified = dateUtils.localNowDateTime();
- this.utcDateModified = dateUtils.utcNowDateTime();
- }
+ this.dateModified = dateUtils.localNowDateTime();
+ this.utcDateModified = dateUtils.utcNowDateTime();
}
// cannot be static!
diff --git a/docs/backend_api/entities_note_revision.js.html b/docs/backend_api/entities_note_revision.js.html
index e1f1a4214..371bc3c06 100644
--- a/docs/backend_api/entities_note_revision.js.html
+++ b/docs/backend_api/entities_note_revision.js.html
@@ -43,7 +43,6 @@ const entityChangesService = require('../services/entity_changes.js');
* @property {string} type
* @property {string} mime
* @property {string} title
- * @property {boolean} isErased
* @property {boolean} isProtected
* @property {string} dateLastEdited
* @property {string} dateCreated
@@ -56,12 +55,11 @@ const entityChangesService = require('../services/entity_changes.js');
class NoteRevision extends Entity {
static get entityName() { return "note_revisions"; }
static get primaryKeyName() { return "noteRevisionId"; }
- static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "isErased", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified"]; }
+ static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified"]; }
constructor(row) {
super(row);
- this.isErased = !!this.isErased;
this.isProtected = !!this.isProtected;
if (this.isProtected) {
@@ -95,7 +93,7 @@ class NoteRevision extends Entity {
/** @returns {*} */
getContent(silentNotFoundError = false) {
if (this.content === undefined) {
- const res = sql.getRow(`SELECT content, hash FROM note_revision_contents WHERE noteRevisionId = ?`, [this.noteRevisionId]);
+ const res = sql.getRow(`SELECT content FROM note_revision_contents WHERE noteRevisionId = ?`, [this.noteRevisionId]);
if (!res) {
if (silentNotFoundError) {
@@ -107,7 +105,6 @@ class NoteRevision extends Entity {
}
this.content = res.content;
-
if (this.isProtected) {
if (protectedSessionService.isProtectedSessionAvailable()) {
this.content = protectedSessionService.decrypt(this.content);
@@ -134,8 +131,7 @@ class NoteRevision extends Entity {
const pojo = {
noteRevisionId: this.noteRevisionId,
content: content,
- utcDateModified: dateUtils.utcNowDateTime(),
- hash: utils.hash(this.noteRevisionId + "|" + content)
+ utcDateModified: dateUtils.utcNowDateTime()
};
if (this.isProtected) {
@@ -149,15 +145,21 @@ class NoteRevision extends Entity {
sql.upsert("note_revision_contents", "noteRevisionId", pojo);
- entityChangesService.addNoteRevisionContentEntityChange(this.noteRevisionId);
+ const hash = utils.hash(this.noteRevisionId + "|" + content);
+
+ entityChangesService.addEntityChange({
+ entityName: 'note_revision_contents',
+ entityId: this.noteRevisionId,
+ hash: hash,
+ isErased: false,
+ utcDateChanged: this.getUtcDateChanged()
+ }, null);
}
beforeSaving() {
super.beforeSaving();
- if (this.isChanged) {
- this.utcDateModified = dateUtils.utcNowDateTime();
- }
+ this.utcDateModified = dateUtils.utcNowDateTime();
}
// cannot be static!
diff --git a/docs/backend_api/entities_option.js.html b/docs/backend_api/entities_option.js.html
index 255779f2f..c1bbab9ff 100644
--- a/docs/backend_api/entities_option.js.html
+++ b/docs/backend_api/entities_option.js.html
@@ -60,13 +60,12 @@ class Option extends Entity {
super.beforeSaving();
- if (this.isChanged) {
- this.utcDateModified = dateUtils.utcNowDateTime();
- }
+ this.utcDateModified = dateUtils.utcNowDateTime();
}
}
-module.exports = Option;
+module.exports = Option;
+
diff --git a/docs/backend_api/entities_recent_note.js.html b/docs/backend_api/entities_recent_note.js.html
index a24776e2d..7af54b3ae 100644
--- a/docs/backend_api/entities_recent_note.js.html
+++ b/docs/backend_api/entities_recent_note.js.html
@@ -36,21 +36,15 @@ const dateUtils = require('../services/date_utils');
*
* @property {string} noteId
* @property {string} notePath
- * @property {boolean} isDeleted
- * @property {string} utcDateModified
+ * @property {string} utcDateCreated
*
* @extends Entity
*/
class RecentNote extends Entity {
static get entityName() { return "recent_notes"; }
static get primaryKeyName() { return "noteId"; }
- static get hashedProperties() { return ["noteId", "notePath", "utcDateCreated", "isDeleted"]; }
beforeSaving() {
- if (!this.isDeleted) {
- this.isDeleted = false;
- }
-
if (!this.utcDateCreated) {
this.utcDateCreated = dateUtils.utcNowDateTime();
}
@@ -59,7 +53,8 @@ class RecentNote extends Entity {
}
}
-module.exports = RecentNote;
+module.exports = RecentNote;
+
diff --git a/docs/backend_api/global.html b/docs/backend_api/global.html
index a5e851b1e..bfb7edec3 100644
--- a/docs/backend_api/global.html
+++ b/docs/backend_api/global.html
@@ -391,7 +391,7 @@
Source:
@@ -579,7 +579,7 @@
Source:
@@ -767,7 +767,7 @@
Source:
@@ -1053,7 +1053,7 @@
Source:
diff --git a/docs/backend_api/services_backend_script_api.js.html b/docs/backend_api/services_backend_script_api.js.html
index f399a4d47..98bf9c0fd 100644
--- a/docs/backend_api/services_backend_script_api.js.html
+++ b/docs/backend_api/services_backend_script_api.js.html
@@ -120,13 +120,15 @@ function BackendScriptApi(currentNote, apiParams) {
*
* @method
* @param {string} query
- * @param {SearchContext} [searchContext]
+ * @param {Object} [searchParams]
* @returns {Note[]}
*/
- this.searchForNotes = (query, searchContext) => {
- searchContext = searchContext || new SearchContext();
+ this.searchForNotes = (query, searchParams = {}) => {
+ if (searchParams.includeArchivedNotes === undefined) {
+ searchParams.includeArchivedNotes = true;
+ }
- const noteIds = searchService.findNotesWithQuery(query, searchContext)
+ const noteIds = searchService.findNotesWithQuery(query, new SearchContext(searchParams))
.map(sr => sr.noteId);
return repository.getNotes(noteIds);
diff --git a/docs/frontend_api/Branch.html b/docs/frontend_api/Branch.html
index a30e20eed..1670c9962 100644
--- a/docs/frontend_api/Branch.html
+++ b/docs/frontend_api/Branch.html
@@ -201,7 +201,7 @@
-isDeleted
+fromSearchNote
@@ -259,6 +259,64 @@
+isDeleted
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
isExpanded
@@ -607,7 +665,7 @@
Source:
@@ -709,7 +767,7 @@
Source:
@@ -811,7 +869,7 @@
Source:
@@ -913,7 +971,7 @@
Source:
diff --git a/docs/frontend_api/FrontendScriptApi.html b/docs/frontend_api/FrontendScriptApi.html
index 4cc250285..2b0aac448 100644
--- a/docs/frontend_api/FrontendScriptApi.html
+++ b/docs/frontend_api/FrontendScriptApi.html
@@ -81,7 +81,7 @@
Source:
@@ -223,7 +223,7 @@
Source:
@@ -329,7 +329,7 @@
Source:
@@ -435,7 +435,7 @@
Source:
@@ -545,7 +545,7 @@
Source:
@@ -658,7 +658,7 @@
Source:
@@ -768,7 +768,7 @@
Source:
@@ -874,7 +874,7 @@
Source:
@@ -980,7 +980,7 @@
Source:
@@ -1109,7 +1109,7 @@
Source:
@@ -1264,7 +1264,7 @@
Source:
@@ -1419,7 +1419,7 @@
Source:
@@ -1556,7 +1556,7 @@
Source:
@@ -1712,7 +1712,7 @@
Source:
@@ -1748,7 +1748,7 @@
- createNoteLink(notePath, noteTitleopt)
+ createNoteLink(notePath, paramsopt, title=opt)
@@ -1825,7 +1825,148 @@
|
- Hoist note. See https://github.com/zadam/trilium/wiki/Note-hoisting
+ Hoist note in the current tab. See https://github.com/zadam/trilium/wiki/Note-hoisting
@@ -5052,7 +5371,7 @@ Internally this serializes the anonymous function into string and sends it to ba
import server from '../services/server.js';
-import Attribute from './attribute.js';
import noteAttributeCache from "../services/note_attribute_cache.js";
+import ws from "../services/ws.js";
+import options from "../services/options.js";
const LABEL = 'label';
const RELATION = 'relation';
+const NOTE_TYPE_ICONS = {
+ "file": "bx bx-file",
+ "image": "bx bx-image",
+ "code": "bx bx-code",
+ "render": "bx bx-extension",
+ "search": "bx bx-file-find",
+ "relation-map": "bx bx-map-alt",
+ "book": "bx bx-book"
+};
+
/**
* FIXME: since there's no "full note" anymore we can rename this to Note
*
@@ -78,7 +89,7 @@ class NoteShort {
/** @param {string} content-type, e.g. "application/json" */
this.mime = row.mime;
/** @param {boolean} */
- this.isDeleted = row.isDeleted;
+ this.isDeleted = !!row.isDeleted;
}
addParent(parentNoteId, branchId) {
@@ -93,14 +104,16 @@ class NoteShort {
this.parentToBranch[parentNoteId] = branchId;
}
- addChild(childNoteId, branchId) {
- if (!this.children.includes(childNoteId)) {
+ addChild(childNoteId, branchId, sort = true) {
+ if (!(childNoteId in this.childToBranch)) {
this.children.push(childNoteId);
}
this.childToBranch[childNoteId] = branchId;
- this.sortChildren();
+ if (sort) {
+ this.sortChildren();
+ }
}
sortChildren() {
@@ -282,6 +295,63 @@ class NoteShort {
return this.getAttributes(LABEL, name);
}
+ getIcon() {
+ const iconClassLabels = this.getLabels('iconClass');
+ const workspaceIconClass = this.getWorkspaceIconClass();
+
+ if (iconClassLabels.length > 0) {
+ return iconClassLabels.map(l => l.value).join(' ');
+ }
+ else if (workspaceIconClass) {
+ return workspaceIconClass;
+ }
+ else if (this.noteId === 'root') {
+ return "bx bx-chevrons-right";
+ }
+ else if (this.type === 'text') {
+ if (this.isFolder()) {
+ return "bx bx-folder";
+ }
+ else {
+ return "bx bx-note";
+ }
+ }
+ else if (this.type === 'code' && this.mime.startsWith('text/x-sql')) {
+ return "bx bx-data";
+ }
+ else {
+ return NOTE_TYPE_ICONS[this.type];
+ }
+ }
+
+ isFolder() {
+ return this.type === 'search'
+ || this.getFilteredChildBranches().length > 0;
+ }
+
+ getFilteredChildBranches() {
+ let childBranches = this.getChildBranches();
+
+ if (!childBranches) {
+ ws.logError(`No children for ${parentNote}. This shouldn't happen.`);
+ return;
+ }
+
+ if (options.is("hideIncludedImages_main")) {
+ const imageLinks = this.getRelations('imageLink');
+
+ // image is already visible in the parent note so no need to display it separately in the book
+ childBranches = childBranches.filter(branch => !imageLinks.find(rel => rel.value === branch.noteId));
+ }
+
+ // we're not checking hideArchivedNotes since that would mean we need to lazy load the child notes
+ // which would seriously slow down everything.
+ // we check this flag only once user chooses to expand the parent. This has the negative consequence that
+ // note may appear as folder but not contain any children when all of them are archived
+
+ return childBranches;
+ }
+
/**
* @param {string} [name] - relation name to filter
* @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones
@@ -466,19 +536,42 @@ class NoteShort {
return relations.map(rel => this.treeCache.notes[rel.value]);
}
- hasAncestor(ancestorNote) {
+ getPromotedDefinitionAttributes() {
+ if (this.hasLabel('hidePromotedAttributes')) {
+ return [];
+ }
+
+ return this.getAttributes()
+ .filter(attr => attr.isDefinition())
+ .filter(attr => {
+ const def = attr.getDefinition();
+
+ return def && def.isPromoted;
+ });
+ }
+
+ hasAncestor(ancestorNote, visitedNoteIds) {
if (this.noteId === ancestorNote.noteId) {
return true;
}
+ if (!visitedNoteIds) {
+ visitedNoteIds = new Set();
+ } else if (visitedNoteIds.has(this.noteId)) {
+ // to avoid infinite cycle when template is descendent of the instance
+ return false;
+ }
+
+ visitedNoteIds.add(this.noteId);
+
for (const templateNote of this.getTemplateNotes()) {
- if (templateNote.hasAncestor(ancestorNote)) {
+ if (templateNote.hasAncestor(ancestorNote, visitedNoteIds)) {
return true;
}
}
for (const parentNote of this.getParentNotes()) {
- if (parentNote.hasAncestor(ancestorNote)) {
+ if (parentNote.hasAncestor(ancestorNote, visitedNoteIds)) {
return true;
}
}
@@ -539,6 +632,16 @@ class NoteShort {
const labels = this.getLabels('cssClass');
return labels.map(l => l.value).join(' ');
}
+
+ getWorkspaceIconClass() {
+ const labels = this.getLabels('workspaceIconClass');
+ return labels.length > 0 ? labels[0].value : "";
+ }
+
+ getWorkspaceTabBackgroundColor() {
+ const labels = this.getLabels('workspaceTabBackgroundColor');
+ return labels.length > 0 ? labels[0].value : "";
+ }
}
export default NoteShort;
diff --git a/docs/frontend_api/global.html b/docs/frontend_api/global.html
index 1be03111a..c0139c144 100644
--- a/docs/frontend_api/global.html
+++ b/docs/frontend_api/global.html
@@ -156,7 +156,7 @@
Source:
@@ -244,7 +244,7 @@
Source:
@@ -333,7 +333,7 @@ separately but should behave uniformly for the user.
Source:
@@ -572,7 +572,7 @@ separately but should behave uniformly for the user.
Source:
diff --git a/docs/frontend_api/services_frontend_script_api.js.html b/docs/frontend_api/services_frontend_script_api.js.html
index 4e20f4e31..55df0a765 100644
--- a/docs/frontend_api/services_frontend_script_api.js.html
+++ b/docs/frontend_api/services_frontend_script_api.js.html
@@ -26,8 +26,7 @@
- import treeService from './tree.js';
-import server from './server.js';
+ import server from './server.js';
import utils from './utils.js';
import toastService from './toast.js';
import linkService from './link.js';
@@ -37,7 +36,6 @@ import protectedSessionService from './protected_session.js';
import dateNotesService from './date_notes.js';
import CollapsibleWidget from '../widgets/collapsible_widget.js';
import ws from "./ws.js";
-import hoistedNoteService from "./hoisted_note.js";
import appContext from "./app_context.js";
import TabAwareWidget from "../widgets/tab_aware_widget.js";
import TabCachingWidget from "../widgets/tab_caching_widget.js";
@@ -101,6 +99,20 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
appContext.triggerEvent('focusAndSelectTitle');
};
+ /**
+ * Open a note in a new tab.
+ *
+ * @param {string} notePath (or noteId)
+ * @param {boolean} activate - set to true to activate the new tab, false to stay on the current tab
+ * @return {Promise<void>}
+ */
+ this.openTabWithNote = async (notePath, activate) => {
+ await ws.waitForMaxKnownEntityChangeId();
+
+ await appContext.tabManager.openTabWithNote(notePath, activate);
+ appContext.triggerEvent('focusAndSelectTitle');
+ };
+
/**
* @typedef {Object} ToolbarButtonOptions
* @property {string} title
@@ -117,7 +129,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
this.addButtonToToolbar = opts => {
const buttonId = "toolbar-button-" + opts.title.replace(/\s/g, "-");
- const button = $('<button>')
+ const button = $('<button class="noborder">')
.addClass("btn btn-sm")
.on('click', opts.action);
@@ -302,7 +314,10 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
*
* @method
* @param {string} notePath (or noteId)
- * @param {string} [noteTitle] - if not present we'll use note title
+ * @param {object} [params]
+ * @param {boolean} [params.showTooltip=true] - enable/disable tooltip on the link
+ * @param {boolean} [params.showNotePath=false] - show also whole note's path as part of the link
+ * @param {string} [title=] - custom link tile with note's title as default
*/
this.createNoteLink = linkService.createNoteLink;
@@ -404,13 +419,19 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
this.getYearNote = dateNotesService.getYearNote;
/**
- * Hoist note. See https://github.com/zadam/trilium/wiki/Note-hoisting
+ * Hoist note in the current tab. See https://github.com/zadam/trilium/wiki/Note-hoisting
*
* @method
* @param {string} noteId - set hoisted note. 'root' will effectively unhoist
* @return {Promise}
*/
- this.setHoistedNoteId = hoistedNoteService.setHoistedNoteId;
+ this.setHoistedNoteId = (noteId) => {
+ const activeTabContext = appContext.tabManager.getActiveTabContext();
+
+ if (activeTabContext) {
+ activeTabContext.setHoistedNoteId(noteId);
+ }
+ };
/**
* @method
diff --git a/docs/frontend_api/widgets_collapsible_widget.js.html b/docs/frontend_api/widgets_collapsible_widget.js.html
index eb09e089e..9b90ba367 100644
--- a/docs/frontend_api/widgets_collapsible_widget.js.html
+++ b/docs/frontend_api/widgets_collapsible_widget.js.html
@@ -31,16 +31,25 @@ import options from "../services/options.js";
const WIDGET_TPL = `
<div class="card widget">
- <div class="card-header">
+ <div class="card-header">
<div>
- <button class="btn btn-sm widget-title" data-toggle="collapse" data-target="#[to be set]">
- Collapsible Group Item
- </button>
-
- <a class="widget-help external no-arrow bx bx-info-circle"></a>
+ <a class="widget-toggle-button no-arrow"
+ title="Minimize/maximize widget"
+ data-toggle="collapse" data-target="#[to be set]">
+
+ <span class="widget-toggle-icon bx"></span>
+
+ <span class="widget-title">
+ Collapsible Group Item
+ </span>
+ </a>
+
+ <span class="widget-header-actions"></span>
</div>
- <div class="widget-header-actions"></div>
+ <div>
+ <a class="widget-help external no-arrow bx bx-info-circle"></a>
+ </div>
</div>
<div id="[to be set]" class="collapse body-wrapper" style="transition: none; ">
@@ -66,13 +75,19 @@ export default class CollapsibleWidget extends TabAwareWidget {
// not using constructor name because of webpack mangling class names ...
this.widgetName = this.widgetTitle.replace(/[^[a-zA-Z0-9]/g, "_");
- if (!options.is(this.widgetName + 'Collapsed')) {
+ this.$toggleButton = this.$widget.find('.widget-toggle-button');
+ this.$toggleIcon = this.$widget.find('.widget-toggle-icon');
+
+ const collapsed = options.is(this.widgetName + 'Collapsed');
+ if (!collapsed) {
this.$bodyWrapper.collapse("show");
}
+ this.updateToggleIcon(collapsed);
+
// using immediate variants of the event so that the previous collapse is not caught
- this.$bodyWrapper.on('hide.bs.collapse', () => this.saveCollapsed(true));
- this.$bodyWrapper.on('show.bs.collapse', () => this.saveCollapsed(false));
+ this.$bodyWrapper.on('hide.bs.collapse', () => this.toggleCollapsed(true));
+ this.$bodyWrapper.on('show.bs.collapse', () => this.toggleCollapsed(false));
this.$body = this.$bodyWrapper.find('.card-body');
@@ -94,19 +109,35 @@ export default class CollapsibleWidget extends TabAwareWidget {
}
this.$headerActions = this.$widget.find('.widget-header-actions');
- this.$headerActions.append(...this.headerActions);
+ this.$headerActions.append(this.headerActions);
this.initialized = this.doRenderBody();
this.decorateWidget();
}
- saveCollapsed(collapse) {
+ toggleCollapsed(collapse) {
+ this.updateToggleIcon(collapse);
+
options.save(this.widgetName + 'Collapsed', collapse.toString());
this.triggerEvent(`widgetCollapsedStateChanged`, {widgetName: this.widgetName, collapse});
}
+ updateToggleIcon(collapse) {
+ if (collapse) {
+ this.$toggleIcon
+ .addClass("bx-chevron-right")
+ .removeClass("bx-chevron-down")
+ .attr("title", "Show");
+ } else {
+ this.$toggleIcon
+ .addClass("bx-chevron-down")
+ .removeClass("bx-chevron-right")
+ .attr("title", "Hide");
+ }
+ }
+
/**
* This event is used to synchronize collapsed state of all the tab-cached widgets since they are all rendered
* separately but should behave uniformly for the user.
diff --git a/src/public/app/services/frontend_script_api.js b/src/public/app/services/frontend_script_api.js
index b3da91cc0..3edee67b1 100644
--- a/src/public/app/services/frontend_script_api.js
+++ b/src/public/app/services/frontend_script_api.js
@@ -71,6 +71,23 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
appContext.triggerEvent('focusAndSelectTitle');
};
+ /**
+ * Open a note in a new tab.
+ *
+ * @param {string} notePath (or noteId)
+ * @param {boolean} activate - set to true to activate the new tab, false to stay on the current tab
+ * @return {Promise}
+ */
+ this.openTabWithNote = async (notePath, activate) => {
+ await ws.waitForMaxKnownEntityChangeId();
+
+ await appContext.tabManager.openTabWithNote(notePath, activate);
+
+ if (activate) {
+ appContext.triggerEvent('focusAndSelectTitle');
+ }
+ };
+
/**
* @typedef {Object} ToolbarButtonOptions
* @property {string} title