mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
WIP of attribute view on click
This commit is contained in:
parent
a04aa6fd36
commit
a87e59cecb
@ -7,6 +7,8 @@ import utils from "../services/utils.js";
|
|||||||
import ws from "../services/ws.js";
|
import ws from "../services/ws.js";
|
||||||
import SpacedUpdate from "../services/spaced_update.js";
|
import SpacedUpdate from "../services/spaced_update.js";
|
||||||
import attributesParser from "../services/attribute_parser.js";
|
import attributesParser from "../services/attribute_parser.js";
|
||||||
|
import linkService from "../services/link.js";
|
||||||
|
import treeService from "../services/tree.js";
|
||||||
|
|
||||||
const mentionSetup = {
|
const mentionSetup = {
|
||||||
feeds: [
|
feeds: [
|
||||||
@ -72,10 +74,41 @@ const mentionSetup = {
|
|||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="note-attributes">
|
<div class="note-attributes">
|
||||||
|
<style>
|
||||||
|
.note-attributes-editor p {
|
||||||
|
color: var(--muted-text-color);
|
||||||
|
margin-top: 5px !important;
|
||||||
|
margin-bottom: 5px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attr-extras {
|
||||||
|
display: block;
|
||||||
|
background-color: var(--accented-background-color);
|
||||||
|
border: 1px solid var(--main-border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
z-index: 1000;
|
||||||
|
padding: 10px;
|
||||||
|
position: absolute;
|
||||||
|
max-width: 400px;
|
||||||
|
max-height: 500px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="attr-extras" style="display: none;">
|
||||||
|
<div class="attr-extras-title"></div>
|
||||||
|
|
||||||
|
<div class="attr-extras-list"></div>
|
||||||
|
|
||||||
|
<div class="attr-extras-more-notes"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="note-attributes-editor"></div>
|
<div class="note-attributes-editor"></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const DISPLAYED_NOTES = 10;
|
||||||
|
|
||||||
export default class NoteAttributesWidget extends TabAwareWidget {
|
export default class NoteAttributesWidget extends TabAwareWidget {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@ -90,6 +123,10 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
|||||||
doRender() {
|
doRender() {
|
||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
this.$editor = this.$widget.find('.note-attributes-editor');
|
this.$editor = this.$widget.find('.note-attributes-editor');
|
||||||
|
this.$attrExtras = this.$widget.find('.attr-extras');
|
||||||
|
this.$attrExtrasTitle = this.$widget.find('.attr-extras-title');
|
||||||
|
this.$attrExtrasList = this.$widget.find('.attr-extras-list');
|
||||||
|
this.$attrExtrasMoreNotes = this.$widget.find('.attr-extras-more-notes');
|
||||||
this.initialized = this.initEditor();
|
this.initialized = this.initEditor();
|
||||||
|
|
||||||
this.$editor.keypress(async e => {
|
this.$editor.keypress(async e => {
|
||||||
@ -114,18 +151,85 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
|||||||
// display of $widget in both branches.
|
// display of $widget in both branches.
|
||||||
this.$widget.show();
|
this.$widget.show();
|
||||||
|
|
||||||
this.$editor.on("click", () => {
|
this.$editor.on("click", async e => {
|
||||||
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) {
|
||||||
const attr = pos.textNode.data;
|
const attrText = pos.textNode.data;
|
||||||
const index = pos.offset - pos.textNode.startOffset;
|
const clickIndex = pos.offset - pos.textNode.startOffset;
|
||||||
|
|
||||||
const attrs = attributesParser.lexAndParse(attr, true);
|
const parsedAttrs = attributesParser.lexAndParse(attrText, true);
|
||||||
|
|
||||||
console.log(attrs);
|
let matchedAttr = null;
|
||||||
|
let matchedPart = null;
|
||||||
|
|
||||||
console.log(attr.substr(0, index) + '|' + attr.substr(index));
|
for (const attr of parsedAttrs) {
|
||||||
|
if (clickIndex >= attr.nameStartIndex && clickIndex <= attr.nameEndIndex) {
|
||||||
|
matchedAttr = attr;
|
||||||
|
matchedPart = 'name';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clickIndex >= attr.valueStartIndex && clickIndex <= attr.valueEndIndex) {
|
||||||
|
matchedAttr = attr;
|
||||||
|
matchedPart = 'value';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matchedAttr) {
|
||||||
|
console.log(`Not found attribute for index ${clickIndex}, attr: ${JSON.stringify(parsedAttrs)}, text: ${attrText}`);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let noteIds = await server.post('attributes/notes-with-attribute', {
|
||||||
|
type: matchedAttr.type,
|
||||||
|
name: matchedAttr.name,
|
||||||
|
value: matchedPart === 'value' ? matchedAttr.value : undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
noteIds = noteIds.filter(noteId => noteId !== this.noteId);
|
||||||
|
|
||||||
|
if (noteIds.length === 0) {
|
||||||
|
this.$attrExtrasTitle.text(
|
||||||
|
`There are no other notes with ${matchedAttr.type} name "${matchedAttr.name}"`
|
||||||
|
// not displaying value since it can be long
|
||||||
|
+ (matchedPart === 'value' ? " and matching value" : ""));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.$attrExtrasTitle.text(
|
||||||
|
`Notes with ${matchedAttr.type} name "${matchedAttr.name}"`
|
||||||
|
// not displaying value since it can be long
|
||||||
|
+ (matchedPart === 'value' ? " and matching value" : "")
|
||||||
|
+ ":"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$attrExtrasList.empty();
|
||||||
|
|
||||||
|
const displayedNoteIds = noteIds.length <= DISPLAYED_NOTES ? noteIds : noteIds.slice(0, DISPLAYED_NOTES);
|
||||||
|
const displayedNotes = await treeCache.getNotes(displayedNoteIds);
|
||||||
|
console.log(displayedNoteIds, displayedNotes);
|
||||||
|
for (const note of displayedNotes) {
|
||||||
|
const notePath = treeService.getSomeNotePath(note);
|
||||||
|
const $noteLink = await linkService.createNoteLink(notePath, {showNotePath: true});
|
||||||
|
|
||||||
|
this.$attrExtrasList.append(
|
||||||
|
$("<li>").append($noteLink)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noteIds.length > DISPLAYED_NOTES) {
|
||||||
|
this.$attrExtrasMoreNotes.show().text(`... and ${noteIds.length - DISPLAYED_NOTES} more.`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.$attrExtrasMoreNotes.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$attrExtras.css("left", e.pageX - this.$attrExtras.width() / 2);
|
||||||
|
this.$attrExtras.css("top", e.pageY + 20);
|
||||||
|
this.$attrExtras.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -9,14 +9,14 @@ const TPL = `
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
z-index: 1000;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hide-left-pane-button, .show-left-pane-button {
|
.hide-left-pane-button, .show-left-pane-button {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
z-index: 1000;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -730,7 +730,7 @@ body {
|
|||||||
|
|
||||||
#context-menu-container, #context-menu-container .dropdown-menu {
|
#context-menu-container, #context-menu-container .dropdown-menu {
|
||||||
padding: 3px 0 0;
|
padding: 3px 0 0;
|
||||||
z-index: 1111;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#context-menu-container .dropdown-item {
|
#context-menu-container .dropdown-item {
|
||||||
|
@ -258,6 +258,14 @@ async function deleteRelation(req) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getNotesWithAttribute(req) {
|
||||||
|
const {type, name, value} = req.body;
|
||||||
|
|
||||||
|
const notes = await attributeService.getNotesWithAttribute(type, name, value);
|
||||||
|
|
||||||
|
return notes.map(note => note.noteId);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
updateNoteAttributes,
|
updateNoteAttributes,
|
||||||
updateNoteAttributes2,
|
updateNoteAttributes2,
|
||||||
@ -267,5 +275,6 @@ module.exports = {
|
|||||||
getValuesForAttribute,
|
getValuesForAttribute,
|
||||||
getEffectiveNoteAttributes,
|
getEffectiveNoteAttributes,
|
||||||
createRelation,
|
createRelation,
|
||||||
deleteRelation
|
deleteRelation,
|
||||||
|
getNotesWithAttribute
|
||||||
};
|
};
|
||||||
|
@ -177,6 +177,7 @@ function register(app) {
|
|||||||
apiRoute(DELETE, '/api/notes/:noteId/attributes/:attributeId', attributesRoute.deleteNoteAttribute);
|
apiRoute(DELETE, '/api/notes/:noteId/attributes/:attributeId', attributesRoute.deleteNoteAttribute);
|
||||||
apiRoute(GET, '/api/attributes/names', attributesRoute.getAttributeNames);
|
apiRoute(GET, '/api/attributes/names', attributesRoute.getAttributeNames);
|
||||||
apiRoute(GET, '/api/attributes/values/:attributeName', attributesRoute.getValuesForAttribute);
|
apiRoute(GET, '/api/attributes/values/:attributeName', attributesRoute.getValuesForAttribute);
|
||||||
|
apiRoute(POST, '/api/attributes/notes-with-attribute', attributesRoute.getNotesWithAttribute);
|
||||||
|
|
||||||
apiRoute(POST, '/api/notes/:noteId/link-map', linkMapRoute.getLinkMap);
|
apiRoute(POST, '/api/notes/:noteId/link-map', linkMapRoute.getLinkMap);
|
||||||
|
|
||||||
|
@ -41,6 +41,27 @@ const BUILTIN_ATTRIBUTES = [
|
|||||||
{ type: 'relation', name: 'renderNote', isDangerous: true }
|
{ type: 'relation', name: 'renderNote', isDangerous: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
async function getNotesWithAttribute(type, name, value) {
|
||||||
|
let valueCondition = "";
|
||||||
|
let params = [type, name];
|
||||||
|
|
||||||
|
if (value !== undefined) {
|
||||||
|
valueCondition = " AND attributes.value = ?";
|
||||||
|
params.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await repository.getEntities(`
|
||||||
|
SELECT notes.*
|
||||||
|
FROM notes
|
||||||
|
JOIN attributes USING (noteId)
|
||||||
|
WHERE notes.isDeleted = 0
|
||||||
|
AND attributes.isDeleted = 0
|
||||||
|
AND attributes.type = ?
|
||||||
|
AND attributes.name = ?
|
||||||
|
${valueCondition}
|
||||||
|
ORDER BY position`, params);
|
||||||
|
}
|
||||||
|
|
||||||
async function getNotesWithLabel(name, value) {
|
async function getNotesWithLabel(name, value) {
|
||||||
let valueCondition = "";
|
let valueCondition = "";
|
||||||
let params = [name];
|
let params = [name];
|
||||||
@ -134,6 +155,7 @@ function getBuiltinAttributeNames() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
getNotesWithAttribute,
|
||||||
getNotesWithLabel,
|
getNotesWithLabel,
|
||||||
getNotesWithLabels,
|
getNotesWithLabels,
|
||||||
getNoteWithLabel,
|
getNoteWithLabel,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user