git.iko.soy/rowtype-yoga/ry-blocks
on main help sign in
Better inputs
5fe49e6Mark Eibes
src/Yoga/Block/Atom/Checkbox/Story.purs+1−9
@@ -2,22 +2,14 @@ module Yoga.Block.Atom.Checkbox.Story (default, checkbox) where
2
2
3import Prelude hiding (div)
3import Prelude hiding (div)
4
4
5−import Color (cssStringRGBA)
6import Effect (Effect)
5import Effect (Effect)
7import Effect.Unsafe (unsafePerformEffect)
6import Effect.Unsafe (unsafePerformEffect)
8−import Fahrtwind as F
9−import React.Basic (JSX, element, fragment)
7+import React.Basic (JSX, element)
10import React.Basic.DOM as R
8import React.Basic.DOM as R
11−import React.Basic.Emotion (str)
12import React.Basic.Emotion as E
9import React.Basic.Emotion as E
13import Yoga ((</>))
10import Yoga ((</>))
14−import Yoga.Block as Block
15−import Yoga.Block.Atom.Button.Types as ButtonType
16import Yoga.Block.Atom.Checkbox.View as Checkbox
11import Yoga.Block.Atom.Checkbox.View as Checkbox
17−import Yoga.Block.Container.Style (colour, size)
18import Yoga.Block.Container.Style as Styles
12import Yoga.Block.Container.Style as Styles
19−import Yoga.Block.Icon.SVG as Icon
20−import Yoga.Block.Internal.CSS (nest)
21
13
22default
14default
23 ∷ { decorators ∷ Array (Effect JSX -> JSX)
15 ∷ { decorators ∷ Array (Effect JSX -> JSX)
src/Yoga/Block/Atom/Input/Story.purs+5−2
@@ -18,6 +18,7 @@ import Type.Proxy (Proxy(..))
18import Yoga ((</>))
18import Yoga ((</>))
19import Yoga.Block as Block
19import Yoga.Block as Block
20import Yoga.Block.Atom.Input as Input
20import Yoga.Block.Atom.Input as Input
21+import Yoga.Block.Atom.Input.Style (SizeVariant(..))
21import Yoga.Block.Atom.Input.Types as HTMLInput
22import Yoga.Block.Atom.Input.Types as HTMLInput
22import Yoga.Block.Atom.Input.View (passwordIcon)
23import Yoga.Block.Atom.Input.View (passwordIcon)
23import Yoga.Block.Container.Style (colour)
24import Yoga.Block.Container.Style (colour)
@@ -47,11 +48,13 @@ input = do
47 , R.h3_ [ R.text "With a label" ]
48 , R.h3_ [ R.text "With a label" ]
48 , Block.cluster {}
49 , Block.cluster {}
49 [ element Input.component { label: nes (Proxy ∷ _ "This has a label"), value: "And text", onChange: handler_ mempty }
50 [ element Input.component { label: nes (Proxy ∷ _ "This has a label"), value: "And text", onChange: handler_ mempty }
50− , element Input.component { label: nes (Proxy ∷ _ "This has a label"), value: "", onChange: handler_ mempty }
51+ , element Input.component { label: nes (Proxy ∷ _ "This z` gggj"), value: "", onChange: handler_ mempty }
51− , element Input.component { label: nes (Proxy ∷ _ "This has a label"), placeholder: "A very long placeholder, too..." }
52+ , element Input.component { label: nes (Proxy ∷ _ "This has a label"), placeholder: "A j very g long y placeholder, too..." }
52 , element Input.component { label: nes (Proxy ∷ _ "Pig nose"), leading: R.text "🐽🤣" }
53 , element Input.component { label: nes (Proxy ∷ _ "Pig nose"), leading: R.text "🐽🤣" }
53 , element Input.component { label: nes (Proxy ∷ _ "Pig nose"), trailing: R.text "🤫" }
54 , element Input.component { label: nes (Proxy ∷ _ "Pig nose"), trailing: R.text "🤫" }
54 , element Input.component { label: nes (Proxy ∷ _ "Pig nose"), leading: R.text "🌭" }
55 , element Input.component { label: nes (Proxy ∷ _ "Pig nose"), leading: R.text "🌭" }
56+ , element Input.component { sizeVariant: SizeSmall, label: nes (Proxy ∷ _ "Small") }
57+ , element Input.component { sizeVariant: SizeSmall, placeholder: "Small without label" }
55 , Block.box
58 , Block.box
56 { background: colour.backgroundLayer3
59 { background: colour.backgroundLayer3
57 }
60 }
src/Yoga/Block/Atom/Input/Style.purs+91−73
@@ -4,15 +4,23 @@ import Yoga.Prelude.Style
4import Data.Interpolate (i)
4import Data.Interpolate (i)
5import Yoga.Block.Container.Style (colour)
5import Yoga.Block.Container.Style (colour)
6
6
7+data SizeVariant = SizeMedium | SizeSmall
8+
9+sizeVariantToFactor ∷ SizeVariant → String
10+sizeVariantToFactor = case _ of
11+ SizeMedium -> "1"
12+ SizeSmall -> "0.8"
13+
7type Props :: forall k. (Type -> k) -> Row k -> Row k
14type Props :: forall k. (Type -> k) -> Row k -> Row k
8−type Props f r
15+type Props f r =
9− = ( css ∷ f Style
16+ ( css ∷ f Style
17+ , sizeVariant :: f SizeVariant
10− , background ∷ f String
18+ , background ∷ f String
11− , textColour ∷ f String
19+ , textColour ∷ f String
12− , placeholderColour ∷ f String
20+ , placeholderColour ∷ f String
13− , borderColour ∷ f String
21+ , borderColour ∷ f String
14− | r
22+ | r
15− )
23+ )
16
24
17leftIconSize ∷ StyleProperty
25leftIconSize ∷ StyleProperty
18leftIconSize = var "--left-icon-size"
26leftIconSize = var "--left-icon-size"
@@ -38,9 +46,9 @@ labelAndInputWrapper =
38 css
46 css
39 { position: relative
47 { position: relative
40 -- , display: inlineBlock
48 -- , display: inlineBlock
41− , "--left-icon-size": var "--s0"
49+ , "--left-icon-size": str "calc(var(--s0) * var(--input-size-factor))"
42− , "--right-icon-size": str "calc(var(--s0) * 1.2)"
50+ , "--right-icon-size": str "calc(var(--s0) * var(--input-size-factor)) * 1.2)"
43− , "--input-border-radius": var "--s-1"
51+ , "--input-border-radius": str "calc(var(--s-1) * var(--input-size-factor))"
44 , "--input-side-padding": var "--s-1"
52 , "--input-side-padding": var "--s-1"
45 , width: inherit
53 , width: inherit
46 , margin: _0
54 , margin: _0
@@ -51,8 +59,8 @@ labelContainer =
51 css
59 css
52 { position: absolute
60 { position: absolute
53 , overflow: visible
61 , overflow: visible
54− , top: _0
55 , left: _0
62 , left: _0
63+ , top: _0
56 , display: inlineBlock
64 , display: inlineBlock
57 , zIndex: str "2"
65 , zIndex: str "2"
58 , pointerEvents: none
66 , pointerEvents: none
@@ -62,7 +70,7 @@ labelSmall ∷ String -> String -> Style
62labelSmall background textColour =
70labelSmall background textColour =
63 css
71 css
64 { fontSize: var "--s-1"
72 { fontSize: var "--s-1"
65− , marginTop: str "calc(var(--s-3) * -1)"
73+ , marginTop: str "calc(var(--s-2) * var(--input-size-factor) * -1)"
66 , marginLeft: var "--s-2"
74 , marginLeft: var "--s-2"
67 , userSelect: none
75 , userSelect: none
68 , """&[data-required="true"] > span:after""":
76 , """&[data-required="true"] > span:after""":
@@ -73,7 +81,7 @@ labelSmall background textColour =
73 nest
81 nest
74 { fontWeight: str "500"
82 { fontWeight: str "500"
75 , whiteSpace: str "nowrap"
83 , whiteSpace: str "nowrap"
76− , background: str $ i "linear-gradient(" background " 51%, transparent 51%, transparent)"
84+ , background: str $ i "linear-gradient(" background " 61%, transparent 61%, transparent)"
77 , color: str textColour
85 , color: str textColour
78 , borderRadius: var "--s-4"
86 , borderRadius: var "--s-4"
79 , paddingLeft: var "--s-4"
87 , paddingLeft: var "--s-4"
@@ -109,32 +117,30 @@ labelSmallFocusBackground =
109labelLarge ∷ { left ∷ Number, width ∷ Number } -> Style
117labelLarge ∷ { left ∷ Number, width ∷ Number } -> Style
110labelLarge { left, width } =
118labelLarge { left, width } =
111 css
119 css
112− { fontSize: str "var(--s0)"
120+ { fontSize: str "calc(var(--input-size-factor) * 16px)"
113 , padding: _0
121 , padding: _0
114 , whiteSpace: nowrap -- force on one line
122 , whiteSpace: nowrap -- force on one line
115− , height: str "calc(var(--s0) * 1.2)"
123+ -- , height: str "calc(var(--s0) * 1.2)"
116 , letterSpacing: em (-0.011)
124 , letterSpacing: em (-0.011)
117 , maxWidth: str $ i "calc(" width "px - 2ch)"
125 , maxWidth: str $ i "calc(" width "px - 2ch)"
118− , marginTop: str "calc(var(--s-1))"
126+ , marginTop: str "calc(var(--s-1) * var(--input-size-factor))"
119 , marginLeft: str $ i left "px"
127 , marginLeft: str $ i left "px"
120 , marginRight: var "--input-side-padding"
128 , marginRight: var "--input-side-padding"
121 , color: str colour.textPaler2
129 , color: str colour.textPaler2
122 , fontWeight: str "400"
130 , fontWeight: str "400"
123 , overflowX: str "hidden"
131 , overflowX: str "hidden"
124 , textOverflow: str "ellipsis"
132 , textOverflow: str "ellipsis"
125− , overflowY: str "visible"
133+ , overflowY: visible
126− , "MsOverflowStyle": none
127 , scrollbarWidth: none
134 , scrollbarWidth: none
128 , "&::-webkit-scrollbar": nested $ css
135 , "&::-webkit-scrollbar": nested $ css
129− { display: none }
136+ { display: none }
130 -- , fontWeight: str "600"
137 -- , fontWeight: str "600"
131 , """&[data-required="true"]:after""":
138 , """&[data-required="true"]:after""":
132 nest
139 nest
133 { content: str "'*'"
140 { content: str "'*'"
134 , color: str colour.required
141 , color: str colour.required
135 , fontFamily: str "Helvetica, Arial, Inter, sans-serif"
142 , fontFamily: str "Helvetica, Arial, Inter, sans-serif"
136− , fontSize: str "calc(var(--s0))"
137− , lineHeight: str "calc(var(--s0) * 0.85)"
143+ , lineHeight: str "calc(var(--s0) * 0.85 * var(--input-size-factor))"
138 }
144 }
139 }
145 }
140
146
@@ -142,31 +148,31 @@ rightIconContainer ∷ Style
142rightIconContainer =
148rightIconContainer =
143 iconContainer
149 iconContainer
144 <> css
150 <> css
145− { display: inlineFlex
151+ { display: inlineFlex
146− , alignItems: center
152+ , alignItems: center
147− , justifyContent: center
153+ , justifyContent: center
148− , "& > *":
154+ , "& > *":
149− nest
155+ nest
150− { display: inlineFlex
156+ { display: inlineFlex
151− , alignItems: center
157+ , alignItems: center
152− , justifyContent: center
158+ , justifyContent: center
153− }
159+ }
154− , ".ry-icon":
160+ , ".ry-icon":
155− nest
161+ nest
156− { "--stroke-colour": str colour.text
162+ { "--stroke-colour": str colour.text
157− }
163+ }
158− }
164+ }
159
165
160leftIconContainer ∷ Style
166leftIconContainer ∷ Style
161leftIconContainer =
167leftIconContainer =
162 iconContainer
168 iconContainer
163 <> css
169 <> css
164− { borderRadius: str "var(--input-border-radius) 0 0 var(--input-border-radius)"
170+ { borderRadius: str "var(--input-border-radius) 0 0 var(--input-border-radius)"
165− , ".ry-icon":
171+ , ".ry-icon":
166− nest
172+ nest
167− { "--stroke-colour": str colour.text
173+ { "--stroke-colour": str colour.text
168− }
174+ }
169− }
175+ }
170
176
171inputContainer ∷ ∀ r. { | Props OptionalProp r } -> Style
177inputContainer ∷ ∀ r. { | Props OptionalProp r } -> Style
172inputContainer props = theCss <>? props.css
178inputContainer props = theCss <>? props.css
@@ -175,9 +181,10 @@ inputContainer props = theCss <>? props.css
175 css
181 css
176 { "--left-icon-size": var "--s0"
182 { "--left-icon-size": var "--s0"
177 , "--right-icon-size": str "calc(var(--s0) * 1.2)"
183 , "--right-icon-size": str "calc(var(--s0) * 1.2)"
178− , "--input-side-padding": str "calc(var(--s-1) * 1.2)"
184+ , "--input-side-padding": str "calc(var(--s-1) * var(--input-size-factor) * 1.2)"
179− , "--input-top-padding": var "--s-5"
185+ , "--input-top-padding": str "calc(var(--s-5) * var(--input-size-factor))"
180− , "--input-bottom-padding": var "--s-5"
186+ , "--input-bottom-padding": str "calc(var(--s-5) * var(--input-size-factor))"
187+ , "--input-size-factor": str ((props.sizeVariant ?|| SizeMedium) # sizeVariantToFactor)
181 , letterSpacing: em (-0.011)
188 , letterSpacing: em (-0.011)
182 , position: relative
189 , position: relative
183 , cursor: str "text"
190 , cursor: str "text"
@@ -200,41 +207,50 @@ inputContainer props = theCss <>? props.css
200 { "--border-width": str "2px"
207 { "--border-width": str "2px"
201 , borderColor: str colour.highlight
208 , borderColor: str colour.highlight
202 , marginTop: str "-1px"
209 , marginTop: str "-1px"
203− , transition: str "border-color 0s linear 0.1s"
204− , animation: plopAnimation <> str " 260ms ease-in"
205 }
210 }
206 , alignItems: center
211 , alignItems: center
207 , justifyContent: center
212 , justifyContent: center
208 , border: str $ "var(--border-width) solid " <> colour.inputBorder
213 , border: str $ "var(--border-width) solid " <> colour.inputBorder
209− , paddingLeft: str "calc(var(--input-side-padding) - var(--border-width))"
214+ , paddingLeft: str "calc((var(--input-side-padding) - var(--border-width)) * var(--input-size-factor))"
210− , paddingRight: str "calc(var(--input-side-padding) - var(--border-width))"
215+ , paddingRight: str "calc((var(--input-side-padding) - var(--border-width)) * var(--input-size-factor))"
211− , paddingTop: str "calc(var(--input-top-padding) - var(--border-width))"
216+ , paddingTop: str "calc((var(--input-top-padding) - var(--border-width)) * var(--input-size-factor))"
212− , paddingBottom: str "calc(var(--input-bottom-padding) - var(--border-width))"
217+ , paddingBottom: str "calc((var(--input-bottom-padding)) - var(--border-width))"
213 , gap: str "calc(var(--input-side-padding) / 2)"
218 , gap: str "calc(var(--input-side-padding) / 2)"
214 , borderRadius: var "--input-border-radius"
219 , borderRadius: var "--input-border-radius"
220+ , overflow: visible
215 }
221 }
216
222
223+ploppedFocusWithin ∷ Style
224+ploppedFocusWithin = css
225+ { "&:focus-within":
226+ nest
227+ { animation: str "plop 180ms ease"
228+ , animationName: plopAnimation
229+ }
230+ }
231+
217containerBackground ∷ ∀ r. { | Props OptionalProp r } -> Style
232containerBackground ∷ ∀ r. { | Props OptionalProp r } -> Style
218−containerBackground props = css {
233+containerBackground props = css
219− background: str (props.background ?|| colour.inputBackground)
234+ { background: str (props.background ?|| colour.inputBackground)
220− , boxShadow: str """
235+ -- , boxShadow: str
236+ -- """
221− 0 1px 1px rgba(200,200,255,0.2),
237+ -- 0 1px 1px rgba(200,200,255,0.2),
222− 0 2px 2px rgba(200,200,255,0.1),
238+ -- 0 2px 2px rgba(200,200,255,0.1),
223− 0 4px 8px rgba(200,200,255,0.03)"""
239+ -- 0 4px 8px rgba(200,200,255,0.03)"""
224 , border: str $ "var(--border-width) solid " <> colour.inputBorder
240 , border: str $ "var(--border-width) solid " <> colour.inputBorder
225 , borderRadius: var "--input-border-radius"
241 , borderRadius: var "--input-border-radius"
226
242
227−}
243+ }
228
244
229containerContainer ∷ ∀ r. { | Props OptionalProp r } -> Style
245containerContainer ∷ ∀ r. { | Props OptionalProp r } -> Style
230−containerContainer props = css {
246+containerContainer props = css
231− "& > *": nested $ css {
247+ { "& > *": nested $ css
232− gridColumn: int 1,
248+ { gridColumn: int 1
233− gridRow: int 1
249+ , gridRow: int 1
234− }, width: str "calc(var(--s4) * 2)"
250+ }
235− , height: str "calc(var(--s2) - var(--border-width) * 2)"
236 , "--border-width": str "1px"
251 , "--border-width": str "1px"
252+ , "--input-size-factor": str ((props.sizeVariant ?|| SizeMedium) # sizeVariantToFactor)
237− , "--input-border-radius": var "--s-1"
253+ , "--input-border-radius": str "calc(var(--s-1) * var(--input-size-factor))"
238 , borderRadius: var "--input-border-radius"
254 , borderRadius: var "--input-border-radius"
239 , display: grid
255 , display: grid
240 , overflow: visible
256 , overflow: visible
@@ -253,7 +269,8 @@ inputWrapper =
253input ∷ ∀ r. { | Props OptionalProp r } -> Style
269input ∷ ∀ r. { | Props OptionalProp r } -> Style
254input props =
270input props =
255 css
271 css
272+ { "--input-size-factor": str ((props.sizeVariant ?|| SizeMedium) # sizeVariantToFactor)
256− { "&[type=text],&[type=search],&[type=password],&[type=number],&:not([type])":
273+ , "&[type=text],&[type=search],&[type=password],&[type=number],&:not([type])":
257 nest
274 nest
258 { background: str "transparent"
275 { background: str "transparent"
259 , touchAction: manipulation
276 , touchAction: manipulation
@@ -262,14 +279,14 @@ input props =
262 , minWidth: _0
279 , minWidth: _0
263 , margin: _0
280 , margin: _0
264 , overflowY: visible
281 , overflowY: visible
282+ , "--padding-top": str "calc(var(--s-1) * var(--input-size-factor))"
283+ , "--padding-bottom": str "calc(var(--s-1) * var(--input-size-factor))"
265 , paddingTop: var "--padding-top"
284 , paddingTop: var "--padding-top"
266 , paddingBottom: var "--padding-bottom"
285 , paddingBottom: var "--padding-bottom"
267 , paddingLeft: _0
286 , paddingLeft: _0
268 , paddingRight: _0
287 , paddingRight: _0
269− -- , fontSize: str "calc(var(--s0) * 0.85)"
288+ , fontSize: str "calc(var(--input-size-factor) * 16px)"
270− , fontSize: str "calc(var(--s0))"
289+ -- , fontSize: str "calc(var(--s0))"
271− , "--padding-top": var "--s-1"
272− , "--padding-bottom": var "--s-1"
273 , "&::placeholder":
290 , "&::placeholder":
274 nest
291 nest
275 { color: str (props.placeholderColour ?|| colour.placeholderText)
292 { color: str (props.placeholderColour ?|| colour.placeholderText)
@@ -297,8 +314,9 @@ input props =
297plopAnimation ∷ StyleProperty
314plopAnimation ∷ StyleProperty
298plopAnimation =
315plopAnimation =
299 keyframes
316 keyframes
317+ $
300− $ { "from": css { transform: str "scale3d(1.01,1.2,1)" }
318+ { "from": css { transform: str "scale3d(1.03,1.02,1)" }
301− , "67.7%": css { transform: str "scale3d(0.993,0.9,1)" }
319+ , "67.7%": css { transform: str "scale3d(0.997,0.98,1)" }
302− , "86.7%": css { transform: str "scale3d(1.002,1.05,1)" }
320+ , "86.7%": css { transform: str "scale3d(1.004,1.01,1)" }
303 , "to": css { transform: str "scale3d(1,1,1)" }
321 , "to": css { transform: str "scale3d(1,1,1)" }
304 }
322 }
src/Yoga/Block/Atom/Input/View.purs+5−3
@@ -14,10 +14,11 @@ import Record.Builder as RB
14import Type.Prelude (Proxy(..))
14import Type.Prelude (Proxy(..))
15import Web.HTML.HTMLInputElement as InputElement
15import Web.HTML.HTMLInputElement as InputElement
16import Yoga.Block.Atom.Icon as Icon
16import Yoga.Block.Atom.Icon as Icon
17+import Yoga.Block.Atom.Input.Style (SizeVariant(..))
17import Yoga.Block.Atom.Input.Style as Style
18import Yoga.Block.Atom.Input.Style as Style
18import Yoga.Block.Atom.Input.Types (HTMLInputType)
19import Yoga.Block.Atom.Input.Types (HTMLInputType)
19import Yoga.Block.Atom.Input.Types as InputTypes
20import Yoga.Block.Atom.Input.Types as InputTypes
20−import Yoga.Block.Atom.Input.View.Container (rawContainer)
21+import Yoga.Block.Atom.Input.View.Container as Container
21import Yoga.Block.Atom.Input.View.HTMLInput as HTMLInput
22import Yoga.Block.Atom.Input.View.HTMLInput as HTMLInput
22import Yoga.Block.Atom.Input.View.Label as Label
23import Yoga.Block.Atom.Input.View.Label as Label
23import Yoga.Block.Container.Style (colour)
24import Yoga.Block.Container.Style (colour)
@@ -99,7 +100,7 @@ rawComponent =
99 , labelText
100 , labelText
100 , background: props.background ?|| colour.interfaceBackground
101 , background: props.background ?|| colour.interfaceBackground
101 , textColour: props.textColour ?|| colour.text
102 , textColour: props.textColour ?|| colour.text
102−
103+ , sizeVariant: props.sizeVariant ?|| SizeMedium
103 }
104 }
104
105
105 leading ∷ Maybe JSX
106 leading ∷ Maybe JSX
@@ -162,7 +163,7 @@ rawComponent =
162 ) # deleteUndefineds
163 ) # deleteUndefineds
163 )
164 )
164 inputContainer =
165 inputContainer =
165− rawContainer
166+ Container.rawContainer
166 </
167 </
167 { hasFocus: hasFocus
168 { hasFocus: hasFocus
168 , isInvalid: aria # Object.lookup "invalid" <#> (_ == "true") # maybeToOp
169 , isInvalid: aria # Object.lookup "invalid" <#> (_ == "true") # maybeToOp
@@ -171,6 +172,7 @@ rawComponent =
171 , borderColour: props.borderColour
172 , borderColour: props.borderColour
172 , textColor: props.textColour
173 , textColor: props.textColour
173 , ref
174 , ref
175+ , hasLabel: props.label # opToMaybe # isJust
174 }
176 }
175 />
177 />
176 [ leading # foldMap \l -> div </ {} /> [ l ]
178 [ leading # foldMap \l -> div </ {} /> [ l ]
src/Yoga/Block/Atom/Input/View/Container.purs+37−22
@@ -24,6 +24,7 @@ type PropsF f =
24type MandatoryProps r =
24type MandatoryProps r =
25 ( children ∷ Array JSX
25 ( children ∷ Array JSX
26 , hasFocus ∷ Boolean
26 , hasFocus ∷ Boolean
27+ , hasLabel :: Boolean
27 | r
28 | r
28 )
29 )
29
30
@@ -49,30 +50,44 @@ rawContainer =
49 let
50 let
50 containerVariants = mkContainerVariants dimensions
51 containerVariants = mkContainerVariants dimensions
51 containerVariantLabels = mkContainerVariantLabels containerVariants
52 containerVariantLabels = mkContainerVariantLabels containerVariants
53+ animated =
54+ M.div
55+ </*
56+ ( { variants: M.variants containerVariants
57+ , initial: M.initial false
58+ , animate: M.animate
59+ if props.hasFocus then
60+ containerVariantLabels.focussed
61+ else
62+ containerVariantLabels.blurred
63+ , className: "ry-input-container"
64+ , key: show sizes.innerWidth -- redraw
65+ , css: Style.inputContainer props
66+ , ref
67+ } `unsafeAddProps`
68+ { "data-invalid":
69+ props.isInvalid
70+ # opToMaybe
71+ # maybe (unsafeCoerce undefined) show
72+ }
73+ )
74+ static =
75+ R.div'
76+ </*
77+ ( { className: "ry-input-container"
78+ , key: show sizes.innerWidth -- redraw
79+ , css: Style.inputContainer props <> Style.ploppedFocusWithin
80+ , ref
81+ } `unsafeAddProps`
82+ { "data-invalid":
83+ props.isInvalid
84+ # opToMaybe
85+ # maybe (unsafeCoerce undefined) show
86+ }
87+ )
52 result = R.div' </* { css: Style.containerContainer props } />
88 result = R.div' </* { css: Style.containerContainer props } />
53 [ R.div' </*> { className: "container-background", css: Style.containerBackground props }
89 [ R.div' </*> { className: "container-background", css: Style.containerBackground props }
54− , M.div
55− </*
56− ( { variants: M.variants containerVariants
57− , initial: M.initial false
58− , animate:
59− M.animate
60− if props.hasFocus then
61− containerVariantLabels.focussed
62− else
63− containerVariantLabels.blurred
64− , className: "ry-input-container"
65− , key: show sizes.innerWidth -- redraw
66− , css: Style.inputContainer props
67− , ref
68− } `unsafeAddProps`
69− { "data-invalid":
70− props.isInvalid
71− # opToMaybe
72− # maybe (unsafeCoerce undefined) show
73− }
74− )
75− /> props.children
90+ , (if props.hasLabel then animated else static) props.children
76 ]
91 ]
77 pure result
92 pure result
78
93
src/Yoga/Block/Atom/Input/View/Label.purs+10−8
@@ -1,6 +1,7 @@
1module Yoga.Block.Atom.Input.View.Label where
1module Yoga.Block.Atom.Input.View.Label where
2
2
3import Yoga.Prelude.View
3import Yoga.Prelude.View
4+
4import Data.String.NonEmpty (NonEmptyString)
5import Data.String.NonEmpty (NonEmptyString)
5import Data.String.NonEmpty as NonEmptyString
6import Data.String.NonEmpty as NonEmptyString
6import Effect.Class.Console (warn)
7import Effect.Class.Console (warn)
@@ -11,6 +12,7 @@ import React.Basic.DOM as R
11import React.Basic.Hooks (reactComponent)
12import React.Basic.Hooks (reactComponent)
12import React.Basic.Hooks as React
13import React.Basic.Hooks as React
13import Record.Extra (sequenceRecord)
14import Record.Extra (sequenceRecord)
15+import Yoga.Block.Atom.Input.Style (SizeVariant, sizeVariantToFactor)
14import Yoga.Block.Atom.Input.Style as Style
16import Yoga.Block.Atom.Input.Style as Style
15
17
16type Props =
18type Props =
@@ -25,6 +27,7 @@ type Props =
25 , labelText ∷ NonEmptyString
27 , labelText ∷ NonEmptyString
26 , background ∷ String
28 , background ∷ String
27 , textColour ∷ String
29 , textColour ∷ String
30+ , sizeVariant :: SizeVariant
28 }
31 }
29
32
30component ∷ ReactComponent Props
33component ∷ ReactComponent Props
@@ -64,14 +67,13 @@ component =
64 -- UI
67 -- UI
65 let
68 let
66 text = R.text $ NonEmptyString.toString props.labelText
69 text = R.text $ NonEmptyString.toString props.labelText
67− result =
68− container
69− [ labelContainer
70+ result = container [ labelContainer [ labelSpan [ text ] ] ]
70− [ labelSpan
71+
71− [ text ]
72+ container = div </*
72− ]
73− ]
74− container = div </* { className: "ry-input-label-container", css: Style.labelContainer }
73+ { className: "ry-input-label-container"
74+ , style: R.css { "--input-size-factor": props.sizeVariant # sizeVariantToFactor }
75+ , css: Style.labelContainer
76+ }
75 labelContainer =
77 labelContainer =
76 maybeOffsets
78 maybeOffsets
77 # foldMap case _ of
79 # foldMap case _ of
src/Yoga/Block/Container/View.purs+17−15
@@ -7,7 +7,9 @@ module Yoga.Block.Container.View
7 ) where
7 ) where
8
8
9import Yoga.Prelude.View
9import Yoga.Prelude.View
10+
10import Data.Array as Array
11import Data.Array as Array
12+import Fahrtwind as F
11import React.Basic.DOM as R
13import React.Basic.DOM as R
12import React.Basic.Emotion as E
14import React.Basic.Emotion as E
13import React.Basic.Hooks as React
15import React.Basic.Hooks as React
@@ -25,7 +27,7 @@ type PropsF f =
25 )
27 )
26
28
27type Props =
29type Props =
28− ( | PropsF Id )
30+ (| PropsF Id)
29
31
30component ∷ ∀ p q. Union p q Props => ReactComponent { | p }
32component ∷ ∀ p q. Union p q Props => ReactComponent { | p }
31component = rawComponent
33component = rawComponent
@@ -78,31 +80,31 @@ rawComponent =
78 $ fragment
80 $ fragment
79 [ R.div' </ { ref }
81 [ R.div' </ { ref }
80 /> Array.cons
82 /> Array.cons
81− ( element E.global
83+ ( element E.global
82− { styles: (_ <>? props.globalStyles)
84+ { styles: F.globalStyles <> (_ <>? props.globalStyles)
83 case propsThemeVariant, systemThemeVariant of
85 case propsThemeVariant, systemThemeVariant of
84 Nothing, Nothing -> Styles.global
86 Nothing, Nothing -> Styles.global
85 Just Styles.DarkMode, _ -> Styles.darkMode
87 Just Styles.DarkMode, _ -> Styles.darkMode
86 Just Styles.LightMode, _ -> Styles.lightMode
88 Just Styles.LightMode, _ -> Styles.lightMode
87 Nothing, Just DarkMode -> Styles.darkMode
89 Nothing, Just DarkMode -> Styles.darkMode
88 Nothing, Just LightMode -> Styles.lightMode
90 Nothing, Just LightMode -> Styles.lightMode
89− }
91+ }
90− )
92+ )
91− children
93+ children
92 , R.div { id: modalContainerId }
94 , R.div { id: modalContainerId }
93 , E.element R.div'
95 , E.element R.div'
94 { id: tooltipContainerId
96 { id: tooltipContainerId
95 , className: tooltipContainerId
97 , className: tooltipContainerId
96 , css:
98 , css:
97− E.css
99+ E.css
98− { width: 100.0 # E.vw
100+ { width: 100.0 # E.vw
99− , height: 100.0 # E.vh
101+ , height: 100.0 # E.vh
100− , position: E.fixed
102+ , position: E.fixed
101− , top: E.str "0"
103+ , top: E.str "0"
102− , left: E.str "0"
104+ , left: E.str "0"
103− , pointerEvents: E.none
105+ , pointerEvents: E.none
104− , zIndex: E.str "9999"
106+ , zIndex: E.str "9999"
105− }
107+ }
106 }
108 }
107 ]
109 ]
108
110
src/Yoga/Block/Hook/UseStateEq.purs+11−11
@@ -8,22 +8,22 @@ import Effect (Effect)
8import React.Basic.Hooks (UseState, Hook, coerceHook)
8import React.Basic.Hooks (UseState, Hook, coerceHook)
9import React.Basic.Hooks as React
9import React.Basic.Hooks as React
10
10
11+-- Thanks to
12+useStateEq :: forall a. Eq a => a -> Hook (UseStateEq a) (a /\ ((a -> a) -> Effect Unit))
13+useStateEq initialValue = coerceHook React.do
14+ value /\ setValue <- React.useState initialValue
15+ let
16+ updateEq f = setValue \value' -> do
17+ let newValue = f value'
18+ if newValue /= value' then newValue else value'
19+ pure (value /\ updateEq)
20+
11newtype UseStateEq a hooks = UseStateEq (UseState a hooks)
21newtype UseStateEq a hooks = UseStateEq (UseState a hooks)
12
22
13derive instance Newtype (UseStateEq a hooks) _
23derive instance Newtype (UseStateEq a hooks) _
14
24
15−-- [TODO] I'm too stupid to write this
16−-- useStateEq :: forall a. Eq a => a -> Hook (UseStateEq a) (a /\ ((a -> a) -> Effect Unit))
17−-- useStateEq initialValue = coerceHook React.do
18−-- value /\ update <- React.useState initialValue
19−-- let
20−-- updateEq modify = do
21−-- modified <- modify value
22−-- when (modified /= value) do update (const modified)
23−-- pure (value /\ updateEq)
24−
25useStateEq' :: forall a. Eq a => a -> Hook (UseStateEq a) (a /\ (a -> Effect Unit))
25useStateEq' :: forall a. Eq a => a -> Hook (UseStateEq a) (a /\ (a -> Effect Unit))
26useStateEq' initialValue = coerceHook React.do
26useStateEq' initialValue = coerceHook React.do
27 value /\ setValue <- React.useState' initialValue
27 value /\ setValue <- React.useState' initialValue
28− let updateEq newValue = do when (newValue /= value) do setValue newValue
28+ let updateEq newValue = when (newValue /= value) (setValue newValue)
29 pure (value /\ updateEq)
29 pure (value /\ updateEq)
src/Yoga/Block/Molecule/TableOfContents/Style.purs+8−8
@@ -18,9 +18,9 @@ button =
18 , "&:focus": nest { outline: str "none" }
18 , "&:focus": nest { outline: str "none" }
19 , border: str $ "1px solid " <> colour.backgroundLayer2
19 , border: str $ "1px solid " <> colour.backgroundLayer2
20 , "&:focus-visible":
20 , "&:focus-visible":
21− nest
21+ nest
22− { boxShadow: str $ "0 0 0 var(--s-4) " <> colour.highlight
22+ { boxShadow: str $ "0 0 0 var(--s-4) " <> colour.highlight
23− }
23+ }
24 , borderRadius: str "calc(var(--s2) / 2)"
24 , borderRadius: str "calc(var(--s2) / 2)"
25 , height: var "--s2"
25 , height: var "--s2"
26 , width: str "calc(var(--s2) + var(--s1))"
26 , width: str "calc(var(--s2) + var(--s1))"
@@ -57,8 +57,8 @@ tableOfContentsTextContainer =
57 , fontWeight: str "bold"
57 , fontWeight: str "bold"
58 , position: absolute
58 , position: absolute
59 , top: str "1px"
59 , top: str "1px"
60− , fontSize: str "calc(var(--s2)*0.5)"
60+ , fontSize: str "calc(var(--s2) * 0.5)"
61− , lineHeight: str "calc(var(--s2)*0.5)"
61+ , lineHeight: str "calc(var(--s2) * 0.5)"
62 , textAlign: center
62 , textAlign: center
63 , height: var "--s2"
63 , height: var "--s2"
64 , display: flex
64 , display: flex
@@ -87,7 +87,7 @@ tableOfContentsText =
87 , alignItems: center
87 , alignItems: center
88 , color: disabledTextColour
88 , color: disabledTextColour
89 , "& > *":
89 , "& > *":
90− nest
90+ nest
91− { color: successTextColour
91+ { color: successTextColour
92− }
92+ }
93 }
93 }