Skip to content

Commit 1d9d907

Browse files
committed
Prototype new Text components
1 parent 52d76a4 commit 1d9d907

File tree

5 files changed

+364
-4
lines changed

5 files changed

+364
-4
lines changed

docs/App.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ const componentv2Loaders = [
126126
"Clip",
127127
"Link",
128128
"QRCode",
129-
"Slat"
129+
"Slat",
130+
"Text"
130131
].map(fromComponentPathv2);
131132

132133
const App = () => {

docs/Examples2/Text.example.purs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
module Lumi.Components2.Examples.Text where
2+
3+
import Prelude
4+
5+
import Lumi.Components (($$$))
6+
import Lumi.Components.Example (example)
7+
import Lumi.Components2.Box (box)
8+
import Lumi.Components2.Text as T
9+
import Lumi.Styles (StyleModifier)
10+
import Lumi.Styles as S
11+
import React.Basic (JSX, fragment)
12+
13+
docs :: JSX
14+
docs =
15+
box
16+
$$$ [ example
17+
$ fragment
18+
$ [ T.sectionHeader $ T.strong $$$ "A tiny story"
19+
, T.subsectionHeader $$$ "Hello!"
20+
, T.paragraph
21+
$$$ [ T.text
22+
$ T.emphasized
23+
$$$ "How are you? "
24+
, T.text
25+
$$$ "Hope you're doing "
26+
, T.text
27+
$ T.strong
28+
$$$ "fine. "
29+
, T.text
30+
$ T.subtext
31+
$ T.muted
32+
$$$ "Yes, I do."
33+
]
34+
, T.paragraph
35+
$$$ [ T.text
36+
$$$ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam congue ligula et odio rutrum, eu imperdiet ante laoreet. Cras mollis faucibus urna, eu luctus ligula condimentum ut. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vivamus et mi mattis, maximus urna id, luctus neque. Sed non lorem molestie nibh suscipit condimentum id quis enim. Nunc tortor elit, posuere eu metus sed, finibus sagittis est. Fusce dapibus lacus vitae augue vulputate, in convallis lectus congue. "
37+
, T.text
38+
$ T.strong
39+
$$$ "Hi! "
40+
, T.text
41+
$ T.subtext
42+
$$$ "Hey!"
43+
]
44+
, T.paragraph
45+
$ T.emphasized
46+
$$$ [ T.text
47+
$$$ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam congue ligula et odio rutrum, eu imperdiet ante laoreet. Cras mollis faucibus urna, eu luctus ligula condimentum ut. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Vivamus et mi mattis, maximus urna id, luctus neque. Sed non lorem molestie nibh suscipit condimentum id quis enim. Nunc tortor elit, posuere eu metus sed, finibus sagittis est. Fusce dapibus lacus vitae augue vulputate, in convallis lectus congue. "
48+
, T.text
49+
$ nonItalic
50+
$$$ "Hi! "
51+
, T.text
52+
$ T.subtext
53+
$ nonItalic
54+
$$$ "Hey!"
55+
]
56+
, T.paragraph_
57+
$$$ "Vestibulum eu arcu eget lectus interdum maximus feugiat sed velit. Integer ullamcorper urna quis cursus mattis. Nam vel hendrerit purus. Aliquam fringilla dictum nunc at ornare. Morbi ornare blandit tincidunt. Etiam sodales fringilla libero, vitae pulvinar sapien luctus ac. Proin condimentum vitae risus id vestibulum. Sed sed turpis leo. Quisque ligula leo, facilisis eget metus ullamcorper, aliquet mollis tortor. Donec purus metus, maximus rutrum nunc eget, rhoncus tempor erat. Sed efficitur tellus id velit ullamcorper, ut dignissim neque pretium. Sed id metus porta, efficitur est non, vulputate sapien."
58+
, T.paragraph_
59+
$ T.subtext
60+
$$$ "Donec maximus commodo ipsum vel elementum. Sed at nunc dapibus, vulputate tellus eu, finibus ante. Nunc dolor ante, auctor et rutrum quis, sagittis ut nulla. Integer vel tempus ipsum, vel laoreet orci. Curabitur eu sem bibendum, rhoncus risus vel, tristique mauris. Cras lobortis elit sit amet quam semper pretium. Nullam fermentum ut velit ac cursus."
61+
]
62+
]
63+
64+
nonItalic :: StyleModifier
65+
nonItalic =
66+
S.style_ (S.css { fontStyle: S.str "normal" })

src/Lumi/Components.purs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Lumi.Components
22
( LumiProps
33
, PropsModifier
4+
, PropsModifier'
45
, propsModifier
56
, LumiComponent
67
, lumiComponent
@@ -35,9 +36,11 @@ type LumiComponent props = (LumiProps props -> LumiProps props) -> JSX
3536
type LumiProps props
3637
= { css :: LumiTheme -> Emotion.Style, className :: String | props }
3738

38-
type PropsModifier props
39+
type PropsModifier props = PropsModifier' props props
40+
41+
type PropsModifier' props props'
3942
= (LumiProps props -> LumiProps props) ->
40-
(LumiProps props -> LumiProps props)
43+
(LumiProps props' -> LumiProps props')
4144

4245
-- | Lift a `props -> props` function for composition with other `PropsModifier` functions.
4346
propsModifier :: forall props. (LumiProps props -> LumiProps props) -> PropsModifier props

src/Lumi/Components2/Text.purs

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
module Lumi.Components2.Text
2+
( Text
3+
, TextProps, TextType
4+
, text
5+
, TextModifier, body, strong, emphasized, subtext
6+
, muted, color
7+
8+
, ParagraphProps
9+
, paragraph_, paragraph
10+
11+
, Header
12+
, HeaderProps
13+
, subsectionHeader
14+
, sectionHeader
15+
, title
16+
, mainHeader
17+
) where
18+
19+
import Prelude
20+
21+
import Data.Maybe (Maybe(..), fromMaybe, maybe)
22+
import Effect.Unsafe (unsafePerformEffect)
23+
import Lumi.Components (LumiComponent, PropsModifier, PropsModifier', lumiComponent, propsModifier)
24+
import Lumi.Components.Color (ColorMap)
25+
import Lumi.Styles (Style, StyleModifier, StyleProperty)
26+
import Lumi.Styles as S
27+
import Lumi.Styles.Theme (LumiTheme(..), TextMap, textFontSize, textLineHeight, textMargin, useTheme)
28+
import React.Basic (JSX, ReactComponent)
29+
import React.Basic.DOM as R
30+
import React.Basic.Emotion as E
31+
import React.Basic.Hooks as Hooks
32+
33+
-- Text
34+
35+
data Text = Text
36+
37+
data TextType
38+
= Body
39+
| Strong
40+
| Emphasis
41+
| Subtext
42+
43+
type TextProps =
44+
( component :: Text
45+
, type :: Maybe TextType
46+
, content :: String
47+
)
48+
49+
type TextElement = ReactComponent { children :: Array JSX, className :: String }
50+
51+
-- | TODO: write documentation
52+
text :: LumiComponent TextProps
53+
text =
54+
unsafePerformEffect $ lumiComponent "Text" defaults \props -> Hooks.do
55+
theme <- useTheme
56+
pure $
57+
E.element (maybe R.span' textElement props.type)
58+
{ children: [ R.text props.content ]
59+
, className: props.className
60+
, css: defaultTextStyle theme props.type <> props.css theme
61+
}
62+
where
63+
defaults :: Record TextProps
64+
defaults =
65+
{ component: Text
66+
, type: Nothing
67+
, content: ""
68+
}
69+
70+
defaultTextStyle :: LumiTheme -> Maybe TextType -> Style
71+
defaultTextStyle theme ty =
72+
S.css
73+
{ fontSize: maybe S.inherit (px <<< textFontSize theme <<< textTheme) ty
74+
, lineHeight: maybe S.inherit (px <<< textLineHeight theme <<< textTheme) ty
75+
, whiteSpace: S.str "pre-wrap"
76+
, margin: S.str "0"
77+
, padding: S.str "0"
78+
}
79+
80+
textElement :: TextType -> TextElement
81+
textElement Body = R.span'
82+
textElement Strong = R.strong'
83+
textElement Emphasis = R.em'
84+
textElement Subtext = R.small'
85+
86+
textTheme :: TextType -> (forall a. TextMap a -> a)
87+
textTheme =
88+
case _ of
89+
Body -> _.body
90+
Strong -> _.body
91+
Emphasis -> _.body
92+
Subtext -> _.subtext
93+
94+
-- | The `c` type parameter lets us constrain the type of component to which
95+
-- | a text modifier may be applied: `Text`, `Header` or any.
96+
type TextModifier c = forall r. PropsModifier (component :: c, type :: Maybe TextType | r)
97+
98+
body :: TextModifier Text
99+
body = propsModifier _{ type = Just Body }
100+
101+
strong :: forall c. TextModifier c
102+
strong =
103+
propsModifier _{ type = Just Strong }
104+
<<< S.style_ (S.css { fontWeight: S.str "600" })
105+
106+
emphasized :: forall c. TextModifier c
107+
emphasized =
108+
propsModifier _{ type = Just Emphasis }
109+
<<< S.style_ (S.css { fontStyle: S.str "italic" })
110+
111+
subtext :: TextModifier Text
112+
subtext = propsModifier _{ type = Just Subtext }
113+
114+
muted :: forall c. TextModifier c
115+
muted =
116+
S.style \(LumiTheme { colors }) ->
117+
S.css { color: S.color colors.black1 }
118+
119+
color :: forall c. (forall a. ColorMap a -> a) -> TextModifier c
120+
color f =
121+
S.style \(LumiTheme { colors }) ->
122+
S.css { color: S.color (f colors) }
123+
124+
-- Paragraph
125+
126+
type ParagraphProps =
127+
( component :: Text
128+
, type :: Maybe TextType
129+
, content :: Array JSX
130+
)
131+
132+
-- | TODO: write documentation
133+
paragraph_ :: LumiComponent TextProps
134+
paragraph_ = paragraph <<< renderInnerText
135+
where
136+
renderInnerText :: PropsModifier' TextProps ParagraphProps
137+
renderInnerText f props =
138+
let
139+
props' = f $ props { content = "" }
140+
in
141+
props'
142+
{ content =
143+
[ text _
144+
{ component = props'.component
145+
, type = props'.type
146+
, content = props'.content
147+
, css = \_ -> S.css { fontSize: S.inherit, lineHeight: S.inherit }
148+
}
149+
]
150+
}
151+
152+
-- | TODO: write documentation
153+
paragraph :: LumiComponent ParagraphProps
154+
paragraph =
155+
unsafePerformEffect $ lumiComponent "Paragraph" defaults \props -> Hooks.do
156+
theme <- useTheme
157+
pure $
158+
E.element R.p'
159+
{ children: props.content
160+
, className: props.className
161+
, css: defaultParagraphStyle theme props.type <> props.css theme
162+
}
163+
where
164+
defaults :: Record ParagraphProps
165+
defaults =
166+
{ component: Text
167+
, type: Nothing
168+
, content: []
169+
}
170+
171+
defaultParagraphStyle :: LumiTheme -> Maybe TextType -> Style
172+
defaultParagraphStyle theme ty =
173+
S.merge
174+
[ S.css
175+
{ whiteSpace: S.str "pre-wrap"
176+
, margin: S.str "0"
177+
, padding: S.str "0"
178+
}
179+
, S.toCSS (textStyle (textTheme (fromMaybe Body ty))) theme
180+
]
181+
182+
-- Headers
183+
184+
data Header = Header
185+
186+
-- Even though the header components never end up using the `type` property, we
187+
-- need it here so that text modifiers such as `strong` may also be applied to
188+
-- headers.
189+
type HeaderProps =
190+
( component :: Header
191+
, type :: Maybe TextType
192+
, content :: String
193+
)
194+
195+
subsectionHeader :: LumiComponent HeaderProps
196+
subsectionHeader = mkHeaderComponent R.h4' <<< textStyle _.subsectionHeader
197+
198+
sectionHeader :: LumiComponent HeaderProps
199+
sectionHeader = mkHeaderComponent R.h3' <<< textStyle _.sectionHeader
200+
201+
title :: LumiComponent HeaderProps
202+
title = mkHeaderComponent R.h2' <<< textStyle _.title
203+
204+
mainHeader :: LumiComponent HeaderProps
205+
mainHeader = mkHeaderComponent R.h1' <<< textStyle _.mainHeader
206+
207+
-- | TODO: write documentation
208+
mkHeaderComponent
209+
:: TextElement
210+
-> LumiComponent HeaderProps
211+
mkHeaderComponent el =
212+
unsafePerformEffect $ lumiComponent "Header" defaults \props -> Hooks.do
213+
theme <- useTheme
214+
pure $
215+
E.element el
216+
{ children: [ R.text props.content ]
217+
, className: props.className
218+
, css: defaultHeaderStyle theme <> props.css theme
219+
}
220+
where
221+
defaults :: Record HeaderProps
222+
defaults =
223+
{ component: Header
224+
, type: Nothing
225+
, content: ""
226+
}
227+
228+
defaultHeaderStyle :: LumiTheme -> Style
229+
defaultHeaderStyle theme =
230+
S.merge
231+
[ S.css
232+
{ fontWeight: S.str "400"
233+
, padding: S.str "0"
234+
, margin: S.str "0"
235+
}
236+
, S.toCSS (textStyle _.subsectionHeader) theme
237+
]
238+
239+
--
240+
241+
textStyle :: (forall a. TextMap a -> a) -> StyleModifier
242+
textStyle selector =
243+
S.style \theme ->
244+
S.css
245+
{ fontSize: px $ textFontSize theme selector
246+
, lineHeight: px $ textLineHeight theme selector
247+
, marginBottom: px $ textMargin theme selector
248+
}
249+
250+
px :: Int -> StyleProperty
251+
px i = S.str $ show i <> "px"

0 commit comments

Comments
 (0)