0 | module CyBy.UI.CSS.Rules
  1 |
  2 | import Chem.Elem
  3 | import CyBy.UI.CSS.Classes
  4 | import CyBy.UI.CSS.Vars
  5 | import CyBy.UI.HTML
  6 | import Derive.Prelude
  7 | import IO.Async.Logging
  8 | import Text.CSS.Cursor
  9 | import Text.HTML.DomID
 10 | import Text.HTML.Ref
 11 | import Text.HTML.Tag
 12 | import Web.Async.Widget.Types
 13 |
 14 | %default total
 15 | %language ElabReflection
 16 |
 17 | data Tag = Util | Templates | Elems | Info | Draw | Dot
 18 |
 19 | %runElab derive "Tag" [Show,Eq]
 20 |
 21 | export
 22 | formValues : List Selector
 23 | formValues =
 24 |   [ elem Li > elem Div
 25 |   , elem Li > class widget
 26 |   , elem Li > elem Input
 27 |   , elem Li > elem Select
 28 |   ]
 29 |
 30 | export
 31 | widgetSelectors : List Selector
 32 | widgetSelectors = [Elem Button, Elem Input, Elem Select, Class widget]
 33 |
 34 | --------------------------------------------------------------------------------
 35 | -- Declarations
 36 | --------------------------------------------------------------------------------
 37 |
 38 | parameters {auto v : Vars}
 39 |   export
 40 |   gridGaps : Declarations
 41 |   gridGaps = [rowGap 0.5.em, columnGap 0.5.em]
 42 |
 43 |   export
 44 |   hpadded : Declaration
 45 |   hpadded = padding (VH 0.px v.padding)
 46 |
 47 |   export
 48 |   hbpadded : Declaration
 49 |   hbpadded = padding (THB 0.px v.padding v.padding)
 50 |
 51 |   export
 52 |   padded : Declaration
 53 |   padded = padding (All v.padding)
 54 |
 55 |   ||| Regular widget with default colors for font, background, and border.
 56 |   export
 57 |   widgetRegular : Declarations
 58 |   widgetRegular =
 59 |     [ backgroundColor widgetBG
 60 |     , color widgetFG
 61 |     , outlineStyle None
 62 |     , round4
 63 |     ] ++ border1 Current ++ exactHeight v.widgetHeight ++ centerRow
 64 |
 65 |   ||| Widget that has either the `data-active` attribute set, or
 66 |   ||| is in an `active` state (has the `:active` pseudoclass).
 67 |   export
 68 |   wactive : Declarations
 69 |   wactive = [backgroundColor activeBG, color activeFG]
 70 |
 71 |   ||| Widget that is being hovered over (has the `:hover` pseudoclass).
 72 |   export
 73 |   whovered : Declarations
 74 |   whovered = [backgroundColor hoverBG]
 75 |
 76 |   ||| Widget that has currently visible focus
 77 |   ||| (has the `:focus-visible` pseudoclass).
 78 |   export
 79 |   wfocus : Declarations
 80 |   wfocus = [borderWidth $ All 2.px]
 81 |
 82 |   ||| Currently invalid widget
 83 |   export
 84 |   winvalid : Declarations
 85 |   winvalid = [color v.errorColor]
 86 |
 87 |   ||| Mandatory widget with currently missing input
 88 |   export
 89 |   wmissing : Declarations
 90 |   wmissing = [color missingFG]
 91 |
 92 |   ||| Disabled widget (has the `:disabled` pseudoclass).
 93 |   export
 94 |   wdisabled : Declarations
 95 |   wdisabled =
 96 |     [color disabledFG, backgroundColor disabledBG, borderColor (All disabledBG)]
 97 |
 98 |   ||| Outline and border of a cyby-draw component.
 99 |   export
