mirror of
https://github.com/mossy1022/Smart-Connections-Visualizer.git
synced 2026-07-22 10:20:27 +00:00
Updates:
- Added foundation for color legend + coloring nodes - Updated nodes to have their own fill property - Removed change background color of global style
This commit is contained in:
parent
f7ffa1085e
commit
378f9b7f90
4 changed files with 223 additions and 65 deletions
209
main.ts
209
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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
75
styles.css
75
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;
|
||||
}
|
||||
Loading…
Reference in a new issue