Skip to content

Commit

Permalink
introduced arg groups in template properties and improved sankey diagram
Browse files Browse the repository at this point in the history
  • Loading branch information
ishubin committed Jan 30, 2025
1 parent 6145af3 commit ab5e96e
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 61 deletions.
3 changes: 3 additions & 0 deletions assets/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -2001,6 +2001,9 @@ ul.button-group.disabled > li, ul.button-group > li.disabled {
box-shadow: none !important;
margin: auto;
}
.vc-chrome-saturation-wrap, .vc-chrome-sliders {
user-select: none;
}
.color-picker .color-picker-tooltip {
position: fixed;
z-index: 1000;
Expand Down
2 changes: 1 addition & 1 deletion assets/templates/diagrams/sankey.json

Large diffs are not rendered by default.

94 changes: 64 additions & 30 deletions assets/templates/diagrams/sankey.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,40 @@ name: Sankey diagram
description: ""
args:
nodesData: {type: "string", value: "a1_1;x=0;y=0;|a1_2|a2", name: "Nodes encoded", hidden: true}
diagramCode: {type: "string", value: "Wages [2000] Budget\nOther [120] Budget\nBudget [1000] Housing\nBudget [450] Taxes\n", name: "Diagram", textarea: true, rows: 15}
font: {type: font, value: Arial, name: Font}
colorTheme: {type: "choice", value: "default", options: ["default", "light", "dark"], name: "Color theme"}
connectorColorType: {type: choice, value: source, options: ['source', 'destination', 'gradient', 'custom'], name: 'Connection color type'}
connectorColor: {type: color, value: '#aaaaaa', name: 'Connection color', depends: {connectorColorType: 'custom'}}
fontSize: {type: number, value: 14, name: "Font size", min: 1}
labelColor: {type: color, value: '#222222', name: 'Label color'}
magnify: {type: number, value: 0, name: "Magnify value", min: -50, max: 50}
connectorOpacity: {type: number, value: 60, name: "Connector opacity", min: 0, max: 100}
valuePrefix: {type: string, value: "", name: "Value prefix"}
valueSuffix: {type: string, value: "", name: "Value suffix"}
numberFormat: {type: choice, value: "1000000.00", options: ["1000000.00", "1000000,00", "1,000,000.00", "1.000.000,00", "1 000 000.00", "1 000 000,00"], name: "Number format"}
diagramCode: {
type: "string",
value: "Wages [2000] Budget\nOther [120] Budget\nBudget [1000] Housing\nBudget [450] Taxes\n",
name: "Diagram",
textarea: true,
rows: 15
}
colorTheme: {group: "Theme & Colors", type: "choice", value: "light", options: ["default", "light", "dark"], name: "Color theme"}
conColorType: {group: "Theme & Colors", type: choice, value: source, options: ['source', 'destination', 'gradient', 'custom'], name: 'Connection color type'}
conColor: {group: "Theme & Colors", type: advanced-color, value: {type: solid, color: '#aaaaaa'}, name: 'Connection color', depends: {conColorType: 'custom'}}
labelColor: {group: "Theme & Colors", type: color, value: '#222222', name: 'Label color'}

nodeWidth: {group: "Nodes", type: number, value: 20, name: "Node width", min: 1}
nodeSpacing: {group: "Nodes", type: number, value: 40, name: "Nodes empty space (%)", min: 0, max: 90}
nodeCornerRadius: {group: "Nodes", type: number, value: 5, name: "Corner radius", min: 0}
nodeStrokeSize: {group: "Nodes", type: number, value: 1, name: "Stroke size", min: 0}
nodeStrokeColor: {group: "Nodes", type: color, value: 'rgba(255,255,255,1)', name: "Stroke color"}

conOpacity: {group: "Connections", type: number, value: 60, name: "Connector opacity", min: 0, max: 100}
conLabel: {group: "Connections", type: boolean, value: true, name: 'Connection labels enabled', description: 'Displays the value of the connection'}
conLabelColor: {group: "Connections", type: advanced-color, value: {type: solid, color: 'rgba(255, 255, 255, 0.3)'}, name: 'Connection label color'}
conLabelStroke: {group: "Connections", type: color, value: 'rgba(120,120,120,0.6)', name: 'Connection label stroke'}
conLabelStrokeSize: {group: "Connections", type: number, value: 1, name: 'Connection label stroke size'}
conHoverStroke: {group: "Connections", type: color, value: 'rgba(30,30,30,1)', name: 'Connection hover stroke'}
conHoverStrokeSize: {group: "Connections", type: number, value: 1, name: 'Connection hover stroke size'}

font: {group: "Labels & Text", type: font, value: Arial, name: Font}
fontSize: {group: "Labels & Text", type: number, value: 14, name: "Font size", min: 1}
magnify: {group: "Labels & Text", type: number, value: 0, name: "Magnify value", min: -50, max: 50}

valuePrefix: {group: "Number formatting", type: string, value: "", name: "Value prefix"}
valueSuffix: {group: "Number formatting", type: string, value: "", name: "Value suffix"}
numberFormat: {group: "Number formatting", type: choice, value: "1000000.00", options: ["1000000.00", "1000000,00", "1,000,000.00", "1.000.000,00", "1 000 000.00", "1 000 000,00"], name: "Number format"}


preview: "/assets/templates/previews/mind-map.svg"
defaultArea: {x: 0, y: 0, w: 200, h: 60}
Expand Down Expand Up @@ -51,12 +73,40 @@ item:
args: {$-expr: "it.getArgs()"}
locked: {$-expr: "it.locked"}
textSlots: {$-expr: "toJSON(it.textSlots)"}
opacity: {$-expr: connectorOpacity}
selfOpacity: {$-expr: conOpacity}
area:
x: {$-expr: "it.x"}
y: {$-expr: "it.y"}
w: {$-expr: "it.w"}
h: {$-expr: "it.h"}
childItems:
- $-foreach: {source: "it.childItems", it: "label"}
id: {$-expr: "label.id"}
name: {$-expr: "label.name"}
shape: 'rect'
tags:
- connector-label
shapeProps: {$-expr: "toJSON(label.shapeProps)"}
args: {$-expr: "label.getArgs()"}
locked: {$-expr: "label.locked"}
textSlots: {$-expr: "toJSON(label.textSlots)"}
visible: true
opacity: 100
area:
x: {$-expr: "label.x"}
y: {$-expr: "label.y"}
w: {$-expr: "label.w"}
h: {$-expr: "label.h"}
behavior:
events:
- id: init
event: init
actions:
- id: a1
element: self
method: hide
args:
animated: false
behavior:
events:
- id: mousein
Expand All @@ -67,7 +117,7 @@ item:
method: set
args:
field: shapeProps.strokeSize
value: 1
value: {$-expr: conHoverStrokeSize}
animated: true
animationDuration: 0.2
transition: ease-in-out
Expand Down Expand Up @@ -125,22 +175,6 @@ item:
args: {$-expr: "it.getArgs()"}
locked: {$-expr: "it.locked"}
textSlots: {$-expr: "toJSON(it.textSlots)"}
area:
x: {$-expr: "it.x"}
y: {$-expr: "it.y"}
w: {$-expr: "it.w"}
h: {$-expr: "it.h"}

- $-foreach: {source: "connectorLabels", it: "it"}
id: {$-expr: "it.id"}
name: {$-expr: "it.name"}
shape: {$-expr: "it.shape"}
shapeProps: {$-expr: "toJSON(it.shapeProps)"}
args: {$-expr: "it.getArgs()"}
locked: {$-expr: "it.locked"}
textSlots: {$-expr: "toJSON(it.textSlots)"}
visible: false
opacity: 0
area:
x: {$-expr: "it.x"}
y: {$-expr: "it.y"}
Expand Down
77 changes: 49 additions & 28 deletions assets/templates/diagrams/src/sankey.sch
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
gapRatio = 0.4
nodeWidth = 20
gapRatio = nodeSpacing / 100
labelPadding = 5

labelFontSize = max(1, round(fontSize * (100 - magnify) / 100))
Expand Down Expand Up @@ -81,7 +80,6 @@ struct Connection {
value: 0
srcNode: null
dstNode: null
item: null
}


Expand Down Expand Up @@ -180,6 +178,23 @@ struct Level {
height: 0
}

func readjustStarterNodeLevels(nodesMap) {
// readjusting node levels for starter nodes
nodesMap.forEach(node => {
if (node.srcNodes.size == 0 && node.dstNodes.size > 0) {
local minDstLevel = node.dstNodes.get(0).level
node.dstNodes.forEach(dstNode => {
if (minDstLevel > dstNode.level) {
minDstLevel = dstNode.level
}
})
if (minDstLevel - node.level > 1) {
node.level = minDstLevel - 1
}
}
})
}

func buildLevels(allNodes, allConnections) {
local nodesMap = Map()
allNodes.forEach(node => {
Expand Down Expand Up @@ -214,6 +229,8 @@ func buildLevels(allNodes, allConnections) {
}
})

readjustStarterNodeLevels(nodesMap)

local levels = Map()
local maxLevel = 0
nodesMap.forEach(node => {
Expand Down Expand Up @@ -253,6 +270,7 @@ func buildLevels(allNodes, allConnections) {

func buildNodeItems(levels) {
local colorPalette = colorThemes.get(colorTheme)
log('Selected color palette', colorTheme, colorPalette)
if (!colorPalette) {
colorPalette = colorThemes.get('default')
}
Expand Down Expand Up @@ -295,9 +313,9 @@ func buildNodeItems(levels) {
nodeItem.h = node.height
nodeItem.x = node.position + node.x * width
nodeItem.y = node.offset + node.y * height
nodeItem.shapeProps.set('strokeSize', 1)
nodeItem.shapeProps.set('strokeColor', '#ffffff')
nodeItem.shapeProps.set('cornerRadius', 2)
nodeItem.shapeProps.set('strokeSize', nodeStrokeSize)
nodeItem.shapeProps.set('strokeColor', nodeStrokeColor)
nodeItem.shapeProps.set('cornerRadius', nodeCornerRadius)
nodeItem.shapeProps.set('fill', Fill.solid(node.color))
nodeItem.args.set('tplArea', 'controlled')
nodeItem.args.set('tplConnector', 'off')
Expand Down Expand Up @@ -358,6 +376,7 @@ func buildConnectorItems(levels, allConnections, allNodes) {
connectorItems
}


struct PathPoint {
t: 'B'
x: 0
Expand Down Expand Up @@ -408,28 +427,46 @@ func buildSingleConnectorItem(connector, srcNode, dstNode) {
item.w = dx
item.h = dy

if (connectorColorType == 'gradient') {
if (conColorType == 'gradient') {
item.shapeProps.set('fill', Fill.linearGradient(90, 0, srcNode.color, 100, dstNode.color))
} else if (connectorColorType == 'source') {
} else if (conColorType == 'source') {
item.shapeProps.set('fill', Fill.solid(srcNode.color))
} else if (connectorColorType == 'destination') {
} else if (conColorType == 'destination') {
item.shapeProps.set('fill', Fill.solid(dstNode.color))
} else {
item.shapeProps.set('fill', Fill.solid(connectorColor))
item.shapeProps.set('fill', conColor)
}
item.shapeProps.set('strokeSize', 0)
item.shapeProps.set('strokeColor', '#000000')
item.shapeProps.set('strokeColor', conHoverStroke)
item.shapeProps.set('paths', List(Map(
'id', 'p-' + connector.id,
'closed', true,
'pos', 'relative',
'points', points
)))

connector.item = item
if (conLabel) {
item.childItems.add(buildConnectorLabel(connector, item.w, item.h))
}
item
}

func buildConnectorLabel(c, connectorWidth, connectorHeight) {
local valueText = formatValue(c.value)
local valueTextSize = calculateTextSize(valueText, font, fontSize)
local valueLabel = buildLabel('cl-' + c.id, valueText, font, fontSize, 'center', 'middle')
valueLabel.w = valueTextSize.w + 4 + 10
valueLabel.h = valueTextSize.h * 1.8 + 14
valueLabel.x = connectorWidth/2 - valueLabel.w/2
valueLabel.y = connectorHeight/2 - valueLabel.h/2
valueLabel.name = 'connector-label-' + c.id
valueLabel.shapeProps.set('fill', conLabelColor)
valueLabel.shapeProps.set('strokeColor', conLabelStroke)
valueLabel.shapeProps.set('strokeSize', conLabelStrokeSize)
valueLabel.shapeProps.set('cornerRadius', 5)
valueLabel
}

func buildLabel(id, text, font, fontSize, halign, valign) {
local item = Item(id, text, 'none')
item.args.set('templateForceText', true)
Expand Down Expand Up @@ -530,21 +567,6 @@ func buildNodeLabels(nodes) {
}


func buildConnectorLabels(connectors) {
local labels = List()
connectors.forEach(c => {
local valueText = formatValue(c.value)
local valueTextSize = calculateTextSize(valueText, font, fontSize)
local valueLabel = buildLabel('cl-' + c.id, valueText, font, fontSize, 'center', 'middle')
valueLabel.w = valueTextSize.w + 4
valueLabel.h = valueTextSize.h * 1.8 + 4
valueLabel.x = c.item.x + c.item.w/2 - valueLabel.w/2
valueLabel.y = c.item.y + c.item.h/2 - valueLabel.h/2
labels.add(valueLabel)
})

labels
}


func onAreaUpdate(itemId, item, area) {
Expand All @@ -568,5 +590,4 @@ local levels = buildLevels(allNodes, allConnections)

nodeItems = buildNodeItems(levels)
connectorItems = buildConnectorItems(levels, allConnections, allNodes)
connectorLabels = buildConnectorLabels(allConnections)
nodeLabels = buildNodeLabels(allNodes)
4 changes: 3 additions & 1 deletion src/ui/components/editor/ArgumentsEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ import ScriptEditor from './ScriptEditor.vue';
import PathCapDropdown from './PathCapDropdown.vue';
import Dropdown from '../Dropdown.vue';
import { getAllFonts } from '../../scheme/Fonts';
import Panel from './Panel.vue';

export default {
props: {
Expand All @@ -162,7 +163,8 @@ export default {

components: {
Modal, ColorPicker, ElementPicker, Tooltip, NumberTextfield, Dropdown,
AdvancedColorEditor, ScriptEditor, DiagramPicker, PathCapDropdown
AdvancedColorEditor, ScriptEditor, DiagramPicker, PathCapDropdown,
Panel
},

beforeMount() {
Expand Down
47 changes: 46 additions & 1 deletion src/ui/components/editor/properties/TemplateProperties.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,34 @@
<h4>{{ template.name }}</h4>
<p class="hint hint-small" v-if="template.description">{{ template.description }}</p>

<ArgumentsEditor v-if="template.args"
<!-- <ArgumentsEditor v-if="template.args"
:key="`item-${item.id}-template-args-${reloadKey}`"
:editorId="editorId"
:schemeContainer="schemeContainer"
:argsDefinition="template.args"
:args="args"
@argument-changed="onArgChanged"
/> -->
<ArgumentsEditor v-if="ungroupedArgs"
:key="`item-${item.id}-template-ungrouped-args-${reloadKey}`"
:editorId="editorId"
:schemeContainer="schemeContainer"
:argsDefinition="ungroupedArgs"
:args="args"
@argument-changed="onArgChanged"
/>

<Panel :key="`template-arg-group-${group.name}`" v-for="group in argGroups" :name="group.name" :uid="`template-arg-group-${group.name}`">
<ArgumentsEditor
:key="`item-${item.id}-template-group-${group.name}-${reloadKey}`"
:editorId="editorId"
:schemeContainer="schemeContainer"
:argsDefinition="group.args"
:args="args"
@argument-changed="onArgChanged"
/>
</Panel>

<Panel :key="`editor-panel-${panel.id}`" v-for="panel in editorPanels" :name="panel.name" :uid="panel.id">
<ul v-if="panel.type === 'item-menu'" class="template-editor-panel-items-container">
<li v-for="item in panel.items" @click="onEditorPanelItemClicked(panel, item)"
Expand Down Expand Up @@ -83,6 +102,9 @@ export default {
data() {
return {
ungroupedArgs: {},
argGroups: [],
isLoading: false,
reloadKey: 0,
errorMessage: null,
Expand Down Expand Up @@ -127,11 +149,34 @@ export default {
this.isLoading = false;
this.template = compiledTemplate
if (compiledTemplate.argsDef) {
const ungroupedArgs = {};
const groupedArgs = new Map();
forEach(compiledTemplate.argsDef, (arg, argName) => {
if (!this.args.hasOwnProperty(argName)) {
this.args[argName] = arg.value;
}
if (arg.group) {
if (groupedArgs.has(arg.group)) {
groupedArgs.get(arg.group)[argName] = arg;
} else {
const obj = {};
obj[argName] = arg;
groupedArgs.set(arg.group, obj);
}
} else {
ungroupedArgs[argName] = arg;
}
});
this.ungroupedArgs = ungroupedArgs;
const argGroups = [];
groupedArgs.forEach((args, groupName) => {
argGroups.push({
name: groupName,
args
});
});
this.argGroups = argGroups;
}
this.updateEditorPanels();
}).catch(err => {
Expand Down

0 comments on commit ab5e96e

Please sign in to comment.