Skip to content

Commit 2e38830

Browse files
authored
Support for calc expressions. Addresses #55. (#140)
1 parent 45b7cd9 commit 2e38830

File tree

8 files changed

+114
-50
lines changed

8 files changed

+114
-50
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Notable changes to this project are documented in this file. The format is based
55
## [Unreleased]
66

77
Breaking changes:
8+
- Add support for `calc` expressions (#140 by @nsaunders)
89

910
New features:
1011
- Add smart constructors for generic font families (#68, #136 by @Unisay and @JordanMartinez)

spago.dhall

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
, "effect"
77
, "either"
88
, "exceptions"
9+
, "exists"
910
, "foldable-traversable"
1011
, "maybe"
1112
, "nonempty"

src/CSS.purs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import CSS.Property (class Val, Key(..), Literal(..), Prefixed(..), Value(..), c
1818
import CSS.Render (Inline(..), Rendered, Sheet(..), collect, collect', face, feature, frame, getInline, getSheet, imp, kframe, mediaQuery, mediaType, merger, nel, predicate, properties, putInline, putStyleSheet, query', render, renderedInline, renderedSheet, rule', rules, selector, selector', selector'', sepWith) as X
1919
import CSS.Pseudo (hover) as X
2020
import CSS.Selector (Path(..), Predicate(..), Refinement(..), Selector(..), star, element, (|*), (|>), (|+), (&), byId, byClass, pseudo, func, attr, (@=), (^=), ($=), (*=), (~=), (|=)) as X
21-
import CSS.Size (Abs, Angle(..), Deg, Rad, Rel, Size(..), deg, em, ex, nil, pct, pt, px, rad, rem, sym, vh, vmax, vmin, vw) as X
21+
import CSS.Size (Combination, LengthUnit, Percentage, Angle(..), Deg, Rad, Size(..), (@+@), (@-@), (@*), (*@), (@/), deg, em, ex, nil, pct, pt, px, rad, rem, sym, vh, vmax, vmin, vw) as X
2222
import CSS.String (class IsString, fromString) as X
2323
import CSS.Stylesheet (App(..), CSS, Feature(..), Keyframes(..), MediaQuery(..), MediaType(..), NotOrOnly(..), Rule(..), StyleM(..), fontFace, importUrl, key, keyframes, keyframesFromTo, prefixed, query, rule, runS, select, (?)) as X
2424
import CSS.Text (TextDecoration(..), blink, letterSpacing, lineThrough, noneTextDecoration, overline, textDecoration, underline) as X

src/CSS/Border.purs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Prelude
44

55
import CSS.Color (Color)
66
import CSS.Property (class Val, Value)
7-
import CSS.Size (Size, Abs)
7+
import CSS.Size (Size, LengthUnit)
88
import CSS.String (fromString)
99
import CSS.Stylesheet (CSS, key)
1010
import Data.Tuple (Tuple(..))
@@ -44,19 +44,19 @@ inset = Stroke $ fromString "inset"
4444
outset :: Stroke
4545
outset = Stroke $ fromString "outset"
4646

47-
border :: Stroke -> Size Abs -> Color -> CSS
47+
border :: Stroke -> Size LengthUnit -> Color -> CSS
4848
border a b c = key (fromString "border") (Tuple a (Tuple b c))
4949

50-
borderTop :: Stroke -> Size Abs -> Color -> CSS
50+
borderTop :: Stroke -> Size LengthUnit -> Color -> CSS
5151
borderTop a b c = key (fromString "border-top") (Tuple a (Tuple b c))
5252

53-
borderBottom :: Stroke -> Size Abs -> Color -> CSS
53+
borderBottom :: Stroke -> Size LengthUnit -> Color -> CSS
5454
borderBottom a b c = key (fromString "border-bottom") (Tuple a (Tuple b c))
5555

56-
borderLeft :: Stroke -> Size Abs -> Color -> CSS
56+
borderLeft :: Stroke -> Size LengthUnit -> Color -> CSS
5757
borderLeft a b c = key (fromString "border-left") (Tuple a (Tuple b c))
5858

59-
borderRight :: Stroke -> Size Abs -> Color -> CSS
59+
borderRight :: Stroke -> Size LengthUnit -> Color -> CSS
6060
borderRight a b c = key (fromString "border-right") (Tuple a (Tuple b c))
6161

6262
borderColor :: Color -> CSS

src/CSS/Gradient.purs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ import CSS.Background (class Loc, BackgroundImage, Direction, sideTop, straight,
3939
import CSS.Color (Color)
4040
import CSS.Common (class Other, browsers, other)
4141
import CSS.Property (class Val, Value(..), value)
42-
import CSS.Size (Size, Abs, Rel, pct)
42+
import CSS.Size (Size, LengthUnit, Percentage, pct)
4343
import CSS.String (fromString)
4444

45-
type Ramp = Array (Tuple Color (Size Rel))
45+
type Ramp = Array (Tuple Color (Size Percentage))
4646

4747
-------------------------------------------------------------------------------
4848

@@ -103,7 +103,7 @@ circle ext = Radial (fromString "circle " <> value ext)
103103
ellipse :: Extend -> Radial
104104
ellipse ext = Radial (fromString "ellipse " <> value ext)
105105

106-
circular :: Size Abs -> Radial
106+
circular :: Size LengthUnit -> Radial
107107
circular radius = Radial (value (Tuple radius radius))
108108

109109
elliptical :: forall a. Size a -> Size a -> Radial

src/CSS/Media.purs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import Prelude
55
import Data.Maybe (Maybe(..))
66

77
import CSS.Property (value)
8-
import CSS.Size (Abs, Size)
8+
import CSS.Size (LengthUnit, Size)
99
import CSS.String (fromString)
1010
import CSS.Stylesheet (Feature(..), MediaType(..))
1111

1212
screen :: MediaType
1313
screen = MediaType $ fromString "screen"
1414

15-
maxWidth :: Size Abs -> Feature
15+
maxWidth :: Size LengthUnit -> Feature
1616
maxWidth = Feature "max-width" <<< Just <<< value

src/CSS/Size.purs

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,127 @@
11
module CSS.Size where
22

33
import Prelude
4-
5-
import CSS.Common (class Auto)
6-
import CSS.Property (class Val, Value, value)
4+
import CSS.Common (class Auto, browsers)
5+
import CSS.Property (class Val, Prefixed(Plain), Value(..), plain, value)
76
import CSS.String (class IsString, fromString)
7+
import Data.Exists (Exists, mkExists, runExists)
88

9-
newtype Size :: Type -> Type
10-
newtype Size a = Size Value
9+
data LengthUnit
1110

12-
type role Size nominal
11+
data Percentage
12+
13+
data Combination
1314

14-
derive instance eqSize :: Eq a => Eq (Size a)
15-
derive instance ordSize :: Ord a => Ord (Size a)
15+
data Size :: Type -> Type
16+
data Size a
17+
= BasicSize Value
18+
| SumSize (Exists Size) (Exists Size)
19+
| DiffSize (Exists Size) (Exists Size)
20+
| MultSize Number (Exists Size)
21+
| DivSize Number (Exists Size)
22+
23+
type role Size nominal
1624

1725
instance isStringSize :: IsString (Size a) where
18-
fromString = Size <<< fromString
26+
fromString = BasicSize <<< fromString
27+
28+
sizeToString :: forall a. Size a -> String
29+
sizeToString (BasicSize (Value x)) = plain x
30+
sizeToString (SumSize a b) = runExists (\a' -> runExists (\b' -> "(" <> sizeToString a' <> " + " <> sizeToString b' <> ")") b) a
31+
sizeToString (DiffSize a b) = runExists (\a' -> runExists (\b' -> "(" <> sizeToString a' <> " - " <> sizeToString b' <> ")") b) a
32+
sizeToString (MultSize a b) = runExists (\b' -> "(" <> show a <> " * " <> sizeToString b' <> ")") b
33+
sizeToString (DivSize a b) = runExists (\b' -> "(" <> sizeToString b' <> " / " <> show a <> ")") b
1934

2035
instance valSize :: Val (Size a) where
21-
value (Size v) = v
36+
value (BasicSize x) = x
37+
value x = Value $ browsers <> Plain ("calc" <> sizeToString x)
2238

2339
instance autoSize :: Auto (Size a) where
2440
auto = fromString "auto"
2541

26-
data Abs
27-
data Rel
28-
2942
-- | Zero size.
3043
nil :: forall a. Size a
31-
nil = Size $ fromString "0"
44+
nil = BasicSize $ fromString "0"
3245

3346
-- | Unitless size (as recommended for line-height).
3447
unitless :: forall a. Number -> Size a
35-
unitless = Size <<< value
48+
unitless = BasicSize <<< value
3649

3750
-- | Size in pixels.
38-
px :: Number -> Size Abs
39-
px i = Size (value i <> fromString "px")
51+
px :: Number -> Size LengthUnit
52+
px i = BasicSize (value i <> fromString "px")
4053

4154
-- | Size in points (1pt = 1/72 of 1in).
42-
pt :: Number -> Size Abs
43-
pt i = Size (value i <> fromString "pt")
55+
pt :: Number -> Size LengthUnit
56+
pt i = BasicSize (value i <> fromString "pt")
4457

4558
-- | Size in em's.
46-
em :: Number -> Size Abs
47-
em i = Size (value i <> fromString "em")
59+
em :: Number -> Size LengthUnit
60+
em i = BasicSize (value i <> fromString "em")
4861

4962
-- | Size in ex'es (x-height of the first avaliable font).
50-
ex :: Number -> Size Abs
51-
ex i = Size (value i <> fromString "ex")
63+
ex :: Number -> Size LengthUnit
64+
ex i = BasicSize (value i <> fromString "ex")
5265

53-
ch :: Number -> Size Abs
54-
ch i = Size (value i <> fromString "ch")
66+
ch :: Number -> Size LengthUnit
67+
ch i = BasicSize (value i <> fromString "ch")
5568

5669
-- | SimpleSize in percents.
57-
pct :: Number -> Size Rel
58-
pct i = Size (value i <> fromString "%")
70+
pct :: Number -> Size Percentage
71+
pct i = BasicSize (value i <> fromString "%")
5972

6073
-- | Size in rem's.
61-
rem :: Number -> Size Rel
62-
rem i = Size (value i <> fromString "rem")
74+
rem :: Number -> Size LengthUnit
75+
rem i = BasicSize (value i <> fromString "rem")
6376

6477
-- | Size in vw's (1vw = 1% of viewport width).
65-
vw :: Number -> Size Rel
66-
vw i = Size (value i <> fromString "vw")
78+
vw :: Number -> Size LengthUnit
79+
vw i = BasicSize (value i <> fromString "vw")
6780

6881
-- | Size in vh's (1vh = 1% of viewport height).
69-
vh :: Number -> Size Rel
70-
vh i = Size (value i <> fromString "vh")
82+
vh :: Number -> Size LengthUnit
83+
vh i = BasicSize (value i <> fromString "vh")
7184

7285
-- | Size in vmin's (the smaller of vw or vh).
73-
vmin :: Number -> Size Rel
74-
vmin i = Size (value i <> fromString "vmin")
86+
vmin :: Number -> Size LengthUnit
87+
vmin i = BasicSize (value i <> fromString "vmin")
7588

7689
-- | Size in vmax's (the larger of vw or vh).
77-
vmax :: Number -> Size Rel
78-
vmax i = Size (value i <> fromString "vmax")
90+
vmax :: Number -> Size LengthUnit
91+
vmax i = BasicSize (value i <> fromString "vmax")
92+
93+
class SizeCombination :: forall a b c. a -> b -> c -> Constraint
94+
class SizeCombination a b c | a -> c, b -> c
95+
96+
instance SizeCombination Percentage Percentage Percentage
97+
instance SizeCombination LengthUnit LengthUnit LengthUnit
98+
instance SizeCombination Percentage LengthUnit Combination
99+
instance SizeCombination LengthUnit Percentage Combination
100+
101+
infixl 6 calcSum as @+@
102+
103+
calcSum :: forall a b c. SizeCombination a b c => Size a -> Size b -> Size c
104+
calcSum a b = SumSize (mkExists a) (mkExists b)
105+
106+
infixl 6 calcDiff as @-@
107+
108+
calcDiff :: forall a b c. SizeCombination a b c => Size a -> Size b -> Size c
109+
calcDiff a b = DiffSize (mkExists a) (mkExists b)
110+
111+
infixl 7 calcMult as *@
112+
113+
calcMult :: forall a. Number -> Size a -> Size a
114+
calcMult a b = MultSize a $ mkExists b
115+
116+
infixl 7 calcMultFlipped as @*
117+
118+
calcMultFlipped :: forall a. Size a -> Number -> Size a
119+
calcMultFlipped = flip calcMult
120+
121+
infixl 7 calcDiv as @/
122+
123+
calcDiv :: forall a. Size a -> Number -> Size a
124+
calcDiv a b = DivSize b $ mkExists a
79125

80126
sym :: forall a b. (a -> a -> a -> a -> b) -> a -> b
81127
sym f a = f a a a a

test/Main.purs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Prelude
44

55
import Effect (Effect)
66
import Effect.Exception (error, throwException)
7-
import CSS (Rendered, Path(..), Predicate(..), Refinement(..), Selector(..), FontFaceSrc(..), FontFaceFormat(..), pct, renderedSheet, renderedInline, fromString, selector, block, display, render, borderBox, boxSizing, contentBox, blue, color, body, a, p, px, dashed, border, inlineBlock, red, (?), (&), (|>), (|*), (|+), byId, byClass, (@=), (^=), ($=), (*=), (~=), (|=), hover, fontFaceSrc, fontStyle, deg, zIndex, textOverflow, opacity, cursor, transform, transition, easeInOut, cubicBezier, ms, direction)
7+
import CSS (Rendered, Path(..), Predicate(..), Refinement(..), Selector(..), FontFaceSrc(..), FontFaceFormat(..), pct, renderedSheet, renderedInline, fromString, selector, block, display, render, borderBox, boxSizing, contentBox, blue, color, body, a, p, px, dashed, border, inlineBlock, red, (?), (&), (|>), (|*), (|+), byId, byClass, (@=), (^=), ($=), (*=), (~=), (|=), hover, fontFaceSrc, fontStyle, deg, zIndex, textOverflow, opacity, cursor, transform, transition, easeInOut, cubicBezier, ms, direction, width, em, (@+@), (@-@), (@*), (*@), (@/))
88
import CSS.Cursor as Cursor
99
import CSS.Flexbox (flex)
1010
import CSS.FontStyle as FontStyle
@@ -160,6 +160,18 @@ transition2 :: Rendered
160160
transition2 = render do
161161
transition "background-color" (ms 1.0) (cubicBezier 0.3 0.3 0.7 1.4) (ms 0.0)
162162

163+
calc1 :: Rendered
164+
calc1 = render do
165+
width $ (em 2.0 @/ 3.0) @+@ px 1.0
166+
167+
calc2 :: Rendered
168+
calc2 = render do
169+
width $ ((pct 100.0) @/ 7.0) @* 4.0
170+
171+
calc3 :: Rendered
172+
calc3 = render do
173+
width $ 5.0 *@ (pct (-20.0) @-@ px 10.0)
174+
163175
assertEqual :: forall a. Eq a => Show a => a -> a -> Effect Unit
164176
assertEqual x y = unless (x == y) <<< throwException <<< error $ "Assertion failed: " <> show x <> " /= " <> show y
165177

@@ -216,3 +228,7 @@ main = do
216228

217229
renderedInline transition1 `assertEqual` Just "-webkit-transition: background-color 1.0ms ease-in-out 0.0ms; -moz-transition: background-color 1.0ms ease-in-out 0.0ms; -ms-transition: background-color 1.0ms ease-in-out 0.0ms; -o-transition: background-color 1.0ms ease-in-out 0.0ms; transition: background-color 1.0ms ease-in-out 0.0ms"
218230
renderedInline transition2 `assertEqual` Just "-webkit-transition: background-color 1.0ms cubic-bezier(0.3, 0.3, 0.7, 1.4) 0.0ms; -moz-transition: background-color 1.0ms cubic-bezier(0.3, 0.3, 0.7, 1.4) 0.0ms; -ms-transition: background-color 1.0ms cubic-bezier(0.3, 0.3, 0.7, 1.4) 0.0ms; -o-transition: background-color 1.0ms cubic-bezier(0.3, 0.3, 0.7, 1.4) 0.0ms; transition: background-color 1.0ms cubic-bezier(0.3, 0.3, 0.7, 1.4) 0.0ms"
231+
232+
renderedInline calc1 `assertEqual` Just "width: -webkit-calc((2.0em / 3.0) + 1.0px); width: -moz-calc((2.0em / 3.0) + 1.0px); width: -ms-calc((2.0em / 3.0) + 1.0px); width: -o-calc((2.0em / 3.0) + 1.0px); width: calc((2.0em / 3.0) + 1.0px)"
233+
renderedInline calc2 `assertEqual` Just "width: -webkit-calc(4.0 * (100.0% / 7.0)); width: -moz-calc(4.0 * (100.0% / 7.0)); width: -ms-calc(4.0 * (100.0% / 7.0)); width: -o-calc(4.0 * (100.0% / 7.0)); width: calc(4.0 * (100.0% / 7.0))"
234+
renderedInline calc3 `assertEqual` Just "width: -webkit-calc(5.0 * (-20.0% - 10.0px)); width: -moz-calc(5.0 * (-20.0% - 10.0px)); width: -ms-calc(5.0 * (-20.0% - 10.0px)); width: -o-calc(5.0 * (-20.0% - 10.0px)); width: calc(5.0 * (-20.0% - 10.0px))"

0 commit comments

Comments
 (0)