...

Source file src/oss.terrastruct.com/d2/d2themes/element.go

Documentation: oss.terrastruct.com/d2/d2themes

     1  package d2themes
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  
     7  	"oss.terrastruct.com/d2/lib/color"
     8  )
     9  
    10  // ThemableElement is a helper class for creating new XML elements.
    11  // This should be preferred over formatting and must be used
    12  // whenever Fill, Stroke, BackgroundColor or Color contains a color from a theme.
    13  // i.e. N[1-7] | B[1-6] | AA[245] | AB[45]
    14  type ThemableElement struct {
    15  	tag string
    16  
    17  	X      float64
    18  	X1     float64
    19  	X2     float64
    20  	Y      float64
    21  	Y1     float64
    22  	Y2     float64
    23  	Width  float64
    24  	Height float64
    25  	R      float64
    26  	Rx     float64
    27  	Ry     float64
    28  	Cx     float64
    29  	Cy     float64
    30  
    31  	D         string
    32  	Mask      string
    33  	Points    string
    34  	Transform string
    35  	Href      string
    36  	Xmlns     string
    37  
    38  	Fill            string
    39  	Stroke          string
    40  	StrokeDashArray string
    41  	BackgroundColor string
    42  	Color           string
    43  
    44  	ClassName  string
    45  	Style      string
    46  	Attributes string
    47  
    48  	Content  string
    49  	ClipPath string
    50  
    51  	FillPattern string
    52  }
    53  
    54  func NewThemableElement(tag string) *ThemableElement {
    55  	xmlns := ""
    56  	if tag == "div" {
    57  		xmlns = "http://www.w3.org/1999/xhtml"
    58  	}
    59  
    60  	return &ThemableElement{
    61  		tag,
    62  		math.MaxFloat64,
    63  		math.MaxFloat64,
    64  		math.MaxFloat64,
    65  		math.MaxFloat64,
    66  		math.MaxFloat64,
    67  		math.MaxFloat64,
    68  		math.MaxFloat64,
    69  		math.MaxFloat64,
    70  		math.MaxFloat64,
    71  		math.MaxFloat64,
    72  		math.MaxFloat64,
    73  		math.MaxFloat64,
    74  		math.MaxFloat64,
    75  		"",
    76  		"",
    77  		"",
    78  		"",
    79  		"",
    80  		xmlns,
    81  		color.Empty,
    82  		color.Empty,
    83  		color.Empty,
    84  		color.Empty,
    85  		"",
    86  		"",
    87  		"",
    88  		"",
    89  		"",
    90  		"",
    91  		"",
    92  	}
    93  }
    94  
    95  func (el *ThemableElement) Copy() *ThemableElement {
    96  	tmp := *el
    97  	return &tmp
    98  }
    99  
   100  func (el *ThemableElement) SetTranslate(x, y float64) {
   101  	el.Transform = fmt.Sprintf("translate(%f %f)", x, y)
   102  }
   103  
   104  func (el *ThemableElement) SetMaskUrl(url string) {
   105  	el.Mask = fmt.Sprintf("url(#%s)", url)
   106  }
   107  
   108  func (el *ThemableElement) Render() string {
   109  	out := "<" + el.tag
   110  
   111  	// href has to be at the top for the img bundler to detect <image> tags correctly
   112  	if len(el.Href) > 0 {
   113  		out += fmt.Sprintf(` href="%s"`, el.Href)
   114  	}
   115  	if el.X != math.MaxFloat64 {
   116  		out += fmt.Sprintf(` x="%f"`, el.X)
   117  	}
   118  	if el.X1 != math.MaxFloat64 {
   119  		out += fmt.Sprintf(` x1="%f"`, el.X1)
   120  	}
   121  	if el.X2 != math.MaxFloat64 {
   122  		out += fmt.Sprintf(` x2="%f"`, el.X2)
   123  	}
   124  	if el.Y != math.MaxFloat64 {
   125  		out += fmt.Sprintf(` y="%f"`, el.Y)
   126  	}
   127  	if el.Y1 != math.MaxFloat64 {
   128  		out += fmt.Sprintf(` y1="%f"`, el.Y1)
   129  	}
   130  	if el.Y2 != math.MaxFloat64 {
   131  		out += fmt.Sprintf(` y2="%f"`, el.Y2)
   132  	}
   133  	if el.Width != math.MaxFloat64 {
   134  		out += fmt.Sprintf(` width="%f"`, el.Width)
   135  	}
   136  	if el.Height != math.MaxFloat64 {
   137  		out += fmt.Sprintf(` height="%f"`, el.Height)
   138  	}
   139  	if el.R != math.MaxFloat64 {
   140  		out += fmt.Sprintf(` r="%f"`, el.R)
   141  	}
   142  	if el.Rx != math.MaxFloat64 {
   143  		out += fmt.Sprintf(` rx="%f"`, calculateAxisRadius(el.Rx, el.Width, el.Height))
   144  	}
   145  	if el.Ry != math.MaxFloat64 {
   146  		out += fmt.Sprintf(` ry="%f"`, calculateAxisRadius(el.Ry, el.Width, el.Height))
   147  	}
   148  	if el.Cx != math.MaxFloat64 {
   149  		out += fmt.Sprintf(` cx="%f"`, el.Cx)
   150  	}
   151  	if el.Cy != math.MaxFloat64 {
   152  		out += fmt.Sprintf(` cy="%f"`, el.Cy)
   153  	}
   154  	if el.StrokeDashArray != "" {
   155  		out += fmt.Sprintf(` stroke-dasharray="%s"`, el.StrokeDashArray)
   156  	}
   157  
   158  	if len(el.D) > 0 {
   159  		out += fmt.Sprintf(` d="%s"`, el.D)
   160  	}
   161  	if len(el.Mask) > 0 {
   162  		out += fmt.Sprintf(` mask="%s"`, el.Mask)
   163  	}
   164  	if len(el.Points) > 0 {
   165  		out += fmt.Sprintf(` points="%s"`, el.Points)
   166  	}
   167  	if len(el.Transform) > 0 {
   168  		out += fmt.Sprintf(` transform="%s"`, el.Transform)
   169  	}
   170  	if len(el.Xmlns) > 0 {
   171  		out += fmt.Sprintf(` xmlns="%s"`, el.Xmlns)
   172  	}
   173  
   174  	class := el.ClassName
   175  	style := el.Style
   176  
   177  	// Add class {property}-{theme color} if the color is from a theme, set the property otherwise
   178  	if color.IsThemeColor(el.Stroke) {
   179  		class += fmt.Sprintf(" stroke-%s", el.Stroke)
   180  	} else if len(el.Stroke) > 0 {
   181  		out += fmt.Sprintf(` stroke="%s"`, el.Stroke)
   182  	}
   183  	if color.IsThemeColor(el.Fill) {
   184  		class += fmt.Sprintf(" fill-%s", el.Fill)
   185  	} else if len(el.Fill) > 0 {
   186  		out += fmt.Sprintf(` fill="%s"`, el.Fill)
   187  	}
   188  	if color.IsThemeColor(el.BackgroundColor) {
   189  		class += fmt.Sprintf(" background-color-%s", el.BackgroundColor)
   190  	} else if len(el.BackgroundColor) > 0 {
   191  		out += fmt.Sprintf(` background-color="%s"`, el.BackgroundColor)
   192  	}
   193  	if color.IsThemeColor(el.Color) {
   194  		class += fmt.Sprintf(" color-%s", el.Color)
   195  	} else if len(el.Color) > 0 {
   196  		out += fmt.Sprintf(` color="%s"`, el.Color)
   197  	}
   198  
   199  	if len(class) > 0 {
   200  		out += fmt.Sprintf(` class="%s"`, class)
   201  	}
   202  	if len(style) > 0 {
   203  		out += fmt.Sprintf(` style="%s"`, style)
   204  	}
   205  	if len(el.Attributes) > 0 {
   206  		out += fmt.Sprintf(` %s`, el.Attributes)
   207  	}
   208  
   209  	if len(el.ClipPath) > 0 {
   210  		out += fmt.Sprintf(` clip-path="url(#%s)"`, el.ClipPath)
   211  	}
   212  
   213  	if len(el.Content) > 0 {
   214  		return fmt.Sprintf("%s>%s</%s>", out, el.Content, el.tag)
   215  	}
   216  
   217  	out += " />"
   218  	if el.FillPattern != "" {
   219  		patternEl := el.Copy()
   220  		patternEl.Fill = ""
   221  		patternEl.Stroke = ""
   222  		patternEl.BackgroundColor = ""
   223  		patternEl.Color = ""
   224  		patternEl.ClassName = fmt.Sprintf("%s-overlay", el.FillPattern)
   225  		patternEl.FillPattern = ""
   226  		out += patternEl.Render()
   227  	}
   228  	return out
   229  }
   230  
   231  func calculateAxisRadius(borderRadius, width, height float64) float64 {
   232  	minimumSideSize := math.Min(width, height)
   233  	maximumBorderRadiusValue := minimumSideSize / 2.0
   234  	return math.Min(borderRadius, maximumBorderRadiusValue)
   235  }
   236  

View as plain text