git.iko.soy/typeable/elm-ui
on master help sign in
Font.sizeByCapital
2cc2080Matthew Griffith
src/Element.elm−3
@@ -457,9 +457,6 @@ layoutWith { options } attrs child =
457 [ classes.root
457 [ classes.root
458 , classes.any
458 , classes.any
459 , classes.single
459 , classes.single
460−
461− -- , classes.contentCenterX
462− -- , classes.contentCenterY
463 ]
460 ]
464 )
461 )
465 :: (Internal.rootStyle ++ attrs)
462 :: (Internal.rootStyle ++ attrs)
src/Element/Font.elm+38−2
@@ -1,7 +1,7 @@
1module Element.Font exposing
1module Element.Font exposing
2 ( color, size
2 ( color, size
3 , family, Font, typeface, serif, sansSerif, monospace
3 , family, Font, typeface, serif, sansSerif, monospace
4− , external
4+ , external, with, sizeByCapital
5 , alignLeft, alignRight, center, justify, letterSpacing, wordSpacing
5 , alignLeft, alignRight, center, justify, letterSpacing, wordSpacing
6 , underline, strike, italic, unitalicized
6 , underline, strike, italic, unitalicized
7 , heavy, extraBold, bold, semiBold, medium, regular, light, extraLight, hairline
7 , heavy, extraBold, bold, semiBold, medium, regular, light, extraLight, hairline
@@ -35,7 +35,7 @@ module Element.Font exposing
35
35
36@docs family, Font, typeface, serif, sansSerif, monospace
36@docs family, Font, typeface, serif, sansSerif, monospace
37
37
38−@docs external
38+@docs external, with, sizeByCapital
39
39
40`Font.external` can be used to import font files. Let's say you found a neat font on <http://fonts.google.com>:
40`Font.external` can be used to import font files. Let's say you found a neat font on <http://fonts.google.com>:
41
41
@@ -137,6 +137,42 @@ typeface =
137 Internal.Typeface
137 Internal.Typeface
138
138
139
139
140+{-| -}
141+type alias Adjustment =
142+ { capital : Float
143+ , lowercase : Float
144+ , baseline : Float
145+ , descender : Float
146+ }
147+
148+
149+{-| -}
150+defaultAdjustment : Adjustment
151+defaultAdjustment =
152+ { capital = 1.0
153+ , lowercase = 0.5
154+ , baseline = 0.2
155+ , descender = 0
156+ }
157+
158+
159+{-| -}
160+with :
161+ { name : String
162+ , adjustment : Adjustment
163+ , url : Maybe String
164+ }
165+ -> Font
166+with =
167+ Internal.FontWith
168+
169+
170+{-| -}
171+sizeByCapital : Attribute msg
172+sizeByCapital =
173+ Internal.htmlClass classes.sizeByCapital
174+
175+
140{-| -}
176{-| -}
141external : { url : String, name : String } -> Font
177external : { url : String, name : String } -> Font
142external { url, name } =
178external { url, name } =
src/Internal/Model.elm+410−118
@@ -80,12 +80,12 @@ module Internal.Model exposing
80 , pageClass
80 , pageClass
81 , paragraphClass
81 , paragraphClass
82 , reduceRecursive
82 , reduceRecursive
83− , reduceRecursiveCalcName
84 , reduceStyles
83 , reduceStyles
85 , reduceStylesRecursive
84 , reduceStylesRecursive
86 , removeNever
85 , removeNever
87− , renderFocusStyle
86+ , renderFocusStyle
88− , renderFont
87+ -- , renderFont
88+
89 , renderFontClassName
89 , renderFontClassName
90 , renderHeight
90 , renderHeight
91 , renderRoot
91 , renderRoot
@@ -93,24 +93,15 @@ module Internal.Model exposing
93 , rootStyle
93 , rootStyle
94 , rowClass
94 , rowClass
95 , singleClass
95 , singleClass
96− , skippable
97− , sortedReduce
98 , spacingName
96 , spacingName
99 , staticRoot
97 , staticRoot
100 , tag
98 , tag
101− , textElement
102− , textElementFill
103 , textShadowName
99 , textShadowName
104 , toHtml
100 , toHtml
105 , toStyleSheet
101 , toStyleSheet
106− , toStyleSheetString
107 , transformClass
102 , transformClass
108− , transformValue
109− , unit
110 , unstyled
103 , unstyled
111− , untransformed
112 , unwrapDecorations
104 , unwrapDecorations
113− , unwrapDecsHelper
114 )
105 )
115
106
116{-| -}
107{-| -}
@@ -213,12 +204,26 @@ type PseudoClass
213 | Active
204 | Active
214
205
215
206
207+{-| -}
208+type alias Adjustment =
209+ { capital : Float
210+ , lowercase : Float
211+ , baseline : Float
212+ , descender : Float
213+ }
214+
215+
216type Font
216type Font
217 = Serif
217 = Serif
218 | SansSerif
218 | SansSerif
219 | Monospace
219 | Monospace
220 | Typeface String
220 | Typeface String
221 | ImportFont String String
221 | ImportFont String String
222+ | FontWith
223+ { name : String
224+ , url : Maybe String
225+ , adjustment : Adjustment
226+ }
222
227
223
228
224type Property
229type Property
@@ -1490,34 +1495,32 @@ createElement context children rendered =
1490 -- Maybe we could unpack text elements in a paragraph as well,
1495 -- Maybe we could unpack text elements in a paragraph as well,
1491 -- however, embedded elements that are larger than the line height will overlap with exisitng text.
1496 -- however, embedded elements that are larger than the line height will overlap with exisitng text.
1492 -- I don't think that's what we want.
1497 -- I don't think that's what we want.
1493− if
1498+ -- if
1494− context
1499+ -- context
1495− == asEl
1500+ -- == asEl
1496− || context
1501+ -- || context
1497− == asParagraph
1502+ -- == asParagraph
1498− then
1503+ -- then
1499− ( VirtualDom.text
1504+ -- ( VirtualDom.text
1500− (if context == asParagraph then
1505+ -- (if context == asParagraph then
1501− str
1506+ -- str
1502−
1503− else
1507+ -- else
1504− str
1508+ -- str
1505− )
1509+ -- )
1506− :: htmls
1510+ -- :: htmls
1507− , existingStyles
1511+ -- , existingStyles
1508− )
1512+ -- )
1509−
1510− else
1513+ -- else
1511− ( textElement
1514+ ( textElement
1512− (if context == asParagraph then
1515+ (if context == asParagraph then
1513− str
1516+ str
1514−
1517+
1515− else
1518+ else
1516− str
1519+ str
1517− )
1518− :: htmls
1519− , existingStyles
1520 )
1520 )
1521+ :: htmls
1522+ , existingStyles
1523+ )
1521
1524
1522 Empty ->
1525 Empty ->
1523 ( htmls, existingStyles )
1526 ( htmls, existingStyles )
@@ -1559,28 +1562,27 @@ createElement context children rendered =
1559 -- TEXT OPTIMIZATION
1562 -- TEXT OPTIMIZATION
1560 -- You can have raw text if the element is an el, and has `width-content` and `height-content`
1563 -- You can have raw text if the element is an el, and has `width-content` and `height-content`
1561 -- Same if it's a column or row with one child and width-content, height-content
1564 -- Same if it's a column or row with one child and width-content, height-content
1562− if
1565+ -- if
1563− context
1566+ -- context
1564− == asEl
1567+ -- == asEl
1565− || context
1568+ -- || context
1566− == asParagraph
1569+ -- == asParagraph
1567− then
1570+ -- then
1568− ( ( key
1571+ -- ( ( key
1569− , VirtualDom.text
1572+ -- , VirtualDom.text
1570− str
1573+ -- str
1571− )
1574+ -- )
1572− :: htmls
1575+ -- :: htmls
1573− , existingStyles
1576+ -- , existingStyles
1574− )
1577+ -- )
1575−
1576− else
1578+ -- else
1577− ( ( key
1579+ ( ( key
1578− , textElement
1580+ , textElement
1579− str
1581+ str
1580− )
1582+ )
1581− :: htmls
1583+ :: htmls
1582− , existingStyles
1584+ , existingStyles
1583− )
1585+ )
1584
1586
1585 Empty ->
1587 Empty ->
1586 ( htmls, existingStyles )
1588 ( htmls, existingStyles )
@@ -2051,6 +2053,12 @@ renderFontClassName font current =
2051 |> String.toLower
2053 |> String.toLower
2052 |> String.words
2054 |> String.words
2053 |> String.join "-"
2055 |> String.join "-"
2056+
2057+ FontWith { name } ->
2058+ name
2059+ |> String.toLower
2060+ |> String.words
2061+ |> String.join "-"
2054 )
2062 )
2055
2063
2056
2064
@@ -2160,66 +2168,279 @@ optionsToRecord options =
2160 options
2168 options
2161
2169
2162
2170
2163−renderFont : List Font -> String
2171+toStyleSheet : OptionRecord -> List Style -> VirtualDom.Node msg
2164−renderFont families =
2172+toStyleSheet options styleSheet =
2173+ VirtualDom.node "style" [] [ VirtualDom.text (toStyleSheetString options styleSheet) ]
2174+
2175+
2176+
2177+-- case typefaceAdjustment typefaces of
2178+-- Nothing ->
2179+-- ""
2180+-- Just ( parentAdj, textAdjustment ) ->
2181+-- String.join " "
2182+-- [ renderStyle
2183+-- maybePseudo
2184+-- ("."
2185+-- ++ name
2186+-- ++ "."
2187+-- ++ classes.sizeByCapital
2188+-- ++ ", "
2189+-- ++ "."
2190+-- ++ name
2191+-- ++ " ."
2192+-- ++ classes.sizeByCapital
2193+-- )
2194+-- parentAdj
2195+-- , renderStyle
2196+-- maybePseudo
2197+-- ("."
2198+-- ++ name
2199+-- ++ "."
2200+-- ++ classes.sizeByCapital
2201+-- ++ "> ."
2202+-- ++ Internal.Style.classes.text
2203+-- ++ ", ."
2204+-- ++ name
2205+-- ++ " ."
2206+-- ++ classes.sizeByCapital
2207+-- ++ " > ."
2208+-- ++ Internal.Style.classes.text
2209+-- )
2210+-- textAdjustment
2211+-- ]
2212+-- renderTopLevels rule =
2213+-- case rule of
2214+-- FontFamily name typefaces ->
2215+-- let
2216+-- getImports font =
2217+-- case font of
2218+-- ImportFont _ url ->
2219+-- Just ("@import url('" ++ url ++ "');")
2220+-- FontWith { url } ->
2221+-- case url of
2222+-- Just x ->
2223+-- Just ("@import url('" ++ x ++ "');")
2224+-- Nothing ->
2225+-- Nothing
2226+-- _ ->
2227+-- Nothing
2228+-- in
2229+-- typefaces
2230+-- |> List.filterMap getImports
2231+-- |> String.join "\n"
2232+-- |> Just
2233+-- _ ->
2234+-- Nothing
2235+
2236+
2237+renderTopLevelValues rules =
2165 let
2238 let
2166− fontName font =
2239+ withImport font =
2167 case font of
2240 case font of
2168− Serif ->
2241+ ImportFont _ url ->
2169− "serif"
2242+ Just ("@import url('" ++ url ++ "');")
2170
2243
2171− SansSerif ->
2244+ FontWith with ->
2172− "sans-serif"
2245+ case with.url of
2246+ Just x ->
2247+ Just ("@import url('" ++ x ++ "');")
2173
2248
2174− Monospace ->
2249+ Nothing ->
2175− "monospace"
2250+ Nothing
2176
2251
2177− Typeface name ->
2252+ _ ->
2178− "\"" ++ name ++ "\""
2253+ Nothing
2179
2254
2180− ImportFont name url ->
2255+ allNames =
2181− "\"" ++ name ++ "\""
2256+ List.map Tuple.first rules
2182− in
2183− families
2184− |> List.map fontName
2185− |> String.join ", "
2186
2257
2258+ fontImports ( name, typefaces ) =
2259+ let
2260+ imports =
2261+ String.join "\n" (List.filterMap withImport typefaces)
2262+ in
2263+ imports
2187
2264
2188−toStyleSheet : OptionRecord -> List Style -> VirtualDom.Node msg
2265+ fontAdjustments ( name, typefaces ) =
2189−toStyleSheet options styleSheet =
2266+ case typefaceAdjustment typefaces of
2190− VirtualDom.node "style" [] [ VirtualDom.text (toStyleSheetString options styleSheet) ]
2267+ Nothing ->
2268+ String.join ""
2269+ (List.map (renderNullAdjustmentRule name) allNames)
2191
2270
2271+ Just adjustment ->
2272+ String.join ""
2273+ (List.map (renderFontAdjustmentRule name adjustment) allNames)
2274+ in
2275+ String.join "\n" (List.map fontImports rules)
2276+ ++ String.join "\n" (List.map fontAdjustments rules)
2192
2277
2193−toStyleSheetString : OptionRecord -> List Style -> String
2278+
2194−toStyleSheetString options stylesheet =
2279+renderNullAdjustmentRule fontToAdjust otherFontName =
2195 let
2280 let
2196− renderTopLevels rule =
2281+ name =
2197− case rule of
2282+ if fontToAdjust == otherFontName then
2198− FontFamily name typefaces ->
2283+ fontToAdjust
2199− let
2200− getImports font =
2201− case font of
2202− ImportFont _ url ->
2203− Just ("@import url('" ++ url ++ "');")
2204
2284
2205− _ ->
2285+ else
2206− Nothing
2286+ otherFontName ++ " ." ++ fontToAdjust
2207− in
2287+ in
2208− typefaces
2209− |> List.filterMap getImports
2210− |> String.join "\n"
2288+ String.join " "
2211− |> Just
2289+ [ bracket
2290+ ("."
2291+ ++ name
2292+ ++ "."
2293+ ++ classes.sizeByCapital
2294+ ++ ", "
2295+ ++ "."
2296+ ++ name
2297+ ++ " ."
2298+ ++ classes.sizeByCapital
2299+ )
2300+ [ ( "line-height", "1" )
2301+ ]
2302+ , bracket
2303+ ("."
2304+ ++ name
2305+ ++ "."
2306+ ++ classes.sizeByCapital
2307+ ++ "> ."
2308+ ++ Internal.Style.classes.text
2309+ ++ ", ."
2310+ ++ name
2311+ ++ " ."
2312+ ++ classes.sizeByCapital
2313+ ++ " > ."
2314+ ++ Internal.Style.classes.text
2315+ )
2316+ [ ( "vertical-align", "0" )
2317+ , ( "line-height", "1" )
2318+ ]
2319+ ]
2212
2320
2213− _ ->
2214− Nothing
2215
2321
2216− renderProps force (Property key val) existing =
2322+renderFontAdjustmentRule fontToAdjust ( parentAdj, textAdjustment ) otherFontName =
2217− if force then
2323+ let
2218− existing ++ "\n " ++ key ++ ": " ++ val ++ " !important;"
2324+ name =
2325+ if fontToAdjust == otherFontName then
2326+ fontToAdjust
2219
2327
2220 else
2328 else
2221− existing ++ "\n " ++ key ++ ": " ++ val ++ ";"
2329+ otherFontName ++ " ." ++ fontToAdjust
2330+ in
2331+ String.join " "
2332+ [ bracket
2333+ ("."
2334+ ++ name
2335+ ++ "."
2336+ ++ classes.sizeByCapital
2337+ ++ ", "
2338+ ++ "."
2339+ ++ name
2340+ ++ " ."
2341+ ++ classes.sizeByCapital
2342+ )
2343+ parentAdj
2344+ , bracket
2345+ ("."
2346+ ++ name
2347+ ++ "."
2348+ ++ classes.sizeByCapital
2349+ ++ "> ."
2350+ ++ Internal.Style.classes.text
2351+ ++ ", ."
2352+ ++ name
2353+ ++ " ."
2354+ ++ classes.sizeByCapital
2355+ ++ " > ."
2356+ ++ Internal.Style.classes.text
2357+ )
2358+ textAdjustment
2359+ ]
2360+
2361+
2362+bracket selector rules =
2363+ let
2364+ renderPair ( name, val ) =
2365+ name ++ ": " ++ val ++ ";"
2366+ in
2367+ selector ++ " {" ++ String.join "" (List.map renderPair rules) ++ "}"
2368+
2369+
2370+fontAdjustmentRules converted =
2371+ ( [ ( "display", "block" )
2372+ , ( "line-height", String.fromFloat converted.height )
2373+ ]
2374+ , [ ( "display", "inline-block" )
2375+ , ( "line-height", String.fromFloat converted.height )
2376+ , ( "vertical-align", String.fromFloat converted.vertical ++ "em" )
2377+ ]
2378+ )
2379+
2380+
2381+typefaceAdjustment typefaces =
2382+ List.foldl
2383+ (\face found ->
2384+ case found of
2385+ Nothing ->
2386+ case face of
2387+ FontWith with ->
2388+ Just
2389+ (fontAdjustmentRules
2390+ (.capital (convertAdjustment with.adjustment))
2391+ )
2392+
2393+ _ ->
2394+ found
2222
2395
2396+ Just _ ->
2397+ found
2398+ )
2399+ Nothing
2400+ typefaces
2401+
2402+
2403+fontName font =
2404+ case font of
2405+ Serif ->
2406+ "serif"
2407+
2408+ SansSerif ->
2409+ "sans-serif"
2410+
2411+ Monospace ->
2412+ "monospace"
2413+
2414+ Typeface name ->
2415+ "\"" ++ name ++ "\""
2416+
2417+ ImportFont name url ->
2418+ "\"" ++ name ++ "\""
2419+
2420+ FontWith { name } ->
2421+ "\"" ++ name ++ "\""
2422+
2423+
2424+topLevelValue rule =
2425+ case rule of
2426+ FontFamily name typefaces ->
2427+ Just ( name, typefaces )
2428+
2429+ _ ->
2430+ Nothing
2431+
2432+
2433+renderProps force (Property key val) existing =
2434+ if force then
2435+ existing ++ "\n " ++ key ++ ": " ++ val ++ " !important;"
2436+
2437+ else
2438+ existing ++ "\n " ++ key ++ ": " ++ val ++ ";"
2439+
2440+
2441+toStyleSheetString : OptionRecord -> List Style -> String
2442+toStyleSheetString options stylesheet =
2443+ let
2223 renderStyle maybePseudo selector props =
2444 renderStyle maybePseudo selector props =
2224 case maybePseudo of
2445 case maybePseudo of
2225 Nothing ->
2446 Nothing ->
@@ -2308,10 +2529,19 @@ toStyleSheetString options stylesheet =
2308 ]
2529 ]
2309
2530
2310 FontFamily name typefaces ->
2531 FontFamily name typefaces ->
2311− renderStyle
2532+ let
2312− maybePseudo
2533+ families =
2313− ("." ++ name)
2314− [ Property "font-family" (renderFont typefaces)
2534+ Property "font-family"
2535+ (typefaces
2536+ |> List.map fontName
2537+ |> String.join ", "
2538+ )
2539+ in
2540+ String.join " "
2541+ [ renderStyle
2542+ maybePseudo
2543+ ("." ++ name)
2544+ [ families ]
2315 ]
2545 ]
2316
2546
2317 Single class prop val ->
2547 Single class prop val ->
@@ -2659,20 +2889,19 @@ toStyleSheetString options stylesheet =
2659 ""
2889 ""
2660
2890
2661 combine style rendered =
2891 combine style rendered =
2662− { rendered
2663− | rules = rendered.rules ++ renderStyleRule style Nothing
2892+ { rules = rendered.rules ++ renderStyleRule style Nothing
2664− , topLevel =
2893+ , topLevel =
2665− case renderTopLevels style of
2894+ case topLevelValue style of
2666− Nothing ->
2895+ Nothing ->
2667− rendered.topLevel
2896+ rendered.topLevel
2668
2897
2669− Just topLevel ->
2898+ Just topLevel ->
2670− rendered.topLevel ++ topLevel
2899+ topLevel :: rendered.topLevel
2671 }
2900 }
2672 in
2901 in
2673− case List.foldl combine { rules = "", topLevel = "" } stylesheet of
2902+ case List.foldl combine { topLevel = [], rules = "" } stylesheet of
2674 { topLevel, rules } ->
2903 { topLevel, rules } ->
2675− topLevel ++ rules
2904+ renderTopLevelValues topLevel ++ rules
2676
2905
2677
2906
2678lengthClassName : Length -> String
2907lengthClassName : Length -> String
@@ -3058,3 +3287,66 @@ onlyStyles attr =
3058
3287
3059 _ ->
3288 _ ->
3060 Nothing
3289 Nothing
3290+
3291+
3292+
3293+{- Font Adjustments -}
3294+
3295+
3296+convertAdjustment adjustment =
3297+ let
3298+ lineHeight =
3299+ 1.5
3300+
3301+ base =
3302+ lineHeight
3303+
3304+ normalDescender =
3305+ (lineHeight - 1)
3306+ / 2
3307+
3308+ oldMiddle =
3309+ lineHeight / 2
3310+
3311+ newCapitalMiddle =
3312+ ((ascender - newBaseline) / 2) + newBaseline
3313+
3314+ newFullMiddle =
3315+ ((ascender - descender) / 2) + descender
3316+
3317+ lines =
3318+ [ adjustment.capital
3319+ , adjustment.baseline
3320+ , adjustment.descender
3321+ , adjustment.lowercase
3322+ ]
3323+
3324+ ascender =
3325+ Maybe.withDefault adjustment.capital (List.maximum lines)
3326+
3327+ descender =
3328+ Maybe.withDefault adjustment.descender (List.minimum lines)
3329+
3330+ newBaseline =
3331+ lines
3332+ |> List.filter (\x -> x /= descender)
3333+ |> List.minimum
3334+ |> Maybe.withDefault adjustment.baseline
3335+
3336+ capitalVertical =
3337+ (oldMiddle - newCapitalMiddle) * 2
3338+
3339+ fullVertical =
3340+ (oldMiddle - newFullMiddle) * 2
3341+ in
3342+ { full =
3343+ { vertical = fullVertical
3344+ , height =
3345+ (ascender - descender)
3346+ - abs fullVertical
3347+ }
3348+ , capital =
3349+ { vertical = capitalVertical
3350+ , height = (ascender - newBaseline) - abs capitalVertical
3351+ }
3352+ }
src/Internal/Style.elm+2
@@ -224,6 +224,8 @@ classes =
224 , borderSolid = "bs"
224 , borderSolid = "bs"
225
225
226 -- text weight
226 -- text weight
227+ , sizeByCapital = "cap"
228+ , fullSize = "fs"
227 , textThin = "w1"
229 , textThin = "w1"
228 , textExtraLight = "w2"
230 , textExtraLight = "w2"
229 , textLight = "w3"
231 , textLight = "w3"