Skip to content

Commit

Permalink
added connection labels in sankey diagram
Browse files Browse the repository at this point in the history
  • Loading branch information
ishubin committed Jan 29, 2025
1 parent 9b03fea commit 6145af3
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 19 deletions.
2 changes: 1 addition & 1 deletion assets/templates/diagrams/mind-map.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/templates/diagrams/sankey.json

Large diffs are not rendered by default.

71 changes: 62 additions & 9 deletions assets/templates/diagrams/sankey.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ args:
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: gradient, options: ['gradient', 'source', 'destination', 'custom'], name: 'Connection color type'}
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"}

preview: "/assets/templates/previews/mind-map.svg"
defaultArea: {x: 0, y: 0, w: 200, h: 60}
Expand Down Expand Up @@ -54,6 +57,48 @@ item:
y: {$-expr: "it.y"}
w: {$-expr: "it.w"}
h: {$-expr: "it.h"}
behavior:
events:
- id: mousein
event: mousein
actions:
- id: a1
element: self
method: set
args:
field: shapeProps.strokeSize
value: 1
animated: true
animationDuration: 0.2
transition: ease-in-out
inBackground: true
- id: a2
element: {$-str: "#cl-${it.id}"}
method: show
args:
animated: true
animationDuration: 0.2

- id: mouseout
event: mouseout
actions:
- id: a1
element: self
method: set
args:
field: shapeProps.strokeSize
value: 0
animated: true
animationDuration: 0.2
transition: ease-in-out
inBackground: true
- id: a2
element: {$-str: "#cl-${it.id}"}
method: hide
args:
animated: true
animationDuration: 0.2


- $-foreach: {source: "nodeItems", it: "it"}
id: {$-expr: "it.id"}
Expand All @@ -69,12 +114,7 @@ item:
y: {$-expr: "it.y"}
w: {$-expr: "it.w"}
h: {$-expr: "it.h"}
# behavior:
# events:
# - id: click
# event: clicked
# actions:
# - id: a1



