package d2themes import ( "fmt" "math" "oss.terrastruct.com/d2/lib/color" ) // ThemableElement is a helper class for creating new XML elements. // This should be preferred over formatting and must be used // whenever Fill, Stroke, BackgroundColor or Color contains a color from a theme. // i.e. N[1-7] | B[1-6] | AA[245] | AB[45] type ThemableElement struct { tag string X float64 X1 float64 X2 float64 Y float64 Y1 float64 Y2 float64 Width float64 Height float64 R float64 Rx float64 Ry float64 Cx float64 Cy float64 D string Mask string Points string Transform string Href string Xmlns string Fill string Stroke string StrokeDashArray string BackgroundColor string Color string ClassName string Style string Attributes string Content string ClipPath string FillPattern string } func NewThemableElement(tag string) *ThemableElement { xmlns := "" if tag == "div" { xmlns = "http://www.w3.org/1999/xhtml" } return &ThemableElement{ tag, math.MaxFloat64, math.MaxFloat64, math.MaxFloat64, math.MaxFloat64, math.MaxFloat64, math.MaxFloat64, math.MaxFloat64, math.MaxFloat64, math.MaxFloat64, math.MaxFloat64, math.MaxFloat64, math.MaxFloat64, math.MaxFloat64, "", "", "", "", "", xmlns, color.Empty, color.Empty, color.Empty, color.Empty, "", "", "", "", "", "", "", } } func (el *ThemableElement) Copy() *ThemableElement { tmp := *el return &tmp } func (el *ThemableElement) SetTranslate(x, y float64) { el.Transform = fmt.Sprintf("translate(%f %f)", x, y) } func (el *ThemableElement) SetMaskUrl(url string) { el.Mask = fmt.Sprintf("url(#%s)", url) } func (el *ThemableElement) Render() string { out := "<" + el.tag // href has to be at the top for the img bundler to detect tags correctly if len(el.Href) > 0 { out += fmt.Sprintf(` href="%s"`, el.Href) } if el.X != math.MaxFloat64 { out += fmt.Sprintf(` x="%f"`, el.X) } if el.X1 != math.MaxFloat64 { out += fmt.Sprintf(` x1="%f"`, el.X1) } if el.X2 != math.MaxFloat64 { out += fmt.Sprintf(` x2="%f"`, el.X2) } if el.Y != math.MaxFloat64 { out += fmt.Sprintf(` y="%f"`, el.Y) } if el.Y1 != math.MaxFloat64 { out += fmt.Sprintf(` y1="%f"`, el.Y1) } if el.Y2 != math.MaxFloat64 { out += fmt.Sprintf(` y2="%f"`, el.Y2) } if el.Width != math.MaxFloat64 { out += fmt.Sprintf(` width="%f"`, el.Width) } if el.Height != math.MaxFloat64 { out += fmt.Sprintf(` height="%f"`, el.Height) } if el.R != math.MaxFloat64 { out += fmt.Sprintf(` r="%f"`, el.R) } if el.Rx != math.MaxFloat64 { out += fmt.Sprintf(` rx="%f"`, calculateAxisRadius(el.Rx, el.Width, el.Height)) } if el.Ry != math.MaxFloat64 { out += fmt.Sprintf(` ry="%f"`, calculateAxisRadius(el.Ry, el.Width, el.Height)) } if el.Cx != math.MaxFloat64 { out += fmt.Sprintf(` cx="%f"`, el.Cx) } if el.Cy != math.MaxFloat64 { out += fmt.Sprintf(` cy="%f"`, el.Cy) } if el.StrokeDashArray != "" { out += fmt.Sprintf(` stroke-dasharray="%s"`, el.StrokeDashArray) } if len(el.D) > 0 { out += fmt.Sprintf(` d="%s"`, el.D) } if len(el.Mask) > 0 { out += fmt.Sprintf(` mask="%s"`, el.Mask) } if len(el.Points) > 0 { out += fmt.Sprintf(` points="%s"`, el.Points) } if len(el.Transform) > 0 { out += fmt.Sprintf(` transform="%s"`, el.Transform) } if len(el.Xmlns) > 0 { out += fmt.Sprintf(` xmlns="%s"`, el.Xmlns) } class := el.ClassName style := el.Style // Add class {property}-{theme color} if the color is from a theme, set the property otherwise if color.IsThemeColor(el.Stroke) { class += fmt.Sprintf(" stroke-%s", el.Stroke) } else if len(el.Stroke) > 0 { out += fmt.Sprintf(` stroke="%s"`, el.Stroke) } if color.IsThemeColor(el.Fill) { class += fmt.Sprintf(" fill-%s", el.Fill) } else if len(el.Fill) > 0 { out += fmt.Sprintf(` fill="%s"`, el.Fill) } if color.IsThemeColor(el.BackgroundColor) { class += fmt.Sprintf(" background-color-%s", el.BackgroundColor) } else if len(el.BackgroundColor) > 0 { out += fmt.Sprintf(` background-color="%s"`, el.BackgroundColor) } if color.IsThemeColor(el.Color) { class += fmt.Sprintf(" color-%s", el.Color) } else if len(el.Color) > 0 { out += fmt.Sprintf(` color="%s"`, el.Color) } if len(class) > 0 { out += fmt.Sprintf(` class="%s"`, class) } if len(style) > 0 { out += fmt.Sprintf(` style="%s"`, style) } if len(el.Attributes) > 0 { out += fmt.Sprintf(` %s`, el.Attributes) } if len(el.ClipPath) > 0 { out += fmt.Sprintf(` clip-path="url(#%s)"`, el.ClipPath) } if len(el.Content) > 0 { return fmt.Sprintf("%s>%s", out, el.Content, el.tag) } out += " />" if el.FillPattern != "" { patternEl := el.Copy() patternEl.Fill = "" patternEl.Stroke = "" patternEl.BackgroundColor = "" patternEl.Color = "" patternEl.ClassName = fmt.Sprintf("%s-overlay", el.FillPattern) patternEl.FillPattern = "" out += patternEl.Render() } return out } func calculateAxisRadius(borderRadius, width, height float64) float64 { minimumSideSize := math.Min(width, height) maximumBorderRadiusValue := minimumSideSize / 2.0 return math.Min(borderRadius, maximumBorderRadiusValue) }