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