Skip to content
This repository was archived by the owner on Nov 20, 2025. It is now read-only.

Commit 9687022

Browse files
Added Extent and generally improved size types
1 parent 19ba964 commit 9687022

File tree

10 files changed

+122
-57
lines changed

10 files changed

+122
-57
lines changed

sandbox-ui/src/main/scala/example/SandboxUI.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ object SandboxUI extends TyrianIOApp[Msg, Model]:
5353
"https://raw.githubusercontent.com/PurpleKingdomGames/roguelike-starterkit/417f4e372b4792972ef62aea0c917088a9fc82fd/roguelike.gif",
5454
"Roguelike"
5555
)
56-
.withSize("200px", "150px")
56+
.withSize(Extent.px(200), Extent.px(150))
5757
.cover
5858
.rounded
5959
.solidBorder(BorderWidth.Medium, RGBA.fromHex("#2563eb"))

tyrian-ui/src/main/scala/tyrian/ui/datatypes/BorderRadius.scala

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,36 @@ package tyrian.ui.datatypes
22

33
enum BorderRadius derives CanEqual:
44
case None, Small, Medium, Large, Full
5-
case Custom(value: String)
5+
case Percent(value: Int)
6+
case Relative(value: Double)
7+
case CSS(value: String)
68

79
def toCSSValue: String = this match
8-
case None => "0"
9-
case Small => "0.125rem" // 2px at 16px base
10-
case Medium => "0.25rem" // 4px at 16px base
11-
case Large => "0.5rem" // 8px at 16px base
12-
case Full => "50%" // Perfect circle/pill shape
13-
case Custom(value) => value
10+
case None => "0"
11+
case Small => "0.125rem" // 2px at 16px base
12+
case Medium => "0.25rem" // 4px at 16px base
13+
case Large => "0.5rem" // 8px at 16px base
14+
case Full => "50%" // Perfect circle/pill shape
15+
case p: Percent => s"${p.clamped}px"
16+
case Relative(value) => s"${value}rem"
17+
case CSS(value) => value
1418

1519
object BorderRadius:
1620

1721
val default: BorderRadius =
1822
BorderRadius.None
1923

2024
def percent(value: Int): BorderRadius =
21-
val clamped = if value < 0 then 0 else if value > 100 then 100 else value
22-
BorderRadius.Custom(s"${clamped}%")
25+
BorderRadius.Percent(value)
2326

2427
def px(value: Int): BorderRadius =
25-
BorderRadius.Custom(s"${value}px")
28+
BorderRadius.CSS(s"${value}px")
2629

2730
def rem(value: Double): BorderRadius =
28-
BorderRadius.Custom(s"${value}rem")
31+
BorderRadius.Relative(value)
32+
33+
object Percent:
34+
extension (p: Percent)
35+
def clamped: Percent =
36+
val c = if p.value < 0 then 0 else if p.value > 100 then 100 else p.value
37+
Percent(c)
Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package tyrian.ui.datatypes
22

3-
/** Border width options using rem units. */
43
enum BorderWidth derives CanEqual:
54
case None, Thin, Medium, Thick
6-
case Custom(value: String)
5+
case Relative(value: Double)
6+
case CSS(value: String)
77

88
def toCSSValue: String = this match
9-
case None => "0"
10-
case Thin => "0.0625rem" // 1px at 16px base
11-
case Medium => "0.125rem" // 2px at 16px base
12-
case Thick => "0.25rem" // 4px at 16px base
13-
case Custom(value) => value
9+
case None => "0"
10+
case Thin => "0.0625rem" // 1px at 16px base
11+
case Medium => "0.125rem" // 2px at 16px base
12+
case Thick => "0.25rem" // 4px at 16px base
13+
case Relative(value) => s"${value}rem"
14+
case CSS(value) => value
1415

1516
object BorderWidth:
1617
val default: BorderWidth = None
18+
19+
def apply(value: Double): BorderWidth =
20+
BorderWidth.Relative(value)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package tyrian.ui.datatypes
2+
3+
enum Extent derives CanEqual:
4+
case Auto, Fill, FitContent
5+
case Percent(value: Int)
6+
case Relative(value: Double)
7+
case CSS(value: String)
8+
9+
def toCSSValue: String = this match
10+
case Auto => "auto"
11+
case Fill => "100%"
12+
case FitContent => "fit-content"
13+
case Percent(value) => s"${value}%"
14+
case Relative(value) => s"${value}rem"
15+
case CSS(value) => value
16+
17+
object Extent:
18+
19+
val auto: Extent = Extent.Auto
20+
val fill: Extent = Extent.Fill
21+
val fitContent: Extent = Extent.FitContent
22+
23+
def rem(value: Double): Extent =
24+
Extent.Relative(value)
25+
26+
def px(value: Int): Extent =
27+
Extent.CSS(s"${value}px")
28+
29+
def percent(value: Int): Extent =
30+
Extent.Percent(value)
31+
32+
val xs: Extent = Extent.Relative(4) // 64px at 16px base
33+
val sm: Extent = Extent.Relative(8) // 128px at 16px base
34+
val md: Extent = Extent.Relative(16) // 256px at 16px base
35+
val lg: Extent = Extent.Relative(24) // 384px at 16px base
36+
val xl: Extent = Extent.Relative(32) // 512px at 16px base

