Merge pull request #1466 from gavin-ts/shape-specific-link-position
position tooltip/link icons on shape
This commit is contained in:
commit
6a1bfaac4f
8 changed files with 10064 additions and 22 deletions
|
|
@ -12,6 +12,7 @@
|
|||
- Improved rendering and text measurement for code shapes [#1425](https://github.com/terrastruct/d2/pull/1425)
|
||||
- The autoformatter moves board declarations to the bottom of its scope [#1424](https://github.com/terrastruct/d2/pull/1424)
|
||||
- All font styles in sketch mode use a consistent font-family [#1463](https://github.com/terrastruct/d2/pull/1463)
|
||||
- Tooltip and link icons are now positioned on shape border [#1466](https://github.com/terrastruct/d2/pull/1466)
|
||||
|
||||
#### Bugfixes ⛑️
|
||||
|
||||
|
|
|
|||
|
|
@ -936,7 +936,7 @@ func drawShape(writer io.Writer, diagramHash string, targetShape d2target.Shape,
|
|||
} else {
|
||||
drawClass(writer, diagramHash, targetShape)
|
||||
}
|
||||
addAppendixItems(writer, targetShape)
|
||||
addAppendixItems(writer, targetShape, s)
|
||||
fmt.Fprint(writer, `</g>`)
|
||||
fmt.Fprint(writer, closingTag)
|
||||
return labelMask, nil
|
||||
|
|
@ -950,7 +950,7 @@ func drawShape(writer io.Writer, diagramHash string, targetShape d2target.Shape,
|
|||
} else {
|
||||
drawTable(writer, diagramHash, targetShape)
|
||||
}
|
||||
addAppendixItems(writer, targetShape)
|
||||
addAppendixItems(writer, targetShape, s)
|
||||
fmt.Fprint(writer, `</g>`)
|
||||
fmt.Fprint(writer, closingTag)
|
||||
return labelMask, nil
|
||||
|
|
@ -1341,28 +1341,70 @@ func drawShape(writer io.Writer, diagramHash string, targetShape d2target.Shape,
|
|||
}
|
||||
}
|
||||
|
||||
addAppendixItems(writer, targetShape)
|
||||
addAppendixItems(writer, targetShape, s)
|
||||
|
||||
fmt.Fprint(writer, closingTag)
|
||||
return labelMask, nil
|
||||
}
|
||||
|
||||
func addAppendixItems(writer io.Writer, shape d2target.Shape) {
|
||||
rightPadForTooltip := 0
|
||||
if shape.Tooltip != "" {
|
||||
rightPadForTooltip = 2 * appendixIconRadius
|
||||
fmt.Fprintf(writer, `<g transform="translate(%d %d)" class="appendix-icon">%s</g>`,
|
||||
shape.Pos.X+shape.Width-appendixIconRadius,
|
||||
shape.Pos.Y-appendixIconRadius,
|
||||
TooltipIcon,
|
||||
func addAppendixItems(writer io.Writer, targetShape d2target.Shape, s shape.Shape) {
|
||||
var p1, p2 *geo.Point
|
||||
if targetShape.Tooltip != "" || targetShape.Link != "" {
|
||||
bothIcons := targetShape.Tooltip != "" && targetShape.Link != ""
|
||||
corner := geo.NewPoint(float64(targetShape.Pos.X+targetShape.Width), float64(targetShape.Pos.Y))
|
||||
center := geo.NewPoint(
|
||||
float64(targetShape.Pos.X)+float64(targetShape.Width)/2.,
|
||||
float64(targetShape.Pos.Y)+float64(targetShape.Height)/2.,
|
||||
)
|
||||
fmt.Fprintf(writer, `<title>%s</title>`, svg.EscapeText(shape.Tooltip))
|
||||
offset := geo.Vector{-2 * appendixIconRadius, 0}
|
||||
var leftOnShape bool
|
||||
switch s.GetType() {
|
||||
case shape.STEP_TYPE, shape.HEXAGON_TYPE, shape.QUEUE_TYPE, shape.PAGE_TYPE:
|
||||
// trace straight left for these
|
||||
center.Y = float64(targetShape.Pos.Y)
|
||||
case shape.PACKAGE_TYPE:
|
||||
// trace straight down
|
||||
center.X = float64(targetShape.Pos.X + targetShape.Width)
|
||||
case shape.CIRCLE_TYPE, shape.OVAL_TYPE, shape.DIAMOND_TYPE,
|
||||
shape.PERSON_TYPE, shape.CLOUD_TYPE, shape.CYLINDER_TYPE:
|
||||
if bothIcons {
|
||||
leftOnShape = true
|
||||
corner = corner.AddVector(offset)
|
||||
}
|
||||
}
|
||||
v1 := center.VectorTo(corner)
|
||||
p1 = shape.TraceToShapeBorder(s, corner, corner.AddVector(v1))
|
||||
if bothIcons {
|
||||
if leftOnShape {
|
||||
// these shapes should have p1 on shape border
|
||||
p2 = p1.AddVector(offset.Reverse())
|
||||
p1, p2 = p2, p1
|
||||
} else {
|
||||
p2 = p1.AddVector(offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if shape.Link != "" {
|
||||
if targetShape.Tooltip != "" {
|
||||
x := int(math.Ceil(p1.X))
|
||||
y := int(math.Ceil(p1.Y))
|
||||
|
||||
fmt.Fprintf(writer, `<g transform="translate(%d %d)" class="appendix-icon">%s</g>`,
|
||||
shape.Pos.X+shape.Width-appendixIconRadius-rightPadForTooltip,
|
||||
shape.Pos.Y-appendixIconRadius,
|
||||
x-appendixIconRadius,
|
||||
y-appendixIconRadius,
|
||||
TooltipIcon,
|
||||
)
|
||||
fmt.Fprintf(writer, `<title>%s</title>`, svg.EscapeText(targetShape.Tooltip))
|
||||
}
|
||||
if targetShape.Link != "" {
|
||||
if p2 == nil {
|
||||
p2 = p1
|
||||
}
|
||||
x := int(math.Ceil(p2.X))
|
||||
y := int(math.Ceil(p2.Y))
|
||||
fmt.Fprintf(writer, `<g transform="translate(%d %d)" class="appendix-icon">%s</g>`,
|
||||
x-appendixIconRadius,
|
||||
y-appendixIconRadius,
|
||||
LinkIcon,
|
||||
)
|
||||
}
|
||||
|
|
@ -1842,17 +1884,17 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
|
|||
case "paper":
|
||||
patternDefs += paper
|
||||
}
|
||||
fmt.Fprint(upperBuf, fmt.Sprintf(`
|
||||
fmt.Fprintf(upperBuf, `
|
||||
.%s-overlay {
|
||||
fill: url(#%s);
|
||||
mix-blend-mode: multiply;
|
||||
}`, pattern, pattern))
|
||||
}`, pattern, pattern)
|
||||
}
|
||||
}
|
||||
if patternDefs != "" {
|
||||
fmt.Fprint(upperBuf, `]]></style>`)
|
||||
fmt.Fprint(upperBuf, "<defs>")
|
||||
fmt.Fprintf(upperBuf, patternDefs)
|
||||
fmt.Fprint(upperBuf, patternDefs)
|
||||
fmt.Fprint(upperBuf, "</defs>")
|
||||
}
|
||||
|
||||
|
|
@ -2101,11 +2143,11 @@ func singleThemeRulesets(diagramHash string, themeID int64) (rulesets string, er
|
|||
out += fmt.Sprintf(".sketch-overlay-%s{fill:url(#streaks-%s);mix-blend-mode:%s}", color.N7, lc, blendMode(lc))
|
||||
|
||||
if theme.IsDark() {
|
||||
out += fmt.Sprintf(".light-code{display: none}")
|
||||
out += fmt.Sprintf(".dark-code{display: block}")
|
||||
out += ".light-code{display: none}"
|
||||
out += ".dark-code{display: block}"
|
||||
} else {
|
||||
out += fmt.Sprintf(".light-code{display: block}")
|
||||
out += fmt.Sprintf(".dark-code{display: none}")
|
||||
out += ".light-code{display: block}"
|
||||
out += ".dark-code{display: none}"
|
||||
}
|
||||
|
||||
return out, nil
|
||||
|
|
|
|||
|
|
@ -2781,6 +2781,7 @@ scenarios: {
|
|||
loadFromFile(t, "outside_bottom_labels"),
|
||||
loadFromFile(t, "label_positions"),
|
||||
loadFromFile(t, "icon_positions"),
|
||||
loadFromFile(t, "all_shapes_link"),
|
||||
}
|
||||
|
||||
runa(t, tcs)
|
||||
|
|
|
|||
141
e2etests/testdata/files/all_shapes_link.d2
vendored
Normal file
141
e2etests/testdata/files/all_shapes_link.d2
vendored
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
linked: {
|
||||
rectangle.shape: rectangle
|
||||
square.shape: square
|
||||
page.shape: page
|
||||
parallelogram.shape: parallelogram
|
||||
document.shape: document
|
||||
cylinder.shape: cylinder
|
||||
queue.shape: queue
|
||||
package.shape: package
|
||||
step.shape: step
|
||||
callout.shape: callout
|
||||
stored_data.shape: stored_data
|
||||
person.shape: person
|
||||
diamond.shape: diamond
|
||||
oval.shape: oval
|
||||
circle.shape: circle
|
||||
hexagon.shape: hexagon
|
||||
cloud.shape: cloud
|
||||
|
||||
rectangle -> square -> page
|
||||
parallelogram -> document -> cylinder
|
||||
queue -> package -> step
|
||||
callout -> stored_data -> person
|
||||
diamond -> oval -> circle
|
||||
hexagon -> cloud
|
||||
|
||||
rectangle.class: linked
|
||||
square.class: linked
|
||||
page.class: linked
|
||||
parallelogram.class: linked
|
||||
document.class: linked
|
||||
cylinder.class: linked
|
||||
queue.class: linked
|
||||
package.class: linked
|
||||
step.class: linked
|
||||
callout.class: linked
|
||||
stored_data.class: linked
|
||||
person.class: linked
|
||||
diamond.class: linked
|
||||
oval.class: linked
|
||||
circle.class: linked
|
||||
hexagon.class: linked
|
||||
cloud.class: linked
|
||||
}
|
||||
|
||||
tooltipped: {
|
||||
rectangle.shape: rectangle
|
||||
square.shape: square
|
||||
page.shape: page
|
||||
parallelogram.shape: parallelogram
|
||||
document.shape: document
|
||||
cylinder.shape: cylinder
|
||||
queue.shape: queue
|
||||
package.shape: package
|
||||
step.shape: step
|
||||
callout.shape: callout
|
||||
stored_data.shape: stored_data
|
||||
person.shape: person
|
||||
diamond.shape: diamond
|
||||
oval.shape: oval
|
||||
circle.shape: circle
|
||||
hexagon.shape: hexagon
|
||||
cloud.shape: cloud
|
||||
|
||||
rectangle -> square -> page
|
||||
parallelogram -> document -> cylinder
|
||||
queue -> package -> step
|
||||
callout -> stored_data -> person
|
||||
diamond -> oval -> circle
|
||||
hexagon -> cloud
|
||||
|
||||
rectangle.class: tooltipped
|
||||
square.class: tooltipped
|
||||
page.class: tooltipped
|
||||
parallelogram.class: tooltipped
|
||||
document.class: tooltipped
|
||||
cylinder.class: tooltipped
|
||||
queue.class: tooltipped
|
||||
package.class: tooltipped
|
||||
step.class: tooltipped
|
||||
callout.class: tooltipped
|
||||
stored_data.class: tooltipped
|
||||
person.class: tooltipped
|
||||
diamond.class: tooltipped
|
||||
oval.class: tooltipped
|
||||
circle.class: tooltipped
|
||||
hexagon.class: tooltipped
|
||||
cloud.class: tooltipped
|
||||
}
|
||||
|
||||
both: {
|
||||
rectangle.shape: rectangle
|
||||
square.shape: square
|
||||
page.shape: page
|
||||
parallelogram.shape: parallelogram
|
||||
document.shape: document
|
||||
cylinder.shape: cylinder
|
||||
queue.shape: queue
|
||||
package.shape: package
|
||||
step.shape: step
|
||||
callout.shape: callout
|
||||
stored_data.shape: stored_data
|
||||
person.shape: person
|
||||
diamond.shape: diamond
|
||||
oval.shape: oval
|
||||
circle.shape: circle
|
||||
hexagon.shape: hexagon
|
||||
cloud.shape: cloud
|
||||
|
||||
rectangle -> square -> page
|
||||
parallelogram -> document -> cylinder
|
||||
queue -> package -> step
|
||||
callout -> stored_data -> person
|
||||
diamond -> oval -> circle
|
||||
hexagon -> cloud
|
||||
|
||||
rectangle.class: [linked; tooltipped]
|
||||
square.class: [linked; tooltipped]
|
||||
page.class: [linked; tooltipped]
|
||||
parallelogram.class: [linked; tooltipped]
|
||||
document.class: [linked; tooltipped]
|
||||
cylinder.class: [linked; tooltipped]
|
||||
queue.class: [linked; tooltipped]
|
||||
package.class: [linked; tooltipped]
|
||||
step.class: [linked; tooltipped]
|
||||
callout.class: [linked; tooltipped]
|
||||
stored_data.class: [linked; tooltipped]
|
||||
person.class: [linked; tooltipped]
|
||||
diamond.class: [linked; tooltipped]
|
||||
oval.class: [linked; tooltipped]
|
||||
circle.class: [linked; tooltipped]
|
||||
hexagon.class: [linked; tooltipped]
|
||||
cloud.class: [linked; tooltipped]
|
||||
}
|
||||
|
||||
linked -> tooltipped -> both
|
||||
|
||||
classes: {
|
||||
linked.link: example.com
|
||||
tooltipped.tooltip: example
|
||||
}
|
||||
4078
e2etests/testdata/stable/all_shapes_link/dagre/board.exp.json
generated
vendored
Normal file
4078
e2etests/testdata/stable/all_shapes_link/dagre/board.exp.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1008
e2etests/testdata/stable/all_shapes_link/dagre/sketch.exp.svg
vendored
Normal file
1008
e2etests/testdata/stable/all_shapes_link/dagre/sketch.exp.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 141 KiB |
3763
e2etests/testdata/stable/all_shapes_link/elk/board.exp.json
generated
vendored
Normal file
3763
e2etests/testdata/stable/all_shapes_link/elk/board.exp.json
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1008
e2etests/testdata/stable/all_shapes_link/elk/sketch.exp.svg
vendored
Normal file
1008
e2etests/testdata/stable/all_shapes_link/elk/sketch.exp.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 139 KiB |
Loading…
Reference in a new issue