256 lines
5.9 KiB
Go
256 lines
5.9 KiB
Go
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
|
|
|
|
inlineTheme *Theme
|
|
}
|
|
|
|
func NewThemableElement(tag string, inlineTheme *Theme) *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,
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
inlineTheme,
|
|
}
|
|
}
|
|
|
|
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 <image> 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)
|
|
if el.inlineTheme != nil {
|
|
out += fmt.Sprintf(` stroke="%s"`, ResolveThemeColor(*el.inlineTheme, el.Stroke))
|
|
}
|
|
} else if len(el.Stroke) > 0 {
|
|
if color.IsGradient(el.Stroke) {
|
|
el.Stroke = fmt.Sprintf("url('#%s')", color.UniqueGradientID(el.Stroke))
|
|
}
|
|
out += fmt.Sprintf(` stroke="%s"`, el.Stroke)
|
|
}
|
|
if color.IsThemeColor(el.Fill) {
|
|
class += fmt.Sprintf(" fill-%s", el.Fill)
|
|
if el.inlineTheme != nil {
|
|
out += fmt.Sprintf(` fill="%s"`, ResolveThemeColor(*el.inlineTheme, el.Fill))
|
|
}
|
|
} else if len(el.Fill) > 0 {
|
|
if color.IsGradient(el.Fill) {
|
|
el.Fill = fmt.Sprintf("url('#%s')", color.UniqueGradientID(el.Fill))
|
|
}
|
|
out += fmt.Sprintf(` fill="%s"`, el.Fill)
|
|
}
|
|
if color.IsThemeColor(el.BackgroundColor) {
|
|
class += fmt.Sprintf(" background-color-%s", el.BackgroundColor)
|
|
if el.inlineTheme != nil {
|
|
out += fmt.Sprintf(` background-color="%s"`, ResolveThemeColor(*el.inlineTheme, 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)
|
|
if el.inlineTheme != nil {
|
|
out += fmt.Sprintf(` color="%s"`, ResolveThemeColor(*el.inlineTheme, 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</%s>", out, el.Content, el.tag)
|
|
}
|
|
|
|
out += " />"
|
|
if el.FillPattern != "" && el.FillPattern != "none" {
|
|
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)
|
|
}
|