diff --git a/d2renderers/d2svg/appendix/appendix.go b/d2renderers/d2svg/appendix/appendix.go index fa44931f7..3aeb5ab94 100644 --- a/d2renderers/d2svg/appendix/appendix.go +++ b/d2renderers/d2svg/appendix/appendix.go @@ -169,7 +169,7 @@ func generateTooltipLine(i, y int, text string, ruler *textmeasure.Ruler) (strin dims := d2graph.GetTextDimensions(nil, ruler, mtext, nil) - line := fmt.Sprintf(`%s`, + line := fmt.Sprintf(`%s`, 0, y, generateNumberedIcon(i, 0, 0)) line += fmt.Sprintf(`%s`, diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 63c0fcc07..f25d1866e 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -40,7 +40,7 @@ const ( MIN_ARROWHEAD_STROKE_WIDTH = 2 threeDeeOffset = 15 - tooltipIconRadius = 16 + appendixIconRadius = 16 ) var multipleOffset = geo.NewVector(10, -10) @@ -48,6 +48,9 @@ var multipleOffset = geo.NewVector(10, -10) //go:embed tooltip.svg var TooltipIcon string +//go:embed link.svg +var LinkIcon string + //go:embed style.css var styleCSS string @@ -588,6 +591,11 @@ func render3dRect(targetShape d2target.Shape) string { } func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2sketch.Runner) (labelMask string, err error) { + closingTag := "" + if targetShape.Link != "" { + fmt.Fprintf(writer, ``, targetShape.Link) + closingTag += "" + } fmt.Fprintf(writer, ``, svg.EscapeText(targetShape.ID)) tl := geo.NewPoint(float64(targetShape.Pos.X), float64(targetShape.Pos.Y)) width := float64(targetShape.Width) @@ -632,7 +640,8 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske } else { drawClass(writer, targetShape) } - fmt.Fprintf(writer, ``) + fmt.Fprintf(writer, ``) + fmt.Fprintf(writer, closingTag) return labelMask, nil case d2target.ShapeSQLTable: if sketchRunner != nil { @@ -644,7 +653,8 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske } else { drawTable(writer, targetShape) } - fmt.Fprintf(writer, ``) + fmt.Fprintf(writer, ``) + fmt.Fprintf(writer, closingTag) return labelMask, nil case d2target.ShapeOval: if targetShape.Multiple { @@ -707,6 +717,7 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske } } + // Closes the class=shape fmt.Fprintf(writer, ``) if targetShape.Icon != nil && targetShape.Type != d2target.ShapeImage { @@ -841,16 +852,26 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske } } + rightPadForTooltip := 0 if targetShape.Tooltip != "" { - fmt.Fprintf(writer, `%s`, - targetShape.Pos.X+targetShape.Width-tooltipIconRadius, - targetShape.Pos.Y-tooltipIconRadius, + rightPadForTooltip = 2 * appendixIconRadius + fmt.Fprintf(writer, `%s`, + targetShape.Pos.X+targetShape.Width-appendixIconRadius, + targetShape.Pos.Y-appendixIconRadius, TooltipIcon, ) fmt.Fprintf(writer, `%s`, targetShape.Tooltip) } - fmt.Fprintf(writer, ``) + if targetShape.Link != "" { + fmt.Fprintf(writer, `%s`, + targetShape.Pos.X+targetShape.Width-appendixIconRadius-rightPadForTooltip, + targetShape.Pos.Y-appendixIconRadius, + LinkIcon, + ) + } + + fmt.Fprintf(writer, closingTag) return labelMask, nil } @@ -952,13 +973,13 @@ func embedFonts(buf *bytes.Buffer, fontFamily *d2fonts.FontFamily) { } triggers = []string{ - `tooltip-icon`, + `appendix-icon`, } for _, t := range triggers { if strings.Contains(content, t) { buf.WriteString(` -.tooltip-icon { +.appendix-icon { filter: drop-shadow(0px 0px 32px rgba(31, 36, 58, 0.1)); }`) break diff --git a/d2renderers/d2svg/link.svg b/d2renderers/d2svg/link.svg new file mode 100644 index 000000000..f59e85f49 --- /dev/null +++ b/d2renderers/d2svg/link.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go index ed8a52027..e43d84921 100644 --- a/e2etests/stable_test.go +++ b/e2etests/stable_test.go @@ -1631,6 +1631,13 @@ papa bear -> bear script: `x: { tooltip: Total abstinence is easier than perfect moderation } y: { tooltip: Gee, I feel kind of LIGHT in the head now,\nknowing I can't make my satellite dish PAYMENTS! } x -> y +`, + }, + { + name: "links", + script: `x: { link: https://d2lang.com } + y: { link: https://terrastruct.com; tooltip: Gee, I feel kind of LIGHT in the head now,\nknowing I can't make my satellite dish PAYMENTS! } +x -> y `, }, } diff --git a/e2etests/testdata/stable/links/dagre/board.exp.json b/e2etests/testdata/stable/links/dagre/board.exp.json new file mode 100644 index 000000000..ad73036b4 --- /dev/null +++ b/e2etests/testdata/stable/links/dagre/board.exp.json @@ -0,0 +1,136 @@ +{ + "name": "", + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "x", + "type": "", + "pos": { + "x": 1, + "y": 0 + }, + "width": 113, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "https://d2lang.com", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "x", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 13, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "y", + "type": "", + "pos": { + "x": 0, + "y": 226 + }, + "width": 114, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "Gee, I feel kind of LIGHT in the head now,\nknowing I can't make my satellite dish PAYMENTS!", + "link": "https://terrastruct.com", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "y", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + } + ], + "connections": [ + { + "id": "(x -> y)[0]", + "src": "x", + "srcArrow": "none", + "srcLabel": "", + "dst": "y", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 57, + "y": 126 + }, + { + "x": 57, + "y": 166 + }, + { + "x": 57, + "y": 186 + }, + { + "x": 57, + "y": 226 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ] +} diff --git a/e2etests/testdata/stable/links/dagre/sketch.exp.svg b/e2etests/testdata/stable/links/dagre/sketch.exp.svg new file mode 100644 index 000000000..abd378bdc --- /dev/null +++ b/e2etests/testdata/stable/links/dagre/sketch.exp.svg @@ -0,0 +1,72 @@ + +x + + + + + + + + + + + +y + + + + + + + + + + + + +Gee, I feel kind of LIGHT in the head now, +knowing I can't make my satellite dish PAYMENTS! + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/e2etests/testdata/stable/links/elk/board.exp.json b/e2etests/testdata/stable/links/elk/board.exp.json new file mode 100644 index 000000000..69c78025b --- /dev/null +++ b/e2etests/testdata/stable/links/elk/board.exp.json @@ -0,0 +1,127 @@ +{ + "name": "", + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "x", + "type": "", + "pos": { + "x": 12, + "y": 12 + }, + "width": 113, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "https://d2lang.com", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "x", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 13, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "y", + "type": "", + "pos": { + "x": 12, + "y": 238 + }, + "width": 114, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#F7F8FE", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "Gee, I feel kind of LIGHT in the head now,\nknowing I can't make my satellite dish PAYMENTS!", + "link": "https://terrastruct.com", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "y", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + } + ], + "connections": [ + { + "id": "(x -> y)[0]", + "src": "x", + "srcArrow": "none", + "srcLabel": "", + "dst": "y", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 69, + "y": 138 + }, + { + "x": 69, + "y": 238 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ] +} diff --git a/e2etests/testdata/stable/links/elk/sketch.exp.svg b/e2etests/testdata/stable/links/elk/sketch.exp.svg new file mode 100644 index 000000000..083055954 --- /dev/null +++ b/e2etests/testdata/stable/links/elk/sketch.exp.svg @@ -0,0 +1,72 @@ + +x + + + + + + + + + + + +y + + + + + + + + + + + + +Gee, I feel kind of LIGHT in the head now, +knowing I can't make my satellite dish PAYMENTS! + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg b/e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg index a18a668c0..083bb23f6 100644 --- a/e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg @@ -18,7 +18,7 @@ width="314" height="552" viewBox="-100 -100 314 552">x +x @@ -31,7 +31,7 @@ width="314" height="552" viewBox="-100 -100 314 552"> -Total abstinence is easier than perfect moderationy +Total abstinence is easier than perfect moderationy @@ -49,7 +49,7 @@ knowing I can't make my satellite dish PAYMENTS! x +x @@ -31,7 +31,7 @@ width="314" height="552" viewBox="-88 -88 314 552"> -Total abstinence is easier than perfect moderationy +Total abstinence is easier than perfect moderationy @@ -49,7 +49,7 @@ knowing I can't make my satellite dish PAYMENTS!