...

Source file src/oss.terrastruct.com/d2/d2renderers/d2svg/class.go

Documentation: oss.terrastruct.com/d2/d2renderers/d2svg

     1  package d2svg
     2  
     3  import (
     4  	"fmt"
     5  	"html"
     6  	"io"
     7  
     8  	"oss.terrastruct.com/d2/d2target"
     9  	"oss.terrastruct.com/d2/d2themes"
    10  	"oss.terrastruct.com/d2/lib/geo"
    11  	"oss.terrastruct.com/d2/lib/label"
    12  	"oss.terrastruct.com/d2/lib/svg"
    13  )
    14  
    15  func classHeader(diagramHash string, shape d2target.Shape, box *geo.Box, text string, textWidth, textHeight, fontSize float64) string {
    16  	rectEl := d2themes.NewThemableElement("rect")
    17  	rectEl.X, rectEl.Y = box.TopLeft.X, box.TopLeft.Y
    18  	rectEl.Width, rectEl.Height = box.Width, box.Height
    19  	rectEl.Fill = shape.Fill
    20  	rectEl.FillPattern = shape.FillPattern
    21  	rectEl.ClassName = "class_header"
    22  	if shape.BorderRadius != 0 {
    23  		rectEl.ClipPath = fmt.Sprintf("%v-%v", diagramHash, shape.ID)
    24  	}
    25  	str := rectEl.Render()
    26  
    27  	if text != "" {
    28  		tl := label.InsideMiddleCenter.GetPointOnBox(
    29  			box,
    30  			0,
    31  			textWidth,
    32  			textHeight,
    33  		)
    34  
    35  		textEl := d2themes.NewThemableElement("text")
    36  		textEl.X = tl.X + textWidth/2
    37  		textEl.Y = tl.Y + textHeight*3/4
    38  		textEl.Fill = shape.GetFontColor()
    39  		textEl.ClassName = "text-mono"
    40  		textEl.Style = fmt.Sprintf(`text-anchor:%s;font-size:%vpx;`,
    41  			"middle", 4+fontSize,
    42  		)
    43  		textEl.Content = svg.EscapeText(text)
    44  		str += textEl.Render()
    45  	}
    46  	return str
    47  }
    48  
    49  func classRow(shape d2target.Shape, box *geo.Box, prefix, nameText, typeText string, fontSize float64) string {
    50  	// Row is made up of prefix, name, and type
    51  	// e.g. | + firstName   string  |
    52  	prefixTL := label.InsideMiddleLeft.GetPointOnBox(
    53  		box,
    54  		d2target.PrefixPadding,
    55  		box.Width,
    56  		fontSize,
    57  	)
    58  	typeTR := label.InsideMiddleRight.GetPointOnBox(
    59  		box,
    60  		d2target.TypePadding,
    61  		0,
    62  		fontSize,
    63  	)
    64  
    65  	textEl := d2themes.NewThemableElement("text")
    66  	textEl.X = prefixTL.X
    67  	textEl.Y = prefixTL.Y + fontSize*3/4
    68  	textEl.Fill = shape.PrimaryAccentColor
    69  	textEl.ClassName = "text-mono"
    70  	textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx", "start", fontSize)
    71  	textEl.Content = prefix
    72  	out := textEl.Render()
    73  
    74  	textEl.X = prefixTL.X + d2target.PrefixWidth
    75  	textEl.Fill = shape.Fill
    76  	textEl.Content = svg.EscapeText(nameText)
    77  	out += textEl.Render()
    78  
    79  	textEl.X = typeTR.X
    80  	textEl.Y = typeTR.Y + fontSize*3/4
    81  	textEl.Fill = shape.SecondaryAccentColor
    82  	textEl.Style = fmt.Sprintf("text-anchor:%s;font-size:%vpx", "end", fontSize)
    83  	textEl.Content = svg.EscapeText(typeText)
    84  	out += textEl.Render()
    85  
    86  	return out
    87  }
    88  
    89  func drawClass(writer io.Writer, diagramHash string, targetShape d2target.Shape) {
    90  	el := d2themes.NewThemableElement("rect")
    91  	el.X = float64(targetShape.Pos.X)
    92  	el.Y = float64(targetShape.Pos.Y)
    93  	el.Width = float64(targetShape.Width)
    94  	el.Height = float64(targetShape.Height)
    95  	el.Fill, el.Stroke = d2themes.ShapeTheme(targetShape)
    96  	el.FillPattern = targetShape.FillPattern
    97  	el.Style = targetShape.CSSStyle()
    98  	if targetShape.BorderRadius != 0 {
    99  		el.Rx = float64(targetShape.BorderRadius)
   100  		el.Ry = float64(targetShape.BorderRadius)
   101  	}
   102  	fmt.Fprint(writer, el.Render())
   103  
   104  	box := geo.NewBox(
   105  		geo.NewPoint(float64(targetShape.Pos.X), float64(targetShape.Pos.Y)),
   106  		float64(targetShape.Width),
   107  		float64(targetShape.Height),
   108  	)
   109  	rowHeight := box.Height / float64(2+len(targetShape.Class.Fields)+len(targetShape.Class.Methods))
   110  	headerBox := geo.NewBox(box.TopLeft, box.Width, 2*rowHeight)
   111  
   112  	fmt.Fprint(writer,
   113  		classHeader(diagramHash, targetShape, headerBox, targetShape.Label, float64(targetShape.LabelWidth), float64(targetShape.LabelHeight), float64(targetShape.FontSize)),
   114  	)
   115  
   116  	rowBox := geo.NewBox(box.TopLeft.Copy(), box.Width, rowHeight)
   117  	rowBox.TopLeft.Y += headerBox.Height
   118  	for _, f := range targetShape.Fields {
   119  		fmt.Fprint(writer,
   120  			classRow(targetShape, rowBox, f.VisibilityToken(), f.Name, f.Type, float64(targetShape.FontSize)),
   121  		)
   122  		rowBox.TopLeft.Y += rowHeight
   123  	}
   124  
   125  	lineEl := d2themes.NewThemableElement("line")
   126  
   127  	if targetShape.BorderRadius != 0 && len(targetShape.Methods) == 0 {
   128  		lineEl.X1, lineEl.Y1 = rowBox.TopLeft.X+float64(targetShape.BorderRadius), rowBox.TopLeft.Y
   129  		lineEl.X2, lineEl.Y2 = rowBox.TopLeft.X+rowBox.Width-float64(targetShape.BorderRadius), rowBox.TopLeft.Y
   130  	} else {
   131  		lineEl.X1, lineEl.Y1 = rowBox.TopLeft.X, rowBox.TopLeft.Y
   132  		lineEl.X2, lineEl.Y2 = rowBox.TopLeft.X+rowBox.Width, rowBox.TopLeft.Y
   133  	}
   134  
   135  	lineEl.Stroke = targetShape.Fill
   136  	lineEl.Style = "stroke-width:1"
   137  	fmt.Fprint(writer, lineEl.Render())
   138  
   139  	for _, m := range targetShape.Methods {
   140  		fmt.Fprint(writer,
   141  			classRow(targetShape, rowBox, m.VisibilityToken(), m.Name, m.Return, float64(targetShape.FontSize)),
   142  		)
   143  		rowBox.TopLeft.Y += rowHeight
   144  	}
   145  
   146  	if targetShape.Icon != nil && targetShape.Type != d2target.ShapeImage {
   147  		iconPosition := label.FromString(targetShape.IconPosition)
   148  		iconSize := d2target.GetIconSize(box, targetShape.IconPosition)
   149  
   150  		tl := iconPosition.GetPointOnBox(box, label.PADDING, float64(iconSize), float64(iconSize))
   151  
   152  		fmt.Fprintf(writer, `<image href="%s" x="%f" y="%f" width="%d" height="%d" />`,
   153  			html.EscapeString(targetShape.Icon.String()),
   154  			tl.X,
   155  			tl.Y,
   156  			iconSize,
   157  			iconSize,
   158  		)
   159  	}
   160  }
   161  

View as plain text