git.iko.soy/rowtype-yoga/ry-blocks
on main help sign in
Mostly buttons
commit3c1f4dfab37cf14de8d82ac4765784f8cff7214c authorMark Eibes <mark.eibes@gmail.com> dateSun 27 Dec 2020, 22:12 (6 years ago) parent20e0552
19 files changed+301−164
src/Yoga/Block/Atom/Button/Story.purs+13−13 src/Yoga/Block/Atom/Button/Style.purs+68−34 src/Yoga/Block/Atom/Button/Types.purs+2 src/Yoga/Block/Atom/Button/View.purs+7−11 src/Yoga/Block/Atom/Icon/Style.purs+13−2 src/Yoga/Block/Atom/Input/Story.purs+11−13 src/Yoga/Block/Atom/Input/Style.purs+12−16 src/Yoga/Block/Atom/Input/View.purs+21−25 src/Yoga/Block/Atom/Popover/Story.purs+47−11 src/Yoga/Block/Container/Story.purs+16−20 src/Yoga/Block/Container/Style.purs+29−5 src/Yoga/Block/Icon/SVG/Cross.purs+21 src/Yoga/Block/Icon/SVG/Icon.purs+2 src/Yoga/Block/Icon/SVG/Warn.purs+25 src/Yoga/Block/Icon/SVG/cross.svg+1 src/Yoga/Block/Icon/SVG/warn.svg+1 src/Yoga/Block/Layout/Cluster/Story.purs+9−13 src/Yoga/Block/Layout/Cluster/View.purs+2 src/Yoga/Block/Layout/Switcher/Spec.purs+1−1
src/Yoga/Block/Atom/Button/Story.purs+13−13
@@ -4,11 +4,9 @@ import Prelude hiding (div)
44import Effect (Effect)
55import Effect.Unsafe (unsafePerformEffect)
66import React.Basic (JSX, element, fragment)
7−import React.Basic.DOM (css)
87import React.Basic.DOM as R
98import React.Basic.Emotion (str)
109import React.Basic.Emotion as E
11−import React.Basic.Events (handler_)
1210import Yoga (div, (/>), (</), (</>))
1311import Yoga.Block as Block
1412import Yoga.Block.Atom.Button.Types as ButtonType
@@ -40,23 +38,25 @@ button = do
4038 [ R.h1_ [ R.text "Button Examples" ]
4139 , R.h2_ [ R.text "Button types" ]
4240 , Block.cluster </ { space: "var(--s-1)" }
43− /> [ div </ {}
44− /> [ Button.component </ { buttonType: ButtonType.Generic } /> [ R.text "Generic" ]
45− , Button.component </ { buttonType: ButtonType.Primary } /> [ R.text "Primary" ]
46− ]
41+ /> [ Button.component </ { buttonType: ButtonType.Generic } /> [ R.text "Generic" ]
42+ , Button.component </ { buttonType: ButtonType.Primary } /> [ R.text "Primary" ]
43+ , Button.component </ { buttonType: ButtonType.Dangerous } /> [ R.text "Dangerous" ]
4744 ]
4845 , R.h2_ [ R.text "Button shapes" ]
4946 , Block.cluster </ { space: "var(--s-1)" }
50− /> [ div </ {}
51− /> [ Button.component </ { buttonType: ButtonType.Generic, buttonShape: ButtonType.Pill } /> [ R.text "Generic" ]
52− , Button.component </ { buttonType: ButtonType.Primary, buttonShape: ButtonType.Pill } /> [ R.text "Primary" ]
53− ]
47+ /> [ Button.component </ { buttonType: ButtonType.Generic, buttonShape: ButtonType.Pill } /> [ R.text "Generic" ]
48+ , Button.component </ { buttonType: ButtonType.Primary, buttonShape: ButtonType.Pill } /> [ R.text "Primary" ]
5449 ]
5550 , R.h2_ [ R.text "Icon button" ]
5651 , Block.cluster </ { space: "var(--s-1)" }
57− /> [ div </ {}
58− /> [ Button.component </ {} /> [ Block.icon </> { icon: Icon.questionMark, size: str "var(--s2)", colour: str colour.highlight } ]
59− ]
52+ /> [ Button.component </ {} /> [ Block.icon </> { icon: Icon.questionMark, size: str "var(--s2)", colour: str colour.highlight } ]
53+ ]
54+ , R.h2_ [ R.text "Disabled" ]
55+ , Block.cluster </ { space: "var(--s-1)" }
56+ /> [ Button.component </ { buttonType: ButtonType.Generic, disabled: true } /> [ R.text "Generic" ]
57+ , Button.component </ { buttonType: ButtonType.Primary, disabled: true } /> [ R.text "Primary" ]
58+ , Button.component </ { buttonType: ButtonType.Generic, buttonShape: ButtonType.Pill, disabled: true } /> [ R.text "Generic" ]
59+ , Button.component </ { buttonType: ButtonType.Primary, buttonShape: ButtonType.Pill, disabled: true } /> [ R.text "Primary" ]
6060 ]
6161 ]
6262 ]
src/Yoga/Block/Atom/Button/Style.purs+68−34
@@ -16,65 +16,99 @@ gradientBackground =
1616 colour.highlightDarker
1717 ","
1818 colour.highlightLighter
19− "),linear-gradient(225deg,"
19+ ")"
20+ ","
21+ "linear-gradient(225deg,"
2022 colour.highlightRotatedBackwards
2123 ","
2224 colour.highlightRotatedForwards
2325 ")"
2426
25−buttonContainer ∷ Style
26−buttonContainer =
27+backgroundAnimation ∷ StyleProperty
28+backgroundAnimation =
29+ keyframes
30+ $ { "from": css { backgroundPosition: str "0% 50%" }
31+ , "to": css { backgroundPosition: str "100% 50%" }
32+ }
33+
34+button ∷ Style
35+button =
2736 css
2837 { background: str colour.interfaceBackground
2938 , boxShadow: str "0 1px 4px rgba(0,0,0,0.30)"
39+ , border: str $ i "1px solid transparent"
3040 , borderTop: str $ i "1px solid " colour.interfaceBackgroundHighlight
3141 , borderBottom: str $ i "1px solid " colour.interfaceBackgroundShadow
3242 , display: inlineFlex
33− , padding: str "2px"
43+ , padding: str "calc(var(--s-1) * 0.85) var(--s0)"
3444 , justifyContent: center
3545 , alignItems: center
3646 , borderRadius: var "--s-1"
47+ , color: str colour.text
48+ , boxSizing: borderBox
49+ , fontSize: var "--s0"
50+ , fontFamily: var "--mainFont"
51+ , fontWeight: str "450"
52+ , letterSpacing: str "calc(var(--s-5)* (-0.1))"
53+ , userSelect: none
54+ , transition: str "all 0.2s ease-out" <> str "transform 50ms ease-in"
3755 , """&[data-button-shape="pill"]""":
3856 nest
39− { borderRadius: var "--s1"
40− , "& > button":
41− nest
42− { padding: str "calc(var(--s-1) * 0.8) var(--s0)"
43− }
57+ { borderRadius: str "calc(var(--s1) * 0.85)"
58+ , padding: str "calc(var(--s-1) * 0.9) var(--s0)"
4459 }
4560 , """&[data-button-type="primary"]""":
4661 nest
4762 { background: gradientBackground
4863 , backgroundSize: str "200% 200%"
64+ , fontWeight: str "600"
65+ , letterSpacing: str "calc(var(--s-5)* (0.1))"
4966 , animation: backgroundAnimation <> str " alternate ease-out 10s infinite"
5067 , boxShadow: str "0 1px 4px 0px rgba(0,0,0,0.40)"
5168 , borderColor: str "transparent"
52− , "& > button":
69+ , color: str colour.highlightText
70+ , """&:focus-visible""":
5371 nest
54− { color: str colour.highlightText
72+ { borderColor: str $ colour.background0
73+ }
74+ , """&:active""":
75+ nest
76+ { boxShadow: str "inset 0 1px 6px rgba(0,0,0,0.40)"
77+ , border: str $ i "1px solid transparent"
78+ }
79+ , "&:disabled":
80+ nest
81+ { background: str colour.highlightDisabled
5582 }
5683 }
57− }
58−
59−backgroundAnimation ∷ StyleProperty
60−backgroundAnimation =
61− keyframes
62− $ { "from": css { backgroundPosition: str "0% 50%" }
63− , "to": css { backgroundPosition: str "100% 50%" }
64− }
65−
66−button ∷ Style
67−button =
68− css
69− { background: str "transparent"
70− , color: str colour.text
71− , boxSizing: borderBox
72− , fontSize: var "--s0"
73− , fontFamily: var "--mainFont"
74− , fontWeight: str "500"
75− , letterSpacing: str "calc(var(--s-5)*0.2)"
76− , padding: str "calc(var(--s-1) * 0.85) var(--s0)"
77− , border: none
78− , userSelect: none
79− , "&:focus": nest { outline: none }
84+ , """&[data-button-type="dangerous"]""":
85+ nest
86+ { color: str $ colour.interfaceDangerousText
87+ , background: str $ colour.interfaceBackgroundDangerous
88+ , fontWeight: str "600"
89+ , letterSpacing: str "calc(var(--s-5) * -0.10)"
90+ }
91+ , """&:focus""": nest { outline: none }
92+ , """&:focus-visible""":
93+ nest
94+ { boxShadow: str $ "0 0 0 var(--s-4) " <> colour.highlight
95+ }
96+ , """&:active""":
97+ nest
98+ { boxShadow:
99+ str
100+ $ "inset 0 1px var(--s-1) rgba(0,0,0,0.20)"
101+ , borderTop: str $ i "1px solid " colour.interfaceBackgroundShadow
102+ , borderBottom: str $ i "1px solid " colour.interfaceBackgroundShadow
103+ , transform: str "scale3d(0.95,0.95,0.95)"
104+ , transition: str "transform 50ms ease"
105+ }
106+ , "&:disabled":
107+ nest
108+ { color: str colour.interfaceTextDisabled
109+ , boxShadow: none
110+ , background: str colour.interfaceBackgroundDisabled
111+ , borderTop: str $ i "1px solid " colour.interfaceBackgroundShadow
112+ , borderBottom: str $ i "1px solid " colour.interfaceBackgroundShadow
113+ }
80114 }
src/Yoga/Block/Atom/Button/Types.purs+2
@@ -4,6 +4,7 @@ import Prelude
44
55data ButtonType
66 = Primary
7+ | Dangerous
78 | Generic
89
910derive instance eqButtonType ∷ Eq ButtonType
@@ -12,6 +13,7 @@ renderButtonType ∷ ButtonType -> String
1213renderButtonType = case _ of
1314 Primary -> "primary"
1415 Generic -> "generic"
16+ Dangerous -> "dangerous"
1517
1618data ButtonShape
1719 = Rounded
src/Yoga/Block/Atom/Button/View.purs+7−11
@@ -36,14 +36,10 @@ rawComponent =
3636 { "button-type": renderButtonType (props.buttonType ?|| Button.Generic)
3737 , "button-shape": renderButtonShape (props.buttonShape ?|| Button.Rounded)
3838 }
39− pure $ div
40− </* { css: Style.buttonContainer
41− , className: "ry-button-container"
42− , _data
43− }
44− /> [ emotionButton propsRef
45− props
46− { className: "ry-button"
47− , css: Style.button
48− }
49− ]
39+ pure
40+ $ emotionButton propsRef
41+ props
42+ { className: "ry-button"
43+ , css: Style.button
44+ , _data
45+ }
src/Yoga/Block/Atom/Icon/Style.purs+13−2
@@ -22,9 +22,20 @@ span props =
2222 , display: inlineFlex
2323 , justifyContent: center
2424 , alignItems: center
25+ , margin: _0
26+ , padding: _0
27+ , width
28+ , height
29+ , overflow: hidden
2530 , "& > svg":
2631 nest
27− { width: (props.width <|> props.size) ?|| (str "auto")
28− , height: (props.height <|> props.size) ?|| (str "1.2ch")
32+ { width
33+ , height
34+ , margin: _0
35+ , padding: _0
2936 }
3037 }
38+ where
39+ width = (props.width <|> props.size) ?|| (str "auto")
40+
41+ height = (props.height <|> props.size) ?|| (str "1ch")
src/Yoga/Block/Atom/Input/Story.purs+11−13
@@ -10,7 +10,7 @@ import React.Basic (JSX, element, fragment)
1010import React.Basic.DOM as R
1111import React.Basic.Emotion as E
1212import React.Basic.Events (handler_)
13−import Yoga (el)
13+import Yoga (el, (</), (/>), (</>))
1414import Yoga.Block as Block
1515import Yoga.Block.Atom.Input as Input
1616import Yoga.Block.Atom.Input.Types as HTMLInput
@@ -38,7 +38,7 @@ input = do
3838 [ R.div_
3939 [ R.h1_ [ R.text "Input Examples" ]
4040 , R.h2_ [ R.text "Generic Input" ]
41− , element Input.component { value: "A Generic Input", onChange: handler_ mempty }
41+ , Input.component </> { value: "A Generic Input", onChange: handler_ mempty }
4242 , R.h2_ [ R.text "Validation on text Input" ]
4343 , element Input.component { type: HTMLInput.Text, label: nes (SProxy ∷ _ "Is undefined a function?"), value: "Yes, why not?", onChange: handler_ mempty, _aria: Object.singleton "invalid" "true" }
4444 , element Input.component { type: HTMLInput.Text, label: nes (SProxy ∷ _ "What's the type of null?"), value: "object", onChange: handler_ mempty, _aria: Object.singleton "invalid" "false" }
@@ -49,17 +49,15 @@ input = do
4949 , _aria: Object.singleton "required" "true"
5050 }
5151 , R.h3_ [ R.text "With a label" ]
52− , el Block.cluster {}
53− [ el R.form' {}
54− [ element Input.component { label: nes (SProxy ∷ _ "This has a label"), value: "And text", onChange: handler_ mempty }
55− , element Input.component { label: nes (SProxy ∷ _ "This has a label"), value: "", onChange: handler_ mempty }
56− , element Input.component { label: nes (SProxy ∷ _ "This has a label"), placeholder: "A very long placeholder, too..." }
57− , element Input.component { label: nes (SProxy ∷ _ "Pig nose"), leading: R.text "🐽🤣" }
58− , element Input.component { label: nes (SProxy ∷ _ "Pig nose"), trailing: R.text "🤫" }
59− , element Input.component { label: nes (SProxy ∷ _ "Pig nose"), leading: R.text "🌭" }
60− , element Input.component { label: nes (SProxy ∷ _ "Pig nose"), leading: R.text "⭐", trailing: R.text "🔮" }
61− ]
62− ]
52+ , Block.cluster </ {}
53+ /> [ element Input.component { label: nes (SProxy ∷ _ "This has a label"), value: "And text", onChange: handler_ mempty }
54+ , element Input.component { label: nes (SProxy ∷ _ "This has a label"), value: "", onChange: handler_ mempty }
55+ , element Input.component { label: nes (SProxy ∷ _ "This has a label"), placeholder: "A very long placeholder, too..." }
56+ , element Input.component { label: nes (SProxy ∷ _ "Pig nose"), leading: R.text "🐽🤣" }
57+ , element Input.component { label: nes (SProxy ∷ _ "Pig nose"), trailing: R.text "🤫" }
58+ , element Input.component { label: nes (SProxy ∷ _ "Pig nose"), leading: R.text "🌭" }
59+ , element Input.component { label: nes (SProxy ∷ _ "Pig nose"), leading: R.text "⭐", trailing: R.text "🔮" }
60+ ]
6361 , R.h2_ [ R.text "[BUG] Overflowing label" ]
6462 , element Input.component
6563 { type: HTMLInput.Text
src/Yoga/Block/Atom/Input/Style.purs+12−16
@@ -43,6 +43,7 @@ labelContainer ∷ Style
4343labelContainer =
4444 css
4545 { position: absolute
46+ , overflow: visible
4647 , top: _0
4748 , left: _0
4849 , display: inlineBlock
@@ -147,13 +148,14 @@ leftIconContainer =
147148 }
148149 }
149150
150−inputWrapper ∷ ∀ r. { | Props OptionalProp r } -> Style
151−inputWrapper props =
151+inputWrapper ∷ Style
152+inputWrapper =
152153 css
153154 { position: relative
154155 , boxSizing: borderBox
155156 , backgroundColor: str colour.inputBackground
156− , display: inlineFlex
157+ , display: flex
158+ , minWidth: str "calc(var(--s4) *2 )"
157159 , "--left-icon-size": var "--s0"
158160 , "--right-icon-size": str "calc(var(--s0) * 1.2)"
159161 , "--input-border-radius": var "--s-1"
@@ -161,7 +163,7 @@ inputWrapper props =
161163 , "--input-top-padding": var "--s-5"
162164 , "--input-bottom-padding": var "--s-5"
163165 , alignItems: center
164− , justifyContent: flexStart
166+ , justifyContent: center
165167 , paddingLeft: str "calc(var(--input-side-padding) - var(--border-width))"
166168 , paddingRight: str "calc(var(--input-side-padding) - var(--border-width))"
167169 , paddingTop: str "calc(var(--input-top-padding) - var(--border-width))"
@@ -173,26 +175,28 @@ inputWrapper props =
173175 , """&[data-invalid="false"]""":
174176 nest
175177 { borderColor: str colour.success
178+ , "--border-width": str "var(--s-5)"
176179 }
177180 , """&[data-invalid="true"]""":
178181 nest
179182 { borderColor: str colour.invalid
183+ , "--border-width": str "var(--s-5)"
180184 }
181185 , "&:focus-within":
182186 nest
183187 { "--border-width": str "var(--s-5)"
184188 , borderColor: str colour.highlight
189+ , transition: str "border-color 0s linear 0.1s"
185190 }
186191 }
187192
188−input ∷ ∀ r. { | Props OptionalProp r } -> Style
189−input props =
193+input ∷ Style
194+input =
190195 css
191196 { "&[type=text],&[type=search],&[type=password],&[type=number],&:not([type])":
192197 nest
193198 { color: str colour.text
194− , width: _100percent
195− , flex: str "1"
199+ , flex: str "2"
196200 , "--padding-top": var "--s-1"
197201 , "--padding-bottom": var "--s-1"
198202 , "&[aria-labelledby]":
@@ -221,12 +225,4 @@ input props =
221225 nest
222226 { outline: none
223227 }
224− , "&[type=button], &[type=submit]":
225− nest
226− { background: str colour.highlight
227− , color: str "white"
228− , boxShadow: str "0 1px 4px rgba(0,0,0,0.5)"
229− , borderColour: str colour.highlight
230− , height: str "auto"
231− }
232228 }
src/Yoga/Block/Atom/Input/View.purs+21−25
@@ -3,11 +3,9 @@ module Yoga.Block.Atom.Input.View where
33import Yoga.Prelude.View
44import Data.Array as Array
55import Data.Interpolate (i)
6−import Data.Semigroup.Foldable (intercalateMap)
76import Data.String.NonEmpty (NonEmptyString)
87import Data.String.NonEmpty as NonEmptyString
98import Data.Symbol (SProxy(..))
10−import Debug.Trace (spy)
119import Foreign.Object (Object)
1210import Foreign.Object as Object
1311import Framer.Motion as M
@@ -21,7 +19,6 @@ import Yoga.Block.Atom.Input.Style as Style
2119import Yoga.Block.Atom.Input.Types (HTMLInput)
2220import Yoga.Block.Atom.Input.Types as HTMLInput
2321import Yoga.Block.Icon.SVG as SVGIcon
24−import Yoga.Block.Internal.OptionalProp (asMaybe)
2522
2623type PropsF f =
2724 ( leading ∷ f JSX
@@ -60,16 +57,16 @@ containerVariants =
6057 { focussed:
6158 css
6259 { clipPath
63− , transition: { duration: 1.3 }
60+ , transition: { duration: 0.7 }
6461 }
6562 , blurred:
6663 css
6764 { clipPath:
68− drawPathUntil (Array.length path) path
65+ drawPathUntil (Array.length path + 1) path
6966 }
7067 }
7168 where
72− clipPath = 3 Array... (Array.length path / 2 + 1) <#> \ln -> drawPathUntil (ln * 2) path
69+ clipPath = 6 Array... (Array.length path) <#> \ln -> drawPathUntil ln path
7370
7471type Point =
7572 { x ∷ Int, y ∷ Int }
@@ -83,28 +80,27 @@ drawPathUntil idx thePath = do
8380 let firstFew = Array.take idx thePath
8481 let lastFew = Array.drop idx thePath $> (Array.last firstFew # fromMaybe' \_ -> unsafeCrashWith "ogod")
8582 let rendered = intercalate "," $ fn <$> (firstFew <> lastFew)
86− spy "polybius" $ i "polygon(" rendered ")"
83+ i "polygon(" rendered ")"
8784
8885path ∷ Array Point
89−path = mkPath 5
86+path = mkPath 3 7
9087
91−mkPath ∷ Int -> Array Point
92−mkPath d = do
88+mkPath ∷ Int -> Int -> Array Point
89+mkPath borderX borderY = do
9390 let
9491 inside =
95− [ p d d
96− , p (100 - d) d
97− , p (100 - d) (100 - d)
98− , p d (100 - d)
99− , p d d
92+ [ {- ⌜ -} p borderX borderY
93+ , {- ⌞ -} p borderX (100 - borderY)
94+ , {- ⌟ -} p (100 - borderX) (100 - borderY)
95+ , {- ⌝ -} p (100 - borderX) borderY
96+ , {- ⌜ -} p borderX borderY
10097 ]
10198 outside =
102− [ p d 0
103− , p 100 0
104− , p 100 100
105− , p 0 100
106− , p 0 0
107− , p d 0 -- one turn, starting way back
99+ [ {- ⌜ -} p 0 0
100+ , {- ⌞ -} p 0 100
101+ , {- ⌟ -} p 100 100
102+ , {- ⌝ -} p 100 0
103+ , {- ⌜ -} p 0 0
108104 ]
109105 inside <> outside <> (Array.reverse inside)
110106
@@ -216,7 +212,7 @@ rawComponent =
216212 , animate: M.animate if hasFocus then containerVariantLabels.focussed else containerVariantLabels.blurred
217213 }
218214 { className: "ry-input-wrapper"
219− , css: Style.inputWrapper props
215+ , css: Style.inputWrapper
220216 , _data:
221217 Object.fromHomogeneous
222218 { "invalid": aria # Object.lookup "invalid" # fromMaybe ""
@@ -230,7 +226,7 @@ rawComponent =
230226 # setOrDelete (SProxy ∷ _ "value") (maybeValue # maybeToOp)
231227 )
232228 { className: "ry-input"
233− , css: Style.input props
229+ , css: Style.input
234230 , onFocus: composeHandler props.onFocus onFocus
235231 , onBlur: composeHandler props.onBlur onBlur
236232 , _aria:
@@ -303,14 +299,14 @@ password =
303299 pure
304300 $ div
305301 </* { className: "ry-input-wrapper"
306− , css: Style.inputWrapper props
302+ , css: Style.inputWrapper
307303 }
308304 /> [ leading
309305 , emotionInput
310306 ref
311307 props { type = HTMLInput.toString <$> props.type # unsafeUnOptional }
312308 { className: "ry-input"
313− , css: Style.input props
309+ , css: Style.input
314310 , type: if hidePassword then "password" else "text"
315311 }
316312 , trailing
src/Yoga/Block/Atom/Popover/Story.purs+47−11
@@ -18,6 +18,7 @@ import React.Basic (JSX, ReactComponent, element, elementKeyed, fragment)
1818import React.Basic.DOM (CSS, css)
1919import React.Basic.DOM as R
2020import React.Basic.DOM.Events (targetValue)
21+import React.Basic.Emotion (str)
2122import React.Basic.Emotion as E
2223import React.Basic.Events (handler, handler_)
2324import React.Basic.Hooks (reactComponent)
@@ -27,11 +28,16 @@ import React.Basic.Popper.Placement.Types as Placement
2728import React.Basic.Popper.Types (nullRef)
2829import Unsafe.Coerce (unsafeCoerce)
2930import Yoga ((/>), (</), (</>))
31+import Yoga.Block as Block
32+import Yoga.Block.Atom.Icon as Icon
3033import Yoga.Block.Atom.Input as Input
3134import Yoga.Block.Atom.Input.Types as HTMLInput
3235import Yoga.Block.Atom.Popover as Popover
36+import Yoga.Block.Container.Style (colour)
3337import Yoga.Block.Container.Style as Styles
38+import Yoga.Block.Icon.SVG as Icons
3439import Yoga.Block.Layout.Box as Box
40+import Yoga.Block.Layout.Cluster as Cluster
3541import Yoga.Block.Layout.Stack as Stack
3642
3743default ∷
@@ -67,20 +73,47 @@ popover = do
6773 reactComponent "PopoverExample" \props -> React.do
6874 referenceElement /\ setReferenceElement <- React.useState' nullRef
6975 text /\ setText <- React.useState' ""
70− visible /\ setVisible <- React.useState' false
76+ visible /\ setVisible <- React.useState' true
77+ selectedAuthors /\ modSelectedAuthors <-
78+ React.useState [ "William Shakespeare", "Agatha Christie" ]
7179 let
7280 matchingAuthors =
7381 authors
82+ # (_ Array.\\ selectedAuthors)
7483 # Array.filter
7584 ( \a ->
7685 String.contains (String.Pattern (String.toLower text))
7786 (String.toLower a)
7887 )
7988 let
80− inputWrapper =
81− R.div'
82− </ { ref: unsafeCoerce (mkEffectFn1 setReferenceElement)
83− }
89+ inputWrapper = R.div' </ { ref: unsafeCoerce (mkEffectFn1 setReferenceElement) }
90+ pill t =
91+ Block.box </ { padding: str "var(--s-3)" }
92+ /> [ Block.cluster
93+ </ { style:
94+ css
95+ { background: colour.highlight
96+ , fontSize: "var(--s-1)"
97+ , space: "var(--s-5)"
98+ , borderRadius: "var(--s-2)"
99+ }
100+ }
101+ /> [ R.text t
102+ , Block.centre
103+ </ { style:
104+ css
105+ { background: "hotpink"
106+ }
107+ , andText: true
108+ }
109+ /> [ Icon.component
110+ </> { icon: Icons.cross, size: str "var(--s-1)"
111+ }
112+ ]
113+ ]
114+ ]
115+ pills = pill <$> selectedAuthors
116+ leading = Block.cluster </ {} /> pills
84117 input =
85118 Input.component
86119 </> { id: "author"
@@ -92,19 +125,20 @@ popover = do
92125 , onChange: handler targetValue $ traverse_ setText
93126 , onBlur: handler_ (setVisible false)
94127 , onFocus: handler_ (setVisible true)
95− , trailing: R.text "▼"
128+ , leading
96129 , autoComplete: "off"
97130 }
98131 pop children =
99132 elementKeyed motionPopover
100133 $ Motion.motion
101134 { initial:
102− css { clipPath: "polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%)" }
135+ css { maxHeight: 30, opacity: 0.0 }
103136 , exit:
104− css { clipPath: "polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%)" }
137+ css { maxHeight: 30, opacity: 0.0 }
105138 , animate:
106139 css
107− { clipPath: "polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)"
140+ { maxHeight: 400
141+ , opacity: 1
108142 }
109143 , transition:
110144 Motion.transition
@@ -120,9 +154,10 @@ popover = do
120154 , style:
121155 css
122156 { boxShadow: "0px 9px 12px rgba(40,40,40,0.5)"
123− , background: "rgba(50,50,70, 0.7)"
157+ , background: colour.interfaceBackground
124158 , borderRadius: "9px"
125− , borderTop: "solid 1px rgba(80,80,100,1.0)"
159+ , borderTop: "solid 1px " <> colour.interfaceBackgroundHighlight
160+ , borderBottom: "solid 1px " <> colour.interfaceBackgroundShadow
126161 , overflowY: "scroll"
127162 , maxHeight: "400px"
128163 , width: "300px"
@@ -149,6 +184,7 @@ popover = do
149184 </ { key: a
150185 -- , layout: Motion.layout true
151186 -- , variants: Motion.variants itemVariants
187+ , onClick: handler_ (modSelectedAuthors (_ `Array.snoc` a))
152188 }
153189 /> [ R.text a ]
154190 pure
src/Yoga/Block/Container/Story.purs+16−20
@@ -22,29 +22,25 @@ container =
2222 [ R.text "Content"
2323 , el Stack.component {}
2424 [ el Cluster.component {}
25− [ R.div_
26− [ R.input { value: "Text" }
27− , R.input { type: "number", value: "0" }
28− , styledLeaf R.input'
29− { className: "styledinput"
30− , css: inputFocus
31− , value: "focus"
32− }
33− , R.input
34− { className: "styledinput"
35− , value: "focus"
36− , disabled: true
37− }
38− ]
25+ [ R.input { value: "Text" }
26+ , R.input { type: "number", value: "0" }
27+ , styledLeaf R.input'
28+ { className: "styledinput"
29+ , css: inputFocus
30+ , value: "focus"
31+ }
32+ , R.input
33+ { className: "styledinput"
34+ , value: "focus"
35+ , disabled: true
36+ }
3937 ]
4038 , R.input { type: "file" }
4139 , el Cluster.component {}
42− [ R.div_
43− [ R.input { type: "checkbox" }
44− , R.input { type: "checkbox", checked: true }
45− , R.input { type: "radio" }
46− , R.input { type: "radio", checked: true }
47− ]
40+ [ R.input { type: "checkbox" }
41+ , R.input { type: "checkbox", checked: true }
42+ , R.input { type: "radio" }
43+ , R.input { type: "radio", checked: true }
4844 ]
4945 , R.input { type: "range" }
5046 ]
src/Yoga/Block/Container/Style.purs+29−5
@@ -131,9 +131,13 @@ defaultColours =
131131 , invalid
132132 , invalidText
133133 , required
134− , interfaceBackground: lightBg
135− , interfaceTextDisabled: darken 0.33 lightBg
134+ , interfaceBackground
135+ , interfaceBackgroundDangerous
136+ , interfaceDangerousText
137+ , interfaceBackgroundDisabled: darken 0.03 lightBg
138+ , interfaceTextDisabled: darken 0.30 lightBg
136139 , interfaceBackgroundHighlight: lighten 0.05 lightBg
140+ , highlightDisabled: (desaturate 0.80 >>> lighten 0.28) highlight
137141 , interfaceBackgroundShadow: darken 0.04 lightBg
138142 , inputBackground: lightBg
139143 , inputBorder: darken 0.1 lightBg
@@ -167,17 +171,21 @@ defaultColours =
167171 , textInverted: darkBg
168172 , backgroundInverted: lightBg
169173 , interfaceBackground: interfaceBackgroundDark
170− , interfaceTextDisabled: lighten 0.8 darkBg
174+ , interfaceBackgroundDangerous: interfaceBackgroundDangerousDark
175+ , interfaceDangerousText: interfaceDangerousTextDark
176+ , interfaceBackgroundDisabled: darken 0.3 interfaceBackgroundDark
177+ , interfaceTextDisabled: (desaturate 0.3 >>> lighten 0.25) interfaceBackgroundDark
171178 , interfaceBackgroundHighlight: lighten 0.1 interfaceBackgroundDark
172179 , interfaceBackgroundShadow: darken 0.1 interfaceBackgroundDark
173180 , inputBackground: darkBg
174181 , inputBorder: lighten 0.17 darkBg
175− , success
182+ , success: successDark
176183 , successText
177184 , required
178185 , invalid
179186 , invalidText
180187 , highlight: highlightDark
188+ , highlightDisabled: (desaturate 0.76 >>> darken 0.32) highlightDark
181189 , highlightLighter: withAlpha 0.2 (Color.lighten 0.5 highlightDark)
182190 , highlightDarker: withAlpha 0.4 (Color.darken 0.5 highlightDark)
183191 , highlightRotatedForwards: highlightDark # rotateHue 30.0
@@ -194,9 +202,21 @@ defaultColours =
194202
195203 interfaceBackgroundDark = Color.hsl 240.0 0.10 0.33
196204
205+ interfaceBackground = lightBg
206+
207+ interfaceBackgroundDangerous = interfaceBackground
208+
209+ interfaceDangerousText = invalid
210+
211+ interfaceBackgroundDangerousDark = Color.hsl 340.0 0.55 0.30
212+
213+ interfaceDangerousTextDark = Color.hsl 340.0 1.0 0.90
214+
197215 highlightText = Color.rgb 0xFF 0xFF 0xFF
198216
199− success = Color.rgb 20 200 60
217+ success = Color.rgb 10 150 25
218+
219+ successDark = Color.rgb 20 200 60
200220
201221 successText = Color.rgb 250 250 250
202222
@@ -232,6 +252,9 @@ type FlatTheme a =
232252 , background100 ∷ a
233253 , backgroundInverted ∷ a
234254 , interfaceBackground ∷ a
255+ , interfaceBackgroundDangerous ∷ a
256+ , interfaceDangerousText ∷ a
257+ , interfaceBackgroundDisabled ∷ a
235258 , interfaceTextDisabled ∷ a
236259 , interfaceBackgroundHighlight ∷ a
237260 , interfaceBackgroundShadow ∷ a
@@ -242,6 +265,7 @@ type FlatTheme a =
242265 , highlightRotatedForwards ∷ a
243266 , highlightDarker ∷ a
244267 , highlightLighter ∷ a
268+ , highlightDisabled ∷ a
245269 , highlightText ∷ a
246270 , success ∷ a
247271 , successText ∷ a
src/Yoga/Block/Icon/SVG/Cross.pursnew file+21
@@ -0,0 +1,21 @@
1+module Yoga.Block.Icon.SVG.Cross where
2+
3+import React.Basic (JSX)
4+import React.Basic.DOM.SVG as SVG
5+
6+cross ∷ JSX
7+cross =
8+ SVG.svg
9+ { viewBox: "0 0 100 100"
10+ , xmlns: "http://www.w3.org/2000/svg"
11+ , fillRule: "evenodd"
12+ , clipRule: "evenodd"
13+ , strokeLinejoin: "round"
14+ , strokeMiterlimit: "2"
15+ , children:
16+ [ SVG.path
17+ { d: "M50 2.339c26.305 0 47.661 21.356 47.661 47.661S76.305 97.661 50 97.661 2.339 76.305 2.339 50 23.695 2.339 50 2.339zm0 38.773L31.838 22.949l-8.889 8.889L41.112 50 22.949 68.162l8.889 8.889L50 58.888l18.162 18.163 8.889-8.889L58.888 50l18.163-18.162-8.889-8.889L50 41.112z"
18+ , fill: "var(--stroke-colour)"
19+ }
20+ ]
21+ }
src/Yoga/Block/Icon/SVG/Icon.purs+2
@@ -7,6 +7,7 @@ module Yoga.Block.Icon.SVG
77 , module Yoga.Block.Icon.SVG.Key
88 , module Yoga.Block.Icon.SVG.SimpleKey
99 , module Yoga.Block.Icon.SVG.QuestionMark
10+ , module Yoga.Block.Icon.SVG.Cross
1011 ) where
1112
1213import Yoga.Block.Icon.SVG.On (on)
@@ -17,3 +18,4 @@ import Yoga.Block.Icon.SVG.EyeOpen (eyeOpen)
1718import Yoga.Block.Icon.SVG.Key (key)
1819import Yoga.Block.Icon.SVG.SimpleKey (simpleKey)
1920import Yoga.Block.Icon.SVG.QuestionMark (questionMark)
21+import Yoga.Block.Icon.SVG.Cross (cross)
src/Yoga/Block/Icon/SVG/Warn.pursnew file+25
@@ -0,0 +1,25 @@
1+module Yoga.Block.Icon.SVG.Warn where
2+
3+import React.Basic (JSX)
4+import React.Basic.DOM as R
5+import React.Basic.DOM.SVG as SVG
6+
7+warn :: JSX
8+warn = SVG.svg
9+ { viewBox: "0 0 100 100"
10+ , xmlns: "http://www.w3.org/2000/svg"
11+ , fillRule: "evenodd"
12+ , clipRule: "evenodd"
13+ , strokeLinejoin: "round"
14+ , strokeMiterlimit: "2"
15+ , children:
16+ [ SVG.path
17+ { fill: "none"
18+ , d: "M0 0h100v100H0z"
19+ }
20+ , SVG.path
21+ { d: "M41.478 15.313a9.704 9.704 0 0117.044 0L91.27 75.397a9.706 9.706 0 01-8.522 14.351H17.252A9.705 9.705 0 018.73 75.397l32.748-60.084zM50 77.357c2.909 0 5.432-2.432 5.455-5.455-.023-2.977-2.546-5.409-5.455-5.409-3 0-5.477 2.432-5.454 5.409C44.523 74.925 47 77.357 50 77.357zm5.046-44.137H44.932l.909 29.591h8.318l.887-29.591z"
22+ , fill: "#333"
23+ }
24+ ]
25+ }
src/Yoga/Block/Icon/SVG/cross.svgnew file+1
@@ -0,0 +1 @@
1+<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2"><path fill="none" d="M0 0h100v100H0z"/><path d="M50 2.339c26.305 0 47.661 21.356 47.661 47.661S76.305 97.661 50 97.661 2.339 76.305 2.339 50 23.695 2.339 50 2.339zm0 38.773L31.838 22.949l-8.889 8.889L41.112 50 22.949 68.162l8.889 8.889L50 58.888l18.162 18.163 8.889-8.889L58.888 50l18.163-18.162-8.889-8.889L50 41.112z" fill="#333"/></svg>
src/Yoga/Block/Icon/SVG/warn.svgnew file+1
@@ -0,0 +1 @@
1+<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2"><path fill="none" d="M0 0h100v100H0z"/><path d="M41.478 15.313a9.704 9.704 0 0117.044 0L91.27 75.397a9.706 9.706 0 01-8.522 14.351H17.252A9.705 9.705 0 018.73 75.397l32.748-60.084zM50 77.357c2.909 0 5.432-2.432 5.455-5.455-.023-2.977-2.546-5.409-5.455-5.409-3 0-5.477 2.432-5.454 5.409C44.523 74.925 47 77.357 50 77.357zm5.046-44.137H44.932l.909 29.591h8.318l.887-29.591z" fill="#333"/></svg>
src/Yoga/Block/Layout/Cluster/Story.purs+9−13
@@ -36,15 +36,13 @@ cluster =
3636 , el Cluster.component
3737 { style: css { backgroundColor: "hotpink" }
3838 }
39− [ R.div_
40− $ [ power
41− ( R.div
42− { children: [ R.text "Content" ]
43− , style: css { backgroundColor: "teal" }
44− }
45− )
46− 30
47− ]
39+ [ power
40+ ( R.div
41+ { children: [ R.text "Content" ]
42+ , style: css { backgroundColor: "teal" }
43+ }
44+ )
45+ 30
4846 ]
4947 , R.h2_ [ R.text "Zero Space" ]
5048 , el Cluster.component
@@ -52,10 +50,8 @@ cluster =
5250 , space: "0"
5351 , justify: "flex-end"
5452 }
55− [ R.div_
56− [ R.div { children: [ R.text "Child 1" ], style: css { backgroundColor: "teal" } }
57− , R.div { children: [ R.text "Child 2" ], style: css { backgroundColor: "rebeccapurple" } }
58− ]
53+ [ R.div { children: [ R.text "Child 1" ], style: css { backgroundColor: "teal" } }
54+ , R.div { children: [ R.text "Child 2" ], style: css { backgroundColor: "rebeccapurple" } }
5955 ]
6056 ]
6157 ]
src/Yoga/Block/Layout/Cluster/View.purs+2
@@ -1,6 +1,7 @@
11module Yoga.Block.Layout.Cluster.View (component, Props, PropsF) where
22
33import Yoga.Prelude.View
4+import React.Basic.DOM as R
45import Yoga.Block.Layout.Cluster.Style as Style
56
67type PropsF f =
@@ -25,4 +26,5 @@ rawComponent =
2526 $ emotionDiv ref props
2627 { className: "ry-cluster " <>? props.className
2728 , css: Style.cluster props
29+ , children: [ R.div' </ {} /> props.children ]
2830 }
src/Yoga/Block/Layout/Switcher/Spec.purs+1−1
@@ -7,7 +7,7 @@ import React.Basic.DOM as R
77spec ∷ Spec Unit
88spec =
99 after_ cleanup do
10− describe "The cluster" do
10+ describe "The switcher" do
1111 it "renders without errors" do
1212 void
1313 $ renderComponent Switcher.component {}