Skip to content

Commit

Permalink
Add text-transform attribute to the style block
Browse files Browse the repository at this point in the history
This provides an option to override the functionality provided by the
theme, or apply `text-transform` outside of the theme.

The functionality of the `text-transform` is as below:

- `text-transform: none` - will disable **any** transformation (like the uppercasing by `terminal` theme)
- `text-transform: uppercase` (uppercase not upper as per your message) - will force all characters into uppercase.
- `text-transform: lowercase` - will force all characters into lowercase.
- `text-transform: capitalize` - will uppercase the first letter of every word

In addition, this commit introduces:
- helper methods on the `d2graph.Style` struct to determine the type of
  `text-transform` to be applied.
- `ApplyTextTransform` method on the `d2graph.Attributes` which will
  transform the `Label.Value` to the correct text case.
  • Loading branch information
alexstoick committed Apr 7, 2023
1 parent d1c980d commit 2048417
Show file tree
Hide file tree
Showing 15 changed files with 939 additions and 802 deletions.
2 changes: 2 additions & 0 deletions d2compiler/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ func compileStyleFieldInit(attrs *d2graph.Attributes, f *d2ir.Field) {
attrs.Left = &d2graph.Scalar{MapKey: f.LastPrimaryKey()}
case "double-border":
attrs.Style.DoubleBorder = &d2graph.Scalar{MapKey: f.LastPrimaryKey()}
case "text-transform":
attrs.Style.TextTransform = &d2graph.Scalar{MapKey: f.LastPrimaryKey()}
}
}

Expand Down
88 changes: 57 additions & 31 deletions d2graph/d2graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"strconv"
"strings"

"golang.org/x/text/cases"
"golang.org/x/text/language"
"oss.terrastruct.com/util-go/go2"

"oss.terrastruct.com/d2/d2ast"
Expand Down Expand Up @@ -131,6 +133,22 @@ type Attributes struct {
Constraint Scalar `json:"constraint"`
}

func (a *Attributes) ApplyTextTransform() {
if a.Style.NoTextTransform() {
return
}

if a.Style.TextTransform != nil && a.Style.TextTransform.Value == "uppercase" {
a.Label.Value = strings.ToUpper(a.Label.Value)
}
if a.Style.TextTransform != nil && a.Style.TextTransform.Value == "lowercase" {
a.Label.Value = strings.ToLower(a.Label.Value)
}
if a.Style.TextTransform != nil && a.Style.TextTransform.Value == "capitalize" {
a.Label.Value = cases.Title(language.Und).String(a.Label.Value)
}
}

// TODO references at the root scope should have their Scope set to root graph AST
type Reference struct {
Key *d2ast.KeyPath `json:"key"`
Expand All @@ -151,25 +169,30 @@ func (r Reference) InEdge() bool {
}

type Style struct {
Opacity *Scalar `json:"opacity,omitempty"`
Stroke *Scalar `json:"stroke,omitempty"`
Fill *Scalar `json:"fill,omitempty"`
FillPattern *Scalar `json:"fillPattern,omitempty"`
StrokeWidth *Scalar `json:"strokeWidth,omitempty"`
StrokeDash *Scalar `json:"strokeDash,omitempty"`
BorderRadius *Scalar `json:"borderRadius,omitempty"`
Shadow *Scalar `json:"shadow,omitempty"`
ThreeDee *Scalar `json:"3d,omitempty"`
Multiple *Scalar `json:"multiple,omitempty"`
Font *Scalar `json:"font,omitempty"`
FontSize *Scalar `json:"fontSize,omitempty"`
FontColor *Scalar `json:"fontColor,omitempty"`
Animated *Scalar `json:"animated,omitempty"`
Bold *Scalar `json:"bold,omitempty"`
Italic *Scalar `json:"italic,omitempty"`
Underline *Scalar `json:"underline,omitempty"`
Filled *Scalar `json:"filled,omitempty"`
DoubleBorder *Scalar `json:"doubleBorder,omitempty"`
Opacity *Scalar `json:"opacity,omitempty"`
Stroke *Scalar `json:"stroke,omitempty"`
Fill *Scalar `json:"fill,omitempty"`
FillPattern *Scalar `json:"fillPattern,omitempty"`
StrokeWidth *Scalar `json:"strokeWidth,omitempty"`
StrokeDash *Scalar `json:"strokeDash,omitempty"`
BorderRadius *Scalar `json:"borderRadius,omitempty"`
Shadow *Scalar `json:"shadow,omitempty"`
ThreeDee *Scalar `json:"3d,omitempty"`
Multiple *Scalar `json:"multiple,omitempty"`
Font *Scalar `json:"font,omitempty"`
FontSize *Scalar `json:"fontSize,omitempty"`
FontColor *Scalar `json:"fontColor,omitempty"`
Animated *Scalar `json:"animated,omitempty"`
Bold *Scalar `json:"bold,omitempty"`
Italic *Scalar `json:"italic,omitempty"`
Underline *Scalar `json:"underline,omitempty"`
Filled *Scalar `json:"filled,omitempty"`
DoubleBorder *Scalar `json:"doubleBorder,omitempty"`
TextTransform *Scalar `json:"textTransform,omitempty"`
}

func (s Style) NoneTextTransform() bool {
return s.TextTransform != nil && s.TextTransform.Value == "none"
}

func (s *Style) Apply(key, value string) error {
Expand Down Expand Up @@ -340,6 +363,14 @@ func (s *Style) Apply(key, value string) error {
return errors.New(`expected "double-border" to be true or false`)
}
s.DoubleBorder.Value = value
case "text-transform":
if s.TextTransform == nil {
break
}
if !go2.Contains(textTransforms, strings.ToLower(value)) {
return fmt.Errorf(`expected "text-transform" to be one of (%s)`, strings.Join(textTransforms, ","))
}
s.TextTransform.Value = value
default:
return fmt.Errorf("unknown style key: %s", key)
}
Expand Down Expand Up @@ -1305,10 +1336,11 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
}

