From e4a7bb521544dfcaf07ea4af4e0eda738ce888d8 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 26 Dec 2022 23:56:23 -0800 Subject: [PATCH 01/11] save --- d2renderers/d2svg/d2svg.go | 28 ++++ d2renderers/d2svg/tooltip.svg | 5 + e2etests/stable_test.go | 7 + .../stable/tooltips/dagre/board.exp.json | 136 ++++++++++++++++++ .../stable/tooltips/dagre/sketch.exp.svg | 44 ++++++ .../stable/tooltips/elk/board.exp.json | 127 ++++++++++++++++ .../stable/tooltips/elk/sketch.exp.svg | 44 ++++++ 7 files changed, 391 insertions(+) create mode 100644 d2renderers/d2svg/tooltip.svg create mode 100644 e2etests/testdata/stable/tooltips/dagre/board.exp.json create mode 100644 e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg create mode 100644 e2etests/testdata/stable/tooltips/elk/board.exp.json create mode 100644 e2etests/testdata/stable/tooltips/elk/sketch.exp.svg diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 90e7bb191..8a0a28546 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -39,10 +39,15 @@ const ( DEFAULT_PADDING = 100 MIN_ARROWHEAD_STROKE_WIDTH = 2 threeDeeOffset = 15 + + tooltipIconLen = 32 ) var multipleOffset = geo.NewVector(10, -10) +//go:embed tooltip.svg +var tooltipIcon string + //go:embed style.css var styleCSS string @@ -835,6 +840,15 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske } } } + + if targetShape.Tooltip != "" { + fmt.Fprintf(writer, `%s`, + targetShape.Pos.X+targetShape.Width-tooltipIconLen/2, + targetShape.Pos.Y-tooltipIconLen/2, + tooltipIcon, + ) + } + fmt.Fprintf(writer, ``) return labelMask, nil } @@ -936,6 +950,20 @@ func embedFonts(buf *bytes.Buffer, fontFamily *d2fonts.FontFamily) { } } + triggers = []string{ + `tooltip-icon`, + } + + for _, t := range triggers { + if strings.Contains(content, t) { + buf.WriteString(` +.tooltip-icon { + box-shadow: 0px 0px 32px rgba(31, 36, 58, 0.1); +}`) + break + } + } + triggers = []string{ `class="text-bold"`, ``, diff --git a/d2renderers/d2svg/tooltip.svg b/d2renderers/d2svg/tooltip.svg new file mode 100644 index 000000000..63da62212 --- /dev/null +++ b/d2renderers/d2svg/tooltip.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go index 2df367f50..ed8a52027 100644 --- a/e2etests/stable_test.go +++ b/e2etests/stable_test.go @@ -1624,6 +1624,13 @@ mama bear: { shape: text; style.font-size: 28; style.italic: true } papa bear: { shape: text; style.font-size: 32; style.underline: true } mama bear -> bear papa bear -> bear +`, + }, + { + name: "tooltips", + 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 `, }, } diff --git a/e2etests/testdata/stable/tooltips/dagre/board.exp.json b/e2etests/testdata/stable/tooltips/dagre/board.exp.json new file mode 100644 index 000000000..79b8db45b --- /dev/null +++ b/e2etests/testdata/stable/tooltips/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": "Total abstinence is easier than perfect moderation", + "link": "", + "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": "", + "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/tooltips/dagre/sketch.exp.svg b/e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg new file mode 100644 index 000000000..50bde4261 --- /dev/null +++ b/e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg @@ -0,0 +1,44 @@ + +x + + + + +y + + + + + + + + \ No newline at end of file diff --git a/e2etests/testdata/stable/tooltips/elk/board.exp.json b/e2etests/testdata/stable/tooltips/elk/board.exp.json new file mode 100644 index 000000000..24850e39e --- /dev/null +++ b/e2etests/testdata/stable/tooltips/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": "Total abstinence is easier than perfect moderation", + "link": "", + "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": "", + "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/tooltips/elk/sketch.exp.svg b/e2etests/testdata/stable/tooltips/elk/sketch.exp.svg new file mode 100644 index 000000000..c08153e08 --- /dev/null +++ b/e2etests/testdata/stable/tooltips/elk/sketch.exp.svg @@ -0,0 +1,44 @@ + +x + + + + +y + + + + + + + + \ No newline at end of file From 51d51709b07830fe21eb6b6fde9073642c0386f4 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Tue, 27 Dec 2022 00:20:49 -0800 Subject: [PATCH 02/11] svg works --- d2renderers/d2svg/d2svg.go | 1 + e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg | 5 +++-- e2etests/testdata/stable/tooltips/elk/sketch.exp.svg | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 8a0a28546..0c6eecd35 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -847,6 +847,7 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske targetShape.Pos.Y-tooltipIconLen/2, tooltipIcon, ) + fmt.Fprintf(writer, `%s`, targetShape.Tooltip) } fmt.Fprintf(writer, ``) diff --git a/e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg b/e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg index 50bde4261..ef4885b88 100644 --- a/e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/tooltips/dagre/sketch.exp.svg @@ -23,12 +23,13 @@ width="314" height="552" viewBox="-100 -100 314 552">x - - - +x + + + + + + + + + + + -Total abstinence is easier than perfect moderationy - - - +Total abstinence is easier than perfect moderationy + + + + + + + + + + + Gee, I feel kind of LIGHT in the head now, knowing I can't make my satellite dish PAYMENTS! x - - - +x + + + + + + + + + + + -Total abstinence is easier than perfect moderationy - - - +Total abstinence is easier than perfect moderationy + + + + + + + + + + + Gee, I feel kind of LIGHT in the head now, knowing I can't make my satellite dish PAYMENTS! `, d2fonts.FontEncodings[d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_REGULAR)]) + } + + closingIndex := strings.LastIndex(svg, "") + svg = svg[:closingIndex] + appendix + svg[closingIndex:] + + return []byte(svg) +} + +func generateTooltipAppendix(diagram *d2target.Diagram, ruler *textmeasure.Ruler, svg string) (string, int, int) { + tl, br := diagram.BoundingBox() + + maxWidth, totalHeight := 0, 0 + + var tooltipLines []string + i := 1 + for _, s := range diagram.Shapes { + if s.Tooltip != "" { + line, w, h := generateTooltipLine(i, br.Y+(PAD_TOP*2)+totalHeight, s.Tooltip, ruler) + i++ + tooltipLines = append(tooltipLines, line) + maxWidth = go2.IntMax(maxWidth, w) + totalHeight += h + SPACER + } + } + + return fmt.Sprintf(`%s +`, tl.X, br.Y, (br.X - tl.X), strings.Join(tooltipLines, "\n")), maxWidth, totalHeight +} + +func generateTooltipLine(i, y int, text string, ruler *textmeasure.Ruler) (string, int, int) { + mtext := &d2target.MText{ + Text: text, + FontSize: FONT_SIZE, + IsBold: false, + IsItalic: false, + Language: "", + } + + dims := d2graph.GetTextDimensions(nil, ruler, mtext, nil) + + // TODO box-shadow: 0px 0px 32px rgba(31, 36, 58, 0.1); + line := fmt.Sprintf(``, + 0, y) + + line += fmt.Sprintf(`%d`, + 0, y+5, 16, i) + + line += fmt.Sprintf(`%s`, + 32, y, FONT_SIZE, d2svg.RenderText(text, 32, float64(dims.Height))) + + return line, dims.Width, dims.Height +} diff --git a/d2renderers/d2svg/appendix/appendix_test.go b/d2renderers/d2svg/appendix/appendix_test.go new file mode 100644 index 000000000..bb26a4372 --- /dev/null +++ b/d2renderers/d2svg/appendix/appendix_test.go @@ -0,0 +1,102 @@ +package appendix_test + +import ( + "context" + "encoding/xml" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + + "cdr.dev/slog" + + tassert "github.com/stretchr/testify/assert" + + "oss.terrastruct.com/util-go/assert" + "oss.terrastruct.com/util-go/diff" + + "oss.terrastruct.com/d2/d2layouts/d2dagrelayout" + "oss.terrastruct.com/d2/d2lib" + "oss.terrastruct.com/d2/d2renderers/d2svg" + "oss.terrastruct.com/d2/d2renderers/d2svg/appendix" + "oss.terrastruct.com/d2/lib/log" + "oss.terrastruct.com/d2/lib/textmeasure" +) + +func TestAppendix(t *testing.T) { + t.Parallel() + + tcs := []testCase{ + { + name: "basic", + 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 +`, + }, + } + runa(t, tcs) +} + +type testCase struct { + name string + script string + skip bool +} + +func runa(t *testing.T, tcs []testCase) { + for _, tc := range tcs { + tc := tc + t.Run(tc.name, func(t *testing.T) { + if tc.skip { + t.Skip() + } + t.Parallel() + + run(t, tc) + }) + } +} + +func run(t *testing.T, tc testCase) { + ctx := context.Background() + ctx = log.WithTB(ctx, t, nil) + ctx = log.Leveled(ctx, slog.LevelDebug) + + ruler, err := textmeasure.NewRuler() + if !tassert.Nil(t, err) { + return + } + + diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{ + Ruler: ruler, + ThemeID: 0, + Layout: d2dagrelayout.Layout, + }) + if !tassert.Nil(t, err) { + return + } + + dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestAppendix/")) + pathGotSVG := filepath.Join(dataPath, "sketch.got.svg") + + svgBytes, err := d2svg.Render(diagram, &d2svg.RenderOpts{ + Pad: d2svg.DEFAULT_PADDING, + }) + assert.Success(t, err) + svgBytes = appendix.AppendTooltips(diagram, ruler, svgBytes) + + err = os.MkdirAll(dataPath, 0755) + assert.Success(t, err) + err = ioutil.WriteFile(pathGotSVG, svgBytes, 0600) + assert.Success(t, err) + defer os.Remove(pathGotSVG) + + var xmlParsed interface{} + err = xml.Unmarshal(svgBytes, &xmlParsed) + assert.Success(t, err) + + err = diff.Testdata(filepath.Join(dataPath, "sketch"), ".svg", svgBytes) + assert.Success(t, err) +} diff --git a/d2renderers/d2svg/appendix/testdata/basic/sketch.exp.svg b/d2renderers/d2svg/appendix/testdata/basic/sketch.exp.svg new file mode 100644 index 000000000..f88e30ee3 --- /dev/null +++ b/d2renderers/d2svg/appendix/testdata/basic/sketch.exp.svg @@ -0,0 +1,68 @@ + +x + + + + + + + + + + + + +Total abstinence is easier than perfect moderationy + + + + + + + + + + + + +Gee, I feel kind of LIGHT in the head now, +knowing I can't make my satellite dish PAYMENTS! + + +1Total abstinence is easier than perfect moderation +2Gee, 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/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 0c6eecd35..2240669a5 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -442,7 +442,7 @@ func drawConnection(writer io.Writer, labelMaskID string, connection d2target.Co fontClass, x, y, textStyle, - renderText(connection.Label, x, float64(connection.LabelHeight)), + RenderText(connection.Label, x, float64(connection.LabelHeight)), ) } @@ -478,7 +478,7 @@ func renderArrowheadLabel(connection d2target.Connection, text string, position, return fmt.Sprintf(`%s`, x, y, textStyle, - renderText(text, x, height), + RenderText(text, x, height), ) } @@ -833,7 +833,7 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske fontClass, x, y, textStyle, - renderText(targetShape.Label, x, float64(targetShape.LabelHeight)), + RenderText(targetShape.Label, x, float64(targetShape.LabelHeight)), ) if targetShape.Blend { labelMask = makeLabelMask(labelTL, targetShape.LabelWidth, targetShape.LabelHeight-d2graph.INNER_LABEL_PADDING) @@ -854,7 +854,7 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske return labelMask, nil } -func renderText(text string, x, height float64) string { +func RenderText(text string, x, height float64) string { if !strings.Contains(text, "\n") { return svg.EscapeText(text) } diff --git a/lib/svg/appendix.go b/lib/svg/appendix.go deleted file mode 100644 index e5aab7cde..000000000 --- a/lib/svg/appendix.go +++ /dev/null @@ -1,3 +0,0 @@ -// appendix.go writes appendices/footnotes to SVG - -package svg diff --git a/main.go b/main.go index 45d154f1f..e87b5149e 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ import ( "oss.terrastruct.com/d2/d2plugin" "oss.terrastruct.com/d2/d2renderers/d2fonts" "oss.terrastruct.com/d2/d2renderers/d2svg" + "oss.terrastruct.com/d2/d2renderers/d2svg/appendix" "oss.terrastruct.com/d2/d2themes" "oss.terrastruct.com/d2/d2themes/d2themescatalog" "oss.terrastruct.com/d2/lib/imgbundler" @@ -247,7 +248,8 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketc out := svg if filepath.Ext(outputPath) == ".png" { - svg := svg + svg := appendix.AppendTooltips(diagram, ruler, svg) + if !bundle { var bundleErr2 error svg, bundleErr2 = imgbundler.BundleRemote(ctx, ms, svg) From 65df91b70ea0580f27b795e91f771414adf00842 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 28 Dec 2022 11:39:20 -0800 Subject: [PATCH 06/11] save --- d2renderers/d2svg/appendix/appendix.go | 36 +++++--- d2renderers/d2svg/appendix/appendix_test.go | 44 +++++++++- .../diagram_wider_than_tooltip/sketch.exp.svg | 87 +++++++++++++++++++ .../sketch.exp.svg | 6 +- 4 files changed, 155 insertions(+), 18 deletions(-) create mode 100644 d2renderers/d2svg/appendix/testdata/diagram_wider_than_tooltip/sketch.exp.svg rename d2renderers/d2svg/appendix/testdata/{basic => tooltip_wider_than_diagram}/sketch.exp.svg (99%) diff --git a/d2renderers/d2svg/appendix/appendix.go b/d2renderers/d2svg/appendix/appendix.go index 368bbefc0..28c5d0f22 100644 --- a/d2renderers/d2svg/appendix/appendix.go +++ b/d2renderers/d2svg/appendix/appendix.go @@ -19,10 +19,11 @@ import ( ) const ( - PAD_TOP = 50 - PAD_SIDES = 40 - FONT_SIZE = 16 - SPACER = 20 + PAD_TOP = 50 + PAD_SIDES = 40 + FONT_SIZE = 16 + SPACER = 20 + ICON_RADIUS = 16 ) var viewboxRegex = regexp.MustCompile(`viewBox=\"([0-9\- ]+)\"`) @@ -47,15 +48,14 @@ func AppendTooltips(diagram *d2target.Diagram, ruler *textmeasure.Ruler, in []by tl, br := diagram.BoundingBox() seperator := fmt.Sprintf(``, - tl.X-PAD_SIDES, br.Y+PAD_TOP, go2.IntMax(w+PAD_SIDES, br.Y)+PAD_SIDES, br.Y+PAD_TOP) + tl.X-PAD_SIDES, br.Y+PAD_TOP, go2.IntMax(w, br.X)+PAD_SIDES, br.Y+PAD_TOP) appendix = seperator + appendix w -= viewboxPadLeft w += PAD_SIDES * 2 if viewboxWidth < w { - viewboxWidth = w + PAD_SIDES + viewboxWidth = w } - viewboxWidth += PAD_SIDES viewboxHeight += h + PAD_TOP @@ -81,6 +81,17 @@ func AppendTooltips(diagram *d2target.Diagram, ruler *textmeasure.Ruler, in []by } ]]>`, d2fonts.FontEncodings[d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_REGULAR)]) } + if !strings.Contains(svg, `font-family: "font-bold"`) { + appendix += fmt.Sprintf(``, d2fonts.FontEncodings[d2fonts.SourceSansPro.Font(0, d2fonts.FONT_STYLE_BOLD)]) + } closingIndex := strings.LastIndex(svg, "") svg = svg[:closingIndex] + appendix + svg[closingIndex:] @@ -113,22 +124,19 @@ func generateTooltipLine(i, y int, text string, ruler *textmeasure.Ruler) (strin mtext := &d2target.MText{ Text: text, FontSize: FONT_SIZE, - IsBold: false, - IsItalic: false, - Language: "", } dims := d2graph.GetTextDimensions(nil, ruler, mtext, nil) // TODO box-shadow: 0px 0px 32px rgba(31, 36, 58, 0.1); line := fmt.Sprintf(``, - 0, y) + ICON_RADIUS, y) line += fmt.Sprintf(`%d`, - 0, y+5, 16, i) + ICON_RADIUS, y+5, FONT_SIZE, i) line += fmt.Sprintf(`%s`, - 32, y, FONT_SIZE, d2svg.RenderText(text, 32, float64(dims.Height))) + ICON_RADIUS*3, y, FONT_SIZE, d2svg.RenderText(text, ICON_RADIUS*3, float64(dims.Height))) - return line, dims.Width, dims.Height + return line, dims.Width + ICON_RADIUS*3, dims.Height } diff --git a/d2renderers/d2svg/appendix/appendix_test.go b/d2renderers/d2svg/appendix/appendix_test.go index bb26a4372..a589498cf 100644 --- a/d2renderers/d2svg/appendix/appendix_test.go +++ b/d2renderers/d2svg/appendix/appendix_test.go @@ -29,10 +29,52 @@ func TestAppendix(t *testing.T) { tcs := []testCase{ { - name: "basic", + name: "tooltip_wider_than_diagram", 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: "diagram_wider_than_tooltip", + script: `shape: sequence_diagram + +customer +issuer +store: { tooltip: Like starbucks or something } +acquirer: { tooltip: I'm not sure what this is } +network +customer bank +store bank + +customer: {shape: person} +customer bank: { + shape: image + icon: https://cdn-icons-png.flaticon.com/512/858/858170.png +} +store bank: { + shape: image + icon: https://cdn-icons-png.flaticon.com/512/858/858170.png +} + +initial transaction: { + customer -> store: 1 banana please + store -> customer: '$10 dollars' +} +customer.internal -> customer.internal: "thinking: wow, inflation" +customer.internal -> customer bank: checks bank account +customer bank -> customer.internal: 'Savings: $11' +customer."An error in judgement is about to occur" +customer -> store: I can do that, here's my card +payment processor behind the scenes: { + store -> acquirer: Run this card + acquirer -> network: Process to card issuer + simplified: { + network -> issuer: Process this payment + issuer -> customer bank: '$10 debit' + acquirer -> store bank: '$10 credit' + } +} `, }, } diff --git a/d2renderers/d2svg/appendix/testdata/diagram_wider_than_tooltip/sketch.exp.svg b/d2renderers/d2svg/appendix/testdata/diagram_wider_than_tooltip/sketch.exp.svg new file mode 100644 index 000000000..66530c536 --- /dev/null +++ b/d2renderers/d2svg/appendix/testdata/diagram_wider_than_tooltip/sketch.exp.svg @@ -0,0 +1,87 @@ + +customerissuerstore + + + + + + + + + + + + +Like starbucks or somethingacquirer + + + + + + + + + + + + +I'm not sure what this isnetworkcustomer bankstore bankinitial transactionpayment processor behind the scenessimplified 1 banana please$10 dollarsthinking: wow, inflationchecks bank accountSavings: $11I can do that, here's my cardRun this cardProcess to card issuerProcess this payment$10 debit$10 creditAn error in judgement is about to occur + + + + + + + + + + + + + + + +1Like starbucks or something +2I'm not sure what this is + \ No newline at end of file diff --git a/d2renderers/d2svg/appendix/testdata/basic/sketch.exp.svg b/d2renderers/d2svg/appendix/testdata/tooltip_wider_than_diagram/sketch.exp.svg similarity index 99% rename from d2renderers/d2svg/appendix/testdata/basic/sketch.exp.svg rename to d2renderers/d2svg/appendix/testdata/tooltip_wider_than_diagram/sketch.exp.svg index f88e30ee3..126c22303 100644 --- a/d2renderers/d2svg/appendix/testdata/basic/sketch.exp.svg +++ b/d2renderers/d2svg/appendix/testdata/tooltip_wider_than_diagram/sketch.exp.svg @@ -2,7 +2,7 @@ 1Total abstinence is easier than perfect moderation +2Gee, I feel kind of LIGHT in the head now,knowing I can't make my satellite dish PAYMENTS! customerissuerstore - - - - - - - - - - - - -Like starbucks or somethingacquirer - - - - - - - - - - - - -I'm not sure what this isnetworkcustomer bankstore bankinitial transactionpayment processor behind the scenessimplified 1 banana please$10 dollarsthinking: wow, inflationchecks bank accountSavings: $11I can do that, here's my cardRun this cardProcess to card issuerProcess this payment$10 debit$10 creditAn error in judgement is about to occur +customerissuerstore1Like starbucks or somethingacquirer2I'm not sure what this isnetworkcustomer bankstore bankinitial transactionpayment processor behind the scenessimplified 1 banana please$10 dollarsthinking: wow, inflationchecks bank accountSavings: $11I can do that, here's my cardRun this cardProcess to card issuerProcess this payment$10 debit$10 creditAn error in judgement is about to occur @@ -74,8 +48,8 @@ width="1959" height="2297" viewBox="-175 -47 1959 2297">1Like starbucks or something -2I'm not sure what this is +}]]>1Like starbucks or something +2I'm not sure what this is x - - - - - - - - - - - - -Total abstinence is easier than perfect moderationy - - - - - - - - - - - - -Gee, I feel kind of LIGHT in the head now, +x1Total abstinence is easier than perfect moderationy2Gee, I feel kind of LIGHT in the head now, knowing I can't make my satellite dish PAYMENTS! @@ -55,8 +29,8 @@ knowing I can't make my satellite dish PAYMENTS!1Total abstinence is easier than perfect moderation -2Gee, I feel kind of LIGHT in the head now,knowing I can't make my satellite dish PAYMENTS! +}]]>1Total abstinence is easier than perfect moderation +2Gee, I feel kind of LIGHT in the head now,knowing I can't make my satellite dish PAYMENTS! x - - - - - +x + + + + + - - + + -Total abstinence is easier than perfect moderationy - - - - - +Total abstinence is easier than perfect moderationy + + + + + - - + + diff --git a/e2etests/testdata/stable/tooltips/elk/sketch.exp.svg b/e2etests/testdata/stable/tooltips/elk/sketch.exp.svg index 5990f5c5c..8c584ce14 100644 --- a/e2etests/testdata/stable/tooltips/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/tooltips/elk/sketch.exp.svg @@ -18,29 +18,29 @@ width="314" height="552" viewBox="-88 -88 314 552">x - - - - - +x + + + + + - - + + -Total abstinence is easier than perfect moderationy - - - - - +Total abstinence is easier than perfect moderationy + + + + + - - + + From 0935ce90af8a176c426f19947e1bdad1b975d7a4 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 28 Dec 2022 14:33:09 -0800 Subject: [PATCH 09/11] changelog --- ci/release/changelogs/next.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index d3ade4923..50ccdb876 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -1,5 +1,7 @@ #### Features ๐Ÿš€ +- Tooltips on shapes. See [https://d2lang.com/tour/tooltips](https://d2lang.com/tour/tooltips). [#545](https://github.com/terrastruct/d2/pull/545) + #### Improvements ๐Ÿงน #### Bugfixes โ›‘๏ธ From 6d4e0cd64cbf417d40fd6bc6e4b4657aca0adbc5 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 28 Dec 2022 14:54:04 -0800 Subject: [PATCH 10/11] add ascii diagram --- d2renderers/d2svg/appendix/appendix.go | 29 ++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/d2renderers/d2svg/appendix/appendix.go b/d2renderers/d2svg/appendix/appendix.go index d3eca6479..8dbc399cb 100644 --- a/d2renderers/d2svg/appendix/appendix.go +++ b/d2renderers/d2svg/appendix/appendix.go @@ -18,11 +18,32 @@ import ( "oss.terrastruct.com/util-go/go2" ) +// โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +// โ”‚ โ”‚ +// โ”‚ DIAGRAM โ”‚ +// โ”‚ โ”‚ +// PAD_ โ”‚ โ”‚ +// SIDES โ”‚ โ”‚ +// โ”‚ โ”‚ โ”‚ +// โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +// โ–ผ โ—„โ”€โ”€โ”€โ”€โ”€โ”€ PAD_TOP +// +// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +// +// +// 1. asdfasdf +// +// โ—„โ”€โ”€โ”€โ”€ SPACER +// 2. qwerqwer +// +// + const ( - PAD_TOP = 50 - PAD_SIDES = 40 + PAD_TOP = 50 + PAD_SIDES = 40 + SPACER = 20 + FONT_SIZE = 16 - SPACER = 20 ICON_RADIUS = 16 ) @@ -63,9 +84,9 @@ func AppendTooltips(diagram *d2target.Diagram, ruler *textmeasure.Ruler, in []by widthMatch := widthRegex.FindStringSubmatch(svg) heightMatch := heightRegex.FindStringSubmatch(svg) - newWidth := fmt.Sprintf(`width="%s"`, strconv.Itoa(viewboxWidth)) newHeight := fmt.Sprintf(`height="%s"`, strconv.Itoa(viewboxHeight)) + svg = strings.Replace(svg, viewboxMatch[0], newViewbox, 1) svg = strings.Replace(svg, widthMatch[0], newWidth, 1) svg = strings.Replace(svg, heightMatch[0], newHeight, 1) From 36ef848a82744b849e21dc0ba25067e1a1817649 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 28 Dec 2022 14:58:27 -0800 Subject: [PATCH 11/11] len -> radius --- d2renderers/d2svg/d2svg.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index bf13bfd72..d6a799274 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -40,7 +40,7 @@ const ( MIN_ARROWHEAD_STROKE_WIDTH = 2 threeDeeOffset = 15 - tooltipIconLen = 32 + tooltipIconRadius = 16 ) var multipleOffset = geo.NewVector(10, -10) @@ -843,8 +843,8 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske if targetShape.Tooltip != "" { fmt.Fprintf(writer, `%s`, - targetShape.Pos.X+targetShape.Width-tooltipIconLen/2, - targetShape.Pos.Y-tooltipIconLen/2, + targetShape.Pos.X+targetShape.Width-tooltipIconRadius, + targetShape.Pos.Y-tooltipIconRadius, TooltipIcon, ) fmt.Fprintf(writer, `%s`, targetShape.Tooltip)