100 |   sectionBorder : Declarations
101 |   sectionBorder = round8 :: borderHB1 headerBG
102 |
103 |   export
104 |   sectionHeader : Declarations
105 |   sectionHeader =
106 |        backgroundColor headerBG
107 |     :: color headerFG
108 |     -- horizontal padding is for readability, vertical padding
109 |     -- is to make sure a separator bar does not cut the header in two
110 |     :: padding (VH v.smallPadding v.padding)
111 |     :: exactHeight v.titleHeight
112 |     ++ centerSepRow
113 |
114 |   export
115 |   sectionList : Declarations
116 |   sectionList = [flex1, overflowY Auto, hbpadded] ++ stretchSepCol
117 |
118 |   export
119 |   vsep : Declarations
120 |   vsep =
121 |        margin (VH v.barSepMargin 0.px)
122 |     :: width 100.perc
123 |     :: borderRadius v.barSepRadius
124 |     :: exactHeight v.barSepWidth
125 |
126 |   export
127 |   hsep : Declarations
128 |   hsep =
129 |        margin (VH 0.px v.barSepMargin)
130 |     :: height 100.perc
131 |     :: borderRadius v.barSepRadius
132 |     :: exactWidth v.barSepWidth
133 |
134 |   export
135 |   iconDecl : Declarations
136 |   iconDecl = [noPadding, aspectRatio 1]
137 |
138 |   export
139 |   roundIconDecl : Declarations
140 |   roundIconDecl = round :: iconDecl
141 |
142 |   levelRule : LogLevel -> Color -> Rule n
143 |   levelRule l c =
144 |     class (level l) [fontWeight Normal, color c, width v.levelWidth]
145 |
146 |   validIcon : Color.Color -> Declarations
147 |   validIcon c =
148 |     [ color c
149 |     , height v.widgetHeight
150 |     , aspectRatio 1
151 |     , position Absolute
152 |     , top 0.px
153 |     , right 5.px
154 |     ]
155 |
156 | --------------------------------------------------------------------------------
157 | -- General
158 | --------------------------------------------------------------------------------
159 |
160 | parameters {auto v : Vars}
161 |
162 |   export
163 |   widgetRules : Selector -> Rules
164 |   widgetRules s =
165 |     [ sel s $ hpadded :: widgetRegular
166 |     , sel [s, Hover] whovered
167 |     , sel [s, FocusVisible] wfocus
168 |     , sel [s, boolAttr active] wactive
169 |     , sel (elem Section > (elem Header > s)) [backgroundColor widgetInvertBG, color widgetInvertFG]
170 |     , sel (elem Section > (elem Header > [s,Hover])) [backgroundColor hoverInvertBG]
171 |     , sel [s, Disabled] wdisabled
172 |     , sel [s, Invalid] winvalid
173 |     , sel [s, attr (validity {s = ()}) (Invalid "")] winvalid
174 |     , sel [s, attr (validity {s = ()}) Missing] wmissing
175 |     ]
176 |
177 |   export
178 |   general : Rules
179 |   general =
180 |     [ elem Html [height 100.perc, width 100.perc]
181 |   
182 |     , elem Body
183 |         [ display Flex
184 |         , height 100.perc
185 |         , width 100.perc
186 |         , backgroundColor bg
187 |         , color fg
188 |         , padded
189 |         , containerType Size
190 |         ]
191 |
192 |     , elem Header [noMargin]
193 |     , elem Ul [noMargin, noPadding, decl "list-style" "none"]
194 |
195 |     -- this makes sure that the text in a label is vertically centered
196 |     , elem Label [display Flex , alignItems Center]
197 |
198 |     , class sep $ [backgroundColor bar, height v.formSepWidth]
199 |     , class spacer [flex1]
200 |     , elem A [color widgetFG]
201 |     ]
202 |
203 |   ||| Rules the main UI components
204 |   export
205 |   components : Rules
206 |   components =
207 |     [ class sketcher $
208 |         area
209 |           [cast v.bardim, 1.fr, cast v.bardim]
210 |           [cast v.bardim, 4.fr, 1.fr]
211 |           [ [Dot,   Util,      Util     ]
212 |           , [Elems, Draw,      Info     ]
213 |           , [Dot,   Templates, Templates]
214 |           ]
215 |
216 |         -- make sure the sketcher always fills the parent perfectly
217 |         -- without resizing the parent in case the sketcher's size
218 |         -- changes.
219 |         :: position Absolute
220 |         :: inset (All 0.px)
221 |
222 |         -- scroll bars in case the parent is too small
223 |         :: overflow Auto
224 |         :: gridGaps
225 |
226 |     -- the following rules make for a responsive design:
227 |     -- by reducing the font size of the sketcher, the dimensions of
228 |     -- all other components as well as paddings and corners are
229 |     -- adjusted as well.
230 |     , Container "width < 1440px" [class sketcher [fontSize v.smallFont]]
231 |     , Container "width < 1024px" [class sketcher [fontSize v.xsmallFont]]
232 |     , Container "width < 768px"  [class sketcher [fontSize v.xxsmallFont]]
233 |
234 |     -- the drawing canvas
235 |     , class moleculeCanvas $
236 |            minWidth 0.px    -- necessary to resize this when parent is resized
237 |         :: minHeight 0.px   -- necessary to resize this when parent is resized
238 |         :: gridArea Draw
239 |         :: outlineStyle None
240 |         :: round4
241 |         :: overflow Hidden
242 |         :: border1 compBorder
243 |
244 |     -- drawing canvas: special states
245 |     , sel [class moleculeCanvas, boolAttr active]
246 |         [ backgroundColor v.gray.c100
247 |         , borderColor (All activeBG)
248 |         ]
249 |     , attribute dragMode Dragging [cursor [Move]]
250 |     , attribute dragMode Rotating
251 |         [cursor [URL_ "data:image/png;base64,\{rotate}", Cursor.Auto]]
252 |
253 |     -- CyBy Draw toolbars
254 |     , class drawUtils $ gridArea Util :: centerSepRow
255 |     , class drawTemplates $ gridArea Templates :: centerSepRow
256 |     , class drawElems $ gridArea Elems :: centerSepCol
257 |     , class drawInfo $ [containerType Size, gridArea Rules.Info] ++ stretchSepCol
258 |     , sel (class drawUtils > class sep) hsep
259 |     , sel (class drawElems > class sep) vsep
260 |     , sel (class drawTemplates > class sep) hsep
261 |
262 |     -- CyBy Sections (Cards)
263 |     , elem Section $ overflow Hidden :: stretchSepCol ++ sectionBorder
264 |     , sel (elem Section > elem Header) sectionHeader
265 |     , sel (elem Section > elem Ul) sectionList
266 |     , class drawDetails [containerType Size, flex2]
267 |
268 |     -- logging
269 |     , class drawLog [fontSize v.smallFont, containerType Size, flex1]
270 |     , levelRule Fatal v.errorColor
271 |     , levelRule Error v.errorColor
272 |     , levelRule Warn  v.warnColor
273 |     , levelRule Info  v.infoColor
274 |     , levelRule Debug v.debugColor
275 |     , levelRule Trace v.traceColor
276 |     ]
277 |
278 |   ||| Rules for form-like lists (label plus description/widget)
279 |   export
280 |   forms : Rules
281 |   forms =
282 |     [ elem Li startSepRow
283 |     , sel (elem Li > elem Label) [width v.formLblWidth, fontWeight Bold]
284 |     , Sel formValues [flex1]
285 |     , Container "width < 300px"
286 |         [ elem Li startSepCol
287 |         , sel (elem Li > elem Label) [width 100.perc]
288 |         , Sel formValues [noflex]
289 |         ]
290 |     ]
291 |
292 |   ||| Rules for interactive UI elements
293 |   export
294 |   widgets : Rules
295 |   widgets =
296 |     (widgetSelectors >>= widgetRules) ++
297 |     [ class icon iconDecl
298 |     , class roundIcon roundIconDecl
299 |     , class elem [fontWeight Bold, justifyContent Center]
300 |     , class pseIcon [decl "letter-spacing" "-2px"]
301 |     , sel [elem Button, Hover] [cursor [Pointer]]
302 |     , sel [elem Button, Disabled] [cursor [NotAllowed]]
303 |     , sel [class widget, Hover] [cursor [Pointer]]
304 |     , sel [class widget, Disabled] [cursor [NotAllowed]]
305 |     , sel [elem Select, Hover] [cursor [Pointer]]
306 |     , sel [elem Select, Disabled] [cursor [NotAllowed]]
307 |     , sel [elem Input, attr type File] [display None]
308 |     , class iconPlaceholder [display None]
309 |     , class iconMissing $ validIcon missingFG
310 |     , class iconError $ validIcon v.errorColor
311 |     , class validatedInput $ position Relative :: flexRow
312 |     , sel (class validatedInput > elem Input) [flex1]
313 |     , sel (elem Li > class validatedInput) [flex1]
314 |     , class deleteIcon $ color v.secondary.c600 :: roundIconDecl
315 |     , class okIcon roundIconDecl
316 |     , class addIcon roundIconDecl
317 |     ]
318 |
319 |   export
320 |   all : Rules
321 |   all = general ++ components ++ forms ++ widgets
322 |
323 |   draw : Rules
324 |   draw = class sketcher [padded] :: all
325 |
326 | main : IO ()
327 | main = traverse_ (putStrLn . interpolate) (draw @{defaultVars})
328 |