if g.Theme != nil && g.Theme.SpecialRules.CapsLock && !strings.EqualFold(obj.Attributes.Shape.Value, d2target.ShapeCode) {
if obj.Attributes.Language != "latex" {
if obj.Attributes.Language != "latex" && !obj.Attributes.Style.NoTextTransform() {
obj.Attributes.Label.Value = strings.ToUpper(obj.Attributes.Label.Value)
}
}
obj.Attributes.ApplyTextTransform()

labelDims, err := obj.GetLabelSize(mtexts, ruler, fontFamily)
if err != nil {
Expand Down Expand Up @@ -1427,9 +1459,11 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
continue
}

if g.Theme != nil && g.Theme.SpecialRules.CapsLock {
if g.Theme != nil && g.Theme.SpecialRules.CapsLock && !edge.Attributes.Style.NoTextTransform() {
edge.Attributes.Label.Value = strings.ToUpper(edge.Attributes.Label.Value)
}
edge.Attributes.ApplyTextTransform()

usedFont := fontFamily
if edge.Attributes.Style.Font != nil {
f := d2fonts.D2_FONT_TO_FAMILY[edge.Attributes.Style.Font.Value]
Expand All @@ -1451,16 +1485,9 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
func (g *Graph) Texts() []*d2target.MText {
var texts []*d2target.MText

capsLock := g.Theme != nil && g.Theme.SpecialRules.CapsLock

for _, obj := range g.Objects {
if obj.Attributes.Label.Value != "" {
text := obj.Text()
if capsLock && !strings.EqualFold(obj.Attributes.Shape.Value, d2target.ShapeCode) {
if obj.Attributes.Language != "latex" {
text.Text = strings.ToUpper(text.Text)
}
}
texts = appendTextDedup(texts, text)
}
if obj.Class != nil {
Expand Down Expand Up @@ -1489,9 +1516,6 @@ func (g *Graph) Texts() []*d2target.MText {
for _, edge := range g.Edges {
if edge.Attributes.Label.Value != "" {
text := edge.Text()
if capsLock {
text.Text = strings.ToUpper(text.Text)
}
texts = appendTextDedup(texts, text)
}
if edge.SrcArrowhead != nil && edge.SrcArrowhead.Label.Value != "" {
Expand Down Expand Up @@ -1597,6 +1621,8 @@ var FillPatterns = []string{
"paper",
}

var textTransforms = []string{"none", "uppercase", "lowercase", "capitalize"}

// BoardKeywords contains the keywords that create new boards.
var BoardKeywords = map[string]struct{}{
"layers": {},
Expand Down
Loading

0 comments on commit 2048417

Please sign in to comment.