mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	unified note map with ribbon map
This commit is contained in:
		| @@ -29,16 +29,25 @@ const TPL = `<div class="note-map-widget" style="position: relative;"> | |||||||
|       <button type="button" class="btn btn-secondary" title="Tree map" data-type="tree"><span class="bx bx-sitemap"></span></button> |       <button type="button" class="btn btn-secondary" title="Tree map" data-type="tree"><span class="bx bx-sitemap"></span></button> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  |     <div class="style-resolver"></div> | ||||||
|  |  | ||||||
|     <div class="note-map-container"></div> |     <div class="note-map-container"></div> | ||||||
| </div>`; | </div>`; | ||||||
|  |  | ||||||
| export default class NoteMapWidget extends NoteContextAwareWidget { | export default class NoteMapWidget extends NoteContextAwareWidget { | ||||||
|  |     constructor(widgetMode) { | ||||||
|  |         super(); | ||||||
|  |  | ||||||
|  |         this.widgetMode = widgetMode; // 'type' or 'ribbon' | ||||||
|  |     } | ||||||
|  |  | ||||||
|     doRender() { |     doRender() { | ||||||
|         this.$widget = $(TPL); |         this.$widget = $(TPL); | ||||||
|  |  | ||||||
|         this.$container = this.$widget.find(".note-map-container"); |         this.$container = this.$widget.find(".note-map-container"); | ||||||
|  |         this.$styleResolver = this.$widget.find('.style-resolver'); | ||||||
|  |  | ||||||
|         window.addEventListener('resize', () => this.setFullHeight(), false); |         window.addEventListener('resize', () => this.setHeight(), false); | ||||||
|  |  | ||||||
|         this.$widget.find(".map-type-switcher button").on("click",  async e => { |         this.$widget.find(".map-type-switcher button").on("click",  async e => { | ||||||
|             const type = $(e.target).closest("button").attr("data-type"); |             const type = $(e.target).closest("button").attr("data-type"); | ||||||
| @@ -49,31 +58,30 @@ export default class NoteMapWidget extends NoteContextAwareWidget { | |||||||
|         super.doRender(); |         super.doRender(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     setFullHeight() { |     setHeight() { | ||||||
|         if (!this.graph) { // no graph has been even rendered |         if (!this.graph) { // no graph has been even rendered | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const {top} = this.$widget[0].getBoundingClientRect(); |         const $parent = this.$widget.parent(); | ||||||
|  |  | ||||||
|         const height = $(window).height() - top; |  | ||||||
|         const width = this.$widget.width(); |  | ||||||
|  |  | ||||||
|         this.$widget.find('.note-map-container') |  | ||||||
|             .css("height", height) |  | ||||||
|             .css("width", this.$widget.width()); |  | ||||||
|  |  | ||||||
|         this.graph |         this.graph | ||||||
|             .height(height) |             .height($parent.height()) | ||||||
|             .width(width); |             .width($parent.width()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async refreshWithNote() { |     async refreshWithNote() { | ||||||
|         this.$widget.show(); |         this.$widget.show(); | ||||||
|  |  | ||||||
|  |         this.css = { | ||||||
|  |             fontFamily: this.$container.css("font-family"), | ||||||
|  |             textColor: this.rgb2hex(this.$container.css("color")), | ||||||
|  |             mutedTextColor: this.rgb2hex(this.$styleResolver.css("color")) | ||||||
|  |         }; | ||||||
|  |  | ||||||
|         this.mapType = this.note.getLabelValue("mapType") === "tree" ? "tree" : "link"; |         this.mapType = this.note.getLabelValue("mapType") === "tree" ? "tree" : "link"; | ||||||
|  |  | ||||||
|         this.setFullHeight(); |         this.setHeight(); | ||||||
|  |  | ||||||
|         await libraryLoader.requireLibrary(libraryLoader.FORCE_GRAPH); |         await libraryLoader.requireLibrary(libraryLoader.FORCE_GRAPH); | ||||||
|  |  | ||||||
| @@ -98,7 +106,7 @@ export default class NoteMapWidget extends NoteContextAwareWidget { | |||||||
|             .linkDirectionalArrowRelPos(1) |             .linkDirectionalArrowRelPos(1) | ||||||
|             .linkWidth(1) |             .linkWidth(1) | ||||||
|             .linkColor(() => this.css.mutedTextColor) |             .linkColor(() => this.css.mutedTextColor) | ||||||
|             .onNodeClick(node => this.nodeClicked(node)); |             .onNodeClick(node => appContext.tabManager.getActiveContext().setNote(node.id)); | ||||||
|  |  | ||||||
|         if (this.mapType === 'link') { |         if (this.mapType === 'link') { | ||||||
|             this.graph |             this.graph | ||||||
| @@ -112,20 +120,29 @@ export default class NoteMapWidget extends NoteContextAwareWidget { | |||||||
|         this.graph.d3Force('charge').strength(-30); |         this.graph.d3Force('charge').strength(-30); | ||||||
|         this.graph.d3Force('charge').distanceMax(1000); |         this.graph.d3Force('charge').distanceMax(1000); | ||||||
|  |  | ||||||
|         let mapRootNoteId = this.note.getLabelValue("mapRootNoteId"); |         let mapRootNoteId = this.getMapRootNoteId(); | ||||||
|  |  | ||||||
|         if (mapRootNoteId === 'hoisted') { |  | ||||||
|             mapRootNoteId = hoistedNoteService.getHoistedNoteId(); |  | ||||||
|         } |  | ||||||
|         else if (!mapRootNoteId) { |  | ||||||
|             mapRootNoteId = appContext.tabManager.getActiveContext().parentNoteId; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         const data = await this.loadNotesAndRelations(mapRootNoteId); |         const data = await this.loadNotesAndRelations(mapRootNoteId); | ||||||
|  |  | ||||||
|         this.renderData(data); |         this.renderData(data); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     getMapRootNoteId() { | ||||||
|  |         if (this.widgetMode === 'ribbon') { | ||||||
|  |             return this.noteId; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let mapRootNoteId = this.note.getLabelValue("mapRootNoteId"); | ||||||
|  |  | ||||||
|  |         if (mapRootNoteId === 'hoisted') { | ||||||
|  |             mapRootNoteId = hoistedNoteService.getHoistedNoteId(); | ||||||
|  |         } else if (!mapRootNoteId) { | ||||||
|  |             mapRootNoteId = appContext.tabManager.getActiveContext().parentNoteId; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return mapRootNoteId; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     stringToColor(str) { |     stringToColor(str) { | ||||||
|         let hash = 0; |         let hash = 0; | ||||||
|         for (let i = 0; i < str.length; i++) { |         for (let i = 0; i < str.length; i++) { | ||||||
| @@ -167,14 +184,6 @@ export default class NoteMapWidget extends NoteContextAwareWidget { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!node.expanded) { |  | ||||||
|             ctx.fillStyle =  this.css.textColor; |  | ||||||
|             ctx.font = 10 + 'px ' + this.css.fontFamily; |  | ||||||
|             ctx.textAlign = 'center'; |  | ||||||
|             ctx.textBaseline = 'middle'; |  | ||||||
|             ctx.fillText("+", x, y + 0.5); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ctx.fillStyle = this.css.textColor; |         ctx.fillStyle = this.css.textColor; | ||||||
|         ctx.font = size + 'px ' + this.css.fontFamily; |         ctx.font = size + 'px ' + this.css.fontFamily; | ||||||
|         ctx.textAlign = 'center'; |         ctx.textAlign = 'center'; | ||||||
| @@ -265,13 +274,14 @@ export default class NoteMapWidget extends NoteContextAwareWidget { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         this.nodes = resp.notes.map(([noteId, title, type]) => ({ | ||||||
|  |             id: noteId, | ||||||
|  |             name: title, | ||||||
|  |             type: type, | ||||||
|  |         })); | ||||||
|  |  | ||||||
|         return { |         return { | ||||||
|             nodes: resp.notes.map(([noteId, title, type]) => ({ |             nodes: this.nodes, | ||||||
|                 id: noteId, |  | ||||||
|                 name: title, |  | ||||||
|                 type: type, |  | ||||||
|                 expanded: true |  | ||||||
|             })), |  | ||||||
|             links: Object.values(linksGroupedBySourceTarget).map(link => ({ |             links: Object.values(linksGroupedBySourceTarget).map(link => ({ | ||||||
|                 id: link.id, |                 id: link.id, | ||||||
|                 source: link.sourceNoteId, |                 source: link.sourceNoteId, | ||||||
| @@ -295,11 +305,20 @@ export default class NoteMapWidget extends NoteContextAwareWidget { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     renderData(data, zoomToFit = true, zoomPadding = 10) { |     renderData(data) { | ||||||
|         this.graph.graphData(data); |         this.graph.graphData(data); | ||||||
|  |  | ||||||
|         if (zoomToFit && data.nodes.length > 1) { |         if (this.widgetMode === 'ribbon') { | ||||||
|             setTimeout(() => this.graph.zoomToFit(400, zoomPadding), 1000); |             setTimeout(() => { | ||||||
|  |                 const node = this.nodes.find(node => node.id === this.noteId); | ||||||
|  |  | ||||||
|  |                 this.graph.centerAt(node.x, node.y, 500); | ||||||
|  |             }, 1000); | ||||||
|  |         } | ||||||
|  |         else if (this.widgetMode === 'type') { | ||||||
|  |             if (data.nodes.length > 1) { | ||||||
|  |                 setTimeout(() => this.graph.zoomToFit(400, 10), 1000); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,8 +1,5 @@ | |||||||
| import NoteContextAwareWidget from "../note_context_aware_widget.js"; | import NoteContextAwareWidget from "../note_context_aware_widget.js"; | ||||||
| import froca from "../../services/froca.js"; | import NoteMapWidget from "../note_map.js"; | ||||||
| import libraryLoader from "../../services/library_loader.js"; |  | ||||||
| import server from "../../services/server.js"; |  | ||||||
| import appContext from "../../services/app_context.js"; |  | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <div class="note-map-ribbon-widget"> | <div class="note-map-ribbon-widget"> | ||||||
| @@ -33,11 +30,16 @@ const TPL = ` | |||||||
|     <button class="bx bx-arrow-to-top icon-action collapse-button" style="display: none;" title="Collapse to normal size"></button> |     <button class="bx bx-arrow-to-top icon-action collapse-button" style="display: none;" title="Collapse to normal size"></button> | ||||||
|  |  | ||||||
|     <div class="note-map-container"></div> |     <div class="note-map-container"></div> | ||||||
|      |  | ||||||
|     <div class="style-resolver"></div> |  | ||||||
| </div>`; | </div>`; | ||||||
|  |  | ||||||
| export default class NoteMapRibbonWidget extends NoteContextAwareWidget { | export default class NoteMapRibbonWidget extends NoteContextAwareWidget { | ||||||
|  |     constructor() { | ||||||
|  |         super(); | ||||||
|  |  | ||||||
|  |         this.noteMapWidget = new NoteMapWidget('ribbon'); | ||||||
|  |         this.child(this.noteMapWidget); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     get name() { |     get name() { | ||||||
|         return "noteMap"; |         return "noteMap"; | ||||||
|     } |     } | ||||||
| @@ -62,6 +64,7 @@ export default class NoteMapRibbonWidget extends NoteContextAwareWidget { | |||||||
|         this.$widget = $(TPL); |         this.$widget = $(TPL); | ||||||
|         this.contentSized(); |         this.contentSized(); | ||||||
|         this.$container = this.$widget.find(".note-map-container"); |         this.$container = this.$widget.find(".note-map-container"); | ||||||
|  |         this.$container.append(this.noteMapWidget.render()); | ||||||
|  |  | ||||||
|         this.openState = 'small'; |         this.openState = 'small'; | ||||||
|  |  | ||||||
| @@ -73,6 +76,8 @@ export default class NoteMapRibbonWidget extends NoteContextAwareWidget { | |||||||
|             this.$collapseButton.show(); |             this.$collapseButton.show(); | ||||||
|  |  | ||||||
|             this.openState = 'full'; |             this.openState = 'full'; | ||||||
|  |  | ||||||
|  |             this.noteMapWidget.setHeight(); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         this.$collapseButton = this.$widget.find('.collapse-button'); |         this.$collapseButton = this.$widget.find('.collapse-button'); | ||||||
| @@ -83,11 +88,10 @@ export default class NoteMapRibbonWidget extends NoteContextAwareWidget { | |||||||
|             this.$collapseButton.hide(); |             this.$collapseButton.hide(); | ||||||
|  |  | ||||||
|             this.openState = 'small'; |             this.openState = 'small'; | ||||||
|  |  | ||||||
|  |             this.noteMapWidget.setHeight(); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         this.$styleResolver = this.$widget.find('.style-resolver'); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         window.addEventListener('resize', () => { |         window.addEventListener('resize', () => { | ||||||
|             if (!this.graph) { // no graph has been even rendered |             if (!this.graph) { // no graph has been even rendered | ||||||
|                 return; |                 return; | ||||||
| @@ -107,10 +111,6 @@ export default class NoteMapRibbonWidget extends NoteContextAwareWidget { | |||||||
|         const width = this.$widget.width(); |         const width = this.$widget.width(); | ||||||
|  |  | ||||||
|         this.$widget.find('.note-map-container') |         this.$widget.find('.note-map-container') | ||||||
|             .css("height", SMALL_SIZE_HEIGHT) |  | ||||||
|             .css("width", width); |  | ||||||
|  |  | ||||||
|         this.graph |  | ||||||
|             .height(SMALL_SIZE_HEIGHT) |             .height(SMALL_SIZE_HEIGHT) | ||||||
|             .width(width); |             .width(width); | ||||||
|     } |     } | ||||||
| @@ -122,237 +122,7 @@ export default class NoteMapRibbonWidget extends NoteContextAwareWidget { | |||||||
|         const width = this.$widget.width(); |         const width = this.$widget.width(); | ||||||
|  |  | ||||||
|         this.$widget.find('.note-map-container') |         this.$widget.find('.note-map-container') | ||||||
|             .css("height", height) |  | ||||||
|             .css("width", this.$widget.width()); |  | ||||||
|  |  | ||||||
|         this.graph |  | ||||||
|             .height(height) |             .height(height) | ||||||
|             .width(width); |             .width(width); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     setZoomLevel(level) { |  | ||||||
|         this.zoomLevel = level; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async refreshWithNote(note) { |  | ||||||
|         this.linkIdToLinkMap = {}; |  | ||||||
|         this.noteIdToLinkCountMap = {}; |  | ||||||
|  |  | ||||||
|         this.$container.empty(); |  | ||||||
|  |  | ||||||
|         this.css = { |  | ||||||
|             fontFamily: this.$container.css("font-family"), |  | ||||||
|             textColor: this.rgb2hex(this.$container.css("color")), |  | ||||||
|             mutedTextColor: this.rgb2hex(this.$styleResolver.css("color")) |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         await libraryLoader.requireLibrary(libraryLoader.FORCE_GRAPH); |  | ||||||
|  |  | ||||||
|         this.graph = ForceGraph()(this.$container[0]) |  | ||||||
|             .width(this.$container.width()) |  | ||||||
|             .height(this.$container.height()) |  | ||||||
|             .onZoom(zoom => this.setZoomLevel(zoom.k)) |  | ||||||
|             .nodeRelSize(7) |  | ||||||
|             .nodeCanvasObject((node, ctx) => this.paintNode(node, this.stringToColor(node.type), ctx)) |  | ||||||
|             .nodePointerAreaPaint((node, ctx) => this.paintNode(node, this.stringToColor(node.type), ctx)) |  | ||||||
|             .nodeLabel(node => node.name) |  | ||||||
|             .maxZoom(7) |  | ||||||
|             .nodePointerAreaPaint((node, color, ctx) => { |  | ||||||
|                 ctx.fillStyle = color; |  | ||||||
|                 ctx.beginPath(); |  | ||||||
|                 ctx.arc(node.x, node.y, 5, 0, 2 * Math.PI, false); |  | ||||||
|                 ctx.fill(); |  | ||||||
|             }) |  | ||||||
|             .linkLabel(l => `${l.source.name} - <strong>${l.name}</strong> - ${l.target.name}`) |  | ||||||
|             .linkCanvasObject((link, ctx) => this.paintLink(link, ctx)) |  | ||||||
|             .linkCanvasObjectMode(() => "after") |  | ||||||
|             .linkDirectionalArrowLength(4) |  | ||||||
|             .linkDirectionalArrowRelPos(1) |  | ||||||
|             .linkWidth(2) |  | ||||||
|             .linkColor(() => this.css.mutedTextColor) |  | ||||||
|             .d3VelocityDecay(0.2) |  | ||||||
|             .onNodeClick(node => this.nodeClicked(node)); |  | ||||||
|  |  | ||||||
|         this.graph.d3Force('link').distance(50); |  | ||||||
|  |  | ||||||
|         this.graph.d3Force('center').strength(0.9); |  | ||||||
|  |  | ||||||
|         this.graph.d3Force('charge').strength(-30); |  | ||||||
|         this.graph.d3Force('charge').distanceMax(400); |  | ||||||
|  |  | ||||||
|         this.renderData(await this.loadNotesAndRelations(this.noteId,2)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     renderData(data, zoomToFit = true, zoomPadding = 10) { |  | ||||||
|         this.graph.graphData(data); |  | ||||||
|  |  | ||||||
|         if (zoomToFit && data.nodes.length > 1) { |  | ||||||
|             setTimeout(() => this.graph.zoomToFit(400, zoomPadding), 1000); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async nodeClicked(node) { |  | ||||||
|         if (!node.expanded) { |  | ||||||
|             this.renderData( |  | ||||||
|                 await this.loadNotesAndRelations(node.id,1), |  | ||||||
|                 false |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             await appContext.tabManager.getActiveContext().setNote(node.id); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     async loadNotesAndRelations(noteId, maxDepth) { |  | ||||||
|         const resp = await server.post(`notes/${noteId}/link-map`, { |  | ||||||
|             maxNotes: 1000, |  | ||||||
|             maxDepth |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         this.noteIdToLinkCountMap = {...this.noteIdToLinkCountMap, ...resp.noteIdToLinkCountMap}; |  | ||||||
|  |  | ||||||
|         for (const link of resp.links) { |  | ||||||
|             this.linkIdToLinkMap[link.id] = link; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // preload all notes |  | ||||||
|         const notes = await froca.getNotes(Object.keys(this.noteIdToLinkCountMap), true); |  | ||||||
|  |  | ||||||
|         const noteIdToLinkIdMap = {}; |  | ||||||
|         noteIdToLinkIdMap[this.noteId] = new Set(); // for case there are no relations |  | ||||||
|         const linksGroupedBySourceTarget = {}; |  | ||||||
|  |  | ||||||
|         for (const link of Object.values(this.linkIdToLinkMap)) { |  | ||||||
|             noteIdToLinkIdMap[link.sourceNoteId] = noteIdToLinkIdMap[link.sourceNoteId] || new Set(); |  | ||||||
|             noteIdToLinkIdMap[link.sourceNoteId].add(link.id); |  | ||||||
|  |  | ||||||
|             noteIdToLinkIdMap[link.targetNoteId] = noteIdToLinkIdMap[link.targetNoteId] || new Set(); |  | ||||||
|             noteIdToLinkIdMap[link.targetNoteId].add(link.id); |  | ||||||
|  |  | ||||||
|             const key = `${link.sourceNoteId}-${link.targetNoteId}`; |  | ||||||
|  |  | ||||||
|             if (key in linksGroupedBySourceTarget) { |  | ||||||
|                 if (!linksGroupedBySourceTarget[key].names.includes(link.name)) { |  | ||||||
|                     linksGroupedBySourceTarget[key].names.push(link.name); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 linksGroupedBySourceTarget[key] = { |  | ||||||
|                     id: key, |  | ||||||
|                     sourceNoteId: link.sourceNoteId, |  | ||||||
|                     targetNoteId: link.targetNoteId, |  | ||||||
|                     names: [link.name] |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return { |  | ||||||
|             nodes: notes.map(note => ({ |  | ||||||
|                 id: note.noteId, |  | ||||||
|                 name: note.title, |  | ||||||
|                 type: note.type, |  | ||||||
|                 expanded: this.noteIdToLinkCountMap[note.noteId] === noteIdToLinkIdMap[note.noteId].size |  | ||||||
|             })), |  | ||||||
|             links: Object.values(linksGroupedBySourceTarget).map(link => ({ |  | ||||||
|                 id: link.id, |  | ||||||
|                 source: link.sourceNoteId, |  | ||||||
|                 target: link.targetNoteId, |  | ||||||
|                 name: link.names.join(", ") |  | ||||||
|             })) |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     paintLink(link, ctx) { |  | ||||||
|         if (this.zoomLevel < 5) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ctx.font = '3px ' + this.css.fontFamily; |  | ||||||
|         ctx.textAlign = 'center'; |  | ||||||
|         ctx.textBaseline = 'middle'; |  | ||||||
|         ctx.fillStyle = this.css.mutedTextColor; |  | ||||||
|  |  | ||||||
|         const {source, target} = link; |  | ||||||
|  |  | ||||||
|         const x = (source.x + target.x) / 2; |  | ||||||
|         const y = (source.y + target.y) / 2; |  | ||||||
|  |  | ||||||
|         ctx.save(); |  | ||||||
|         ctx.translate(x, y); |  | ||||||
|  |  | ||||||
|         const deltaY = source.y - target.y; |  | ||||||
|         const deltaX = source.x - target.x; |  | ||||||
|  |  | ||||||
|         let angle = Math.atan2(deltaY, deltaX); |  | ||||||
|         let moveY = 2; |  | ||||||
|  |  | ||||||
|         if (angle < -Math.PI / 2 || angle > Math.PI / 2) { |  | ||||||
|             angle += Math.PI; |  | ||||||
|             moveY = -2; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ctx.rotate(angle); |  | ||||||
|         ctx.fillText(link.name, 0, moveY); |  | ||||||
|         ctx.restore(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     paintNode(node, color, ctx) { |  | ||||||
|         const {x, y} = node; |  | ||||||
|  |  | ||||||
|         ctx.fillStyle = node.id === this.noteId ? 'red' : color; |  | ||||||
|         ctx.beginPath(); |  | ||||||
|         ctx.arc(x, y, node.id === this.noteId ? 8 : 4, 0, 2 * Math.PI, false); |  | ||||||
|         ctx.fill(); |  | ||||||
|  |  | ||||||
|         if (this.zoomLevel < 2) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (!node.expanded) { |  | ||||||
|             ctx.fillStyle =  this.css.textColor; |  | ||||||
|             ctx.font = 10 + 'px ' + this.css.fontFamily; |  | ||||||
|             ctx.textAlign = 'center'; |  | ||||||
|             ctx.textBaseline = 'middle'; |  | ||||||
|             ctx.fillText("+", x, y + 0.5); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ctx.fillStyle = this.css.textColor; |  | ||||||
|         ctx.font = 5 + 'px ' + this.css.fontFamily; |  | ||||||
|         ctx.textAlign = 'center'; |  | ||||||
|         ctx.textBaseline = 'middle'; |  | ||||||
|  |  | ||||||
|         let title = node.name; |  | ||||||
|  |  | ||||||
|         if (title.length > 15) { |  | ||||||
|             title = title.substr(0, 15) + "..."; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ctx.fillText(title, x, y + (node.id === this.noteId ? 11 : 7)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     stringToColor(str) { |  | ||||||
|         let hash = 0; |  | ||||||
|         for (let i = 0; i < str.length; i++) { |  | ||||||
|             hash = str.charCodeAt(i) + ((hash << 5) - hash); |  | ||||||
|         } |  | ||||||
|         let colour = '#'; |  | ||||||
|         for (let i = 0; i < 3; i++) { |  | ||||||
|             const value = (hash >> (i * 8)) & 0xFF; |  | ||||||
|             colour += ('00' + value.toString(16)).substr(-2); |  | ||||||
|         } |  | ||||||
|         return colour; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     rgb2hex(rgb) { |  | ||||||
|         return `#${rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/) |  | ||||||
|             .slice(1) |  | ||||||
|             .map(n => parseInt(n, 10).toString(16).padStart(2, '0')) |  | ||||||
|             .join('')}` |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     entitiesReloadedEvent({loadResults}) { |  | ||||||
|         if (loadResults.getAttributes().find(attr => attr.type === 'relation' && (attr.noteId === this.noteId || attr.value === this.noteId))) { |  | ||||||
|             this.refresh(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ export default class NoteMapTypeWidget extends TypeWidget { | |||||||
|     constructor() { |     constructor() { | ||||||
|         super(); |         super(); | ||||||
|  |  | ||||||
|         this.noteMapWidget = new NoteMapWidget(); |         this.noteMapWidget = new NoteMapWidget('type'); | ||||||
|         this.child(this.noteMapWidget); |         this.child(this.noteMapWidget); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -21,8 +21,6 @@ export default class NoteMapTypeWidget extends TypeWidget { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     async doRefresh(note) { |     async doRefresh(note) { | ||||||
|         console.log("isEnabled", this.noteMapWidget.isEnabled()); |  | ||||||
|  |  | ||||||
|         await this.noteMapWidget.refresh(); |         await this.noteMapWidget.refresh(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user