- $-foreach: {source: "nodeLabels", it: "it"}
Expand All @@ -91,5 +131,18 @@ item:
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"}
w: {$-expr: "it.w"}
h: {$-expr: "it.h"}
2 changes: 2 additions & 0 deletions assets/templates/diagrams/src/item.sch
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct Item {
args: Map()
locked: true
textSlots: Map()
description: ""


traverse(callback) {
Expand Down Expand Up @@ -39,6 +40,7 @@ struct Item {
'id', this.id,
'childItems', childItems,
'name', this.name,
'description', this.description,
'shape', this.shape,
'area', Map('x', this.x, 'y', this.y, 'w', this.w, 'h', this.h, 'r', 0, 'sx', 1, 'sy', 1, 'px', 0.5, 'py', 0.5),
'shapeProps', this.shapeProps,
Expand Down
71 changes: 66 additions & 5 deletions assets/templates/diagrams/src/sankey.sch
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ struct Connection {
value: 0
srcNode: null
dstNode: null
item: null
}


Expand Down Expand Up @@ -334,7 +335,7 @@ func buildConnectorItems(levels, allConnections, allNodes) {
local connections = connectionsBySource.get(node.id)
if (connections) {
connections.sort((a, b) => {
a.dstNode.level * k1 + a.dstNode.sortOrder * k2 - (b.dstNode.level * k1 + b.dstNode.sortOrder * k2)
a.dstNode.offset + a.dstNode.y * height - (b.dstNode.offset + b.dstNode.y * height)
})
connections.forEach(c => {
local dstNode = level.nodesMap.get(c.dstId)
Expand All @@ -347,7 +348,7 @@ func buildConnectorItems(levels, allConnections, allNodes) {
})

cs.sort((a, b) => {
a.srcNode.offset - b.srcNode.offset
a.srcNode.offset + a.srcNode.y * height - (b.srcNode.offset + b.srcNode.y * height)
})

cs.forEach(c => {
Expand All @@ -368,7 +369,7 @@ struct PathPoint {
}

func buildSingleConnectorItem(connector, srcNode, dstNode) {
local item = Item(connector.id, 'Connection', 'path')
local item = Item(connector.id, `${srcNode.id} -> ${dstNode.id}`, 'path')
local connectorSize = srcNode.unitSize * connector.value

local xs = srcNode.position + srcNode.x * width + srcNode.width
Expand Down Expand Up @@ -417,12 +418,15 @@ func buildSingleConnectorItem(connector, srcNode, dstNode) {
item.shapeProps.set('fill', Fill.solid(connectorColor))
}
item.shapeProps.set('strokeSize', 0)
item.shapeProps.set('strokeColor', '#000000')
item.shapeProps.set('paths', List(Map(
'id', 'p-' + connector.id,
'closed', true,
'pos', 'relative',
'points', points
)))

connector.item = item
item
}

Expand All @@ -445,11 +449,50 @@ func buildLabel(id, text, font, fontSize, halign, valign) {
item
}

// "1000000.00", "1000000,00", "1,000,000.00", "1.000.000,00", "1 000 000.00", "1 000 000,00"

/*

en-US 100,000.2
de-DE 100.000,2
fi-FI 100 000,2
*/

local valueFormaters = Map(
'1000000.00', (value) => {
numberToLocaleString(value, 'en-US').replaceAll(',', '')
},
'1000000,00', (value) => {
numberToLocaleString(value, 'de-DE').replaceAll('.', '')
},
'1,000,000.00', (value) => {
numberToLocaleString(value, 'en-US')
},
'1.000.000,00', (value) => {
numberToLocaleString(value, 'de-DE')
},
'1 000 000.00', (value) => {
numberToLocaleString(value, 'fi-FI').replaceAll(',', '.')
},
'1 000 000,00', (value) => {
numberToLocaleString(value, 'fi-FI')
},
)

func formatValue(value) {
local valueText = value
if (valueFormaters.has(numberFormat)) {
valueText = valueFormaters.get(numberFormat)(value)
}
valuePrefix + valueText + valueSuffix
}

func buildNodeLabels(nodes) {
local labelItems = List()
nodes.forEach(node => {
local textSize = calculateTextSize(node.name, font, labelFontSize)
local valueTextSize = calculateTextSize('' + node.value, font, valueFontSize)
local valueText = formatValue(node.value)
local valueTextSize = calculateTextSize(valueText, font, valueFontSize)
local totalHeight = (textSize.h + valueTextSize.h)*1.8 + 8
local isLeft = node.dstNodes.size == 0
local halign = 'left'
Expand All @@ -470,7 +513,7 @@ func buildNodeLabels(nodes) {

labelItems.add(item)

local valueLabel = buildLabel('lv-' + node.id, '' + node.value, font, valueFontSize, halign, 'top')
local valueLabel = buildLabel('lv-' + node.id, valueText, font, valueFontSize, halign, 'top')
valueLabel.w = valueTextSize.w + 4
valueLabel.h = valueTextSize.h * 1.8 + 4
if (isLeft) {
Expand All @@ -487,6 +530,23 @@ 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) {
local node = null
if (itemId.startsWith('n-')) {
Expand All @@ -508,4 +568,5 @@ local levels = buildLevels(allNodes, allConnections)

nodeItems = buildNodeItems(levels)
connectorItems = buildConnectorItems(levels, allConnections, allNodes)
connectorLabels = buildConnectorLabels(allConnections)
nodeLabels = buildNodeLabels(allNodes)
3 changes: 3 additions & 0 deletions src/ui/components/editor/items/ItemTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@ export function regenerateTemplatedItem(rootItem, template, templateArgs, width,
if (key === 'textSlots' && item.args.templateForceText) {
shouldCopyField = true;
}
if (key === 'description' && item.args.tplForceDescription) {
shouldCopyField = true;
}
if (shouldCopyField) {
if (key === 'shapeProps' && regeneratedItem.shapeProps) {
if (!srcItem.shapeProps) {
Expand Down
6 changes: 3 additions & 3 deletions src/ui/scheme/SchemeContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3060,9 +3060,9 @@ class SchemeContainer {
item.area.w = modifiedArea.w;
item.area.h = modifiedArea.h;

if (!isSoft) {
this.regenerateTemplatedItem(templateRootItem, template, templateRootItem.args.templateArgs, templateRootItem.area.w, templateRootItem.area.h);
}
// if (!isSoft) {
this.regenerateTemplatedItem(templateRootItem, template, templateRootItem.args.templateArgs, templateRootItem.area.w, templateRootItem.area.h);
// }
EditorEventBus.item.templateArgsUpdated.specific.$emit(this.editorId, templateRootItem.id);
});
}
Expand Down
2 changes: 2 additions & 0 deletions src/ui/templater/nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,8 @@ const reservedFunctions = new Map(Object.entries({
Color : (r,g,b,a) => new Color(r,g,b,a),
decodeColor : (text) => {const c = parseColor(text); return new Color(c.r, c.g, c.b, c.a)},

numberToLocaleString: (value, locale) => parseFloat(value).toLocaleString(locale, {}),

Fill : Fill
}));

Expand Down
1 change: 1 addition & 0 deletions src/ui/typedef.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@
* that this item can be moved and its movement will be controlled by the template
* @property {String} tplConnector - 'on' or 'off'. If specified as 'off' - it tells that for this item it should not render connector starter in the edit box
* @property {String} tplRotation - 'on' or 'off'. Tells whether rotation of this item is supported
* @property {String} tplForceDescription - if set to true, then template will set the description of the item
*/


Expand Down

0 comments on commit 6145af3

Please sign in to comment.