From 378f9b7f905519200dbbb37a2d9effc1ac0dc6fc Mon Sep 17 00:00:00 2001 From: Mossy426 Date: Tue, 18 Jun 2024 12:47:26 -0400 Subject: [PATCH] Updates: - Added foundation for color legend + coloring nodes - Updated nodes to have their own fill property - Removed change background color of global style --- main.ts | 209 ++++++++++++++++++++++++++++++++++++-------------- manifest.json | 2 +- package.json | 2 +- styles.css | 75 ++++++++++++++++-- 4 files changed, 223 insertions(+), 65 deletions(-) diff --git a/main.ts b/main.ts index d0f5219..2466439 100644 --- a/main.ts +++ b/main.ts @@ -15,9 +15,21 @@ const DEFAULT_NETWORK_SETTINGS : any = { maxLabelCharacters: 18, linkLabelSize: 7, nodeLabelSize: 6, - connectionType: 'block' + connectionType: 'block', + noteFillColor: '#7c8594', + blockFillColor: '#926ec9' } +/* + Main Colors + Menu text: #a3aecb + HoveredOverNode: #d46ebe + NormalNode: #926ec9 + centralNode: #7c8594 + Link: #4c7787 + SliderKnob: #f3ee5d +*/ + interface PluginSettings { relevanceScoreThreshold: number; nodeSize: number; @@ -33,6 +45,8 @@ interface PluginSettings { linkLabelSize: number; nodeLabelSize: number; connectionType: string; + noteFillColor: string; + blockFillColor: string; } declare global { @@ -78,6 +92,8 @@ class ScGraphItemView extends ItemView { maxLabelCharacters = 18; linkLabelSize = 7; nodeLabelSize = 6; + blockFillColor = '#926ec9'; + noteFillColor = '#7c8594'; startX = 0; startY = 0; nodes : any = []; @@ -90,8 +106,8 @@ class ScGraphItemView extends ItemView { dragging = false; highlightedNodeId = '-1'; currentNoteChanging = false; - isFiltering = false; - + isFiltering = false; + settingsMade = false; constructor(leaf: WorkspaceLeaf, plugin: ScGraphView) { super(leaf); @@ -114,6 +130,8 @@ class ScGraphItemView extends ItemView { this.linkLabelSize = this.plugin.settings.linkLabelSize; this.nodeLabelSize = this.plugin.settings.nodeLabelSize; this.connectionType = this.plugin.settings.connectionType; + this.noteFillColor = this.plugin.settings.noteFillColor; + this.blockFillColor = this.plugin.settings.blockFillColor; } @@ -131,18 +149,18 @@ class ScGraphItemView extends ItemView { updateNodeAppearance() { this.nodeSelection.transition().duration(500) - .attr('fill', (d: any) => this.getNodeFill(d)) + .attr('fill', (d: any) => d.fill) .attr('stroke', (d: any) => d.selected ? 'blanchedalmond' : (d.highlighted ? '#d46ebe' : 'transparent')) .attr('stroke-width', (d: any) => d.selected ? 1.5 : (d.highlighted ? 0.3 : 0)) .attr('opacity', (d: any) => this.getNodeOpacity(d)); } - getNodeFill(d: any) { - if (d.id === this.centralNode.id) return '#7c8594'; - if (d.highlighted && !d.selected) return '#d46ebe'; - return d.group === 'note' ? '#7c8594' : '#926ec9'; - } + // getNodeFill(d: any) { + // if (d.id === this.centralNode.id) return '#7c8594'; + // if (d.highlighted && !d.selected) return '#d46ebe'; + // return d.group === 'note' ? '#7c8594' : '#926ec9'; + // } getNodeOpacity(d: any) { if (d.id === this.centralNode.id) return 1; @@ -452,6 +470,100 @@ class ScGraphItemView extends ItemView { } + + renderLegend() { + if (this.validatedLinks.length === 0) { + return; + } + + const types = ['block', 'note']; // Connection types + const counts = types.map(type => this.nodes.filter((node: any) => (node.group === type) && node.id !== this.centralNode.id).length); + + // Initialize colors with default values + let colors: { [key: string]: string } = { 'block': DEFAULT_NETWORK_SETTINGS.blockFillColor, 'note': DEFAULT_NETWORK_SETTINGS.noteFillColor }; + + // Iterate over nodes to find the color for each type + for (let node of this.nodes) { + if (colors[node.group]) { + colors[node.group] = node.fill; + } + } + + // Use contentEl to create a table container + const tableContainer = this.contentEl.createEl('div', { cls: 'legend-container' }); + + // Create table header + const header = tableContainer.createEl('div', { cls: 'legend-header' }); + ['Connection Type', 'Count', 'Color'].forEach(headerTitle => { + + // Assign appropiate class based on column + switch(headerTitle) { + case "Connection Type": + header.createEl('div', { text: headerTitle, cls: 'variable-col' }); + break; + case "Count": + header.createEl('div', { text: headerTitle, cls: 'count-col' }); + break; + case "Color": + header.createEl('div', { text: headerTitle, cls: 'color-col' }); + break; + default: + header.createEl('div', { text: headerTitle, cls: 'variable-col' }); + break + } + + }); + + // Create rows for each type + types.forEach((type, index) => { + if (counts[index] > 0) { // Check if the count is greater than zero + const row = tableContainer.createEl('div', { cls: 'legend-row' }); + + row.createEl('div', { text: this.capitalizeFirstLetter(type), cls: 'variable-col' }); + row.createEl('div', { text: `${counts[index]}`, cls: 'count-col' }); + + const colorCell = row.createEl('div', { cls: 'color-col' }); + const colorPicker = colorCell.createEl('input', { type: 'color', value: colors[type as keyof typeof colors], cls: 'legend-color-picker' }); + + colorPicker.addEventListener('change', (e) => this.updateNodeColors(type, (e.target as HTMLInputElement).value)); + } + }); + } + + capitalizeFirstLetter(str: string): string { + if (!str) return str; + console.log('string: ', str); + return str.charAt(0).toUpperCase() + str.slice(1); + } + + updateNodeColors(type: string, color: string) { + + if (type === 'note' && color !== this.noteFillColor) { + this.noteFillColor = color; + this.plugin.settings.noteFillColor = color; + this.plugin.saveSettings(); // Save the settings + } + + if (type === 'block' && color !== this.blockFillColor) { + this.blockFillColor = color; + this.plugin.settings.noteFillColor = color; + this.plugin.saveSettings(); // Save the settings + } + + + this.nodes.forEach((node : any) => { + if (node.group === type) { + node.fill = color; + } + }); + this.updateNodeFill(); + } + + updateNodeFill() { + // Update the D3 visualization here + this.nodeSelection.attr('fill', (d: any) => d.fill); + } + // Ensure node labels dont collide with any elements avoidLabelCollisions() { const padding = 5; // Adjust padding as needed @@ -484,33 +596,6 @@ class ScGraphItemView extends ItemView { }); }; } - - - createNodes(svgGroup: any) { - return svgGroup.append('g') - .attr('class', 'nodes') - .selectAll('circle') - .data([]) - .enter().append('circle') - .attr('r', 20) - .attr('fill', 'blue') - .style('cursor', 'pointer') // Ensure cursor is pointer - .style('pointer-events', 'all') // Ensure nodes can capture pointer events - .call(d3.drag().on('start', this.onDragStart.bind(this)) - .on('drag', this.onDrag.bind(this)) - .on('end', this.onDragEnd.bind(this))); - } - - createLinks(svgGroup: any) { - return svgGroup.append('g') - .attr('class', 'links') - .selectAll('line') - .data([]) - .enter().append('line') - .attr('stroke', 'blue') - .attr('stroke-width', 2) - .attr('stroke-opacity', 1); - } addEventListeners() { this.setupSVGEventListeners(); @@ -566,7 +651,7 @@ class ScGraphItemView extends ItemView { } setupSettingsMenu() { - if (!document.querySelector('.settings-icon')) { + if (!document.querySelector('.smart-connections-visualizer-settings-icon')) { this.createSettingsIcon(); this.createDropdownMenu(); this.setupAccordionHeaders(); @@ -766,7 +851,7 @@ class ScGraphItemView extends ItemView { createSettingsIcon() { // Create the container div for the settings icon const settingsIcon = this.contentEl.createEl('div', { - cls: ['settings-icon', ], + cls: ['smart-connections-visualizer-settings-icon', ], attr: { 'aria-label': 'Open graph settings' } }); @@ -1122,6 +1207,8 @@ class ScGraphItemView extends ItemView { } resetToDefault() { + + // Reset all values to their default this.relevanceScoreThreshold = DEFAULT_NETWORK_SETTINGS.relevanceScoreThreshold; this.nodeSize = DEFAULT_NETWORK_SETTINGS.nodeSize; this.linkThickness = DEFAULT_NETWORK_SETTINGS.lineThickness; @@ -1136,15 +1223,10 @@ class ScGraphItemView extends ItemView { this.linkLabelSize = DEFAULT_NETWORK_SETTINGS.linkLabelSize; this.nodeLabelSize = DEFAULT_NETWORK_SETTINGS.nodeLabelSize; this.connectionType = DEFAULT_NETWORK_SETTINGS.connectionType; + this.noteFillColor = DEFAULT_NETWORK_SETTINGS.noteFillColor; + this.blockFillColor = DEFAULT_NETWORK_SETTINGS.blockFillColor; - this.updateLabelsToDefaults(); - this.updateSliders(); - this.updateNodeSizes(); - this.updateLinkThickness(); - this.updateSimulationForces(); - this.updateVisualization(this.relevanceScoreThreshold); - - // Update plugin settings + // Save plugin settings this.plugin.settings.relevanceScoreThreshold = DEFAULT_NETWORK_SETTINGS.relevanceScoreThreshold; this.plugin.settings.nodeSize = DEFAULT_NETWORK_SETTINGS.nodeSize; this.plugin.settings.linkThickness = DEFAULT_NETWORK_SETTINGS.lineThickness; @@ -1159,8 +1241,18 @@ class ScGraphItemView extends ItemView { this.plugin.settings.linkLabelSize = DEFAULT_NETWORK_SETTINGS.linkLabelSize; this.plugin.settings.nodeLabelSize = DEFAULT_NETWORK_SETTINGS.nodeLabelSize; this.plugin.settings.connectionType = DEFAULT_NETWORK_SETTINGS.connectionType; + this.plugin.settings.noteFillColor = DEFAULT_NETWORK_SETTINGS.noteFillColor; + this.plugin.settings.blockFillColor = DEFAULT_NETWORK_SETTINGS.blockFillColor; this.plugin.saveSettings(); // Save the settings + // Update visualization + this.updateLabelsToDefaults(); + this.updateSliders(); + this.updateNodeSizes(); + this.updateLinkThickness(); + this.updateSimulationForces(); + this.updateVisualization(this.relevanceScoreThreshold); + } updateLabelsToDefaults() { @@ -1315,6 +1407,16 @@ class ScGraphItemView extends ItemView { this.simulation.alpha(1).restart(); this.updatingVisualization = false; + + // TODO: Comment back when pushing legend + // First, clear the existing legend if it exists + // const existingLegend = this.contentEl.querySelector('.legend-container'); + // if (existingLegend) { + // existingLegend.remove(); + // } + + // Now, re-render the legend with updated node data + // this.renderLegend(); } simulationTickHandler() { @@ -1363,6 +1465,7 @@ class ScGraphItemView extends ItemView { y: height / 2, fx: null, fy: null, + fill: this.noteFillColor, selected: false, highlighted: false }); @@ -1409,6 +1512,7 @@ class ScGraphItemView extends ItemView { y: Math.random() * 1000, fx: null, fy: null, + fill: connection.__proto__.constructor.name === 'SmartBlock' ? this.blockFillColor : this.noteFillColor, selected: false, highlighted: false }); @@ -1523,7 +1627,7 @@ class ScGraphItemView extends ItemView { return enter.append('circle') .attr('class', 'node') .attr('r', (d: any) => d.id === this.centralNode.id ? this.nodeSize + 2 : this.nodeSize) - .attr('fill', (d: any) => this.getNodeFill(d)) + .attr('fill', (d: any) => d.fill) .attr('stroke', (d: any) => d.selected ? 'blanchedalmond' : 'transparent') .attr('stroke-width', (d: any) => d.selected ? 1.5 : 0.3) .attr('opacity', 1) @@ -1538,7 +1642,7 @@ class ScGraphItemView extends ItemView { updateNode(update: any) { return update.attr('r', (d: any) => d.id === this.centralNode.id ? this.nodeSize + 2 : this.nodeSize) - .attr('fill', (d: any) => d.selected ? '#f3ee5d' : this.getNodeFill(d)) + .attr('fill', (d: any) => d.selected ? '#f3ee5d' : d.fill) .attr('stroke', (d: any) => d.selected ? 'blanchedalmond' : 'transparent') .attr('stroke-width', (d: any) => d.selected ? 1.5 : 0.3); } @@ -1930,15 +2034,6 @@ class ScGraphItemView extends ItemView { } -/* - Main Colors - Menu text: #a3aecb - HoveredOverNode: #d46ebe - NormalNode: #926ec9 - centralNode: #7c8594 - Link: #4c7787 - SliderKnob: #f3ee5d -*/ export default class ScGraphView extends Plugin { diff --git a/manifest.json b/manifest.json index c89d3d6..84af486 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "smart-connections-visualizer", "name": "Smart Connections Visualizer", - "version": "1.0.9", + "version": "1.0.10", "minAppVersion": "0.15.0", "description": "View your Smart Connections in a visualized format.", "author": "Evan Moscoso", diff --git a/package.json b/package.json index 4dc9624..905a013 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "smart-connections-visualizer", - "version": "1.0.9", + "version": "1.0.10", "description": "A plugin that allows you to view your Smart Connections in a visualized format.", "main": "main.js", "scripts": { diff --git a/styles.css b/styles.css index a0406c1..4795f2b 100644 --- a/styles.css +++ b/styles.css @@ -40,7 +40,7 @@ svg .nodes circle { cursor: pointer; } -.settings-icon { +.smart-connections-visualizer-settings-icon { position: absolute; top: 14px; right: 14px; @@ -115,11 +115,7 @@ svg .nodes circle { width: 24px !important; } -.workspace-leaf-content { - background-color: #2d3039 !important; -} - -.settings-icon { +.smart-connections-visualizer-settings-icon { -webkit-app-region: no-drag; background-color: transparent; display: flex; @@ -184,3 +180,70 @@ svg .nodes circle { stroke-width: 1; fill: rgba(0, 0, 255, 0.3); } + +/* Styles for the legend container */ +.legend-container { + width: 295px; + border-collapse: collapse; + background-color: #2d3039; /* Dark background color similar to settings menu */ + color: #a3aecb; /* Light text color similar to settings menu */ + position: absolute; + top: 14; + left: 10; +} + +/* Styles for the legend header */ +.legend-header { + display: flex; + background-color: #303030; /* Darker background color for header */ + text-align: left; + /* border-bottom: 1px solid #ddd; */ +} + +/* Styles for each header cell in the legend */ +.legend-header div { + padding: 8px; +} + +/* Styles for each row in the legend */ +.legend-row { + display: flex; + align-items: center; + /* border-bottom: 1px solid #ddd; */ +} + +/* Alternate row coloring for better readability */ +.legend-row:nth-child(even) { + background-color: #3d4149; /* Slightly lighter row color for contrast */ +} + +.legend-row:nth-child(odd) { + background-color: #2d3039; /* Same as container background color */ +} + +/* Styles for each cell in the row */ +.legend-row div { + padding: 8px; +} + +/* Styles for the color picker input */ +.legend-color-picker { + width: 30px; + height: 30px; + border: none; + cursor: pointer; +} + +.variable-col { + flex: 2; +} + +.count-col { + flex: 1; + text-align: center; +} + +.color-col { + flex: 1; + text-align: center; +} \ No newline at end of file