mirror of
https://github.com/zadam/trilium.git
synced 2025-11-05 04:45:47 +01:00
WIP
This commit is contained in:
@@ -195,6 +195,7 @@ function lexAndParse(str, allowEmptyRelations = false) {
|
||||
}
|
||||
|
||||
export default {
|
||||
preprocess,
|
||||
lexer,
|
||||
parser,
|
||||
lexAndParse
|
||||
|
||||
@@ -115,13 +115,16 @@ export default class AttributeDetailWidget extends BasicWidget {
|
||||
});
|
||||
}
|
||||
|
||||
async showAttributeDetail({attribute, isOwned, x, y}) {
|
||||
async showAttributeDetail({allAttributes, attribute, isOwned, x, y}) {
|
||||
if (!attribute) {
|
||||
this.hide();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.allAttributes = allAttributes;
|
||||
this.attribute = attribute;
|
||||
|
||||
this.toggleInt(true);
|
||||
|
||||
let {results, count} = await server.post('search-related', attribute);
|
||||
@@ -173,11 +176,13 @@ export default class AttributeDetailWidget extends BasicWidget {
|
||||
|
||||
this.$attrEditName
|
||||
.val(attribute.name)
|
||||
.attr('readonly', () => !isOwned);
|
||||
.attr('readonly', () => !isOwned)
|
||||
.on('keyup', () => this.updateParent());
|
||||
|
||||
this.$attrEditValue
|
||||
.val(attribute.value)
|
||||
.attr('readonly', () => !isOwned);
|
||||
.attr('readonly', () => !isOwned)
|
||||
.on('keyup', () => this.updateParent());
|
||||
|
||||
this.$attrEditButtonRow.toggle(!!isOwned);
|
||||
|
||||
@@ -186,6 +191,13 @@ export default class AttributeDetailWidget extends BasicWidget {
|
||||
this.$widget.show();
|
||||
}
|
||||
|
||||
updateParent() {
|
||||
this.attribute.name = this.$attrEditName.val();
|
||||
this.attribute.value = this.$attrEditValue.val();
|
||||
|
||||
this.triggerCommand('updateAttributeList', { attributes: this.allAttributes });
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.toggleInt(false);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,59 @@ const mentionSetup = {
|
||||
]
|
||||
};
|
||||
|
||||
const editorConfig = {
|
||||
removePlugins: [
|
||||
'Enter',
|
||||
'ShiftEnter',
|
||||
'Heading',
|
||||
'Link',
|
||||
'Autoformat',
|
||||
'Bold',
|
||||
'Italic',
|
||||
'Underline',
|
||||
'Strikethrough',
|
||||
'Code',
|
||||
'Superscript',
|
||||
'Subscript',
|
||||
'BlockQuote',
|
||||
'Image',
|
||||
'ImageCaption',
|
||||
'ImageStyle',
|
||||
'ImageToolbar',
|
||||
'ImageUpload',
|
||||
'ImageResize',
|
||||
'List',
|
||||
'TodoList',
|
||||
'PasteFromOffice',
|
||||
'Table',
|
||||
'TableToolbar',
|
||||
'TableProperties',
|
||||
'TableCellProperties',
|
||||
'Indent',
|
||||
'IndentBlock',
|
||||
'BlockToolbar',
|
||||
'ParagraphButtonUI',
|
||||
'HeadingButtonsUI',
|
||||
'UploadimagePlugin',
|
||||
'InternalLinkPlugin',
|
||||
'MarkdownImportPlugin',
|
||||
'CuttonotePlugin',
|
||||
'TextTransformation',
|
||||
'Font',
|
||||
'FontColor',
|
||||
'FontBackgroundColor',
|
||||
'CodeBlock',
|
||||
'SelectAll',
|
||||
'IncludeNote',
|
||||
'CutToNote'
|
||||
],
|
||||
toolbar: {
|
||||
items: []
|
||||
},
|
||||
placeholder: "Type the labels and relations here ...",
|
||||
mention: mentionSetup
|
||||
};
|
||||
|
||||
const TPL = `
|
||||
<div class="note-attributes">
|
||||
<style>
|
||||
@@ -321,102 +374,48 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
||||
async initEditor() {
|
||||
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
|
||||
|
||||
// CKEditor since version 12 needs the element to be visible before initialization. At the same time
|
||||
// we want to avoid flicker - i.e. show editor only once everything is ready. That's why we have separate
|
||||
// display of $widget in both branches.
|
||||
this.$widget.show();
|
||||
|
||||
this.$editor.on("click", async e => {
|
||||
const pos = this.textEditor.model.document.selection.getFirstPosition();
|
||||
|
||||
if (pos && pos.textNode && pos.textNode.data) {
|
||||
const attrText = pos.textNode.data;
|
||||
const clickIndex = pos.offset - pos.textNode.startOffset;
|
||||
|
||||
const parsedAttrs = attributesParser.lexAndParse(attrText, true);
|
||||
|
||||
let matchedAttr = null;
|
||||
|
||||
for (const attr of parsedAttrs) {
|
||||
if (clickIndex >= attr.startIndex && clickIndex <= attr.endIndex) {
|
||||
matchedAttr = attr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.attributeDetailWidget.showAttributeDetail({
|
||||
attribute: matchedAttr,
|
||||
isOwned: true,
|
||||
x: e.pageX,
|
||||
y: e.pageY
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.textEditor = await BalloonEditor.create(this.$editor[0], {
|
||||
removePlugins: [
|
||||
'Enter',
|
||||
'ShiftEnter',
|
||||
'Heading',
|
||||
'Link',
|
||||
'Autoformat',
|
||||
'Bold',
|
||||
'Italic',
|
||||
'Underline',
|
||||
'Strikethrough',
|
||||
'Code',
|
||||
'Superscript',
|
||||
'Subscript',
|
||||
'BlockQuote',
|
||||
'Image',
|
||||
'ImageCaption',
|
||||
'ImageStyle',
|
||||
'ImageToolbar',
|
||||
'ImageUpload',
|
||||
'ImageResize',
|
||||
'List',
|
||||
'TodoList',
|
||||
'PasteFromOffice',
|
||||
'Table',
|
||||
'TableToolbar',
|
||||
'TableProperties',
|
||||
'TableCellProperties',
|
||||
'Indent',
|
||||
'IndentBlock',
|
||||
'BlockToolbar',
|
||||
'ParagraphButtonUI',
|
||||
'HeadingButtonsUI',
|
||||
'UploadimagePlugin',
|
||||
'InternalLinkPlugin',
|
||||
'MarkdownImportPlugin',
|
||||
'CuttonotePlugin',
|
||||
'TextTransformation',
|
||||
'Font',
|
||||
'FontColor',
|
||||
'FontBackgroundColor',
|
||||
'CodeBlock',
|
||||
'SelectAll',
|
||||
'IncludeNote',
|
||||
'CutToNote'
|
||||
],
|
||||
toolbar: {
|
||||
items: []
|
||||
},
|
||||
placeholder: "Type the labels and relations here ...",
|
||||
mention: mentionSetup
|
||||
});
|
||||
this.$editor.on("click", e => this.handleEditorClick(e));
|
||||
|
||||
this.textEditor = await BalloonEditor.create(this.$editor[0], editorConfig);
|
||||
this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate());
|
||||
|
||||
// disable spellcheck for attribute editor
|
||||
this.textEditor.editing.view.change( writer => {
|
||||
writer.setAttribute( 'spellcheck', 'false', this.textEditor.editing.view.document.getRoot() );
|
||||
} );
|
||||
this.textEditor.editing.view.change(writer => writer.setAttribute('spellcheck', 'false', this.textEditor.editing.view.document.getRoot()));
|
||||
|
||||
//await import(/* webpackIgnore: true */'../../libraries/ckeditor/inspector.js');
|
||||
//CKEditorInspector.attach(this.textEditor);
|
||||
}
|
||||
|
||||
async handleEditorClick(e) {
|
||||
const pos = this.textEditor.model.document.selection.getFirstPosition();
|
||||
|
||||
if (pos && pos.textNode && pos.textNode.data) {
|
||||
const attrText = pos.textNode.data;
|
||||
const clickIndex = pos.offset - pos.textNode.startOffset;
|
||||
|
||||
const parsedAttrs = attributesParser.lexAndParse(attrText, true);
|
||||
|
||||
let matchedAttr = null;
|
||||
|
||||
for (const attr of parsedAttrs) {
|
||||
if (clickIndex >= attr.startIndex && clickIndex <= attr.endIndex) {
|
||||
matchedAttr = attr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.attributeDetailWidget.showAttributeDetail({
|
||||
allAttributes: parsedAttrs,
|
||||
attribute: matchedAttr,
|
||||
isOwned: true,
|
||||
x: e.pageX,
|
||||
y: e.pageY
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async loadReferenceLinkTitle(noteId, $el) {
|
||||
const note = await treeCache.getNote(noteId, true);
|
||||
|
||||
@@ -436,14 +435,7 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
||||
}
|
||||
|
||||
async refreshWithNote(note) {
|
||||
const ownedAttributes = note.getOwnedAttributes();
|
||||
const $attributesContainer = $("<div>");
|
||||
|
||||
await this.renderAttributesIntoCKEditor(ownedAttributes, $attributesContainer);
|
||||
|
||||
await this.spacedUpdate.allowUpdateWithoutChange(() => {
|
||||
this.textEditor.setData($attributesContainer.html());
|
||||
});
|
||||
await this.renderOwnedAttributes(note.getOwnedAttributes());
|
||||
|
||||
const inheritedAttributes = note.getAttributes().filter(attr => attr.noteId !== this.noteId);
|
||||
|
||||
@@ -465,6 +457,16 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
||||
this.parseAttributes();
|
||||
}
|
||||
|
||||
async renderOwnedAttributes(ownedAttributes) {
|
||||
const $attributesContainer = $("<div>");
|
||||
|
||||
await this.renderAttributesIntoCKEditor(ownedAttributes, $attributesContainer);
|
||||
|
||||
await this.spacedUpdate.allowUpdateWithoutChange(() => {
|
||||
this.textEditor.setData($attributesContainer.html());
|
||||
});
|
||||
}
|
||||
|
||||
attrPlural(number) {
|
||||
return 'attribute' + (number === 1 ? '' : 's');
|
||||
}
|
||||
@@ -554,4 +556,8 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
||||
this.$editor.trigger('focus');
|
||||
}
|
||||
}
|
||||
|
||||
updateAttributeListCommand({attributes}) {
|
||||
this.renderOwnedAttributes(attributes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +168,23 @@ function executeWithoutTransaction(query, params = []) {
|
||||
}
|
||||
|
||||
function executeMany(query, params) {
|
||||
getManyRows(query, params);
|
||||
while (params.length > 0) {
|
||||
const curParams = params.slice(0, Math.min(params.length, PARAM_LIMIT));
|
||||
params = params.slice(curParams.length);
|
||||
|
||||
const curParamsObj = {};
|
||||
|
||||
let j = 1;
|
||||
for (const param of curParams) {
|
||||
curParamsObj['param' + j++] = param;
|
||||
}
|
||||
|
||||
let i = 1;
|
||||
const questionMarks = curParams.map(() => ":param" + i++).join(",");
|
||||
const curQuery = query.replace(/\?\?\?/g, questionMarks);
|
||||
|
||||
dbConnection.prepare(curQuery).run(curParamsObj);
|
||||
}
|
||||
}
|
||||
|
||||
function executeScript(query) {
|
||||
|
||||
Reference in New Issue
Block a user