@@ -0,0 +1,3564 @@
1+module Internal.Model exposing
2+ ( Aligned(..)
3+ , Angle
4+ , Attribute(..)
5+ , Axis(..)
6+ , Children(..)
7+ , Color(..)
8+ , Description(..)
9+ , Element(..)
10+ , EmbedStyle(..)
11+ , FocusStyle
12+ , Font(..)
13+ , Gathered
14+ , HAlign(..)
15+ , HoverSetting(..)
16+ , LayoutContext(..)
17+ , Length(..)
18+ , Location(..)
19+ , NearbyChildren(..)
20+ , NodeName(..)
21+ , Option(..)
22+ , OptionRecord
23+ , Padding(..)
24+ , Property(..)
25+ , PseudoClass(..)
26+ , RenderMode(..)
27+ , Shadow
28+ , Spacing(..)
29+ , Style(..)
30+ , TransformComponent(..)
31+ , Transformation(..)
32+ , VAlign(..)
33+ , Variant(..)
34+ , XYZ
35+ , addNodeName
36+ , addWhen
37+ , alignXName
38+ , alignYName
39+ , asColumn
40+ , asEl
41+ , asGrid
42+ , asParagraph
43+ , asRow
44+ , asTextColumn
45+ , boxShadowClass
46+ , columnClass
47+ , composeTransformation
48+ , contextClasses
49+ , createElement
50+ , defaultOptions
51+ , div
52+ , element
53+ , embedKeyed
54+ , embedWith
55+ , extractSpacingAndPadding
56+ , filter
57+ , finalizeNode
58+ , floatClass
59+ , focusDefaultStyle
60+ , formatBoxShadow
61+ , formatColor
62+ , formatColorClass
63+ , formatDropShadow
64+ , formatTextShadow
65+ , gatherAttrRecursive
66+ , get
67+ , getHeight
68+ , getSpacing
69+ , getStyleName
70+ , getWidth
71+ , gridClass
72+ , htmlClass
73+ , isContent
74+ , lengthClassName
75+ , map
76+ , mapAttr
77+ , mapAttrFromStyle
78+ , noStyleSheet
79+ , onlyStyles
80+ , optionsToRecord
81+ , paddingName
82+ , paddingNameFloat
83+ , pageClass
84+ , paragraphClass
85+ , reduceRecursive
86+ , reduceStyles
87+ , reduceStylesRecursive
88+ , removeNever
89+ , renderFontClassName
90+ , renderHeight
91+ , renderRoot
92+ , renderVariant
93+ , renderWidth
94+ , rootStyle
95+ , rowClass
96+ , singleClass
97+ , spacingName
98+ , tag
99+ , textShadowClass
100+ , toHtml
101+ , toStyleSheet
102+ , transformClass
103+ , unstyled
104+ , unwrapDecorations
105+ , variantName
106+ )
107+
108+{-| -}
109+
110+import Html
111+import Html.Attributes
112+import Html.Keyed
113+import Internal.Flag as Flag exposing (Flag)
114+import Internal.Style exposing (classes, dot)
115+import Json.Encode as Encode
116+import Set exposing (Set)
117+import VirtualDom
118+
119+
120+type Element msg
121+ = Unstyled (LayoutContext -> VirtualDom.Node msg)
122+ | Styled
123+ { styles : List Style
124+ , html : EmbedStyle -> LayoutContext -> VirtualDom.Node msg
125+ }
126+ | Text String
127+ | Empty
128+
129+
130+type EmbedStyle
131+ = NoStyleSheet
132+ | StaticRootAndDynamic OptionRecord (List Style)
133+ | OnlyDynamic OptionRecord (List Style)
134+
135+
136+noStyleSheet : EmbedStyle
137+noStyleSheet =
138+ NoStyleSheet
139+
140+
141+type LayoutContext
142+ = AsRow
143+ | AsColumn
144+ | AsEl
145+ | AsGrid
146+ | AsParagraph
147+ | AsTextColumn
148+
149+
150+type Aligned
151+ = Unaligned
152+ | Aligned (Maybe HAlign) (Maybe VAlign)
153+
154+
155+type HAlign
156+ = Left
157+ | CenterX
158+ | Right
159+
160+
161+type VAlign
162+ = Top
163+ | CenterY
164+ | Bottom
165+
166+
167+type Style
168+ = Style String (List Property)
169+ -- class prop val
170+ | FontFamily String (List Font)
171+ | FontSize Int
172+ -- classname, prop, value
173+ | Single String String String
174+ | Colored String String Color
175+ | SpacingStyle String Int Int
176+ | BorderWidth String Int Int Int Int
177+ | PaddingStyle String Float Float Float Float
178+ | GridTemplateStyle
179+ { spacing : ( Length, Length )
180+ , columns : List Length
181+ , rows : List Length
182+ }
183+ | GridPosition
184+ { row : Int
185+ , col : Int
186+ , width : Int
187+ , height : Int
188+ }
189+ | Transform Transformation
190+ | PseudoSelector PseudoClass (List Style)
191+ | Transparency String Float
192+ | Shadows String String
193+
194+
195+type Transformation
196+ = Untransformed
197+ | Moved XYZ
198+ -- translate, scale, rotate
199+ | FullTransform XYZ XYZ XYZ Angle
200+
201+
202+type PseudoClass
203+ = Focus
204+ | Hover
205+ | Active
206+
207+
208+{-| -}
209+type alias Adjustment =
210+ { capital : Float
211+ , lowercase : Float
212+ , baseline : Float
213+ , descender : Float
214+ }
215+
216+
217+type Font
218+ = Serif
219+ | SansSerif
220+ | Monospace
221+ | Typeface String
222+ | ImportFont String String
223+ | FontWith
224+ { name : String
225+ , adjustment : Maybe Adjustment
226+ , variants : List Variant
227+ }
228+
229+
230+type Variant
231+ = VariantActive String
232+ | VariantOff String
233+ | VariantIndexed String Int
234+
235+
236+renderVariant var =
237+ case var of
238+ VariantActive name ->
239+ "\"" ++ name ++ "\""
240+
241+ VariantOff name ->
242+ "\"" ++ name ++ "\" 0"
243+
244+ VariantIndexed name index ->
245+ "\"" ++ name ++ "\" " ++ String.fromInt index
246+
247+
248+variantName var =
249+ case var of
250+ VariantActive name ->
251+ name
252+
253+ VariantOff name ->
254+ name ++ "-0"
255+
256+ VariantIndexed name index ->
257+ name ++ "-" ++ String.fromInt index
258+
259+
260+renderVariants typeface =
261+ case typeface of
262+ FontWith font ->
263+ Just (String.join ", " (List.map renderVariant font.variants))
264+
265+ _ ->
266+ Nothing
267+
268+
269+isSmallCaps var =
270+ case var of
271+ VariantActive name ->
272+ name == "smcp"
273+
274+ VariantOff name ->
275+ False
276+
277+ VariantIndexed name index ->
278+ name == "smcp" && index == 1
279+
280+
281+hasSmallCaps typeface =
282+ case typeface of
283+ FontWith font ->
284+ List.any isSmallCaps font.variants
285+
286+ _ ->
287+ False
288+
289+
290+type Property
291+ = Property String String
292+
293+
294+type alias XYZ =
295+ ( Float, Float, Float )
296+
297+
298+type alias Angle =
299+ Float
300+
301+
302+type Attribute aligned msg
303+ = NoAttribute
304+ | Attr (VirtualDom.Attribute msg)
305+ | Describe Description
306+ -- invalidation key and literal class
307+ | Class Flag String
308+ -- invalidation key "border-color" as opposed to "border-color-10-10-10" that will be the key for the class
309+ | StyleClass Flag Style
310+ | AlignY VAlign
311+ | AlignX HAlign
312+ | Width Length
313+ | Height Length
314+ | Nearby Location (Element msg)
315+ | TransformComponent Flag TransformComponent
316+
317+
318+type TransformComponent
319+ = MoveX Float
320+ | MoveY Float
321+ | MoveZ Float
322+ | MoveXYZ XYZ
323+ | Rotate XYZ Float
324+ | Scale XYZ
325+
326+
327+type Description
328+ = Main
329+ | Navigation
330+ -- | Search
331+ | ContentInfo
332+ | Complementary
333+ | Heading Int
334+ | Label String
335+ | LivePolite
336+ | LiveAssertive
337+ | Button
338+ | Paragraph
339+
340+
341+type Length
342+ = Px Int
343+ | Content
344+ | Fill Int
345+ | Min Int Length
346+ | Max Int Length
347+
348+
349+type Axis
350+ = XAxis
351+ | YAxis
352+ | AllAxis
353+
354+
355+type Location
356+ = Above
357+ | Below
358+ | OnRight
359+ | OnLeft
360+ | InFront
361+ | Behind
362+
363+
364+{-| -}
365+type Color
366+ = Rgba Float Float Float Float
367+
368+
369+type NodeName
370+ = Generic
371+ | NodeName String
372+ | Embedded String String
373+
374+
375+type NearbyChildren msg
376+ = NoNearbyChildren
377+ | ChildrenBehind (List (Html.Html msg))
378+ | ChildrenInFront (List (Html.Html msg))
379+ | ChildrenBehindAndInFront (List (Html.Html msg)) (List (Html.Html msg))
380+
381+
382+div =
383+ Generic
384+
385+
386+type alias Gathered msg =
387+ { node : NodeName
388+ , attributes : List (VirtualDom.Attribute msg)
389+ , styles : List Style
390+ , children : NearbyChildren msg
391+ , has : Flag.Field
392+ }
393+
394+
395+htmlClass : String -> Attribute aligned msg
396+htmlClass cls =
397+ Attr <| Html.Attributes.class cls
398+
399+
400+{-| -}
401+unstyled : VirtualDom.Node msg -> Element msg
402+unstyled =
403+ Unstyled << always
404+
405+
406+finalizeNode has node attributes children embedMode parentContext =
407+ let
408+ createNode nodeName attrs =
409+ case children of
410+ Keyed keyed ->
411+ VirtualDom.keyedNode nodeName
412+ attrs
413+ (case embedMode of
414+ NoStyleSheet ->
415+ keyed
416+
417+ OnlyDynamic opts styles ->
418+ embedKeyed False opts styles keyed
419+
420+ StaticRootAndDynamic opts styles ->
421+ embedKeyed True opts styles keyed
422+ )
423+
424+ Unkeyed unkeyed ->
425+ (case nodeName of
426+ "div" ->
427+ Html.div
428+
429+ "p" ->
430+ Html.p
431+
432+ _ ->
433+ VirtualDom.node nodeName
434+ )
435+ attrs
436+ (case embedMode of
437+ NoStyleSheet ->
438+ unkeyed
439+
440+ OnlyDynamic opts styles ->
441+ embedWith False opts styles unkeyed
442+
443+ StaticRootAndDynamic opts styles ->
444+ embedWith True opts styles unkeyed
445+ )
446+
447+ html =
448+ case node of
449+ Generic ->
450+ createNode "div" attributes
451+
452+ NodeName nodeName ->
453+ createNode nodeName attributes
454+
455+ Embedded nodeName internal ->
456+ VirtualDom.node nodeName
457+ attributes
458+ [ createNode internal
459+ [ Html.Attributes.class
460+ (classes.any ++ " " ++ classes.single)
461+ ]
462+ ]
463+ in
464+ case parentContext of
465+ AsRow ->
466+ if Flag.present Flag.widthFill has && not (Flag.present Flag.widthBetween has) then
467+ html
468+
469+ else if Flag.present Flag.alignRight has then
470+ Html.u
471+ [ Html.Attributes.class
472+ (String.join " "
473+ [ classes.any
474+ , classes.single
475+ , classes.container
476+ , classes.contentCenterY
477+ , classes.alignContainerRight
478+ ]
479+ )
480+ ]
481+ [ html ]
482+
483+ else if Flag.present Flag.centerX has then
484+ Html.s
485+ [ Html.Attributes.class
486+ (String.join " "
487+ [ classes.any
488+ , classes.single
489+ , classes.container
490+ , classes.contentCenterY
491+ , classes.alignContainerCenterX
492+ ]
493+ )
494+ ]
495+ [ html ]
496+
497+ else
498+ html
499+
500+ AsColumn ->
501+ if Flag.present Flag.heightFill has && not (Flag.present Flag.heightBetween has) then
502+ html
503+
504+ else if Flag.present Flag.centerY has then
505+ Html.s
506+ [ Html.Attributes.class
507+ (String.join " "
508+ [ classes.any
509+ , classes.single
510+ , classes.container
511+ , classes.alignContainerCenterY
512+ ]
513+ )
514+ ]
515+ [ html ]
516+
517+ else if Flag.present Flag.alignBottom has then
518+ Html.u
519+ [ Html.Attributes.class
520+ (String.join " "
521+ [ classes.any
522+ , classes.single
523+ , classes.container
524+ , classes.alignContainerBottom
525+ ]
526+ )
527+ ]
528+ [ html ]
529+
530+ else
531+ html
532+
533+ _ ->
534+ html
535+
536+
537+embedWith static opts styles children =
538+ let
539+ dynamicStyleSheet =
540+ styles
541+ |> List.foldl reduceStyles ( Set.empty, renderFocusStyle opts.focus )
542+ |> Tuple.second
543+ -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
544+ -- |> sortedReduce
545+ |> toStyleSheet opts
546+ in
547+ if static then
548+ staticRoot opts
549+ :: dynamicStyleSheet
550+ :: children
551+
552+ else
553+ dynamicStyleSheet
554+ :: children
555+
556+
557+embedKeyed static opts styles children =
558+ let
559+ dynamicStyleSheet =
560+ styles
561+ |> List.foldl reduceStyles ( Set.empty, renderFocusStyle opts.focus )
562+ |> Tuple.second
563+ -- |> reduceStylesRecursive Set.empty [ ]) --renderFocusStyle opts.focus ]
564+ -- |> sortedReduce
565+ |> toStyleSheet opts
566+ in
567+ if static then
568+ ( "static-stylesheet", staticRoot opts )
569+ :: ( "dynamic-stylesheet"
570+ , dynamicStyleSheet
571+ )
572+ :: children
573+
574+ else
575+ ( "dynamic-stylesheet"
576+ , dynamicStyleSheet
577+ )
578+ :: children
579+
580+
581+reduceStylesRecursive : Set String -> List Style -> List Style -> List Style
582+reduceStylesRecursive cache found styles =
583+ case styles of
584+ [] ->
585+ found
586+
587+ head :: remaining ->
588+ let
589+ styleName =
590+ getStyleName head
591+ in
592+ if Set.member styleName cache then
593+ reduceStylesRecursive cache found remaining
594+
595+ else
596+ reduceStylesRecursive (Set.insert styleName cache) (head :: found) remaining
597+
598+
599+reduceStyles : Style -> ( Set String, List Style ) -> ( Set String, List Style )
600+reduceStyles style (( cache, existing ) as nevermind) =
601+ let
602+ styleName =
603+ getStyleName style
604+ in
605+ if Set.member styleName cache then
606+ nevermind
607+
608+ else
609+ ( Set.insert styleName cache
610+ , style :: existing
611+ )
612+
613+
614+sortedReduce styles =
615+ styles
616+ -- |> List.map (\x -> ( getStyleName x, x ))
617+ |> List.sortBy getStyleName
618+ |> reduceRecursiveCalcName []
619+
620+
621+reduceRecursiveCalcName : List Style -> List Style -> List Style
622+reduceRecursiveCalcName found styles =
623+ case styles of
624+ [] ->
625+ found
626+
627+ headOfList :: [] ->
628+ headOfList :: found
629+
630+ headOfList :: other :: remaining ->
631+ if headOfList /= other then
632+ reduceRecursiveCalcName (headOfList :: found) (other :: remaining)
633+
634+ else
635+ reduceRecursiveCalcName found (other :: remaining)
636+
637+
638+reduceRecursive : List Style -> List ( String, Style ) -> List Style
639+reduceRecursive found styles =
640+ case styles of
641+ [] ->
642+ found
643+
644+ ( _, headOfList ) :: [] ->
645+ headOfList :: found
646+
647+ ( headOfListName, headOfList ) :: ( otherName, other ) :: remaining ->
648+ if headOfListName /= otherName then
649+ reduceRecursive (headOfList :: found) (( otherName, other ) :: remaining)
650+
651+ else
652+ reduceRecursive found (( otherName, other ) :: remaining)
653+
654+
655+addNodeName : String -> NodeName -> NodeName
656+addNodeName newNode old =
657+ case old of
658+ Generic ->
659+ NodeName newNode
660+
661+ NodeName name ->
662+ Embedded name newNode
663+
664+ Embedded x y ->
665+ Embedded x y
666+
667+
668+alignXName : HAlign -> String
669+alignXName align =
670+ case align of
671+ Left ->
672+ classes.alignedHorizontally ++ " " ++ classes.alignLeft
673+
674+ Right ->
675+ classes.alignedHorizontally ++ " " ++ classes.alignRight
676+
677+ CenterX ->
678+ classes.alignedHorizontally ++ " " ++ classes.alignCenterX
679+
680+
681+alignYName : VAlign -> String
682+alignYName align =
683+ case align of
684+ Top ->
685+ classes.alignedVertically ++ " " ++ classes.alignTop
686+
687+ Bottom ->
688+ classes.alignedVertically ++ " " ++ classes.alignBottom
689+
690+ CenterY ->
691+ classes.alignedVertically ++ " " ++ classes.alignCenterY
692+
693+
694+transformClass transform =
695+ case transform of
696+ Untransformed ->
697+ Nothing
698+
699+ Moved ( x, y, z ) ->
700+ Just <|
701+ "mv-"
702+ ++ floatClass x
703+ ++ "-"
704+ ++ floatClass y
705+ ++ "-"
706+ ++ floatClass z
707+
708+ FullTransform ( tx, ty, tz ) ( sx, sy, sz ) ( ox, oy, oz ) angle ->
709+ Just <|
710+ "tfrm-"
711+ ++ floatClass tx
712+ ++ "-"
713+ ++ floatClass ty
714+ ++ "-"
715+ ++ floatClass tz
716+ ++ "-"
717+ ++ floatClass sx
718+ ++ "-"
719+ ++ floatClass sy
720+ ++ "-"
721+ ++ floatClass sz
722+ ++ "-"
723+ ++ floatClass ox
724+ ++ "-"
725+ ++ floatClass oy
726+ ++ "-"
727+ ++ floatClass oz
728+ ++ "-"
729+ ++ floatClass angle
730+
731+
732+transformValue transform =
733+ case transform of
734+ Untransformed ->
735+ Nothing
736+
737+ Moved ( x, y, z ) ->
738+ Just <|
739+ "translate3d("
740+ ++ String.fromFloat x
741+ ++ "px, "
742+ ++ String.fromFloat y
743+ ++ "px, "
744+ ++ String.fromFloat z
745+ ++ "px)"
746+
747+ FullTransform ( tx, ty, tz ) ( sx, sy, sz ) ( ox, oy, oz ) angle ->
748+ let
749+ translate =
750+ "translate3d("
751+ ++ String.fromFloat tx
752+ ++ "px, "
753+ ++ String.fromFloat ty
754+ ++ "px, "
755+ ++ String.fromFloat tz
756+ ++ "px)"
757+
758+ scale =
759+ "scale3d("
760+ ++ String.fromFloat sx
761+ ++ ", "
762+ ++ String.fromFloat sy
763+ ++ ", "
764+ ++ String.fromFloat sz
765+ ++ ")"
766+
767+ rotate =
768+ "rotate3d("
769+ ++ String.fromFloat ox
770+ ++ ", "
771+ ++ String.fromFloat oy
772+ ++ ", "
773+ ++ String.fromFloat oz
774+ ++ ", "
775+ ++ String.fromFloat angle
776+ ++ "rad)"
777+ in
778+ Just <| translate ++ " " ++ scale ++ " " ++ rotate
779+
780+
781+composeTransformation transform component =
782+ case transform of
783+ Untransformed ->
784+ case component of
785+ MoveX x ->
786+ Moved ( x, 0, 0 )
787+
788+ MoveY y ->
789+ Moved ( 0, y, 0 )
790+
791+ MoveZ z ->
792+ Moved ( 0, 0, z )
793+
794+ MoveXYZ xyz ->
795+ Moved xyz
796+
797+ Rotate xyz angle ->
798+ FullTransform ( 0, 0, 0 ) ( 1, 1, 1 ) xyz angle
799+
800+ Scale xyz ->
801+ FullTransform ( 0, 0, 0 ) xyz ( 0, 0, 1 ) 0
802+
803+ Moved (( x, y, z ) as moved) ->
804+ case component of
805+ MoveX newX ->
806+ Moved ( newX, y, z )
807+
808+ MoveY newY ->
809+ Moved ( x, newY, z )
810+
811+ MoveZ newZ ->
812+ Moved ( x, y, newZ )
813+
814+ MoveXYZ xyz ->
815+ Moved xyz
816+
817+ Rotate xyz angle ->
818+ FullTransform moved ( 1, 1, 1 ) xyz angle
819+
820+ Scale scale ->
821+ FullTransform moved scale ( 0, 0, 1 ) 0
822+
823+ FullTransform (( x, y, z ) as moved) scaled origin angle ->
824+ case component of
825+ MoveX newX ->
826+ FullTransform ( newX, y, z ) scaled origin angle
827+
828+ MoveY newY ->
829+ FullTransform ( x, newY, z ) scaled origin angle
830+
831+ MoveZ newZ ->
832+ FullTransform ( x, y, newZ ) scaled origin angle
833+
834+ MoveXYZ newMove ->
835+ FullTransform newMove scaled origin angle
836+
837+ Rotate newOrigin newAngle ->
838+ FullTransform moved scaled newOrigin newAngle
839+
840+ Scale newScale ->
841+ FullTransform moved newScale origin angle
842+
843+
844+skippable flag style =
845+ if flag == Flag.borderWidth then
846+ case style of
847+ Single _ _ val ->
848+ case val of
849+ "0px" ->
850+ True
851+
852+ "1px" ->
853+ True
854+
855+ "2px" ->
856+ True
857+
858+ "3px" ->
859+ True
860+
861+ "4px" ->
862+ True
863+
864+ "5px" ->
865+ True
866+
867+ "6px" ->
868+ True
869+
870+ _ ->
871+ False
872+
873+ _ ->
874+ False
875+
876+ else
877+ case style of
878+ FontSize i ->
879+ i >= 8 && i <= 32
880+
881+ PaddingStyle name t r b l ->
882+ t == b && t == r && t == l && t >= 0 && t <= 24
883+
884+ -- SpacingStyle _ _ _ ->
885+ -- True
886+ -- FontFamily _ _ ->
887+ -- True
888+ _ ->
889+ False
890+
891+
892+gatherAttrRecursive :
893+ String
894+ -> NodeName
895+ -> Flag.Field
896+ -> Transformation
897+ -> List Style
898+ -> List (VirtualDom.Attribute msg)
899+ -> NearbyChildren msg
900+ -> List (Attribute aligned msg)
901+ -> Gathered msg
902+gatherAttrRecursive classes node has transform styles attrs children elementAttrs =
903+ case elementAttrs of
904+ [] ->
905+ case transformClass transform of
906+ Nothing ->
907+ { attributes = Html.Attributes.class classes :: attrs
908+ , styles = styles
909+ , node = node
910+ , children = children
911+ , has = has
912+ }
913+
914+ Just class ->
915+ { attributes = Html.Attributes.class (classes ++ " " ++ class) :: attrs
916+ , styles = Transform transform :: styles
917+ , node = node
918+ , children = children
919+ , has = has
920+ }
921+
922+ attribute :: remaining ->
923+ case attribute of
924+ NoAttribute ->
925+ gatherAttrRecursive classes node has transform styles attrs children remaining
926+
927+ Class flag exactClassName ->
928+ if Flag.present flag has then
929+ gatherAttrRecursive classes node has transform styles attrs children remaining
930+
931+ else
932+ gatherAttrRecursive (exactClassName ++ " " ++ classes) node (Flag.add flag has) transform styles attrs children remaining
933+
934+ Attr actualAttribute ->
935+ gatherAttrRecursive classes node has transform styles (actualAttribute :: attrs) children remaining
936+
937+ StyleClass flag style ->
938+ if Flag.present flag has then
939+ gatherAttrRecursive classes node has transform styles attrs children remaining
940+
941+ else if skippable flag style then
942+ gatherAttrRecursive (getStyleName style ++ " " ++ classes)
943+ node
944+ (Flag.add flag has)
945+ transform
946+ styles
947+ attrs
948+ children
949+ remaining
950+
951+ else
952+ gatherAttrRecursive (getStyleName style ++ " " ++ classes)
953+ node
954+ (Flag.add flag has)
955+ transform
956+ (style :: styles)
957+ attrs
958+ children
959+ remaining
960+
961+ TransformComponent flag component ->
962+ gatherAttrRecursive classes
963+ node
964+ (Flag.add flag has)
965+ (composeTransformation transform component)
966+ styles
967+ attrs
968+ children
969+ remaining
970+
971+ Width width ->
972+ if Flag.present Flag.width has then
973+ gatherAttrRecursive classes node has transform styles attrs children remaining
974+
975+ else
976+ case width of
977+ Px px ->
978+ gatherAttrRecursive ((Internal.Style.classes.widthExact ++ " width-px-" ++ String.fromInt px) ++ " " ++ classes)
979+ node
980+ (Flag.add Flag.width has)
981+ transform
982+ (Single ("width-px-" ++ String.fromInt px) "width" (String.fromInt px ++ "px") :: styles)
983+ attrs
984+ children
985+ remaining
986+
987+ Content ->
988+ gatherAttrRecursive (classes ++ " " ++ Internal.Style.classes.widthContent)
989+ node
990+ (Flag.add Flag.widthContent (Flag.add Flag.width has))
991+ transform
992+ styles
993+ attrs
994+ children
995+ remaining
996+
997+ Fill portion ->
998+ if portion == 1 then
999+ gatherAttrRecursive (classes ++ " " ++ Internal.Style.classes.widthFill)
1000+ node
1001+ (Flag.add Flag.widthFill (Flag.add Flag.width has))
1002+ transform
1003+ styles
1004+ attrs
1005+ children
1006+ remaining
1007+
1008+ else
1009+ gatherAttrRecursive (classes ++ " " ++ Internal.Style.classes.widthFillPortion ++ " width-fill-" ++ String.fromInt portion)
1010+ node
1011+ (Flag.add Flag.widthFill (Flag.add Flag.width has))
1012+ transform
1013+ (Single
1014+ (Internal.Style.classes.any
1015+ ++ "."
1016+ ++ Internal.Style.classes.row
1017+ ++ " > "
1018+ ++ (Internal.Style.dot <| "width-fill-" ++ String.fromInt portion)
1019+ )
1020+ "flex-grow"
1021+ (String.fromInt (portion * 100000))
1022+ :: styles
1023+ )
1024+ attrs
1025+ children
1026+ remaining
1027+
1028+ _ ->
1029+ let
1030+ ( addToFlags, newClass, newStyles ) =
1031+ renderWidth width
1032+ in
1033+ gatherAttrRecursive (classes ++ " " ++ newClass)
1034+ node
1035+ (Flag.merge addToFlags (Flag.add Flag.width has))
1036+ transform
1037+ (newStyles ++ styles)
1038+ attrs
1039+ children
1040+ remaining
1041+
1042+ Height height ->
1043+ if Flag.present Flag.height has then
1044+ gatherAttrRecursive classes node has transform styles attrs children remaining
1045+
1046+ else
1047+ case height of
1048+ Px px ->
1049+ let
1050+ val =
1051+ String.fromInt px ++ "px"
1052+
1053+ name =
1054+ "height-px-" ++ val
1055+ in
1056+ gatherAttrRecursive (Internal.Style.classes.heightExact ++ " " ++ name ++ " " ++ classes)
1057+ node
1058+ (Flag.add Flag.height has)
1059+ transform
1060+ (Single name "height " val :: styles)
1061+ attrs
1062+ children
1063+ remaining
1064+
1065+ Content ->
1066+ gatherAttrRecursive (Internal.Style.classes.heightContent ++ " " ++ classes)
1067+ node
1068+ (Flag.add Flag.heightContent (Flag.add Flag.height has))
1069+ transform
1070+ styles
1071+ attrs
1072+ children
1073+ remaining
1074+
1075+ Fill portion ->
1076+ if portion == 1 then
1077+ gatherAttrRecursive (Internal.Style.classes.heightFill ++ " " ++ classes)
1078+ node
1079+ (Flag.add Flag.heightFill (Flag.add Flag.height has))
1080+ transform
1081+ styles
1082+ attrs
1083+ children
1084+ remaining
1085+
1086+ else
1087+ gatherAttrRecursive (classes ++ " " ++ (Internal.Style.classes.heightFillPortion ++ " height-fill-" ++ String.fromInt portion))
1088+ node
1089+ (Flag.add Flag.heightFill (Flag.add Flag.height has))
1090+ transform
1091+ (Single
1092+ (Internal.Style.classes.any
1093+ ++ "."
1094+ ++ Internal.Style.classes.column
1095+ ++ " > "
1096+ ++ (Internal.Style.dot <| "height-fill-" ++ String.fromInt portion)
1097+ )
1098+ "flex-grow"
1099+ (String.fromInt (portion * 100000))
1100+ :: styles
1101+ )
1102+ attrs
1103+ children
1104+ remaining
1105+
1106+ _ ->
1107+ let
1108+ ( addToFlags, newClass, newStyles ) =
1109+ renderHeight height
1110+ in
1111+ gatherAttrRecursive (classes ++ " " ++ newClass)
1112+ node
1113+ (Flag.merge addToFlags (Flag.add Flag.height has))
1114+ transform
1115+ (newStyles ++ styles)
1116+ attrs
1117+ children
1118+ remaining
1119+
1120+ Describe description ->
1121+ case description of
1122+ Main ->
1123+ gatherAttrRecursive classes (addNodeName "main" node) has transform styles attrs children remaining
1124+
1125+ Navigation ->
1126+ gatherAttrRecursive classes (addNodeName "nav" node) has transform styles attrs children remaining
1127+
1128+ ContentInfo ->
1129+ gatherAttrRecursive classes (addNodeName "footer" node) has transform styles attrs children remaining
1130+
1131+ Complementary ->
1132+ gatherAttrRecursive classes (addNodeName "aside" node) has transform styles attrs children remaining
1133+
1134+ Heading i ->
1135+ if i <= 1 then
1136+ gatherAttrRecursive classes (addNodeName "h1" node) has transform styles attrs children remaining
1137+
1138+ else if i < 7 then
1139+ gatherAttrRecursive classes (addNodeName ("h" ++ String.fromInt i) node) has transform styles attrs children remaining
1140+
1141+ else
1142+ gatherAttrRecursive classes (addNodeName "h6" node) has transform styles attrs children remaining
1143+
1144+ Paragraph ->
1145+ -- previously we rendered a <p> tag, though apparently this invalidates the html if it has <div>s inside.
1146+ -- Since we can't guaranteee that there are no divs, we need another strategy.
1147+ -- While it's not documented in many places, there apparently is a paragraph aria role
1148+ -- https://github.com/w3c/aria/blob/11f85f41a5b621fdbe85fc9bcdcd270e653a48ba/common/script/roleInfo.js
1149+ -- Though we'll need to wait till it gets released in an official wai-aria spec to use it.
1150+ -- If it's used at the moment, then Lighthouse complains (likely rightfully) that role paragraph is not recognized.
1151+ gatherAttrRecursive
1152+ classes
1153+ node
1154+ has
1155+ transform
1156+ styles
1157+ attrs
1158+ children
1159+ remaining
1160+
1161+ Button ->
1162+ gatherAttrRecursive classes node has transform styles (VirtualDom.attribute "role" "button" :: attrs) children remaining
1163+
1164+ Label label ->
1165+ gatherAttrRecursive classes node has transform styles (VirtualDom.attribute "aria-label" label :: attrs) children remaining
1166+
1167+ LivePolite ->
1168+ gatherAttrRecursive classes node has transform styles (VirtualDom.attribute "aria-live" "polite" :: attrs) children remaining
1169+
1170+ LiveAssertive ->
1171+ gatherAttrRecursive classes node has transform styles (VirtualDom.attribute "aria-live" "assertive" :: attrs) children remaining
1172+
1173+ Nearby location elem ->
1174+ let
1175+ newStyles =
1176+ case elem of
1177+ Empty ->
1178+ styles
1179+
1180+ Text str ->
1181+ styles
1182+
1183+ Unstyled html ->
1184+ styles
1185+
1186+ Styled styled ->
1187+ styles ++ styled.styles
1188+ in
1189+ gatherAttrRecursive
1190+ classes
1191+ node
1192+ has
1193+ transform
1194+ newStyles
1195+ attrs
1196+ (addNearbyElement location elem children)
1197+ remaining
1198+
1199+ AlignX x ->
1200+ if Flag.present Flag.xAlign has then
1201+ gatherAttrRecursive classes node has transform styles attrs children remaining
1202+
1203+ else
1204+ gatherAttrRecursive (alignXName x ++ " " ++ classes)
1205+ node
1206+ (has
1207+ |> Flag.add Flag.xAlign
1208+ |> (\flags ->
1209+ case x of
1210+ CenterX ->
1211+ Flag.add Flag.centerX flags
1212+
1213+ Right ->
1214+ Flag.add Flag.alignRight flags
1215+
1216+ _ ->
1217+ flags
1218+ )
1219+ )
1220+ transform
1221+ styles
1222+ attrs
1223+ children
1224+ remaining
1225+
1226+ AlignY y ->
1227+ if Flag.present Flag.yAlign has then
1228+ gatherAttrRecursive classes node has transform styles attrs children remaining
1229+
1230+ else
1231+ gatherAttrRecursive (alignYName y ++ " " ++ classes)
1232+ node
1233+ (Flag.add Flag.yAlign has
1234+ |> (\flags ->
1235+ case y of
1236+ CenterY ->
1237+ Flag.add Flag.centerY flags
1238+
1239+ Bottom ->
1240+ Flag.add Flag.alignBottom flags
1241+
1242+ _ ->
1243+ flags
1244+ )
1245+ )
1246+ transform
1247+ styles
1248+ attrs
1249+ children
1250+ remaining
1251+
1252+
1253+addNearbyElement location elem existing =
1254+ let
1255+ nearby =
1256+ nearbyElement location elem
1257+ in
1258+ case existing of
1259+ NoNearbyChildren ->
1260+ case location of
1261+ Behind ->
1262+ ChildrenBehind [ nearby ]
1263+
1264+ _ ->
1265+ ChildrenInFront [ nearby ]
1266+
1267+ ChildrenBehind existingBehind ->
1268+ case location of
1269+ Behind ->
1270+ ChildrenBehind (nearby :: existingBehind)
1271+
1272+ _ ->
1273+ ChildrenBehindAndInFront existingBehind [ nearby ]
1274+
1275+ ChildrenInFront existingInFront ->
1276+ case location of
1277+ Behind ->
1278+ ChildrenBehindAndInFront [ nearby ] existingInFront
1279+
1280+ _ ->
1281+ ChildrenInFront (nearby :: existingInFront)
1282+
1283+ ChildrenBehindAndInFront existingBehind existingInFront ->
1284+ case location of
1285+ Behind ->
1286+ ChildrenBehindAndInFront (nearby :: existingBehind) existingInFront
1287+
1288+ _ ->
1289+ ChildrenBehindAndInFront existingBehind (nearby :: existingInFront)
1290+
1291+
1292+nearbyElement location elem =
1293+ Html.div
1294+ [ Html.Attributes.class <|
1295+ case location of
1296+ Above ->
1297+ String.join " "
1298+ [ Internal.Style.classes.nearby
1299+ , Internal.Style.classes.single
1300+ , Internal.Style.classes.above
1301+ ]
1302+
1303+ Below ->
1304+ String.join " "
1305+ [ Internal.Style.classes.nearby
1306+ , Internal.Style.classes.single
1307+ , Internal.Style.classes.below
1308+ ]
1309+
1310+ OnRight ->
1311+ String.join " "
1312+ [ Internal.Style.classes.nearby
1313+ , Internal.Style.classes.single
1314+ , Internal.Style.classes.onRight
1315+ ]
1316+
1317+ OnLeft ->
1318+ String.join " "
1319+ [ Internal.Style.classes.nearby
1320+ , Internal.Style.classes.single
1321+ , Internal.Style.classes.onLeft
1322+ ]
1323+
1324+ InFront ->
1325+ String.join " "
1326+ [ Internal.Style.classes.nearby
1327+ , Internal.Style.classes.single
1328+ , Internal.Style.classes.inFront
1329+ ]
1330+
1331+ Behind ->
1332+ String.join " "
1333+ [ Internal.Style.classes.nearby
1334+ , Internal.Style.classes.single
1335+ , Internal.Style.classes.behind
1336+ ]
1337+ ]
1338+ [ case elem of
1339+ Empty ->
1340+ VirtualDom.text ""
1341+
1342+ Text str ->
1343+ textElement str
1344+
1345+ Unstyled html ->
1346+ html asEl
1347+
1348+ Styled styled ->
1349+ styled.html NoStyleSheet asEl
1350+ ]
1351+
1352+
1353+renderWidth w =
1354+ case w of
1355+ Px px ->
1356+ ( Flag.none
1357+ , Internal.Style.classes.widthExact ++ " width-px-" ++ String.fromInt px
1358+ , [ Single ("width-px-" ++ String.fromInt px) "width" (String.fromInt px ++ "px") ]
1359+ )
1360+
1361+ Content ->
1362+ ( Flag.add Flag.widthContent Flag.none
1363+ , Internal.Style.classes.widthContent
1364+ , []
1365+ )
1366+
1367+ Fill portion ->
1368+ if portion == 1 then
1369+ ( Flag.add Flag.widthFill Flag.none
1370+ , Internal.Style.classes.widthFill
1371+ , []
1372+ )
1373+
1374+ else
1375+ ( Flag.add Flag.widthFill Flag.none
1376+ , Internal.Style.classes.widthFillPortion ++ " width-fill-" ++ String.fromInt portion
1377+ , [ Single
1378+ (Internal.Style.classes.any
1379+ ++ "."
1380+ ++ Internal.Style.classes.row
1381+ ++ " > "
1382+ ++ (Internal.Style.dot <| "width-fill-" ++ String.fromInt portion)
1383+ )
1384+ "flex-grow"
1385+ (String.fromInt (portion * 100000))
1386+ ]
1387+ )
1388+
1389+ Min minSize len ->
1390+ let
1391+ cls =
1392+ "min-width-"
1393+ ++ String.fromInt minSize
1394+
1395+ style =
1396+ Single
1397+ cls
1398+ "min-width"
1399+ (String.fromInt minSize ++ "px")
1400+
1401+ ( newFlag, newAttrs, newStyle ) =
1402+ renderWidth len
1403+ in
1404+ ( Flag.add Flag.widthBetween newFlag
1405+ , cls ++ " " ++ newAttrs
1406+ , style :: newStyle
1407+ )
1408+
1409+ Max maxSize len ->
1410+ let
1411+ cls =
1412+ "max-width-" ++ String.fromInt maxSize
1413+
1414+ style =
1415+ Single cls
1416+ "max-width"
1417+ (String.fromInt maxSize ++ "px")
1418+
1419+ ( newFlag, newAttrs, newStyle ) =
1420+ renderWidth len
1421+ in
1422+ ( Flag.add Flag.widthBetween newFlag
1423+ , cls ++ " " ++ newAttrs
1424+ , style :: newStyle
1425+ )
1426+
1427+
1428+renderHeight h =
1429+ case h of
1430+ Px px ->
1431+ let
1432+ val =
1433+ String.fromInt px
1434+
1435+ name =
1436+ "height-px-" ++ val
1437+ in
1438+ ( Flag.none
1439+ , Internal.Style.classes.heightExact ++ " " ++ name
1440+ , [ Single name "height" (val ++ "px") ]
1441+ )
1442+
1443+ Content ->
1444+ ( Flag.add Flag.heightContent Flag.none
1445+ , Internal.Style.classes.heightContent
1446+ , []
1447+ )
1448+
1449+ Fill portion ->
1450+ if portion == 1 then
1451+ ( Flag.add Flag.heightFill Flag.none
1452+ , Internal.Style.classes.heightFill
1453+ , []
1454+ )
1455+
1456+ else
1457+ ( Flag.add Flag.heightFill Flag.none
1458+ , Internal.Style.classes.heightFillPortion ++ " height-fill-" ++ String.fromInt portion
1459+ , [ Single
1460+ (Internal.Style.classes.any
1461+ ++ "."
1462+ ++ Internal.Style.classes.column
1463+ ++ " > "
1464+ ++ (Internal.Style.dot <| "height-fill-" ++ String.fromInt portion)
1465+ )
1466+ "flex-grow"
1467+ (String.fromInt (portion * 100000))
1468+ ]
1469+ )
1470+
1471+ Min minSize len ->
1472+ let
1473+ cls =
1474+ "min-height-"
1475+ ++ String.fromInt minSize
1476+
1477+ style =
1478+ Single
1479+ cls
1480+ "min-height"
1481+ -- This needs to be !important because we're using `min-height: min-content`
1482+ -- to correct for safari's incorrect implementation of flexbox.
1483+ (String.fromInt minSize ++ "px !important")
1484+
1485+ ( newFlag, newAttrs, newStyle ) =
1486+ renderHeight len
1487+ in
1488+ ( Flag.add Flag.heightBetween newFlag
1489+ , cls ++ " " ++ newAttrs
1490+ , style :: newStyle
1491+ )
1492+
1493+ Max maxSize len ->
1494+ let
1495+ cls =
1496+ "max-height-" ++ String.fromInt maxSize
1497+
1498+ style =
1499+ Single cls
1500+ "max-height"
1501+ (String.fromInt maxSize ++ "px")
1502+
1503+ ( newFlag, newAttrs, newStyle ) =
1504+ renderHeight len
1505+ in
1506+ ( Flag.add Flag.heightBetween newFlag
1507+ , cls ++ " " ++ newAttrs
1508+ , style :: newStyle
1509+ )
1510+
1511+
1512+rowClass =
1513+ classes.any ++ " " ++ classes.row
1514+
1515+
1516+columnClass =
1517+ classes.any ++ " " ++ classes.column
1518+
1519+
1520+singleClass =
1521+ classes.any ++ " " ++ classes.single
1522+
1523+
1524+gridClass =
1525+ classes.any ++ " " ++ classes.grid
1526+
1527+
1528+paragraphClass =
1529+ classes.any ++ " " ++ classes.paragraph
1530+
1531+
1532+pageClass =
1533+ classes.any ++ " " ++ classes.page
1534+
1535+
1536+contextClasses context =
1537+ case context of
1538+ AsRow ->
1539+ rowClass
1540+
1541+ AsColumn ->
1542+ columnClass
1543+
1544+ AsEl ->
1545+ singleClass
1546+
1547+ AsGrid ->
1548+ gridClass
1549+
1550+ AsParagraph ->
1551+ paragraphClass
1552+
1553+ AsTextColumn ->
1554+ pageClass
1555+
1556+
1557+element : LayoutContext -> NodeName -> List (Attribute aligned msg) -> Children (Element msg) -> Element msg
1558+element context node attributes children =
1559+ attributes
1560+ |> List.reverse
1561+ |> gatherAttrRecursive (contextClasses context) node Flag.none untransformed [] [] NoNearbyChildren
1562+ |> createElement context children
1563+
1564+
1565+untransformed =
1566+ Untransformed
1567+
1568+
1569+createElement : LayoutContext -> Children (Element msg) -> Gathered msg -> Element msg
1570+createElement context children rendered =
1571+ let
1572+ gather child ( htmls, existingStyles ) =
1573+ case child of
1574+ Unstyled html ->
1575+ if context == asParagraph then
1576+ ( html context :: htmls
1577+ , existingStyles
1578+ )
1579+
1580+ else
1581+ ( html context :: htmls
1582+ , existingStyles
1583+ )
1584+
1585+ Styled styled ->
1586+ if context == asParagraph then
1587+ ( styled.html NoStyleSheet context :: htmls
1588+ , if List.isEmpty existingStyles then
1589+ styled.styles
1590+
1591+ else
1592+ styled.styles ++ existingStyles
1593+ )
1594+
1595+ else
1596+ ( styled.html NoStyleSheet context :: htmls
1597+ , if List.isEmpty existingStyles then
1598+ styled.styles
1599+
1600+ else
1601+ styled.styles ++ existingStyles
1602+ )
1603+
1604+ Text str ->
1605+ -- TEXT OPTIMIZATION
1606+ -- You can have raw text if the element is an el, and has `width-content` and `height-content`
1607+ -- Same if it's a column or row with one child and width-content, height-content
1608+ -- interferes with css grid
1609+ -- Maybe we could unpack text elements in a paragraph as well,
1610+ -- however, embedded elements that are larger than the line height will overlap with exisitng text.
1611+ -- I don't think that's what we want.
1612+ -- if
1613+ -- context
1614+ -- == asEl
1615+ -- || context
1616+ -- == asParagraph
1617+ -- then
1618+ -- ( VirtualDom.text
1619+ -- (if context == asParagraph then
1620+ -- str
1621+ -- else
1622+ -- str
1623+ -- )
1624+ -- :: htmls
1625+ -- , existingStyles
1626+ -- )
1627+ -- else
1628+ ( (if context == asEl then
1629+ textElementFill str
1630+
1631+ else
1632+ textElement str
1633+ )
1634+ :: htmls
1635+ , existingStyles
1636+ )
1637+
1638+ Empty ->
1639+ ( htmls, existingStyles )
1640+
1641+ gatherKeyed ( key, child ) ( htmls, existingStyles ) =
1642+ case child of
1643+ Unstyled html ->
1644+ if context == asParagraph then
1645+ ( ( key, html context ) :: htmls
1646+ , existingStyles
1647+ )
1648+
1649+ else
1650+ ( ( key, html context ) :: htmls
1651+ , existingStyles
1652+ )
1653+
1654+ Styled styled ->
1655+ if context == asParagraph then
1656+ ( ( key, styled.html NoStyleSheet context )
1657+ :: htmls
1658+ , if List.isEmpty existingStyles then
1659+ styled.styles
1660+
1661+ else
1662+ styled.styles ++ existingStyles
1663+ )
1664+
1665+ else
1666+ ( ( key, styled.html NoStyleSheet context ) :: htmls
1667+ , if List.isEmpty existingStyles then
1668+ styled.styles
1669+
1670+ else
1671+ styled.styles ++ existingStyles
1672+ )
1673+
1674+ Text str ->
1675+ -- TEXT OPTIMIZATION
1676+ -- You can have raw text if the element is an el, and has `width-content` and `height-content`
1677+ -- Same if it's a column or row with one child and width-content, height-content
1678+ -- if
1679+ -- context
1680+ -- == asEl
1681+ -- || context
1682+ -- == asParagraph
1683+ -- then
1684+ -- ( ( key
1685+ -- , VirtualDom.text
1686+ -- str
1687+ -- )
1688+ -- :: htmls
1689+ -- , existingStyles
1690+ -- )
1691+ -- else
1692+ ( ( key
1693+ , if context == asEl then
1694+ textElementFill str
1695+
1696+ else
1697+ textElement str
1698+ )
1699+ :: htmls
1700+ , existingStyles
1701+ )
1702+
1703+ Empty ->
1704+ ( htmls, existingStyles )
1705+ in
1706+ case children of
1707+ Keyed keyedChildren ->
1708+ case List.foldr gatherKeyed ( [], [] ) keyedChildren of
1709+ ( keyed, styles ) ->
1710+ let
1711+ newStyles =
1712+ if List.isEmpty styles then
1713+ rendered.styles
1714+
1715+ else
1716+ rendered.styles ++ styles
1717+ in
1718+ case newStyles of
1719+ [] ->
1720+ Unstyled
1721+ (finalizeNode rendered.has
1722+ rendered.node
1723+ rendered.attributes
1724+ (Keyed
1725+ (addKeyedChildren "nearby-element-pls" keyed rendered.children)
1726+ )
1727+ NoStyleSheet
1728+ )
1729+
1730+ allStyles ->
1731+ Styled
1732+ { styles = allStyles
1733+ , html =
1734+ finalizeNode
1735+ rendered.has
1736+ rendered.node
1737+ rendered.attributes
1738+ (Keyed
1739+ (addKeyedChildren "nearby-element-pls" keyed rendered.children)
1740+ )
1741+ }
1742+
1743+ Unkeyed unkeyedChildren ->
1744+ case List.foldr gather ( [], [] ) unkeyedChildren of
1745+ ( unkeyed, styles ) ->
1746+ let
1747+ newStyles =
1748+ if List.isEmpty styles then
1749+ rendered.styles
1750+
1751+ else
1752+ rendered.styles ++ styles
1753+ in
1754+ case newStyles of
1755+ [] ->
1756+ Unstyled
1757+ (finalizeNode
1758+ rendered.has
1759+ rendered.node
1760+ rendered.attributes
1761+ (Unkeyed (addChildren unkeyed rendered.children))
1762+ NoStyleSheet
1763+ )
1764+
1765+ allStyles ->
1766+ Styled
1767+ { styles = allStyles
1768+ , html =
1769+ finalizeNode
1770+ rendered.has
1771+ rendered.node
1772+ rendered.attributes
1773+ (Unkeyed (addChildren unkeyed rendered.children))
1774+ }
1775+
1776+
1777+addChildren existing nearbyChildren =
1778+ case nearbyChildren of
1779+ NoNearbyChildren ->
1780+ existing
1781+
1782+ ChildrenBehind behind ->
1783+ behind ++ existing
1784+
1785+ ChildrenInFront inFront ->
1786+ existing ++ inFront
1787+
1788+ ChildrenBehindAndInFront behind inFront ->
1789+ behind ++ existing ++ inFront
1790+
1791+
1792+addKeyedChildren key existing nearbyChildren =
1793+ case nearbyChildren of
1794+ NoNearbyChildren ->
1795+ existing
1796+
1797+ ChildrenBehind behind ->
1798+ List.map (\x -> ( key, x )) behind ++ existing
1799+
1800+ ChildrenInFront inFront ->
1801+ existing ++ List.map (\x -> ( key, x )) inFront
1802+
1803+ ChildrenBehindAndInFront behind inFront ->
1804+ List.map (\x -> ( key, x )) behind
1805+ ++ existing
1806+ ++ List.map (\x -> ( key, x )) inFront
1807+
1808+
1809+unit =
1810+ 0
1811+
1812+
1813+defaultOptions =
1814+ { hover = AllowHover
1815+ , focus = focusDefaultStyle
1816+ , mode = Layout
1817+ }
1818+
1819+
1820+staticRoot : OptionRecord -> VirtualDom.Node msg
1821+staticRoot opts =
1822+ case opts.mode of
1823+ Layout ->
1824+ -- wrap the style node in a div to prevent `Dark Reader` from blowin up the dom.
1825+ VirtualDom.node "div"
1826+ []
1827+ [ VirtualDom.node "style" [] [ VirtualDom.text Internal.Style.rules ] ]
1828+
1829+ NoStaticStyleSheet ->
1830+ VirtualDom.text ""
1831+
1832+ WithVirtualCss ->
1833+ VirtualDom.node "elm-ui-static-rules" [ VirtualDom.property "rules" (Encode.string Internal.Style.rules) ] []
1834+
1835+
1836+addWhen ifThis x to =
1837+ if ifThis then
1838+ x :: to
1839+
1840+ else
1841+ to
1842+
1843+
1844+{-| TODO:
1845+
1846+This doesn't reduce equivalent attributes completely.
1847+
1848+-}
1849+filter : List (Attribute aligned msg) -> List (Attribute aligned msg)
1850+filter attrs =
1851+ Tuple.first <|
1852+ List.foldr
1853+ (\x ( found, has ) ->
1854+ case x of
1855+ NoAttribute ->
1856+ ( found, has )
1857+
1858+ Class key _ ->
1859+ ( x :: found, has )
1860+
1861+ Attr attr ->
1862+ ( x :: found, has )
1863+
1864+ StyleClass _ style ->
1865+ ( x :: found, has )
1866+
1867+ Width width ->
1868+ if Set.member "width" has then
1869+ ( found, has )
1870+
1871+ else
1872+ ( x :: found, Set.insert "width" has )
1873+
1874+ Height height ->
1875+ if Set.member "height" has then
1876+ ( found, has )
1877+
1878+ else
1879+ ( x :: found, Set.insert "height" has )
1880+
1881+ Describe description ->
1882+ if Set.member "described" has then
1883+ ( found, has )
1884+
1885+ else
1886+ ( x :: found, Set.insert "described" has )
1887+
1888+ Nearby location elem ->
1889+ ( x :: found, has )
1890+
1891+ AlignX _ ->
1892+ if Set.member "align-x" has then
1893+ ( found, has )
1894+
1895+ else
1896+ ( x :: found, Set.insert "align-x" has )
1897+
1898+ AlignY _ ->
1899+ if Set.member "align-y" has then
1900+ ( found, has )
1901+
1902+ else
1903+ ( x :: found, Set.insert "align-y" has )
1904+
1905+ TransformComponent _ _ ->
1906+ if Set.member "transform" has then
1907+ ( found, has )
1908+
1909+ else
1910+ ( x :: found, Set.insert "transform" has )
1911+ )
1912+ ( [], Set.empty )
1913+ attrs
1914+
1915+
1916+isContent len =
1917+ case len of
1918+ Content ->
1919+ True
1920+
1921+ Max _ l ->
1922+ isContent l
1923+
1924+ Min _ l ->
1925+ isContent l
1926+
1927+ _ ->
1928+ False
1929+
1930+
1931+get : List (Attribute aligned msg) -> (Attribute aligned msg -> Bool) -> List (Attribute aligned msg)
1932+get attrs isAttr =
1933+ attrs
1934+ |> filter
1935+ |> List.foldr
1936+ (\x found ->
1937+ if isAttr x then
1938+ x :: found
1939+
1940+ else
1941+ found
1942+ )
1943+ []
1944+
1945+
1946+type Spacing
1947+ = Spaced String Int Int
1948+
1949+
1950+type Padding
1951+ = Padding String Float Float Float Float
1952+
1953+
1954+extractSpacingAndPadding : List (Attribute aligned msg) -> ( Maybe Padding, Maybe Spacing )
1955+extractSpacingAndPadding attrs =
1956+ List.foldr
1957+ (\attr ( pad, spacing ) ->
1958+ ( case pad of
1959+ Just x ->
1960+ pad
1961+
1962+ Nothing ->
1963+ case attr of
1964+ StyleClass _ (PaddingStyle name t r b l) ->
1965+ Just (Padding name t r b l)
1966+
1967+ _ ->
1968+ Nothing
1969+ , case spacing of
1970+ Just x ->
1971+ spacing
1972+
1973+ Nothing ->
1974+ case attr of
1975+ StyleClass _ (SpacingStyle name x y) ->
1976+ Just (Spaced name x y)
1977+
1978+ _ ->
1979+ Nothing
1980+ )
1981+ )
1982+ ( Nothing, Nothing )
1983+ attrs
1984+
1985+
1986+getSpacing : List (Attribute aligned msg) -> ( Int, Int ) -> ( Int, Int )
1987+getSpacing attrs default =
1988+ attrs
1989+ |> List.foldr
1990+ (\attr acc ->
1991+ case acc of
1992+ Just x ->
1993+ Just x
1994+
1995+ Nothing ->
1996+ case attr of
1997+ StyleClass _ (SpacingStyle _ x y) ->
1998+ Just ( x, y )
1999+
2000+ _ ->
2001+ Nothing
2002+ )
2003+ Nothing
2004+ |> Maybe.withDefault default
2005+
2006+
2007+getWidth : List (Attribute aligned msg) -> Maybe Length
2008+getWidth attrs =
2009+ attrs
2010+ |> List.foldr
2011+ (\attr acc ->
2012+ case acc of
2013+ Just x ->
2014+ Just x
2015+
2016+ Nothing ->
2017+ case attr of
2018+ Width len ->
2019+ Just len
2020+
2021+ _ ->
2022+ Nothing
2023+ )
2024+ Nothing
2025+
2026+
2027+getHeight : List (Attribute aligned msg) -> Maybe Length
2028+getHeight attrs =
2029+ attrs
2030+ |> List.foldr
2031+ (\attr acc ->
2032+ case acc of
2033+ Just x ->
2034+ Just x
2035+
2036+ Nothing ->
2037+ case attr of
2038+ Height len ->
2039+ Just len
2040+
2041+ _ ->
2042+ Nothing
2043+ )
2044+ Nothing
2045+
2046+
2047+textElementClasses : String
2048+textElementClasses =
2049+ classes.any
2050+ ++ " "
2051+ ++ classes.text
2052+ ++ " "
2053+ ++ classes.widthContent
2054+ ++ " "
2055+ ++ classes.heightContent
2056+
2057+
2058+textElement : String -> VirtualDom.Node msg
2059+textElement str =
2060+ Html.div
2061+ [ Html.Attributes.class
2062+ textElementClasses
2063+ ]
2064+ [ Html.text str ]
2065+
2066+
2067+textElementFillClasses : String
2068+textElementFillClasses =
2069+ classes.any
2070+ ++ " "
2071+ ++ classes.text
2072+ ++ " "
2073+ ++ classes.widthFill
2074+ ++ " "
2075+ ++ classes.heightFill
2076+
2077+
2078+textElementFill : String -> VirtualDom.Node msg
2079+textElementFill str =
2080+ Html.div
2081+ [ Html.Attributes.class
2082+ textElementFillClasses
2083+ ]
2084+ [ Html.text str ]
2085+
2086+
2087+type Children x
2088+ = Unkeyed (List x)
2089+ | Keyed (List ( String, x ))
2090+
2091+
2092+toHtml mode el =
2093+ case el of
2094+ Unstyled html ->
2095+ html asEl
2096+
2097+ Styled { styles, html } ->
2098+ html (mode styles) asEl
2099+
2100+ Text text ->
2101+ textElement text
2102+
2103+ Empty ->
2104+ textElement ""
2105+
2106+
2107+{-| -}
2108+renderRoot : List Option -> List (Attribute aligned msg) -> Element msg -> VirtualDom.Node msg
2109+renderRoot optionList attributes child =
2110+ let
2111+ options =
2112+ optionsToRecord optionList
2113+
2114+ embedStyle =
2115+ case options.mode of
2116+ NoStaticStyleSheet ->
2117+ OnlyDynamic options
2118+
2119+ _ ->
2120+ StaticRootAndDynamic options
2121+ in
2122+ element asEl div attributes (Unkeyed [ child ])
2123+ |> toHtml embedStyle
2124+
2125+
2126+type RenderMode
2127+ = Layout
2128+ | NoStaticStyleSheet
2129+ | WithVirtualCss
2130+
2131+
2132+type alias OptionRecord =
2133+ { hover : HoverSetting
2134+ , focus : FocusStyle
2135+ , mode : RenderMode
2136+ }
2137+
2138+
2139+type HoverSetting
2140+ = NoHover
2141+ | AllowHover
2142+ | ForceHover
2143+
2144+
2145+type Option
2146+ = HoverOption HoverSetting
2147+ | FocusStyleOption FocusStyle
2148+ | RenderModeOption RenderMode
2149+
2150+
2151+type alias FocusStyle =
2152+ { borderColor : Maybe Color
2153+ , shadow : Maybe Shadow
2154+ , backgroundColor : Maybe Color
2155+ }
2156+
2157+
2158+type alias Shadow =
2159+ { color : Color
2160+ , offset : ( Int, Int )
2161+ , blur : Int
2162+ , size : Int
2163+ }
2164+
2165+
2166+rootStyle : List (Attribute aligned msg)
2167+rootStyle =
2168+ let
2169+ families =
2170+ [ Typeface "Open Sans"
2171+ , Typeface "Helvetica"
2172+ , Typeface "Verdana"
2173+ , SansSerif
2174+ ]
2175+ in
2176+ [ StyleClass Flag.bgColor (Colored ("bg-" ++ formatColorClass (Rgba 1 1 1 0)) "background-color" (Rgba 1 1 1 0))
2177+ , StyleClass Flag.fontColor (Colored ("fc-" ++ formatColorClass (Rgba 0 0 0 1)) "color" (Rgba 0 0 0 1))
2178+ , StyleClass Flag.fontSize (FontSize 20)
2179+ , StyleClass Flag.fontFamily <|
2180+ FontFamily (List.foldl renderFontClassName "font-" families)
2181+ families
2182+ ]
2183+
2184+
2185+renderFontClassName : Font -> String -> String
2186+renderFontClassName font current =
2187+ current
2188+ ++ (case font of
2189+ Serif ->
2190+ "serif"
2191+
2192+ SansSerif ->
2193+ "sans-serif"
2194+
2195+ Monospace ->
2196+ "monospace"
2197+
2198+ Typeface name ->
2199+ name
2200+ |> String.toLower
2201+ |> String.words
2202+ |> String.join "-"
2203+
2204+ ImportFont name url ->
2205+ name
2206+ |> String.toLower
2207+ |> String.words
2208+ |> String.join "-"
2209+
2210+ FontWith { name } ->
2211+ name
2212+ |> String.toLower
2213+ |> String.words
2214+ |> String.join "-"
2215+ )
2216+
2217+
2218+renderFocusStyle :
2219+ FocusStyle
2220+ -> List Style
2221+renderFocusStyle focus =
2222+ [ Style (Internal.Style.dot classes.focusedWithin ++ ":focus-within")
2223+ (List.filterMap identity
2224+ [ Maybe.map (\color -> Property "border-color" (formatColor color)) focus.borderColor
2225+ , Maybe.map (\color -> Property "background-color" (formatColor color)) focus.backgroundColor
2226+ , Maybe.map
2227+ (\shadow ->
2228+ Property "box-shadow"
2229+ (formatBoxShadow
2230+ { color = shadow.color
2231+ , offset =
2232+ shadow.offset
2233+ |> Tuple.mapFirst toFloat
2234+ |> Tuple.mapSecond toFloat
2235+ , inset = False
2236+ , blur =
2237+ toFloat shadow.blur
2238+ , size =
2239+ toFloat shadow.size
2240+ }
2241+ )
2242+ )
2243+ focus.shadow
2244+ , Just <| Property "outline" "none"
2245+ ]
2246+ )
2247+ , Style
2248+ ((Internal.Style.dot classes.any ++ ":focus .focusable, ")
2249+ ++ (Internal.Style.dot classes.any ++ ".focusable:focus, ")
2250+ ++ (".ui-slide-bar:focus + " ++ Internal.Style.dot classes.any ++ " .focusable-thumb")
2251+ )
2252+ (List.filterMap identity
2253+ [ Maybe.map (\color -> Property "border-color" (formatColor color)) focus.borderColor
2254+ , Maybe.map (\color -> Property "background-color" (formatColor color)) focus.backgroundColor
2255+ , Maybe.map
2256+ (\shadow ->
2257+ Property "box-shadow"
2258+ (formatBoxShadow
2259+ { color = shadow.color
2260+ , offset =
2261+ shadow.offset
2262+ |> Tuple.mapFirst toFloat
2263+ |> Tuple.mapSecond toFloat
2264+ , inset = False
2265+ , blur = toFloat shadow.blur
2266+ , size = toFloat shadow.size
2267+ }
2268+ )
2269+ )
2270+ focus.shadow
2271+ , Just <| Property "outline" "none"
2272+ ]
2273+ )
2274+ ]
2275+
2276+
2277+focusDefaultStyle : { backgroundColor : Maybe Color, borderColor : Maybe Color, shadow : Maybe Shadow }
2278+focusDefaultStyle =
2279+ { backgroundColor = Nothing
2280+ , borderColor = Nothing
2281+ , shadow =
2282+ Just
2283+ { color =
2284+ Rgba (155 / 255) (203 / 255) 1 1
2285+ , offset = ( 0, 0 )
2286+ , blur = 0
2287+ , size = 3
2288+ }
2289+ }
2290+
2291+
2292+optionsToRecord : List Option -> OptionRecord
2293+optionsToRecord options =
2294+ let
2295+ combine opt record =
2296+ case opt of
2297+ HoverOption hoverable ->
2298+ case record.hover of
2299+ Nothing ->
2300+ { record | hover = Just hoverable }
2301+
2302+ _ ->
2303+ record
2304+
2305+ FocusStyleOption focusStyle ->
2306+ case record.focus of
2307+ Nothing ->
2308+ { record | focus = Just focusStyle }
2309+
2310+ _ ->
2311+ record
2312+
2313+ RenderModeOption renderMode ->
2314+ case record.mode of
2315+ Nothing ->
2316+ { record | mode = Just renderMode }
2317+
2318+ _ ->
2319+ record
2320+
2321+ andFinally record =
2322+ { hover =
2323+ case record.hover of
2324+ Nothing ->
2325+ AllowHover
2326+
2327+ Just hoverable ->
2328+ hoverable
2329+ , focus =
2330+ case record.focus of
2331+ Nothing ->
2332+ focusDefaultStyle
2333+
2334+ Just focusable ->
2335+ focusable
2336+ , mode =
2337+ case record.mode of
2338+ Nothing ->
2339+ Layout
2340+
2341+ Just actualMode ->
2342+ actualMode
2343+ }
2344+ in
2345+ andFinally <|
2346+ List.foldr combine
2347+ { hover = Nothing
2348+ , focus = Nothing
2349+ , mode = Nothing
2350+ }
2351+ options
2352+
2353+
2354+toStyleSheet : OptionRecord -> List Style -> VirtualDom.Node msg
2355+toStyleSheet options styleSheet =
2356+ case options.mode of
2357+ Layout ->
2358+ -- wrap the style node in a div to prevent `Dark Reader` from blowin up the dom.
2359+ VirtualDom.node "div"
2360+ []
2361+ [ VirtualDom.node "style"
2362+ []
2363+ [ VirtualDom.text (toStyleSheetString options styleSheet) ]
2364+ ]
2365+
2366+ NoStaticStyleSheet ->
2367+ -- wrap the style node in a div to prevent `Dark Reader` from blowin up the dom.
2368+ VirtualDom.node "div"
2369+ []
2370+ [ VirtualDom.node "style"
2371+ []
2372+ [ VirtualDom.text (toStyleSheetString options styleSheet) ]
2373+ ]
2374+
2375+ WithVirtualCss ->
2376+ VirtualDom.node "elm-ui-rules"
2377+ [ VirtualDom.property "rules"
2378+ (encodeStyles options styleSheet)
2379+ ]
2380+ []
2381+
2382+
2383+renderTopLevelValues rules =
2384+ let
2385+ withImport font =
2386+ case font of
2387+ ImportFont _ url ->
2388+ Just ("@import url('" ++ url ++ "');")
2389+
2390+ -- FontWith with ->
2391+ -- case with.url of
2392+ -- Just x ->
2393+ -- Just ("@import url('" ++ x ++ "');")
2394+ -- Nothing ->
2395+ -- Nothing
2396+ _ ->
2397+ Nothing
2398+
2399+ allNames =
2400+ List.map Tuple.first rules
2401+
2402+ fontImports ( name, typefaces ) =
2403+ let
2404+ imports =
2405+ String.join "\n" (List.filterMap withImport typefaces)
2406+ in
2407+ imports
2408+
2409+ fontAdjustments ( name, typefaces ) =
2410+ case typefaceAdjustment typefaces of
2411+ Nothing ->
2412+ String.join ""
2413+ (List.map (renderNullAdjustmentRule name) allNames)
2414+
2415+ Just adjustment ->
2416+ String.join ""
2417+ (List.map (renderFontAdjustmentRule name adjustment) allNames)
2418+ in
2419+ String.join "\n" (List.map fontImports rules)
2420+ ++ String.join "\n" (List.map fontAdjustments rules)
2421+
2422+
2423+renderNullAdjustmentRule fontToAdjust otherFontName =
2424+ let
2425+ name =
2426+ if fontToAdjust == otherFontName then
2427+ fontToAdjust
2428+
2429+ else
2430+ otherFontName ++ " ." ++ fontToAdjust
2431+ in
2432+ String.join " "
2433+ [ bracket
2434+ ("."
2435+ ++ name
2436+ ++ "."
2437+ ++ classes.sizeByCapital
2438+ ++ ", "
2439+ ++ "."
2440+ ++ name
2441+ ++ " ."
2442+ ++ classes.sizeByCapital
2443+ )
2444+ [ ( "line-height", "1" )
2445+ ]
2446+ , bracket
2447+ ("."
2448+ ++ name
2449+ ++ "."
2450+ ++ classes.sizeByCapital
2451+ ++ "> ."
2452+ ++ Internal.Style.classes.text
2453+ ++ ", ."
2454+ ++ name
2455+ ++ " ."
2456+ ++ classes.sizeByCapital
2457+ ++ " > ."
2458+ ++ Internal.Style.classes.text
2459+ )
2460+ [ ( "vertical-align", "0" )
2461+ , ( "line-height", "1" )
2462+ ]
2463+ ]
2464+
2465+
2466+fontRule name modifier ( parentAdj, textAdjustment ) =
2467+ [ bracket
2468+ ("."
2469+ ++ name
2470+ ++ "."
2471+ ++ modifier
2472+ ++ ", "
2473+ ++ "."
2474+ ++ name
2475+ ++ " ."
2476+ ++ modifier
2477+ )
2478+ parentAdj
2479+ , bracket
2480+ ("."
2481+ ++ name
2482+ ++ "."
2483+ ++ modifier
2484+ ++ "> ."
2485+ ++ Internal.Style.classes.text
2486+ ++ ", ."
2487+ ++ name
2488+ ++ " ."
2489+ ++ modifier
2490+ ++ " > ."
2491+ ++ Internal.Style.classes.text
2492+ )
2493+ textAdjustment
2494+ ]
2495+
2496+
2497+renderFontAdjustmentRule fontToAdjust ( full, capital ) otherFontName =
2498+ let
2499+ name =
2500+ if fontToAdjust == otherFontName then
2501+ fontToAdjust
2502+
2503+ else
2504+ otherFontName ++ " ." ++ fontToAdjust
2505+ in
2506+ String.join " "
2507+ (fontRule name classes.sizeByCapital capital ++ fontRule name classes.fullSize full)
2508+
2509+
2510+bracket selector rules =
2511+ let
2512+ renderPair ( name, val ) =
2513+ name ++ ": " ++ val ++ ";"
2514+ in
2515+ selector ++ " {" ++ String.join "" (List.map renderPair rules) ++ "}"
2516+
2517+
2518+fontAdjustmentRules converted =
2519+ ( [ ( "display", "block" )
2520+ ]
2521+ , [ ( "display", "inline-block" )
2522+ , ( "line-height", String.fromFloat converted.height )
2523+ , ( "vertical-align", String.fromFloat converted.vertical ++ "em" )
2524+ , ( "font-size", String.fromFloat converted.size ++ "em" )
2525+ ]
2526+ )
2527+
2528+
2529+typefaceAdjustment typefaces =
2530+ List.foldl
2531+ (\face found ->
2532+ case found of
2533+ Nothing ->
2534+ case face of
2535+ FontWith with ->
2536+ case with.adjustment of
2537+ Nothing ->
2538+ found
2539+
2540+ Just adjustment ->
2541+ Just
2542+ ( fontAdjustmentRules
2543+ (.full (convertAdjustment adjustment))
2544+ , fontAdjustmentRules
2545+ (.capital (convertAdjustment adjustment))
2546+ )
2547+
2548+ _ ->
2549+ found
2550+
2551+ Just _ ->
2552+ found
2553+ )
2554+ Nothing
2555+ typefaces
2556+
2557+
2558+fontName font =
2559+ case font of
2560+ Serif ->
2561+ "serif"
2562+
2563+ SansSerif ->
2564+ "sans-serif"
2565+
2566+ Monospace ->
2567+ "monospace"
2568+
2569+ Typeface name ->
2570+ "\"" ++ name ++ "\""
2571+
2572+ ImportFont name url ->
2573+ "\"" ++ name ++ "\""
2574+
2575+ FontWith { name } ->
2576+ "\"" ++ name ++ "\""
2577+
2578+
2579+topLevelValue rule =
2580+ case rule of
2581+ FontFamily name typefaces ->
2582+ Just ( name, typefaces )
2583+
2584+ _ ->
2585+ Nothing
2586+
2587+
2588+renderProps force (Property key val) existing =
2589+ if force then
2590+ existing ++ "\n " ++ key ++ ": " ++ val ++ " !important;"
2591+
2592+ else
2593+ existing ++ "\n " ++ key ++ ": " ++ val ++ ";"
2594+
2595+
2596+encodeStyles options stylesheet =
2597+ stylesheet
2598+ |> List.map
2599+ (\style ->
2600+ let
2601+ styled =
2602+ renderStyleRule options style Nothing
2603+ in
2604+ ( getStyleName style, Encode.list Encode.string styled )
2605+ )
2606+ |> Encode.object
2607+
2608+
2609+toStyleSheetString : OptionRecord -> List Style -> String
2610+toStyleSheetString options stylesheet =
2611+ let
2612+ combine style rendered =
2613+ { rules = rendered.rules ++ renderStyleRule options style Nothing
2614+ , topLevel =
2615+ case topLevelValue style of
2616+ Nothing ->
2617+ rendered.topLevel
2618+
2619+ Just topLevel ->
2620+ topLevel :: rendered.topLevel
2621+ }
2622+ in
2623+ case List.foldl combine { topLevel = [], rules = [] } stylesheet of
2624+ { topLevel, rules } ->
2625+ let
2626+ ruleWeight s =
2627+ if String.contains ":hover" s then
2628+ 1
2629+
2630+ else if String.contains ":focus" s then
2631+ 2
2632+
2633+ else if String.contains ":active" s then
2634+ 3
2635+
2636+ else
2637+ 0
2638+ in
2639+ renderTopLevelValues topLevel ++ String.concat (List.sortBy ruleWeight rules)
2640+
2641+
2642+renderStyle : OptionRecord -> Maybe PseudoClass -> String -> List Property -> List String
2643+renderStyle options maybePseudo selector props =
2644+ case maybePseudo of
2645+ Nothing ->
2646+ [ selector ++ "{" ++ List.foldl (renderProps False) "" props ++ "\n}" ]
2647+
2648+ Just pseudo ->
2649+ case pseudo of
2650+ Hover ->
2651+ case options.hover of
2652+ NoHover ->
2653+ []
2654+
2655+ ForceHover ->
2656+ [ selector ++ "-hv {" ++ List.foldl (renderProps True) "" props ++ "\n}" ]
2657+
2658+ AllowHover ->
2659+ [ selector ++ "-hv:hover {" ++ List.foldl (renderProps False) "" props ++ "\n}" ]
2660+
2661+ Focus ->
2662+ let
2663+ renderedProps =
2664+ List.foldl (renderProps False) "" props
2665+ in
2666+ [ selector ++ "-fs:focus {" ++ renderedProps ++ "\n}"
2667+ , ("." ++ classes.any ++ ":focus " ++ selector ++ "-fs {")
2668+ ++ renderedProps
2669+ ++ "\n}"
2670+ , (selector ++ "-fs:focus-within {")
2671+ ++ renderedProps
2672+ ++ "\n}"
2673+ , (".ui-slide-bar:focus + " ++ Internal.Style.dot classes.any ++ " .focusable-thumb" ++ selector ++ "-fs {")
2674+ ++ renderedProps
2675+ ++ "\n}"
2676+ ]
2677+
2678+ Active ->
2679+ [ selector ++ "-act:active {" ++ List.foldl (renderProps False) "" props ++ "\n}" ]
2680+
2681+
2682+renderStyleRule : OptionRecord -> Style -> Maybe PseudoClass -> List String
2683+renderStyleRule options rule maybePseudo =
2684+ case rule of
2685+ Style selector props ->
2686+ renderStyle options maybePseudo selector props
2687+
2688+ Shadows name prop ->
2689+ renderStyle options
2690+ maybePseudo
2691+ ("." ++ name)
2692+ [ Property "box-shadow" prop
2693+ ]
2694+
2695+ Transparency name transparency ->
2696+ let
2697+ opacity =
2698+ (1 - transparency)
2699+ |> min 1
2700+ |> max 0
2701+ in
2702+ renderStyle options
2703+ maybePseudo
2704+ ("." ++ name)
2705+ [ Property "opacity" (String.fromFloat opacity)
2706+ ]
2707+
2708+ FontSize i ->
2709+ renderStyle options
2710+ maybePseudo
2711+ (".font-size-" ++ String.fromInt i)
2712+ [ Property "font-size" (String.fromInt i ++ "px")
2713+ ]
2714+
2715+ FontFamily name typefaces ->
2716+ let
2717+ features =
2718+ typefaces
2719+ |> List.filterMap renderVariants
2720+ |> String.join ", "
2721+
2722+ families =
2723+ [ Property "font-family"
2724+ (typefaces
2725+ |> List.map fontName
2726+ |> String.join ", "
2727+ )
2728+ , Property "font-feature-settings" features
2729+ , Property "font-variant"
2730+ (if List.any hasSmallCaps typefaces then
2731+ "small-caps"
2732+
2733+ else
2734+ "normal"
2735+ )
2736+ ]
2737+ in
2738+ renderStyle options
2739+ maybePseudo
2740+ ("." ++ name)
2741+ families
2742+
2743+ Single class prop val ->
2744+ renderStyle options
2745+ maybePseudo
2746+ ("." ++ class)
2747+ [ Property prop val
2748+ ]
2749+
2750+ Colored class prop color ->
2751+ renderStyle options
2752+ maybePseudo
2753+ ("." ++ class)
2754+ [ Property prop (formatColor color)
2755+ ]
2756+
2757+ SpacingStyle cls x y ->
2758+ let
2759+ class =
2760+ "." ++ cls
2761+
2762+ halfX =
2763+ String.fromFloat (toFloat x / 2) ++ "px"
2764+
2765+ halfY =
2766+ String.fromFloat (toFloat y / 2) ++ "px"
2767+
2768+ xPx =
2769+ String.fromInt x ++ "px"
2770+
2771+ yPx =
2772+ String.fromInt y ++ "px"
2773+
2774+ row =
2775+ "." ++ Internal.Style.classes.row
2776+
2777+ wrappedRow =
2778+ "." ++ Internal.Style.classes.wrapped ++ row
2779+
2780+ column =
2781+ "." ++ Internal.Style.classes.column
2782+
2783+ page =
2784+ "." ++ Internal.Style.classes.page
2785+
2786+ paragraph =
2787+ "." ++ Internal.Style.classes.paragraph
2788+
2789+ left =
2790+ "." ++ Internal.Style.classes.alignLeft
2791+
2792+ right =
2793+ "." ++ Internal.Style.classes.alignRight
2794+
2795+ any =
2796+ "." ++ Internal.Style.classes.any
2797+
2798+ single =
2799+ "." ++ Internal.Style.classes.single
2800+ in
2801+ List.concat
2802+ [ renderStyle options maybePseudo (class ++ row ++ " > " ++ any ++ " + " ++ any) [ Property "margin-left" xPx ]
2803+
2804+ -- margins don't apply to last element of normal, unwrapped rows
2805+ -- , renderStyle options maybePseudo (class ++ row ++ " > " ++ any ++ ":first-child") [ Property "margin" "0" ]
2806+ -- For wrapped rows, margins always apply because we handle "canceling out" the other margins manually in the element.
2807+ , renderStyle options
2808+ maybePseudo
2809+ (class ++ wrappedRow ++ " > " ++ any)
2810+ [ Property "margin" (halfY ++ " " ++ halfX)
2811+ ]
2812+
2813+ -- , renderStyle options maybePseudo
2814+ -- (class ++ wrappedRow ++ " > " ++ any ++ ":last-child")
2815+ -- [ Property "margin-right" "0"
2816+ -- ]
2817+ -- columns
2818+ , renderStyle options maybePseudo (class ++ column ++ " > " ++ any ++ " + " ++ any) [ Property "margin-top" yPx ]
2819+ , renderStyle options maybePseudo (class ++ page ++ " > " ++ any ++ " + " ++ any) [ Property "margin-top" yPx ]
2820+ , renderStyle options maybePseudo (class ++ page ++ " > " ++ left) [ Property "margin-right" xPx ]
2821+ , renderStyle options maybePseudo (class ++ page ++ " > " ++ right) [ Property "margin-left" xPx ]
2822+ , renderStyle options
2823+ maybePseudo
2824+ (class ++ paragraph)
2825+ [ Property "line-height" ("calc(1em + " ++ String.fromInt y ++ "px)")
2826+ ]
2827+ , renderStyle options
2828+ maybePseudo
2829+ ("textarea" ++ any ++ class)
2830+ [ Property "line-height" ("calc(1em + " ++ String.fromInt y ++ "px)")
2831+ , Property "height" ("calc(100% + " ++ String.fromInt y ++ "px)")
2832+ ]
2833+
2834+ -- , renderStyle options
2835+ -- maybePseudo
2836+ -- (class ++ paragraph ++ " > " ++ any)
2837+ -- [ Property "margin-right" xPx
2838+ -- , Property "margin-bottom" yPx
2839+ -- ]
2840+ , renderStyle options
2841+ maybePseudo
2842+ (class ++ paragraph ++ " > " ++ left)
2843+ [ Property "margin-right" xPx
2844+ ]
2845+ , renderStyle options
2846+ maybePseudo
2847+ (class ++ paragraph ++ " > " ++ right)
2848+ [ Property "margin-left" xPx
2849+ ]
2850+ , renderStyle options
2851+ maybePseudo
2852+ (class ++ paragraph ++ "::after")
2853+ [ Property "content" "''"
2854+ , Property "display" "block"
2855+ , Property "height" "0"
2856+ , Property "width" "0"
2857+ , Property "margin-top" (String.fromInt (-1 * (y // 2)) ++ "px")
2858+ ]
2859+ , renderStyle options
2860+ maybePseudo
2861+ (class ++ paragraph ++ "::before")
2862+ [ Property "content" "''"
2863+ , Property "display" "block"
2864+ , Property "height" "0"
2865+ , Property "width" "0"
2866+ , Property "margin-bottom" (String.fromInt (-1 * (y // 2)) ++ "px")
2867+ ]
2868+ ]
2869+
2870+ PaddingStyle cls top right bottom left ->
2871+ let
2872+ class =
2873+ "."
2874+ ++ cls
2875+ in
2876+ renderStyle options
2877+ maybePseudo
2878+ class
2879+ [ Property "padding"
2880+ (String.fromFloat top
2881+ ++ "px "
2882+ ++ String.fromFloat right
2883+ ++ "px "
2884+ ++ String.fromFloat bottom
2885+ ++ "px "
2886+ ++ String.fromFloat left
2887+ ++ "px"
2888+ )
2889+ ]
2890+
2891+ BorderWidth cls top right bottom left ->
2892+ let
2893+ class =
2894+ "."
2895+ ++ cls
2896+ in
2897+ renderStyle options
2898+ maybePseudo
2899+ class
2900+ [ Property "border-width"
2901+ (String.fromInt top
2902+ ++ "px "
2903+ ++ String.fromInt right
2904+ ++ "px "
2905+ ++ String.fromInt bottom
2906+ ++ "px "
2907+ ++ String.fromInt left
2908+ ++ "px"
2909+ )
2910+ ]
2911+
2912+ GridTemplateStyle template ->
2913+ let
2914+ class =
2915+ ".grid-rows-"
2916+ ++ String.join "-" (List.map lengthClassName template.rows)
2917+ ++ "-cols-"
2918+ ++ String.join "-" (List.map lengthClassName template.columns)
2919+ ++ "-space-x-"
2920+ ++ lengthClassName (Tuple.first template.spacing)
2921+ ++ "-space-y-"
2922+ ++ lengthClassName (Tuple.second template.spacing)
2923+
2924+ ySpacing =
2925+ toGridLength (Tuple.second template.spacing)
2926+
2927+ xSpacing =
2928+ toGridLength (Tuple.first template.spacing)
2929+
2930+ toGridLength x =
2931+ toGridLengthHelper Nothing Nothing x
2932+
2933+ toGridLengthHelper minimum maximum x =
2934+ case x of
2935+ Px px ->
2936+ String.fromInt px ++ "px"
2937+
2938+ Content ->
2939+ case ( minimum, maximum ) of
2940+ ( Nothing, Nothing ) ->
2941+ "max-content"
2942+
2943+ ( Just minSize, Nothing ) ->
2944+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ "max-content)"
2945+
2946+ ( Nothing, Just maxSize ) ->
2947+ "minmax(max-content, " ++ String.fromInt maxSize ++ "px)"
2948+
2949+ ( Just minSize, Just maxSize ) ->
2950+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt maxSize ++ "px)"
2951+
2952+ Fill i ->
2953+ case ( minimum, maximum ) of
2954+ ( Nothing, Nothing ) ->
2955+ String.fromInt i ++ "fr"
2956+
2957+ ( Just minSize, Nothing ) ->
2958+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt i ++ "fr" ++ "fr)"
2959+
2960+ ( Nothing, Just maxSize ) ->
2961+ "minmax(max-content, " ++ String.fromInt maxSize ++ "px)"
2962+
2963+ ( Just minSize, Just maxSize ) ->
2964+ "minmax(" ++ String.fromInt minSize ++ "px, " ++ String.fromInt maxSize ++ "px)"
2965+
2966+ Min m len ->
2967+ toGridLengthHelper (Just m) maximum len
2968+
2969+ Max m len ->
2970+ toGridLengthHelper minimum (Just m) len
2971+
2972+ msColumns =
2973+ template.columns
2974+ |> List.map toGridLength
2975+ |> String.join ySpacing
2976+ |> (\x -> "-ms-grid-columns: " ++ x ++ ";")
2977+
2978+ msRows =
2979+ template.columns
2980+ |> List.map toGridLength
2981+ |> String.join ySpacing
2982+ |> (\x -> "-ms-grid-rows: " ++ x ++ ";")
2983+
2984+ base =
2985+ class ++ "{" ++ msColumns ++ msRows ++ "}"
2986+
2987+ columns =
2988+ template.columns
2989+ |> List.map toGridLength
2990+ |> String.join " "
2991+ |> (\x -> "grid-template-columns: " ++ x ++ ";")
2992+
2993+ rows =
2994+ template.rows
2995+ |> List.map toGridLength
2996+ |> String.join " "
2997+ |> (\x -> "grid-template-rows: " ++ x ++ ";")
2998+
2999+ gapX =
3000+ "grid-column-gap:" ++ toGridLength (Tuple.first template.spacing) ++ ";"
3001+
3002+ gapY =
3003+ "grid-row-gap:" ++ toGridLength (Tuple.second template.spacing) ++ ";"
3004+
3005+ modernGrid =
3006+ class ++ "{" ++ columns ++ rows ++ gapX ++ gapY ++ "}"
3007+
3008+ supports =
3009+ "@supports (display:grid) {" ++ modernGrid ++ "}"
3010+ in
3011+ [ base
3012+ , supports
3013+ ]
3014+
3015+ GridPosition position ->
3016+ let
3017+ class =
3018+ ".grid-pos-"
3019+ ++ String.fromInt position.row
3020+ ++ "-"
3021+ ++ String.fromInt position.col
3022+ ++ "-"
3023+ ++ String.fromInt position.width
3024+ ++ "-"
3025+ ++ String.fromInt position.height
3026+
3027+ msPosition =
3028+ String.join " "
3029+ [ "-ms-grid-row: "
3030+ ++ String.fromInt position.row
3031+ ++ ";"
3032+ , "-ms-grid-row-span: "
3033+ ++ String.fromInt position.height
3034+ ++ ";"
3035+ , "-ms-grid-column: "
3036+ ++ String.fromInt position.col
3037+ ++ ";"
3038+ , "-ms-grid-column-span: "
3039+ ++ String.fromInt position.width
3040+ ++ ";"
3041+ ]
3042+
3043+ base =
3044+ class ++ "{" ++ msPosition ++ "}"
3045+
3046+ modernPosition =
3047+ String.join " "
3048+ [ "grid-row: "
3049+ ++ String.fromInt position.row
3050+ ++ " / "
3051+ ++ String.fromInt (position.row + position.height)
3052+ ++ ";"
3053+ , "grid-column: "
3054+ ++ String.fromInt position.col
3055+ ++ " / "
3056+ ++ String.fromInt (position.col + position.width)
3057+ ++ ";"
3058+ ]
3059+
3060+ modernGrid =
3061+ class ++ "{" ++ modernPosition ++ "}"
3062+
3063+ supports =
3064+ "@supports (display:grid) {" ++ modernGrid ++ "}"
3065+ in
3066+ [ base
3067+ , supports
3068+ ]
3069+
3070+ PseudoSelector class styles ->
3071+ let
3072+ renderPseudoRule style =
3073+ renderStyleRule options style (Just class)
3074+ in
3075+ List.concatMap renderPseudoRule styles
3076+
3077+ Transform transform ->
3078+ let
3079+ val =
3080+ transformValue transform
3081+
3082+ class =
3083+ transformClass transform
3084+ in
3085+ case ( class, val ) of
3086+ ( Just cls, Just v ) ->
3087+ renderStyle options
3088+ maybePseudo
3089+ ("." ++ cls)
3090+ [ Property "transform"
3091+ v
3092+ ]
3093+
3094+ _ ->
3095+ []
3096+
3097+
3098+lengthClassName : Length -> String
3099+lengthClassName x =
3100+ case x of
3101+ Px px ->
3102+ String.fromInt px ++ "px"
3103+
3104+ Content ->
3105+ "auto"
3106+
3107+ Fill i ->
3108+ String.fromInt i ++ "fr"
3109+
3110+ Min min len ->
3111+ "min" ++ String.fromInt min ++ lengthClassName len
3112+
3113+ Max max len ->
3114+ "max" ++ String.fromInt max ++ lengthClassName len
3115+
3116+
3117+formatDropShadow shadow =
3118+ String.join " "
3119+ [ String.fromFloat (Tuple.first shadow.offset) ++ "px"
3120+ , String.fromFloat (Tuple.second shadow.offset) ++ "px"
3121+ , String.fromFloat shadow.blur ++ "px"
3122+ , formatColor shadow.color
3123+ ]
3124+
3125+
3126+formatTextShadow shadow =
3127+ String.join " "
3128+ [ String.fromFloat (Tuple.first shadow.offset) ++ "px"
3129+ , String.fromFloat (Tuple.second shadow.offset) ++ "px"
3130+ , String.fromFloat shadow.blur ++ "px"
3131+ , formatColor shadow.color
3132+ ]
3133+
3134+
3135+textShadowClass shadow =
3136+ String.concat
3137+ [ "txt"
3138+ , floatClass (Tuple.first shadow.offset) ++ "px"
3139+ , floatClass (Tuple.second shadow.offset) ++ "px"
3140+ , floatClass shadow.blur ++ "px"
3141+ , formatColorClass shadow.color
3142+ ]
3143+
3144+
3145+formatBoxShadow shadow =
3146+ String.join " " <|
3147+ List.filterMap identity
3148+ [ if shadow.inset then
3149+ Just "inset"
3150+
3151+ else
3152+ Nothing
3153+ , Just <| String.fromFloat (Tuple.first shadow.offset) ++ "px"
3154+ , Just <| String.fromFloat (Tuple.second shadow.offset) ++ "px"
3155+ , Just <| String.fromFloat shadow.blur ++ "px"
3156+ , Just <| String.fromFloat shadow.size ++ "px"
3157+ , Just <| formatColor shadow.color
3158+ ]
3159+
3160+
3161+boxShadowClass shadow =
3162+ String.concat <|
3163+ [ if shadow.inset then
3164+ "box-inset"
3165+
3166+ else
3167+ "box-"
3168+ , floatClass (Tuple.first shadow.offset) ++ "px"
3169+ , floatClass (Tuple.second shadow.offset) ++ "px"
3170+ , floatClass shadow.blur ++ "px"
3171+ , floatClass shadow.size ++ "px"
3172+ , formatColorClass shadow.color
3173+ ]
3174+
3175+
3176+floatClass : Float -> String
3177+floatClass x =
3178+ String.fromInt (round (x * 255))
3179+
3180+
3181+formatColor : Color -> String
3182+formatColor (Rgba red green blue alpha) =
3183+ "rgba("
3184+ ++ String.fromInt (round (red * 255))
3185+ ++ ("," ++ String.fromInt (round (green * 255)))
3186+ ++ ("," ++ String.fromInt (round (blue * 255)))
3187+ ++ ("," ++ String.fromFloat alpha ++ ")")
3188+
3189+
3190+formatColorClass : Color -> String
3191+formatColorClass (Rgba red green blue alpha) =
3192+ floatClass red
3193+ ++ "-"
3194+ ++ floatClass green
3195+ ++ "-"
3196+ ++ floatClass blue
3197+ ++ "-"
3198+ ++ floatClass alpha
3199+
3200+
3201+spacingName x y =
3202+ "spacing-" ++ String.fromInt x ++ "-" ++ String.fromInt y
3203+
3204+
3205+paddingName top right bottom left =
3206+ "pad-"
3207+ ++ String.fromInt top
3208+ ++ "-"
3209+ ++ String.fromInt right
3210+ ++ "-"
3211+ ++ String.fromInt bottom
3212+ ++ "-"
3213+ ++ String.fromInt left
3214+
3215+
3216+paddingNameFloat top right bottom left =
3217+ "pad-"
3218+ ++ floatClass top
3219+ ++ "-"
3220+ ++ floatClass right
3221+ ++ "-"
3222+ ++ floatClass bottom
3223+ ++ "-"
3224+ ++ floatClass left
3225+
3226+
3227+getStyleName : Style -> String
3228+getStyleName style =
3229+ case style of
3230+ Shadows name _ ->
3231+ name
3232+
3233+ Transparency name o ->
3234+ name
3235+
3236+ Style class _ ->
3237+ class
3238+
3239+ FontFamily name _ ->
3240+ name
3241+
3242+ FontSize i ->
3243+ "font-size-" ++ String.fromInt i
3244+
3245+ Single class _ _ ->
3246+ class
3247+
3248+ Colored class _ _ ->
3249+ class
3250+
3251+ SpacingStyle cls x y ->
3252+ cls
3253+
3254+ PaddingStyle cls top right bottom left ->
3255+ cls
3256+
3257+ BorderWidth cls top right bottom left ->
3258+ cls
3259+
3260+ GridTemplateStyle template ->
3261+ "grid-rows-"
3262+ ++ String.join "-" (List.map lengthClassName template.rows)
3263+ ++ "-cols-"
3264+ ++ String.join "-" (List.map lengthClassName template.columns)
3265+ ++ "-space-x-"
3266+ ++ lengthClassName (Tuple.first template.spacing)
3267+ ++ "-space-y-"
3268+ ++ lengthClassName (Tuple.second template.spacing)
3269+
3270+ GridPosition pos ->
3271+ "gp grid-pos-"
3272+ ++ String.fromInt pos.row
3273+ ++ "-"
3274+ ++ String.fromInt pos.col
3275+ ++ "-"
3276+ ++ String.fromInt pos.width
3277+ ++ "-"
3278+ ++ String.fromInt pos.height
3279+
3280+ PseudoSelector selector subStyle ->
3281+ let
3282+ name =
3283+ case selector of
3284+ Focus ->
3285+ "fs"
3286+
3287+ Hover ->
3288+ "hv"
3289+
3290+ Active ->
3291+ "act"
3292+ in
3293+ List.map
3294+ (\sty ->
3295+ case getStyleName sty of
3296+ "" ->
3297+ ""
3298+
3299+ styleName ->
3300+ styleName ++ "-" ++ name
3301+ )
3302+ subStyle
3303+ |> String.join " "
3304+
3305+ Transform x ->
3306+ Maybe.withDefault "" (transformClass x)
3307+
3308+
3309+
3310+{- Constants -}
3311+
3312+
3313+asGrid : LayoutContext
3314+asGrid =
3315+ AsGrid
3316+
3317+
3318+asRow : LayoutContext
3319+asRow =
3320+ AsRow
3321+
3322+
3323+asColumn : LayoutContext
3324+asColumn =
3325+ AsColumn
3326+
3327+
3328+asEl : LayoutContext
3329+asEl =
3330+ AsEl
3331+
3332+
3333+asParagraph : LayoutContext
3334+asParagraph =
3335+ AsParagraph
3336+
3337+
3338+asTextColumn : LayoutContext
3339+asTextColumn =
3340+ AsTextColumn
3341+
3342+
3343+
3344+{- Mapping -}
3345+
3346+
3347+map : (msg -> msg1) -> Element msg -> Element msg1
3348+map fn el =
3349+ case el of
3350+ Styled styled ->
3351+ Styled
3352+ { styles = styled.styles
3353+ , html = \add context -> VirtualDom.map fn <| styled.html add context
3354+ }
3355+
3356+ Unstyled html ->
3357+ Unstyled (VirtualDom.map fn << html)
3358+
3359+ Text str ->
3360+ Text str
3361+
3362+ Empty ->
3363+ Empty
3364+
3365+
3366+mapAttr : (msg -> msg1) -> Attribute aligned msg -> Attribute aligned msg1
3367+mapAttr fn attr =
3368+ case attr of
3369+ NoAttribute ->
3370+ NoAttribute
3371+
3372+ Describe description ->
3373+ Describe description
3374+
3375+ AlignX x ->
3376+ AlignX x
3377+
3378+ AlignY y ->
3379+ AlignY y
3380+
3381+ Width x ->
3382+ Width x
3383+
3384+ Height x ->
3385+ Height x
3386+
3387+ Class x y ->
3388+ Class x y
3389+
3390+ StyleClass flag style ->
3391+ StyleClass flag style
3392+
3393+ Nearby location elem ->
3394+ Nearby location (map fn elem)
3395+
3396+ Attr htmlAttr ->
3397+ Attr (VirtualDom.mapAttribute fn htmlAttr)
3398+
3399+ TransformComponent fl trans ->
3400+ TransformComponent fl trans
3401+
3402+
3403+mapAttrFromStyle : (msg -> msg1) -> Attribute Never msg -> Attribute () msg1
3404+mapAttrFromStyle fn attr =
3405+ case attr of
3406+ NoAttribute ->
3407+ NoAttribute
3408+
3409+ Describe description ->
3410+ Describe description
3411+
3412+ AlignX x ->
3413+ AlignX x
3414+
3415+ AlignY y ->
3416+ AlignY y
3417+
3418+ Width x ->
3419+ Width x
3420+
3421+ Height x ->
3422+ Height x
3423+
3424+ -- invalidation key "border-color" as opposed to "border-color-10-10-10" that will be the key for the class
3425+ Class x y ->
3426+ Class x y
3427+
3428+ StyleClass flag style ->
3429+ StyleClass flag style
3430+
3431+ Nearby location elem ->
3432+ Nearby location (map fn elem)
3433+
3434+ Attr htmlAttr ->
3435+ Attr (VirtualDom.mapAttribute fn htmlAttr)
3436+
3437+ TransformComponent fl trans ->
3438+ TransformComponent fl trans
3439+
3440+
3441+unwrapDecorations : List (Attribute Never Never) -> List Style
3442+unwrapDecorations attrs =
3443+ case List.foldl unwrapDecsHelper ( [], Untransformed ) attrs of
3444+ ( styles, transform ) ->
3445+ Transform transform :: styles
3446+
3447+
3448+unwrapDecsHelper attr ( styles, trans ) =
3449+ case removeNever attr of
3450+ StyleClass _ style ->
3451+ ( style :: styles, trans )
3452+
3453+ TransformComponent flag component ->
3454+ ( styles, composeTransformation trans component )
3455+
3456+ _ ->
3457+ ( styles, trans )
3458+
3459+
3460+removeNever : Attribute Never Never -> Attribute () msg
3461+removeNever style =
3462+ mapAttrFromStyle Basics.never style
3463+
3464+
3465+tag : String -> Style -> Style
3466+tag label style =
3467+ case style of
3468+ Single class prop val ->
3469+ Single (label ++ "-" ++ class) prop val
3470+
3471+ Colored class prop val ->
3472+ Colored (label ++ "-" ++ class) prop val
3473+
3474+ Style class props ->
3475+ Style (label ++ "-" ++ class) props
3476+
3477+ Transparency class o ->
3478+ Transparency (label ++ "-" ++ class) o
3479+
3480+ x ->
3481+ x
3482+
3483+
3484+onlyStyles : Attribute aligned msg -> Maybe Style
3485+onlyStyles attr =
3486+ case attr of
3487+ StyleClass _ style ->
3488+ Just style
3489+
3490+ _ ->
3491+ Nothing
3492+
3493+
3494+
3495+{- Font Adjustments -}
3496+
3497+
3498+convertAdjustment adjustment =
3499+ let
3500+ lineHeight =
3501+ 1.5
3502+
3503+ base =
3504+ lineHeight
3505+
3506+ normalDescender =
3507+ (lineHeight - 1)
3508+ / 2
3509+
3510+ oldMiddle =
3511+ lineHeight / 2
3512+
3513+ newCapitalMiddle =
3514+ ((ascender - newBaseline) / 2) + newBaseline
3515+
3516+ newFullMiddle =
3517+ ((ascender - descender) / 2) + descender
3518+
3519+ lines =
3520+ [ adjustment.capital
3521+ , adjustment.baseline
3522+ , adjustment.descender
3523+ , adjustment.lowercase
3524+ ]
3525+
3526+ ascender =
3527+ Maybe.withDefault adjustment.capital (List.maximum lines)
3528+
3529+ descender =
3530+ Maybe.withDefault adjustment.descender (List.minimum lines)
3531+
3532+ newBaseline =
3533+ lines
3534+ |> List.filter (\x -> x /= descender)
3535+ |> List.minimum
3536+ |> Maybe.withDefault adjustment.baseline
3537+
3538+ capitalVertical =
3539+ 1 - ascender
3540+
3541+ capitalSize =
3542+ 1 / (ascender - newBaseline)
3543+
3544+ fullSize =
3545+ 1 / (ascender - descender)
3546+
3547+ fullVertical =
3548+ 1 - ascender
3549+
3550+ -- (oldMiddle - newFullMiddle) * 2
3551+ in
3552+ { full =
3553+ adjust fullSize (ascender - descender) fullVertical
3554+ , capital =
3555+ adjust capitalSize (ascender - newBaseline) capitalVertical
3556+ }
3557+
3558+
3559+adjust size height vertical =
3560+ { vertical = vertical
3561+ , height =
3562+ height / size
3563+ , size = size
3564+ }