mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
attr experiments
This commit is contained in:
parent
1a262fe680
commit
bbdedac9ed
@ -117,7 +117,7 @@ function checkAttributeName(attrName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parse(tokens, str, allowEmptyRelations = false) {
|
function parse(tokens, str, opts) {console.log("tokens", tokens);
|
||||||
const attrs = [];
|
const attrs = [];
|
||||||
|
|
||||||
function context(i) {
|
function context(i) {
|
||||||
@ -162,15 +162,23 @@ function parse(tokens, str, allowEmptyRelations = false) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (i + 1 < tokens.length && tokens[i + 1].text === "=") {
|
if (i + 1 < tokens.length && tokens[i + 1].text === "=") {
|
||||||
if (i + 2 >= tokens.length) {
|
if (i + 2 >= tokens.length || tokens[i + 2].text.startsWith('#')) {
|
||||||
|
if (opts.allowEmptyValues) {
|
||||||
|
attr.endIndex = tokens[i + 1].endIndex + 1;
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
throw new Error(`Missing value for label "${text}" in ${context(i)}`);
|
throw new Error(`Missing value for label "${text}" in ${context(i)}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
i += 2;
|
i += 2;
|
||||||
|
|
||||||
attr.value = tokens[i].text;
|
attr.value = tokens[i].text;
|
||||||
attr.endIndex = tokens[i].endIndex;
|
attr.endIndex = tokens[i].endIndex;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
attrs.push(attr);
|
attrs.push(attr);
|
||||||
}
|
}
|
||||||
@ -190,7 +198,7 @@ function parse(tokens, str, allowEmptyRelations = false) {
|
|||||||
attrs.push(attr);
|
attrs.push(attr);
|
||||||
|
|
||||||
if (i + 2 >= tokens.length || tokens[i + 1].text !== '=') {
|
if (i + 2 >= tokens.length || tokens[i + 1].text !== '=') {
|
||||||
if (allowEmptyRelations) {
|
if (opts.allowEmptyValues) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -218,10 +226,14 @@ function parse(tokens, str, allowEmptyRelations = false) {
|
|||||||
return attrs;
|
return attrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
function lexAndParse(str, allowEmptyRelations = false) {
|
function lexAndParse(str, opts) {
|
||||||
|
const options = Object.assign({
|
||||||
|
allowEmptyValues: false
|
||||||
|
}, opts);
|
||||||
|
|
||||||
const tokens = lex(str);
|
const tokens = lex(str);
|
||||||
|
|
||||||
return parse(tokens, str, allowEmptyRelations);
|
return parse(tokens, str, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -71,106 +71,6 @@ const TPL = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const mentionSetup = {
|
|
||||||
feeds: [
|
|
||||||
{
|
|
||||||
marker: '@',
|
|
||||||
feed: queryText => noteAutocompleteService.autocompleteSourceForCKEditor(queryText),
|
|
||||||
itemRenderer: item => {
|
|
||||||
const itemElement = document.createElement('button');
|
|
||||||
|
|
||||||
itemElement.innerHTML = `${item.highlightedNotePathTitle} `;
|
|
||||||
|
|
||||||
return itemElement;
|
|
||||||
},
|
|
||||||
minimumCharacters: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
marker: '#',
|
|
||||||
feed: async queryText => {
|
|
||||||
const names = await server.get(`attributes/names/?type=label&query=${encodeURIComponent(queryText)}`);
|
|
||||||
|
|
||||||
return names.map(name => {
|
|
||||||
return {
|
|
||||||
id: '#' + name,
|
|
||||||
name: name
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
minimumCharacters: 0,
|
|
||||||
attributeMention: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
marker: '~',
|
|
||||||
feed: async queryText => {
|
|
||||||
const names = await server.get(`attributes/names/?type=relation&query=${encodeURIComponent(queryText)}`);
|
|
||||||
|
|
||||||
return names.map(name => {
|
|
||||||
return {
|
|
||||||
id: '~' + name,
|
|
||||||
name: name
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
minimumCharacters: 0,
|
|
||||||
attributeMention: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class AttributeEditorWidget extends TabAwareWidget {
|
export default class AttributeEditorWidget extends TabAwareWidget {
|
||||||
constructor(attributeDetailWidget) {
|
constructor(attributeDetailWidget) {
|
||||||
super();
|
super();
|
||||||
@ -331,7 +231,125 @@ export default class AttributeEditorWidget extends TabAwareWidget {
|
|||||||
|
|
||||||
this.$editor.on("click", e => this.handleEditorClick(e));
|
this.$editor.on("click", e => this.handleEditorClick(e));
|
||||||
|
|
||||||
this.textEditor = await BalloonEditor.create(this.$editor[0], editorConfig);
|
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: {
|
||||||
|
feeds: [
|
||||||
|
{
|
||||||
|
marker: '@',
|
||||||
|
feed: queryText => noteAutocompleteService.autocompleteSourceForCKEditor(queryText),
|
||||||
|
itemRenderer: item => {
|
||||||
|
const itemElement = document.createElement('button');
|
||||||
|
|
||||||
|
itemElement.innerHTML = `${item.highlightedNotePathTitle} `;
|
||||||
|
|
||||||
|
return itemElement;
|
||||||
|
},
|
||||||
|
minimumCharacters: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
marker: '#',
|
||||||
|
feed: async queryText => {
|
||||||
|
const names = await server.get(`attributes/names/?type=label&query=${encodeURIComponent(queryText)}`);
|
||||||
|
|
||||||
|
return names.map(name => {
|
||||||
|
return {
|
||||||
|
id: '#' + name,
|
||||||
|
name: name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
minimumCharacters: 0,
|
||||||
|
attributeMention: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
marker: '~',
|
||||||
|
feed: async queryText => {
|
||||||
|
const names = await server.get(`attributes/names/?type=relation&query=${encodeURIComponent(queryText)}`);
|
||||||
|
|
||||||
|
return names.map(name => {
|
||||||
|
return {
|
||||||
|
id: '~' + name,
|
||||||
|
name: name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
minimumCharacters: 0,
|
||||||
|
attributeMention: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
marker: '=',
|
||||||
|
feed: async queryText => {
|
||||||
|
const attr = this.getCurrentAttr();
|
||||||
|
|
||||||
|
console.log("= attr", attr);
|
||||||
|
|
||||||
|
const names = await server.get(`attributes/values/${encodeURIComponent(attr.name)}`);
|
||||||
|
|
||||||
|
console.log("names", names);
|
||||||
|
|
||||||
|
return names.map(name => {
|
||||||
|
return {
|
||||||
|
id: '=' + name,
|
||||||
|
name: ' ' + name
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
minimumCharacters: 0,
|
||||||
|
attributeMention: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.textEditor.model.document.on('change:data', () => this.dataChanged());
|
this.textEditor.model.document.on('change:data', () => this.dataChanged());
|
||||||
|
|
||||||
// disable spellcheck for attribute editor
|
// disable spellcheck for attribute editor
|
||||||
@ -356,30 +374,35 @@ export default class AttributeEditorWidget extends TabAwareWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleEditorClick(e) {
|
getCurrentAttr() {
|
||||||
const pos = this.textEditor.model.document.selection.getFirstPosition();
|
const pos = this.textEditor.model.document.selection.getFirstPosition();
|
||||||
|
|
||||||
if (pos && pos.textNode && pos.textNode.data) {
|
if (!pos || !pos.textNode || !pos.textNode.data) {
|
||||||
|
console.log("no pos", pos, pos.textNode);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const clickIndex = this.getClickIndex(pos);
|
const clickIndex = this.getClickIndex(pos);
|
||||||
|
|
||||||
let parsedAttrs;
|
let parsedAttrs;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
parsedAttrs = attributesParser.lexAndParse(this.getPreprocessedData(), true);
|
parsedAttrs = attributesParser.lexAndParse(this.getPreprocessedData(), {
|
||||||
}
|
allowEmptyValues: true
|
||||||
catch (e) {
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
|
||||||
// the input is incorrect because user messed up with it and now needs to fix it manually
|
// the input is incorrect because user messed up with it and now needs to fix it manually
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let matchedAttr = null;
|
return parsedAttrs.find(attr => clickIndex > attr.startIndex && clickIndex <= attr.endIndex);
|
||||||
|
}
|
||||||
|
|
||||||
for (const attr of parsedAttrs) {
|
async handleEditorClick(e) {
|
||||||
if (clickIndex > attr.startIndex && clickIndex <= attr.endIndex) {
|
const matchedAttr = this.getCurrentAttr();
|
||||||
matchedAttr = attr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (matchedAttr) {
|
if (matchedAttr) {
|
||||||
@ -398,10 +421,6 @@ export default class AttributeEditorWidget extends TabAwareWidget {
|
|||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
this.showHelpTooltip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showHelpTooltip() {
|
showHelpTooltip() {
|
||||||
this.attributeDetailWidget.hide();
|
this.attributeDetailWidget.hide();
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
const repository = require('./repository');
|
const repository = require('./repository');
|
||||||
const sql = require('./sql');
|
const sql = require('./sql');
|
||||||
const utils = require('./utils');
|
|
||||||
const Attribute = require('../entities/attribute');
|
const Attribute = require('../entities/attribute');
|
||||||
|
|
||||||
const ATTRIBUTE_TYPES = [ 'label', 'relation' ];
|
const ATTRIBUTE_TYPES = [ 'label', 'relation' ];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user