tyrian-ui/src/main/scala/tyrian/ui/datatypes/FontSize.scala

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,29 @@ package tyrian.ui.datatypes
22

33
enum FontSize derives CanEqual:
44
case XSmall, Small, Medium, Large, XLarge, XXLarge
5-
case Custom(value: String)
5+
case Relative(value: Double)
6+
case CSS(value: String)
67

78
def toCSSValue: String =
89
this match
9-
case XSmall => "0.75rem" // 12px at 16px base
10-
case Small => "0.875rem" // 14px at 16px base
11-
case Medium => "1rem" // 16px at 16px base
12-
case Large => "1.125rem" // 18px at 16px base
13-
case XLarge => "1.5rem" // 24px at 16px base
14-
case XXLarge => "2rem" // 32px at 16px base
15-
case Custom(value) => value
10+
case XSmall => "0.75rem" // 12px at 16px base
11+
case Small => "0.875rem" // 14px at 16px base
12+
case Medium => "1rem" // 16px at 16px base
13+
case Large => "1.125rem" // 18px at 16px base
14+
case XLarge => "1.5rem" // 24px at 16px base
15+
case XXLarge => "2rem" // 32px at 16px base
16+
case Relative(value) => s"${value}rem"
17+
case CSS(value) => value
1618

1719
object FontSize:
1820
val default: FontSize = Medium
1921

20-
val heading1: FontSize = Custom("2rem") // 32px at 16px base
21-
val heading2: FontSize = Custom("1.75rem") // 28px at 16px base
22-
val heading3: FontSize = Custom("1.5rem") // 24px at 16px base
23-
val heading4: FontSize = Custom("1.25rem") // 20px at 16px base
24-
val heading5: FontSize = Custom("1.125rem") // 18px at 16px base
25-
val heading6: FontSize = Custom("1rem") // 16px at 16px base
22+
val heading1: FontSize = FontSize.Relative(2) // 32px at 16px base
23+
val heading2: FontSize = FontSize.Relative(1.75) // 28px at 16px base
24+
val heading3: FontSize = FontSize.Relative(1.5) // 24px at 16px base
25+
val heading4: FontSize = FontSize.Relative(1.25) // 20px at 16px base
26+
val heading5: FontSize = FontSize.Relative(1.125) // 18px at 16px base
27+
val heading6: FontSize = FontSize.Relative(1) // 16px at 16px base
2628

2729
val caption: FontSize = XSmall
2830
val button: FontSize = Medium

tyrian-ui/src/main/scala/tyrian/ui/datatypes/LineHeight.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ package tyrian.ui.datatypes
22

33
enum LineHeight derives CanEqual:
44
case Tight, Normal, Relaxed, Loose
5-
case Custom(value: String)
5+
case Relative(value: Double)
6+
case CSS(value: String)
67

78
def toCSSValue: String =
89
this match
910
case Tight => "1.2rem"
1011
case Normal => "1.4rem"
1112
case Relaxed => "1.5rem"
1213
case Loose => "1.7rem"
13-
case Custom(value) => value
14+
case Relative(value) => s"${value}rem"
15+
case CSS(value) => value
1416

1517
object LineHeight:
1618
val default: LineHeight = Normal

tyrian-ui/src/main/scala/tyrian/ui/datatypes/Spacing.scala

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,30 @@ package tyrian.ui.datatypes
22

33
enum Spacing derives CanEqual:
44
case None, XSmall, Small, Medium, Large, XLarge, XXLarge
5-
case Custom(value: String)
5+
case Relative(value: Double)
6+
case CSS(value: String)
67

78
def toCSSValue: String =
89
this match
9-
case None => "0"
10-
case XSmall => "0.25rem" // 4px at 16px base
11-
case Small => "0.5rem" // 8px at 16px base
12-
case Medium => "1rem" // 16px at 16px base
13-
case Large => "1.5rem" // 24px at 16px base
14-
case XLarge => "2rem" // 32px at 16px base
15-
case XXLarge => "3rem" // 48px at 16px base
16-
case Custom(value) => value
10+
case None => "0"
11+
case XSmall => "0.25rem" // 4px at 16px base
12+
case Small => "0.5rem" // 8px at 16px base
13+
case Medium => "1rem" // 16px at 16px base
14+
case Large => "1.5rem" // 24px at 16px base
15+
case XLarge => "2rem" // 32px at 16px base
16+
case XXLarge => "3rem" // 48px at 16px base
17+
case Relative(value) => s"${value}rem"
18+
case CSS(value) => value
1719

