Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for calc expressions. Addresses #55. #140

Merged
merged 1 commit into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Notable changes to this project are documented in this file. The format is based
## [Unreleased]

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

New features:
- Add smart constructors for generic font families (#68, #136 by @Unisay and @JordanMartinez)
Expand Down
1 change: 1 addition & 0 deletions spago.dhall
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
, "effect"
, "either"
, "exceptions"
, "exists"
, "foldable-traversable"
, "maybe"
, "nonempty"
Expand Down
2 changes: 1 addition & 1 deletion src/CSS.purs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import CSS.Property (class Val, Key(..), Literal(..), Prefixed(..), Value(..), c
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
import CSS.Pseudo (hover) as X
import CSS.Selector (Path(..), Predicate(..), Refinement(..), Selector(..), star, element, (|*), (|>), (|+), (&), byId, byClass, pseudo, func, attr, (@=), (^=), ($=), (*=), (~=), (|=)) as X
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
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
import CSS.String (class IsString, fromString) as X
import CSS.Stylesheet (App(..), CSS, Feature(..), Keyframes(..), MediaQuery(..), MediaType(..), NotOrOnly(..), Rule(..), StyleM(..), fontFace, importUrl, key, keyframes, keyframesFromTo, prefixed, query, rule, runS, select, (?)) as X
import CSS.Text (TextDecoration(..), blink, letterSpacing, lineThrough, noneTextDecoration, overline, textDecoration, underline) as X
Expand Down
12 changes: 6 additions & 6 deletions src/CSS/Border.purs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Prelude

import CSS.Color (Color)
import CSS.Property (class Val, Value)
import CSS.Size (Size, Abs)
import CSS.Size (Size, LengthUnit)
import CSS.String (fromString)
import CSS.Stylesheet (CSS, key)
import Data.Tuple (Tuple(..))
Expand Down Expand Up @@ -44,19 +44,19 @@ inset = Stroke $ fromString "inset"
outset :: Stroke
outset = Stroke $ fromString "outset"

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

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

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

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

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

borderColor :: Color -> CSS
Expand Down
6 changes: 3 additions & 3 deletions src/CSS/Gradient.purs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ import CSS.Background (class Loc, BackgroundImage, Direction, sideTop, straight,
import CSS.Color (Color)
import CSS.Common (class Other, browsers, other)
import CSS.Property (class Val, Value(..), value)
import CSS.Size (Size, Abs, Rel, pct)
import CSS.Size (Size, LengthUnit, Percentage, pct)
import CSS.String (fromString)

type Ramp = Array (Tuple Color (Size Rel))
type Ramp = Array (Tuple Color (Size Percentage))

-------------------------------------------------------------------------------

Expand Down Expand Up @@ -103,7 +103,7 @@ circle ext = Radial (fromString "circle " <> value ext)
ellipse :: Extend -> Radial
ellipse ext = Radial (fromString "ellipse " <> value ext)

circular :: Size Abs -> Radial
circular :: Size LengthUnit -> Radial
circular radius = Radial (value (Tuple radius radius))

elliptical :: forall a. Size a -> Size a -> Radial
Expand Down
4 changes: 2 additions & 2 deletions src/CSS/Media.purs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import Prelude
import Data.Maybe (Maybe(..))

import CSS.Property (value)
import CSS.Size (Abs, Size)
import CSS.Size (LengthUnit, Size)
import CSS.String (fromString)
import CSS.Stylesheet (Feature(..), MediaType(..))

screen :: MediaType
screen = MediaType $ fromString "screen"

maxWidth :: Size Abs -> Feature
maxWidth :: Size LengthUnit -> Feature
maxWidth = Feature "max-width" <<< Just <<< value
120 changes: 83 additions & 37 deletions src/CSS/Size.purs
Original file line number Diff line number Diff line change
@@ -1,81 +1,127 @@
module CSS.Size where

import Prelude

import CSS.Common (class Auto)
import CSS.Property (class Val, Value, value)
import CSS.Common (class Auto, browsers)
import CSS.Property (class Val, Prefixed(Plain), Value(..), plain, value)
import CSS.String (class IsString, fromString)
import Data.Exists (Exists, mkExists, runExists)

newtype Size :: Type -> Type
newtype Size a = Size Value
data LengthUnit

type role Size nominal
data Percentage

data Combination

derive instance eqSize :: Eq a => Eq (Size a)
derive instance ordSize :: Ord a => Ord (Size a)
data Size :: Type -> Type
data Size a
= BasicSize Value
| SumSize (Exists Size) (Exists Size)
| DiffSize (Exists Size) (Exists Size)
| MultSize Number (Exists Size)
| DivSize Number (Exists Size)

type role Size nominal

instance isStringSize :: IsString (Size a) where
fromString = Size <<< fromString
fromString = BasicSize <<< fromString

sizeToString :: forall a. Size a -> String
sizeToString (BasicSize (Value x)) = plain x
sizeToString (SumSize a b) = runExists (\a' -> runExists (\b' -> "(" <> sizeToString a' <> " + " <> sizeToString b' <> ")") b) a
sizeToString (DiffSize a b) = runExists (\a' -> runExists (\b' -> "(" <> sizeToString a' <> " - " <> sizeToString b' <> ")") b) a
sizeToString (MultSize a b) = runExists (\b' -> "(" <> show a <> " * " <> sizeToString b' <> ")") b
sizeToString (DivSize a b) = runExists (\b' -> "(" <> sizeToString b' <> " / " <> show a <> ")") b

instance valSize :: Val (Size a) where
value (Size v) = v
value (BasicSize x) = x
value x = Value $ browsers <> Plain ("calc" <> sizeToString x)

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

data Abs
data Rel

-- | Zero size.
nil :: forall a. Size a
nil = Size $ fromString "0"
nil = BasicSize $ fromString "0"

-- | Unitless size (as recommended for line-height).
unitless :: forall a. Number -> Size a
unitless = Size <<< value
unitless = BasicSize <<< value

-- | Size in pixels.
px :: Number -> Size Abs
px i = Size (value i <> fromString "px")
px :: Number -> Size LengthUnit
px i = BasicSize (value i <> fromString "px")

-- | Size in points (1pt = 1/72 of 1in).
pt :: Number -> Size Abs
pt i = Size (value i <> fromString "pt")
pt :: Number -> Size LengthUnit
pt i = BasicSize (value i <> fromString "pt")

-- | Size in em's.
em :: Number -> Size Abs
em i = Size (value i <> fromString "em")
em :: Number -> Size LengthUnit
em i = BasicSize (value i <> fromString "em")

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

ch :: Number -> Size Abs
ch i = Size (value i <> fromString "ch")
ch :: Number -> Size LengthUnit
ch i = BasicSize (value i <> fromString "ch")

-- | SimpleSize in percents.
pct :: Number -> Size Rel
pct i = Size (value i <> fromString "%")
pct :: Number -> Size Percentage
pct i = BasicSize (value i <> fromString "%")

-- | Size in rem's.
rem :: Number -> Size Rel
rem i = Size (value i <> fromString "rem")
rem :: Number -> Size LengthUnit
rem i = BasicSize (value i <> fromString "rem")

-- | Size in vw's (1vw = 1% of viewport width).
vw :: Number -> Size Rel
vw i = Size (value i <> fromString "vw")
vw :: Number -> Size LengthUnit
vw i = BasicSize (value i <> fromString "vw")

-- | Size in vh's (1vh = 1% of viewport height).
vh :: Number -> Size Rel
vh i = Size (value i <> fromString "vh")
vh :: Number -> Size LengthUnit
vh i = BasicSize (value i <> fromString "vh")

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

-- | Size in vmax's (the larger of vw or vh).
vmax :: Number -> Size Rel
vmax i = Size (value i <> fromString "vmax")
vmax :: Number -> Size LengthUnit
vmax i = BasicSize (value i <> fromString "vmax")

class SizeCombination :: forall a b c. a -> b -> c -> Constraint
class SizeCombination a b c | a -> c, b -> c

instance SizeCombination Percentage Percentage Percentage
instance SizeCombination LengthUnit LengthUnit LengthUnit
instance SizeCombination Percentage LengthUnit Combination
instance SizeCombination LengthUnit Percentage Combination

infixl 6 calcSum as @+@

calcSum :: forall a b c. SizeCombination a b c => Size a -> Size b -> Size c
calcSum a b = SumSize (mkExists a) (mkExists b)

infixl 6 calcDiff as @-@

calcDiff :: forall a b c. SizeCombination a b c => Size a -> Size b -> Size c
calcDiff a b = DiffSize (mkExists a) (mkExists b)

infixl 7 calcMult as *@

calcMult :: forall a. Number -> Size a -> Size a
calcMult a b = MultSize a $ mkExists b

infixl 7 calcMultFlipped as @*

calcMultFlipped :: forall a. Size a -> Number -> Size a
calcMultFlipped = flip calcMult

infixl 7 calcDiv as @/

calcDiv :: forall a. Size a -> Number -> Size a
calcDiv a b = DivSize b $ mkExists a

sym :: forall a b. (a -> a -> a -> a -> b) -> a -> b
sym f a = f a a a a
Expand Down
18 changes: 17 additions & 1 deletion test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Prelude

import Effect (Effect)
import Effect.Exception (error, throwException)
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)
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, (@+@), (@-@), (@*), (*@), (@/))
import CSS.Cursor as Cursor
import CSS.Flexbox (flex)
import CSS.FontStyle as FontStyle
Expand Down Expand Up @@ -160,6 +160,18 @@ transition2 :: Rendered
transition2 = render do
transition "background-color" (ms 1.0) (cubicBezier 0.3 0.3 0.7 1.4) (ms 0.0)

calc1 :: Rendered
calc1 = render do
width $ (em 2.0 @/ 3.0) @+@ px 1.0

calc2 :: Rendered
calc2 = render do
width $ ((pct 100.0) @/ 7.0) @* 4.0

calc3 :: Rendered
calc3 = render do
width $ 5.0 *@ (pct (-20.0) @-@ px 10.0)

assertEqual :: forall a. Eq a => Show a => a -> a -> Effect Unit
assertEqual x y = unless (x == y) <<< throwException <<< error $ "Assertion failed: " <> show x <> " /= " <> show y

Expand Down Expand Up @@ -216,3 +228,7 @@ main = do

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"
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"

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)"
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))"
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))"