mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
Merge branch 'zadam:master' into Highlighted-Text
This commit is contained in:
commit
cde13dc580
@ -40,19 +40,25 @@ function register(router) {
|
||||
}
|
||||
});
|
||||
|
||||
const ALLOWED_PROPERTIES_FOR_PATCH = {
|
||||
const ALLOWED_PROPERTIES_FOR_PATCH_LABEL = {
|
||||
'value': [v.notNull, v.isString],
|
||||
'position': [v.notNull, v.isInteger]
|
||||
};
|
||||
|
||||
const ALLOWED_PROPERTIES_FOR_PATCH_RELATION = {
|
||||
'position': [v.notNull, v.isInteger]
|
||||
};
|
||||
|
||||
eu.route(router, 'patch' ,'/etapi/attributes/:attributeId', (req, res, next) => {
|
||||
const attribute = eu.getAndCheckAttribute(req.params.attributeId);
|
||||
|
||||
if (attribute.type === 'relation') {
|
||||
if (attribute.type === 'label') {
|
||||
eu.validateAndPatch(attribute, req.body, ALLOWED_PROPERTIES_FOR_PATCH_LABEL);
|
||||
} else if (attribute.type === 'relation') {
|
||||
eu.getAndCheckNote(req.body.value);
|
||||
}
|
||||
|
||||
eu.validateAndPatch(attribute, req.body, ALLOWED_PROPERTIES_FOR_PATCH);
|
||||
eu.validateAndPatch(attribute, req.body, ALLOWED_PROPERTIES_FOR_PATCH_RELATION);
|
||||
}
|
||||
|
||||
attribute.save();
|
||||
|
||||
|
@ -374,7 +374,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
patch:
|
||||
description: patch a branch identified by the branchId with changes in the body
|
||||
description: patch a branch identified by the branchId with changes in the body. Only prefix and notePosition can be updated. If you want to update other properties, you need to delete the old branch and create a new one.
|
||||
operationId: patchBranchById
|
||||
requestBody:
|
||||
required: true
|
||||
@ -456,7 +456,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
patch:
|
||||
description: patch a attribute identified by the attributeId with changes in the body
|
||||
description: patch a attribute identified by the attributeId with changes in the body. For labels, only value and position can be updated. For relations, only position can be updated. If you want to modify other properties, you need to delete the old attribute and create a new one.
|
||||
operationId: patchAttributeById
|
||||
requestBody:
|
||||
required: true
|
||||
|
@ -451,16 +451,23 @@ export default class TabManager extends Component {
|
||||
this.tabsUpdate.scheduleUpdate();
|
||||
}
|
||||
|
||||
noteContextReorderEvent({ntxIdsInOrder}) {
|
||||
const order = {};
|
||||
let i = 0;
|
||||
|
||||
for (const ntxId of ntxIdsInOrder) {
|
||||
order[ntxId] = i++;
|
||||
}
|
||||
noteContextReorderEvent({ntxIdsInOrder, oldMainNtxId, newMainNtxId}) {
|
||||
const order = Object.fromEntries(ntxIdsInOrder.map((v, i) => [v, i]));
|
||||
|
||||
this.children.sort((a, b) => order[a.ntxId] < order[b.ntxId] ? -1 : 1);
|
||||
|
||||
if (oldMainNtxId && newMainNtxId) {
|
||||
this.children.forEach(c => {
|
||||
if (c.ntxId === newMainNtxId) {
|
||||
// new main context has null mainNtxId
|
||||
c.mainNtxId = null;
|
||||
} else if (c.ntxId === oldMainNtxId || c.mainNtxId === oldMainNtxId) {
|
||||
// old main context or subcontexts all have the new mainNtxId
|
||||
c.mainNtxId = newMainNtxId;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.tabsUpdate.scheduleUpdate();
|
||||
}
|
||||
|
||||
|
@ -76,6 +76,7 @@ import CodeButtonsWidget from "../widgets/floating_buttons/code_buttons.js";
|
||||
import ApiLogWidget from "../widgets/api_log.js";
|
||||
import HideFloatingButtonsButton from "../widgets/floating_buttons/hide_floating_buttons_button.js";
|
||||
import ScriptExecutorWidget from "../widgets/ribbon_widgets/script_executor.js";
|
||||
import MovePaneButton from "../widgets/buttons/move_pane_button.js";
|
||||
|
||||
export default class DesktopLayout {
|
||||
constructor(customWidgets) {
|
||||
@ -124,6 +125,8 @@ export default class DesktopLayout {
|
||||
.child(new NoteIconWidget())
|
||||
.child(new NoteTitleWidget())
|
||||
.child(new SpacerWidget(0, 1))
|
||||
.child(new MovePaneButton(true))
|
||||
.child(new MovePaneButton(false))
|
||||
.child(new ClosePaneButton())
|
||||
.child(new CreatePaneButton())
|
||||
)
|
||||
|
@ -483,6 +483,13 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
||||
*/
|
||||
this.randomString = utils.randomString;
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @param {int} size in bytes
|
||||
* @return {string} formatted string
|
||||
*/
|
||||
this.formatNoteSize = utils.formatNoteSize;
|
||||
|
||||
this.logMessages = {};
|
||||
this.logSpacedUpdates = {};
|
||||
|
||||
|
@ -354,6 +354,17 @@ function escapeRegExp(str) {
|
||||
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
||||
}
|
||||
|
||||
function formatNoteSize(size) {
|
||||
size = Math.max(Math.round(size / 1024), 1);
|
||||
|
||||
if (size < 1024) {
|
||||
return `${size} KiB`;
|
||||
}
|
||||
else {
|
||||
return `${Math.round(size / 102.4) / 10} MiB`;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
reloadFrontendApp,
|
||||
parseDate,
|
||||
@ -396,5 +407,6 @@ export default {
|
||||
filterAttributeName,
|
||||
isValidAttributeName,
|
||||
sleep,
|
||||
escapeRegExp
|
||||
escapeRegExp,
|
||||
formatNoteSize
|
||||
};
|
||||
|
@ -7,6 +7,10 @@ export default class ClosePaneButton extends OnClickButtonWidget {
|
||||
&& this.noteContext && !!this.noteContext.mainNtxId;
|
||||
}
|
||||
|
||||
async noteContextReorderEvent({ntxIdsInOrder}) {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
|
47
src/public/app/widgets/buttons/move_pane_button.js
Normal file
47
src/public/app/widgets/buttons/move_pane_button.js
Normal file
@ -0,0 +1,47 @@
|
||||
import OnClickButtonWidget from "./onclick_button.js";
|
||||
import appContext from "../../components/app_context.js";
|
||||
|
||||
export default class MovePaneButton extends OnClickButtonWidget {
|
||||
constructor(isMovingLeft) {
|
||||
super();
|
||||
|
||||
this.isMovingLeft = isMovingLeft;
|
||||
|
||||
this.icon(isMovingLeft ? "bx-chevron-left" : "bx-chevron-right")
|
||||
.title(isMovingLeft ? "Move left" : "Move right")
|
||||
.titlePlacement("bottom")
|
||||
.onClick(async (widget, e) => {
|
||||
e.stopPropagation();
|
||||
widget.triggerCommand("moveThisNoteSplit", {ntxId: widget.getClosestNtxId(), isMovingLeft: this.isMovingLeft});
|
||||
})
|
||||
.class("icon-action");
|
||||
}
|
||||
|
||||
isEnabled() {
|
||||
if (!super.isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isMovingLeft) {
|
||||
// movable if the current context is not a main context, i.e. non-null mainNtxId
|
||||
return !!this.noteContext?.mainNtxId;
|
||||
} else {
|
||||
const currentIndex = appContext.tabManager.noteContexts.findIndex(c => c.ntxId === this.ntxId);
|
||||
const nextContext = appContext.tabManager.noteContexts[currentIndex + 1];
|
||||
// movable if the next context is not null and not a main context, i.e. non-null mainNtxId
|
||||
return !!nextContext?.mainNtxId;
|
||||
}
|
||||
}
|
||||
|
||||
async noteContextRemovedEvent() {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
async newNoteContextCreatedEvent() {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
async noteContextReorderEvent() {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
@ -74,6 +74,50 @@ export default class SplitNoteContainer extends FlexContainer {
|
||||
appContext.tabManager.removeNoteContext(ntxId);
|
||||
}
|
||||
|
||||
async moveThisNoteSplitCommand({ntxId, isMovingLeft}) {
|
||||
if (!ntxId) {
|
||||
logError("empty ntxId!");
|
||||
return;
|
||||
}
|
||||
|
||||
const contexts = appContext.tabManager.noteContexts;
|
||||
|
||||
const currentIndex = contexts.findIndex(c => c.ntxId === ntxId);
|
||||
const leftIndex = isMovingLeft ? currentIndex - 1 : currentIndex;
|
||||
|
||||
if (currentIndex === -1 || leftIndex < 0 || leftIndex + 1 >= contexts.length) {
|
||||
logError(`invalid context! currentIndex: ${currentIndex}, leftIndex: ${leftIndex}, contexts.length: ${contexts.length}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (contexts[leftIndex].isEmpty() && contexts[leftIndex + 1].isEmpty()) {
|
||||
// no op
|
||||
return;
|
||||
}
|
||||
|
||||
const ntxIds = contexts.map(c => c.ntxId);
|
||||
const newNtxIds = [
|
||||
...ntxIds.slice(0, leftIndex),
|
||||
ntxIds[leftIndex + 1],
|
||||
ntxIds[leftIndex],
|
||||
...ntxIds.slice(leftIndex + 2),
|
||||
];
|
||||
const isChangingMainContext = !contexts[leftIndex].mainNtxId;
|
||||
|
||||
this.triggerCommand("noteContextReorder", {
|
||||
ntxIdsInOrder: newNtxIds,
|
||||
oldMainNtxId: isChangingMainContext ? ntxIds[leftIndex] : null,
|
||||
newMainNtxId: isChangingMainContext ? ntxIds[leftIndex + 1]: null,
|
||||
});
|
||||
|
||||
// reorder the note context widgets
|
||||
this.$widget.find(`[data-ntx-id="${ntxIds[leftIndex]}"]`)
|
||||
.insertAfter(this.$widget.find(`[data-ntx-id="${ntxIds[leftIndex + 1]}"]`));
|
||||
|
||||
// activate context that now contains the original note
|
||||
await appContext.tabManager.activateNoteContext(isMovingLeft ? ntxIds[leftIndex + 1] : ntxIds[leftIndex]);
|
||||
}
|
||||
|
||||
activeContextChangedEvent() {
|
||||
this.refresh();
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ export default class FilePropertiesWidget extends NoteContextAwareWidget {
|
||||
|
||||
const noteComplement = await this.noteContext.getNoteComplement();
|
||||
|
||||
this.$fileSize.text(`${noteComplement.contentLength} bytes`);
|
||||
this.$fileSize.text(utils.formatNoteSize(noteComplement.contentLength));
|
||||
|
||||
// open doesn't work for protected notes since it works through browser which isn't in protected session
|
||||
this.$openButton.toggle(!note.isProtected);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||
import server from "../../services/server.js";
|
||||
import utils from "../../services/utils.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="note-info-widget">
|
||||
@ -105,12 +106,12 @@ export default class NoteInfoWidget extends NoteContextAwareWidget {
|
||||
this.$subTreeSize.empty().append($('<span class="bx bx-loader bx-spin"></span>'));
|
||||
|
||||
const noteSizeResp = await server.get(`stats/note-size/${this.noteId}`);
|
||||
this.$noteSize.text(this.formatSize(noteSizeResp.noteSize));
|
||||
this.$noteSize.text(utils.formatNoteSize(noteSizeResp.noteSize));
|
||||
|
||||
const subTreeResp = await server.get(`stats/subtree-size/${this.noteId}`);
|
||||
|
||||
if (subTreeResp.subTreeNoteCount > 1) {
|
||||
this.$subTreeSize.text(`(subtree size: ${this.formatSize(subTreeResp.subTreeSize)} in ${subTreeResp.subTreeNoteCount} notes)`);
|
||||
this.$subTreeSize.text(`(subtree size: ${utils.formatNoteSize(subTreeResp.subTreeSize)} in ${subTreeResp.subTreeNoteCount} notes)`);
|
||||
}
|
||||
else {
|
||||
this.$subTreeSize.text("");
|
||||
@ -142,18 +143,7 @@ export default class NoteInfoWidget extends NoteContextAwareWidget {
|
||||
this.$calculateButton.show();
|
||||
this.$noteSizesWrapper.hide();
|
||||
}
|
||||
|
||||
formatSize(size) {
|
||||
size = Math.max(Math.round(size / 1024), 1);
|
||||
|
||||
if (size < 1024) {
|
||||
return `${size} KiB`;
|
||||
}
|
||||
else {
|
||||
return `${Math.round(size / 102.4) / 10} MiB`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
entitiesReloadedEvent({loadResults}) {
|
||||
if (loadResults.isNoteReloaded(this.noteId) || loadResults.isNoteContentReloaded(this.noteId)) {
|
||||
this.refresh();
|
||||
|
@ -609,6 +609,17 @@ export default class TabRowWidget extends BasicWidget {
|
||||
this.updateTabById(noteContext.mainNtxId || noteContext.ntxId);
|
||||
}
|
||||
|
||||
noteContextReorderEvent({oldMainNtxId, newMainNtxId}) {
|
||||
if (!oldMainNtxId || !newMainNtxId) {
|
||||
// no need to update tab row
|
||||
return;
|
||||
}
|
||||
|
||||
// update tab id for the new main context
|
||||
this.getTabById(oldMainNtxId).attr("data-ntx-id", newMainNtxId);
|
||||
this.updateTabById(newMainNtxId);
|
||||
}
|
||||
|
||||
updateTabById(ntxId) {
|
||||
const $tab = this.getTabById(ntxId);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user