1820
object Spacing:
1921

2022
val default: Spacing = Medium
2123

2224
val tight: Spacing = Small
2325
val comfortable: Spacing = Large
26+
27+
def px(value: Int): Spacing =
28+
Spacing.CSS(s"${value}px")
29+
30+
def rem(value: Double): Spacing =
31+
Spacing.Relative(value)

tyrian-ui/src/main/scala/tyrian/ui/image/Image.scala

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import tyrian.ui.Theme
55
import tyrian.ui.UIElement
66
import tyrian.ui.datatypes.Border
77
import tyrian.ui.datatypes.BorderWidth
8+
import tyrian.ui.datatypes.Extent
89
import tyrian.ui.datatypes.ImageFit
910
import tyrian.ui.datatypes.RGBA
1011

1112
final case class Image[+Msg](
1213
src: String,
1314
alt: String,
14-
width: Option[String], // TODO: Yuk. Strings for sizes!
15-
height: Option[String], // TODO: Yuk. Strings for sizes!
15+
width: Option[Extent],
16+
height: Option[Extent],
1617
fit: ImageFit,
1718
classNames: Set[String],
1819
_modifyTheme: Option[Theme => Theme]
@@ -24,17 +25,17 @@ final case class Image[+Msg](
2425
def withAlt(alt: String): Image[Msg] =
2526
this.copy(alt = alt)
2627

27-
def withWidth(width: String): Image[Msg] =
28+
def withWidth(width: Extent): Image[Msg] =
2829
this.copy(width = Some(width))
29-
def fillWidth: Image[Msg] = withWidth("100%")
30+
def fillWidth: Image[Msg] = withWidth(Extent.Fill)
3031

31-
def withHeight(height: String): Image[Msg] =
32+
def withHeight(height: Extent): Image[Msg] =
3233
this.copy(height = Some(height))
33-
def fillHeight: Image[Msg] = withHeight("100%")
34+
def fillHeight: Image[Msg] = withHeight(Extent.Fill)
3435

35-
def withSize(width: String, height: String): Image[Msg] =
36+
def withSize(width: Extent, height: Extent): Image[Msg] =
3637
this.copy(width = Some(width), height = Some(height))
37-
def fillContainer: Image[Msg] = withSize("100%", "100%")
38+
def fillContainer: Image[Msg] = withSize(Extent.Fill, Extent.Fill)
3839

3940
def withFit(fit: ImageFit): Image[Msg] =
4041
this.copy(fit = fit)
@@ -104,7 +105,7 @@ object Image:
104105
_modifyTheme = None
105106
)
106107

107-
def apply[Msg](src: String, alt: String, width: String, height: String): Image[Msg] =
108+
def apply[Msg](src: String, alt: String, width: Extent, height: Extent): Image[Msg] =
108109
Image(
109110
src = src,
110111
alt = alt,
@@ -126,8 +127,8 @@ object Image:
126127
)
127128

128129
val sizeAttributes = List(
129-
image.width.map(w => width := w).toList,
130-
image.height.map(h => height := h).toList
130+
image.width.map(w => width := w.toCSSValue).toList,
131+
image.height.map(h => height := h.toCSSValue).toList
131132
).flatten
132133

133134
val styles =

tyrian-ui/src/main/scala/tyrian/ui/package.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ val BorderStyle: datatypes.BorderStyle.type = datatypes.BorderStyle
8989
type BorderWidth = datatypes.BorderWidth
9090
val BorderWidth: datatypes.BorderWidth.type = datatypes.BorderWidth
9191

92+
type Extent = datatypes.Extent
93+
val Extent: datatypes.Extent.type = datatypes.Extent
94+
9295
// Text
9396

9497
type TextBlock = text.TextBlock

tyrian-ui/src/main/scala/tyrian/ui/text/TextThemes.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ object TextThemes:
133133
weight = FontWeight.SemiBold,
134134
color = RGBA.fromHex("#1a1a1a"),
135135
alignment = TextAlignment.Left,
136-
lineHeight = LineHeight.Custom("1.3"),
136+
lineHeight = LineHeight.Relative(1.3),
137137
wrap = true,
138138
style = TextStyle.Normal,
139139
decoration = TextDecoration.None
@@ -145,7 +145,7 @@ object TextThemes:
145145
weight = FontWeight.SemiBold,
146146
color = RGBA.fromHex("#1a1a1a"),
147147
alignment = TextAlignment.Left,
148-
lineHeight = LineHeight.Custom("1.3"),
148+
lineHeight = LineHeight.Relative(1.3),
149149
wrap = true,
150150
style = TextStyle.Normal,
151151
decoration = TextDecoration.None

0 commit comments

Comments
 (0)