on a268421 help sign in
elk/libraries/Graphics/Element.elm
262 lines 8.7 kB history raw
2module Graphics.Element where
4import open Basics
5import Native.Utils
6import JavaScript as JS
7import JavaScript (JSString)
8import List as List
9import open Color
10import Maybe (Maybe, Just, Nothing)
12type Properties = {
13 id : Int,
14 width : Int,
15 height : Int,
16 opacity : Float,
17 color : Maybe Color,
18 href : JSString,
19 tag : JSString,
20 hover : ()
21 }
23type Element = { props : Properties, element : ElementPrim }
25-- Get the width of an Element
26widthOf : Element -> Int
27widthOf e = e.props.width
29-- Get the height of an Element
30heightOf : Element -> Int
31heightOf e = e.props.height
33-- Get the width and height of an Element
34sizeOf : Element -> (Int,Int)
35sizeOf e = (e.props.width, e.props.height)
37-- Create an `Element` with a given width.
38width : Int -> Element -> Element
39width nw e =
40 let p = e.props
41 props = case e.element of
42 Image _ w h _ -> {p| height <- round (toFloat h / toFloat w * toFloat nw) }
43 RawHtml html -> {p| height <- snd (Native.Utils.htmlHeight nw html)}
44 _ -> p
45 in { element=e.element, props={ props | width <- nw } }
47-- Create an `Element` with a given height.
48height : Int -> Element -> Element
49height nh e =
50 let p = e.props
51 props = case e.element of
52 Image _ w h _ -> {p| width <- round (toFloat w / toFloat h * toFloat nh) }
53 _ -> p
54 in { element=e.element, props={ p | height <- nh} }
56-- Create an `Element` with a new width and height.
57size : Int -> Int -> Element -> Element
58size w h e = height h (width w e)
60-- Create an `Element` with a given opacity. Opacity is a number between 0 and 1
61-- where 0 means totally clear.
62opacity : Float -> Element -> Element
63opacity o e = let p = e.props in { element=e.element, props={p| opacity <- o} }
65-- Create an `Element` with a given background color.
66color : Color -> Element -> Element
67color c e = let p = e.props in
68 { element = e.element
69 , props = { p | color <- Just c}
70 }
72-- Create an `Element` with a tag. This lets you link directly to it.
73-- The element `(tag "all-about-badgers" thirdParagraph)` can be reached
74-- with a link lik this: `/facts-about-animals.elm#all-about-badgers`
75tag : String -> Element -> Element
76tag name e = let p = e.props in
77 { element=e.element, props={p | tag <- JS.fromString name} }
79-- Create an `Element` that is a hyper-link.
80link : String -> Element -> Element
81link href e = let p = e.props in
82 { element=e.element, props={p | href <- JS.fromString href} }
84emptyStr = JS.fromString ""
85newElement w h e =
86 { props = Properties (Native.Utils.guid ()) w h 1 Nothing emptyStr emptyStr (), element = e }
88data ElementPrim
89 = Image ImageStyle Int Int JSString
90 | Container Position Element
91 | Flow Direction [Element]
92 | Spacer
93 | RawHtml JSString
94 | Custom -- for custom Elements implemented in JS, see collage for example
96data ImageStyle = Plain | Fitted | Cropped (Int,Int) | Tiled
98-- Create an image given a width, height, and image source.
99image : Int -> Int -> String -> Element
100image w h src = newElement w h (Image Plain w h (JS.fromString src))
102-- Create a fitted image given a width, height, and image source.
103-- This will crop the picture to best fill the given dimensions.
104fittedImage : Int -> Int -> String -> Element
105fittedImage w h src = newElement w h (Image Fitted w h (JS.fromString src))
107-- Create a cropped image. Take a rectangle out of the picture starting
108-- at the given top left coordinate. If you have a 140-by-140 image,
109-- the following will cut a 100-by-100 square out of the middle of it.
110--
111-- croppedImage (20,20) 100 100 "yogi.jpg"
112croppedImage : (Int,Int) -> Int -> Int -> String -> Element
113croppedImage pos w h src =
114 newElement w h (Image (Cropped pos) w h (JS.fromString src))
116tiledImage : Int -> Int -> String -> Element
117tiledImage w h src =
118 newElement w h (Image Tiled w h (JS.fromString src))
120markdown : Element
121markdown = Native.Utils.undefined
123data Three = P | Z | N
124data Pos = Absolute Int | Relative Float
125type Position = { horizontal : Three, vertical : Three, x : Pos, y : Pos }
127-- Put an element in a container. This lets you position the element really
128-- easily, and there are tons of ways to set the `Position`.
129-- To center `element` exactly in a 300-by-300 square you would say:
130--
131-- container 300 300 middle element
132--
133-- By setting the color of the container, you can create borders.
134container : Int -> Int -> Position -> Element -> Element
135container w h pos e = newElement w h (Container pos e)
137-- Create an empty box. This is useful for getting your spacing right and
138-- for making borders.
139spacer : Int -> Int -> Element
140spacer w h = newElement w h Spacer
142data Direction = DUp | DDown | DLeft | DRight | DIn | DOut
144-- Have a list of elements flow in a particular direction.
145-- The `Direction` starts from the first element in the list.
146--
147-- flow right [a,b,c]
148--
149-- +---+---+---+
150-- | a | b | c |
151-- +---+---+---+
152flow : Direction -> [Element] -> Element
153flow dir es =
154 let ws = List.map widthOf es
155 hs = List.map heightOf es
156 newFlow w h = newElement w h (Flow dir es)
157 in
158 if es == [] then spacer 0 0 else
159 case dir of
160 DUp -> newFlow (List.maximum ws) (List.sum hs)
161 DDown -> newFlow (List.maximum ws) (List.sum hs)
162 DLeft -> newFlow (List.sum ws) (List.maximum hs)
163 DRight -> newFlow (List.sum ws) (List.maximum hs)
164 DIn -> newFlow (List.maximum ws) (List.maximum hs)
165 DOut -> newFlow (List.maximum ws) (List.maximum hs)
167-- Stack elements vertically. To put `a` above `b` you would say:
168--
169-- a `above` b
170above : Element -> Element -> Element
171above hi lo =
172 newElement (max (widthOf hi) (widthOf lo))
173 (heightOf hi + heightOf lo)
174 (Flow DDown [hi,lo])
176-- Stack elements vertically. To put `a` below `b` you would say:
177--
178-- a `below` b
179below : Element -> Element -> Element
180below lo hi =
181 newElement (max (widthOf hi) (widthOf lo))
182 (heightOf hi + heightOf lo)
183 (Flow DDown [hi,lo])
185-- Put elements beside each other horizontally.
186beside : Element -> Element -> Element
187beside lft rht =
188 newElement (widthOf lft + widthOf rht)
189 (max (heightOf lft) (heightOf rht))
190 (Flow right [lft,rht])
192-- Layer elements on top of each other, starting from the bottom.
193-- `(layers == flow outward)`
194layers : [Element] -> Element
195layers es =
196 let ws = List.map widthOf es
197 hs = List.map heightOf es
198 in newElement (List.maximum ws) (List.maximum hs) (Flow DOut es)
201-- Repetitive things --
203absolute : Int -> Pos
204absolute = Absolute
205relative : Float -> Pos
206relative = Relative
208middle : Position
209middle = { horizontal=Z, vertical=Z, x=Relative 0.5, y=Relative 0.5 }
210topLeft : Position
211topLeft = { horizontal=N, vertical=P, x=Absolute 0, y=Absolute 0 }
212topRight : Position
213topRight = { topLeft | horizontal <- P }
214bottomLeft : Position
215bottomLeft = { topLeft | vertical <- N }
216bottomRight : Position
217bottomRight = { bottomLeft | horizontal <- P }
218midLeft : Position
219midLeft = { middle | horizontal <- N, x <- Absolute 0 }
220midRight : Position
221midRight = { midLeft | horizontal <- P }
222midTop : Position
223midTop = { middle | vertical <- P, y <- Absolute 0 }
224midBottom : Position
225midBottom = { midTop | vertical <- N }
227middleAt : Pos -> Pos -> Position
228middleAt x y = { horizontal = Z, vertical = Z, x = x, y = y }
229topLeftAt : Pos -> Pos -> Position
230topLeftAt x y = { horizontal = N, vertical = P, x = x, y = y }
231topRightAt : Pos -> Pos -> Position
232topRightAt x y = { horizontal = P, vertical = P, x = x, y = y }
233bottomLeftAt : Pos -> Pos -> Position
234bottomLeftAt x y = { horizontal = N, vertical = N, x = x, y = y }
235bottomRightAt : Pos -> Pos -> Position
236bottomRightAt x y = { horizontal = P, vertical = N, x = x, y = y }
237midLeftAt : Pos -> Pos -> Position
238midLeftAt x y = { horizontal = N, vertical = Z, x = x, y = y }
239midRightAt : Pos -> Pos -> Position
240midRightAt x y = { horizontal = P, vertical = Z, x = x, y = y }
241midTopAt : Pos -> Pos -> Position
242midTopAt x y = { horizontal = Z, vertical = P, x = x, y = y }
243midBottomAt : Pos -> Pos -> Position
244midBottomAt x y = { horizontal = Z, vertical = N, x = x, y = y }
246up : Direction
247up = DUp
249down : Direction
250down = DDown
252left : Direction
253left = DLeft
255right : Direction
256right = DRight
258inward : Direction
259inward = DIn
261outward : Direction
262outward = DOut