git.iko.soy/rowtype-yoga/ry-blocks
on main help sign in
Buggy autosuggest
commit6a35fb56de6960c02f071d80a0f005645665462d authorMark Eibes <mark.eibes@gmail.com> dateSun 28 Mar 2021, 23:47 (6 years ago) parentdca8494
32 files changed+596−421
src/React/Basic/Popper/Popper.js+19 src/React/Basic/Popper/Popper.purs+17 src/React/Basic/Popper/Story.purs+2−3 src/React/Basic/Popper/Types.purs+1−11 src/Yoga/Block.purs+1−1 src/Yoga/Block/Atom/Button/Style.purs+1 src/Yoga/Block/Atom/CodeInput/Style.purs+1 src/Yoga/Block/Atom/Icon/Style.purs+1 src/Yoga/Block/Atom/Input/View.purs+10−5 src/Yoga/Block/Atom/Input/View/Container.purs−1 src/Yoga/Block/Atom/Input/View/Label.purs+60−31 src/Yoga/Block/Atom/Popover/Story.purs+338−262 src/Yoga/Block/Atom/Popover/View.purs+3−1 src/Yoga/Block/Atom/Range/Style.purs+1 src/Yoga/Block/Atom/Segmented/Style.purs+1 src/Yoga/Block/Atom/Toggle.purs+1−1 src/Yoga/Block/Atom/Toggle/Spec.purs+2−4 src/Yoga/Block/Atom/Toggle/Story.purs+4−4 src/Yoga/Block/Atom/Toggle/Style.purs+1 src/Yoga/Block/Atom/Toggle/View.purs+24−24 src/Yoga/Block/Atom/Tooltip/Style.purs+1 src/Yoga/Block/Atom/Tooltip/View.purs+1−1 src/Yoga/Block/Container/Style.purs+18−5 src/Yoga/Block/Internal.purs+1−1 src/Yoga/Block/Layout/Cluster/Style.purs+17−16 src/Yoga/Block/Layout/Cluster/View.purs+1−1 src/Yoga/Block/Layout/Switcher/Story.purs+3−3 src/Yoga/Block/Molecule/TableOfContents/Story.purs+2−11 src/Yoga/Block/Molecule/TableOfContents/Style.purs+1 src/Yoga/Block/Organism/Form.purs+38−17 src/Yoga/Block/Organism/Form/Story.purs+24−15 src/Yoga/Block/Organism/Form/Validation.purs+1−3
src/React/Basic/Popper/Popper.jsnew file+19
@@ -0,0 +1,19 @@
1+exports.modifierMatchReferenceSize = {
2+ name: "matchReferenceSize",
3+ enabled: true,
4+ fn: ({ state, instance }) => {
5+ const widthOrHeight =
6+ state.placement.startsWith("left") ||
7+ state.placement.startsWith("right")
8+ ? "height"
9+ : "width";
10+ const popperSize = state.rects.popper[widthOrHeight];
11+ const referenceSize = state.rects.reference[widthOrHeight];
12+ if (popperSize >= referenceSize) return;
13+
14+ state.styles.popper[widthOrHeight] = `${referenceSize}px`;
15+ instance.update();
16+ },
17+ phase: "beforeWrite",
18+ requires: ["computeStyles"]
19+}
src/React/Basic/Popper/Popper.pursnew file+17
@@ -0,0 +1,17 @@
1+module React.Basic.Popper where
2+
3+import Data.Nullable (null)
4+import React.Basic.Popper.Types (ArrowElement, Modifier)
5+import Unsafe.Coerce (unsafeCoerce)
6+import Yoga.Block.Internal (NodeRef)
7+
8+nullRef ∷ NodeRef
9+nullRef = unsafeCoerce null
10+
11+modifierArrow ∷ ArrowElement -> Modifier
12+modifierArrow element = unsafeCoerce { name: "arrow", options: { element } }
13+
14+modifierOffset ∷ { x ∷ Number, y ∷ Number } -> Modifier
15+modifierOffset { x, y } = unsafeCoerce { name: "offset", options: { offset: [ x, y ] } }
16+
17+foreign import modifierMatchReferenceSize ∷ Modifier
src/React/Basic/Popper/Story.purs+2−3
@@ -10,7 +10,7 @@ import React.Basic.DOM as R
1010import React.Basic.Emotion as E
1111import React.Basic.Hooks as React
1212import React.Basic.Popper.Hook (usePopper)
13−import React.Basic.Popper.Types (modifierArrow, modifierOffset)
13+import React.Basic.Popper (modifierArrow, modifierOffset)
1414import Unsafe.Coerce (unsafeCoerce)
1515import Yoga.Block as Block
1616import Yoga.Block.Container.Style as Styles
@@ -143,8 +143,7 @@ animatedPopper = do
143143 }
144144 on /\ setOn <- React.useState' true
145145 -- Handlers
146− let
147− buttonClicked = setOn (not on)
146+ let buttonClicked = setOn (not on)
148147 -- Elements
149148 let
150149 result =
src/React/Basic/Popper/Types.purs+1−11
@@ -2,11 +2,7 @@ module React.Basic.Popper.Types where
22
33import Foreign.Object (Object)
44import React.Basic.DOM (CSS)
5−import Unsafe.Coerce (unsafeCoerce)
6−import Yoga.Prelude.View (NodeRef, null)
7−
8−nullRef ∷ NodeRef
9−nullRef = unsafeCoerce null
5+import Yoga.Prelude.View (NodeRef)
106
117type ReferenceElement =
128 NodeRef
@@ -28,12 +24,6 @@ type PopperData =
2824
2925foreign import data Modifier ∷ Type
3026
31−modifierArrow ∷ ArrowElement -> Modifier
32−modifierArrow element = unsafeCoerce { name: "arrow", options: { element } }
33−
34−modifierOffset ∷ { x ∷ Number, y ∷ Number } -> Modifier
35−modifierOffset { x, y } = unsafeCoerce { name: "offset", options: { offset: [ x, y ] } }
36−
3727type Options =
3828 ( modifiers ∷ Array Modifier
3929 , strategy ∷ String
src/Yoga/Block.purs+1−1
@@ -61,7 +61,7 @@ stack = Stack.component
6161switcher ∷ ∀ p q. Union p q Switcher.Props => ReactComponent { | p }
6262switcher = Switcher.component
6363
64−toggle ∷ ∀ p q. Union p q Toggle.Props => ReactComponent { | Toggle.MandatoryProps p }
64+toggle ∷ ∀ p q. Union p q Toggle.Props => ReactComponent { | p }
6565toggle = Toggle.component
6666
6767tooltip ∷ ∀ p q. Union p q Tooltip.Props => ReactComponent { | Tooltip.MandatoryProps p }
src/Yoga/Block/Atom/Button/Style.purs+1
@@ -4,6 +4,7 @@ import Yoga.Prelude.Style
44import Data.Interpolate (i)
55import Yoga.Block.Container.Style (colour)
66
7+type Props :: forall k. (Type -> k) -> Row k -> Row k
78type Props f r =
89 ( css ∷ f Style
910 | r
src/Yoga/Block/Atom/CodeInput/Style.purs+1
@@ -4,6 +4,7 @@ import Yoga.Prelude.Style
44import Data.Interpolate (i)
55import Yoga.Block.Container.Style (colour)
66
7+type Props :: forall k. (Type -> k) -> Row k -> Row k
78type Props f r =
89 ( css ∷ f Style
910 | r
src/Yoga/Block/Atom/Icon/Style.purs+1
@@ -3,6 +3,7 @@ module Yoga.Block.Atom.Icon.Style where
33import Yoga.Prelude.Style
44import Yoga.Block.Container.Style (colour)
55
6+type Props :: forall k. (Type -> k) -> Row k -> Row k
67type Props f r =
78 ( css ∷ f Style
89 , colour ∷ f StyleProperty
src/Yoga/Block/Atom/Input/View.purs+10−5
@@ -29,6 +29,8 @@ type PropsF f r =
2929 , trailing ∷ f JSX
3030 , label ∷ f NonEmptyString
3131 , type ∷ f HTMLInputType
32+ , inputRef ∷ f NodeRef
33+ , forceSmallLabel ∷ f Boolean
3234 | Style.Props f r
3335 )
3436
@@ -58,7 +60,8 @@ rawComponent =
5860 mkForwardRefComponent "InputContainer" do
5961 \(props ∷ { | PropsOptional }) propsRef -> React.do
6062 hasFocus /\ setHasFocus <- useState' false
61− parentRef ∷ NodeRef <- useRef null
63+ inputBackupRef ∷ NodeRef <- useRef null
64+ let inputRef = props.inputRef ?|| inputBackupRef
6265 backupRef ∷ NodeRef <- useRef null
6366 let ref = forwardedRefAsMaybe propsRef # fromMaybe backupRef
6467 let
@@ -89,8 +92,8 @@ rawComponent =
8992 , renderLargeLabel
9093 , labelId
9194 , inputId: props.id ?|| "no-id" -- [TODO] Enforce ID?
92− , inputRef: ref
93− , parentRef
95+ , inputRef
96+ , parentRef:ref
9497 , labelText
9598 , background: props.background ?|| E.str colour.interfaceBackground
9699 }
@@ -138,7 +141,9 @@ rawComponent =
138141 ( RB.delete (Proxy ∷ _ "leading")
139142 >>> RB.delete (Proxy ∷ _ "label")
140143 >>> RB.delete (Proxy ∷ _ "trailing")
141− >>> RB.insert (Proxy ∷ _ "ref") ref
144+ >>> RB.delete (Proxy ∷ _ "inputRef")
145+ >>> RB.delete (Proxy ∷ _ "forceSmallLabel")
146+ >>> RB.insert (Proxy ∷ _ "ref") inputRef
142147 )
143148 )
144149 )
@@ -147,7 +152,7 @@ rawComponent =
147152 </ { hasFocus: hasFocus
148153 , isInvalid: aria # Object.lookup "invalid" <#> (_ == "true") # maybeToOp
149154 , css: props.css
150− , ref: parentRef
155+ , ref
151156 }
152157 /> [ leading # foldMap \l -> div </ {} /> [ l ]
153158 , theInput
src/Yoga/Block/Atom/Input/View/Container.purs−1
@@ -2,7 +2,6 @@ module Yoga.Block.Atom.Input.View.Container where
22
33import Yoga.Prelude.View
44import Data.Array as Array
5−import Data.Int (toNumber)
65import Data.Interpolate (i)
76import Foreign.Object as Object
87import Framer.Motion as M
src/Yoga/Block/Atom/Input/View/Label.purs+60−31
@@ -33,22 +33,33 @@ component =
3333 unsafePerformEffect
3434 $ reactComponent "Input Label" \props@{ inputRef, parentRef } -> React.do
3535 maybeOffsets /\ setOffsets <- useState' Nothing
36− parentBbox /\ setParentBbox <- useState' (zero ∷ DOMRect)
3736 -- this is so complicated because we need to take scale into account
38− useLayoutEffectOnce do
37+ useLayoutEffectAlways do
3938 inputWidth <- getOffsetWidthFromRef inputRef
4039 inputRect <- getBoundingBoxFromRef inputRef
4140 parentWidth <- getOffsetWidthFromRef parentRef
4241 parentRect <- getBoundingBoxFromRef parentRef
43− case sequenceRecord { inputWidth, parentWidth, inputRect, parentRect } of
42+ case sequenceRecord $ { inputWidth, parentWidth, inputRect, parentRect } of
4443 Just p -> do
45− setOffsets
46− $ Just
47− { largeLeft:
48− (p.inputRect.left * (p.inputWidth / p.inputRect.width))
49− - (p.parentRect.left * (p.parentWidth / p.parentRect.width))
50− , largeWidth: p.inputWidth
51− }
44+ let
45+ newOffsets =
46+ Just
47+ -- We cannot properly render even an ellipsised large label
48+ -- below 5 pixels
49+ if p.inputRect.width <= 5.0 then
50+ Nothing
51+ else do
52+ let
53+ parentScale = p.parentWidth / p.parentRect.width
54+ parentLeft = p.parentRect.left * parentScale
55+ inputScale = p.inputWidth / p.inputRect.width
56+ inputLeft = p.inputRect.left * inputScale
57+ Just
58+ { largeLeft: inputLeft - parentLeft
59+ , largeWidth: p.parentWidth
60+ }
61+ unless (newOffsets == maybeOffsets) do
62+ setOffsets newOffsets
5263 Nothing -> warn "couldn't get stuff"
5364 mempty
5465 -- UI
@@ -62,30 +73,48 @@ component =
6273 ]
6374 ]
6475 container = div </* { className: "ry-input-label-container", css: Style.labelContainer }
76+ renderLargeLabel = props.renderLargeLabel
6577 labelContainer =
6678 maybeOffsets
67− # foldMap \{ largeLeft, largeWidth } ->
68− M.div
69− </* { className: if props.renderLargeLabel then "ry-input-label-large" else "ry-input-label-small"
70− , layout: M.layout true
71− , layoutId: M.layoutId "ry-input-label"
72− , css:
73− if props.renderLargeLabel then
74− Style.labelLarge
75− { left: largeLeft
76− , width: largeWidth
79+ # foldMap case _ of
80+ Nothing ->
81+ M.div
82+ </* { className: "ry-input-label-small"
83+ , layout: M.layout true
84+ , layoutId: M.layoutId "ry-input-label"
85+ , css: Style.labelSmall props.background
86+ , transition: M.transition { duration: 0.18, ease: "easeOut" }
87+ , _data:
88+ Object.fromHomogeneous
89+ { "has-focus": show props.isFocussed
90+ , "invalid": show props.isInvalid
91+ , "required": show props.isRequired
7792 }
78− else
79− Style.labelSmall props.background
80− , transition: M.transition { duration: 0.18, ease: "easeOut" }
81− , _data:
82− Object.fromHomogeneous
83− { "has-focus": show props.isFocussed
84− , "invalid": show props.isInvalid
85− , "required": show props.isRequired
86− }
87− , initial: M.initial false
88− }
93+ , initial: M.initial false
94+ }
95+ Just { largeLeft, largeWidth } ->
96+ M.div
97+ </* { className:
98+ if props.renderLargeLabel then
99+ "ry-input-label-large"
100+ else
101+ "ry-input-label-small"
102+ , layout: M.layout true
103+ , layoutId: M.layoutId "ry-input-label"
104+ , css:
105+ if props.renderLargeLabel then
106+ Style.labelLarge { left: largeLeft, width: largeWidth }
107+ else
108+ Style.labelSmall props.background
109+ , transition: M.transition { duration: 0.18, ease: "easeOut" }
110+ , _data:
111+ Object.fromHomogeneous
112+ { "has-focus": show props.isFocussed
113+ , "invalid": show props.isInvalid
114+ , "required": show props.isRequired
115+ }
116+ , initial: M.initial false
117+ }
89118 labelSpan =
90119 M.span
91120 </ { onClick: props.onClickLargeLabel
src/Yoga/Block/Atom/Popover/Story.purs+338−262
@@ -2,9 +2,10 @@ module Yoga.Block.Atom.Popover.Story where
22
33import Prelude
44import Data.Array as Array
5−import Data.Foldable (traverse_)
5+import Data.Foldable (for_, traverse_)
66import Data.Maybe (Maybe(..))
77import Data.Monoid (guard)
8+import Data.Nullable (null)
89import Data.String as String
910import Data.String.NonEmpty.Internal (nes)
1011import Data.Symbol (SProxy(..))
@@ -17,17 +18,19 @@ import Framer.Motion as Motion
1718import React.Basic (JSX, ReactComponent, element, elementKeyed, fragment)
1819import React.Basic.DOM (CSS, css)
1920import React.Basic.DOM as R
20−import React.Basic.DOM.Events (targetValue)
21+import React.Basic.DOM.Events (preventDefault, targetValue)
2122import React.Basic.Emotion (str)
2223import React.Basic.Emotion as E
2324import React.Basic.Events (handler, handler_)
24−import React.Basic.Hooks (reactComponent)
25+import React.Basic.Hooks (reactComponent, readRefMaybe)
2526import React.Basic.Hooks as React
27+import React.Basic.Popper (nullRef)
2628import React.Basic.Popper.Placement.Types (Placement(..))
2729import React.Basic.Popper.Placement.Types as Placement
28−import React.Basic.Popper.Types (nullRef)
2930import Unsafe.Coerce (unsafeCoerce)
30−import Yoga ((/>), (</), (</>))
31+import Web.HTML.HTMLElement (focus)
32+import Web.HTML.HTMLElement as HTMLElement
33+import Yoga ((/>), (</), (</*), (</>))
3134import Yoga.Block as Block
3235import Yoga.Block.Atom.Icon as Icon
3336import Yoga.Block.Atom.Input as Input
@@ -36,6 +39,7 @@ import Yoga.Block.Atom.Popover as Popover
3639import Yoga.Block.Container.Style (colour)
3740import Yoga.Block.Container.Style as Styles
3841import Yoga.Block.Icon.SVG as Icons
42+import Yoga.Block.Internal.CSS (nest)
3943import Yoga.Block.Layout.Box as Box
4044import Yoga.Block.Layout.Stack as Stack
4145
@@ -65,276 +69,348 @@ popover = do
6569 ]
6670 ]
6771 where
68− mkAutosuggest ∷ Effect (ReactComponent {})
69− mkAutosuggest = do
70− motionStack <- Motion.custom Stack.component
71− motionPopover <- Motion.custom Popover.component
72− reactComponent "PopoverExample" \props -> React.do
73− referenceElement /\ setReferenceElement <- React.useState' nullRef
74− text /\ setText <- React.useState' ""
75− visible /\ setVisible <- React.useState' true
76− selectedAuthors /\ modSelectedAuthors <-
77− React.useState [ "William Shakespeare", "Agatha Christie" ]
78− let
79− matchingAuthors =
80− authors
81− # (_ Array.\\ selectedAuthors)
82− # Array.filter
83− ( \a ->
84− String.contains (String.Pattern (String.toLower text))
85− (String.toLower a)
86− )
87− let
88− inputWrapper = R.div' </ { ref: unsafeCoerce (mkEffectFn1 setReferenceElement) }
89− pill t =
90− Block.box </ { padding: str "var(--s-3)" }
91− /> [ Block.cluster
92− </ { style:
72+ mkAutosuggest ∷ Effect (ReactComponent {})
73+ mkAutosuggest = do
74+ motionStack <- Motion.custom Stack.component
75+ motionCluster <- Motion.custom Block.cluster
76+ motionPopover <- Motion.custom Popover.component
77+ reactComponent "PopoverExample" \props -> React.do
78+ referenceElement /\ setReferenceElement <- React.useState' nullRef
79+ inputRef <- React.useRef null
80+ text /\ setText <- React.useState' ""
81+ clicking /\ setClicking <- React.useState' false
82+ visible /\ setVisible <- React.useState' false
83+ selectedAuthors /\ modSelectedAuthors' <- React.useState []
84+ let
85+ modSelectedAuthors f = modSelectedAuthors' (f >>> Array.sort)
86+ matchingAuthors =
87+ authors
88+ # (_ Array.\\ selectedAuthors)
89+ # Array.filter
90+ ( \a ->
91+ String.contains (String.Pattern (String.toLower text))
92+ (String.toLower a)
93+ )
94+ let
95+ inputWrapper = R.div' </ { ref: unsafeCoerce (mkEffectFn1 setReferenceElement) }
96+ pill t =
97+ animatePresence </ {}
98+ /> [ motionCluster
99+ </ Motion.withMotion
100+ { style:
93101 css
94− { background: colour.highlight
95− , fontSize: "var(--s-1)"
96− , space: "var(--s-5)"
102+ { fontSize: "var(--s-1)"
97103 , borderRadius: "var(--s-2)"
104+ , padding: "var(--s-2)"
105+ , background: colour.highlightAlpha33
98106 }
107+ , space: "4px"
108+ , justify: "flex-end"
99109 }
100− /> [ R.text t
101− , Block.centre
102− </ { style:
103− css
104− { background: "hotpink"
105− }
106− , andText: true
107− }
108− /> [ Icon.component
109− </> { icon: Icons.cross, size: str "var(--s-1)"
110− }
111− ]
112− ]
113− ]
114− pills = pill <$> selectedAuthors
115− leading = Block.cluster </ {} /> pills
116− input =
117− Input.component
118− </> { id: "author"
119− , type: HTMLInput.Text
120− , label: nes (SProxy ∷ _ "Author")
121− , placeholder: "e.g. William Shakespeare"
122− , value: text
123− , style: css { width: "280px" }
124− , onChange: handler targetValue $ traverse_ setText
125− , onBlur: handler_ (setVisible false)
126− , onFocus: handler_ (setVisible true)
127− , leading
128− , autoComplete: "off"
129− }
130− pop children =
131− elementKeyed motionPopover
132− $ Motion.motion
133− { initial:
134− css { maxHeight: 30, opacity: 0.0 }
135− , exit:
136− css { maxHeight: 30, opacity: 0.0 }
137− , animate:
138− css
139− { maxHeight: 400
140− , opacity: 1
141− }
142− , transition:
143− Motion.transition
144− $ { type: "spring"
145− , stiffness: 200.0
146− , damping: 20.0
147− , mass: 0.7
148− , delay: 0.3
149− }
150− }
151− { placement: Placement Placement.Bottom (Just Placement.Start)
152− , referenceElement
153− , style:
154− css
155− { boxShadow: "0px 9px 12px rgba(40,40,40,0.5)"
156− , background: colour.interfaceBackground
157− , borderRadius: "9px"
158− , borderTop: "solid 1px " <> colour.interfaceBackgroundHighlight
159− , borderBottom: "solid 1px " <> colour.interfaceBackgroundShadow
160− , overflowY: "scroll"
161− , maxHeight: "400px"
162− , width: "300px"
110+ { initial: Motion.initial $ css { scale: 0.95, opacity: 0.5 }
111+ , animate: Motion.animate $ css { scale: 1, opacity: 1 }
112+ , exit: Motion.exit $ css { scale: 0.7, opacity: 0 }
113+ , transition: Motion.transition { duration: 0.4 }
163114 }
164− , key: "popover"
165− , children
166− }
167− box =
168− Box.component
169− </ { style:
115+ /> [ R.text t
116+ , Motion.div
117+ </ { onClick: handler_ (modSelectedAuthors (Array.delete t)) }
118+ /> [ Icon.component
119+ </> { icon: Icons.cross, size: str "var(--s-1)" }
120+ ]
121+ ]
122+ ]
123+ pills = pill <$> selectedAuthors
124+ leading =
125+ motionCluster
126+ </ Motion.withMotion
127+ { style:
128+ css
129+ { marginLeft: "-6px"
130+ }
131+ , space: "var(--s-5)"
132+ , justify: "flex-start"
133+ }
134+ {}
135+ /> pills
136+ input =
137+ Input.component
138+ </> { id: "author"
139+ , type: HTMLInput.Text
140+ , label: nes (SProxy ∷ _ "Author")
141+ , placeholder: "e.g. William Shakespeare"
142+ , forceSmallLabel: Array.length selectedAuthors > 0
143+ , inputRef
144+ , value:
145+ if text /= mempty then
146+ text
147+ else
148+ if selectedAuthors /= mempty then
149+ " "
150+ else
151+ text
152+ , onChange: handler targetValue $ traverse_ setText
153+ , onBlur: handler preventDefault (const (setVisible false))
154+ , onFocus: handler_ (setVisible true)
155+ , leading
156+ , autoComplete: "off"
157+ , css: E.css { width: E.percent 100.0 }
158+ }
159+ pop children =
160+ elementKeyed motionPopover
161+ $ Motion.motion
162+ { initial:
163+ css { maxHeight: 0, opacity: 1.0 }
164+ , exit:
165+ css { maxHeight: 0, opacity: 1.0 }
166+ , animate:
170167 css
171− {}
168+ { maxHeight: 400
169+ , opacity: 1
170+ }
171+ , transition:
172+ Motion.transition
173+ $ { type: "spring"
174+ , stiffness: 200.0
175+ , damping: 20.0
176+ , mass: 0.7
177+ , delay: 0.3
178+ }
172179 }
173− stack =
174− motionStack
175− </ Motion.motion {}
176− -- { variants: Motion.variants containerVariants
177− -- , animate: Motion.animate (if visible then containerVariant.visible else containerVariant.hidden)
178− -- , layout: Motion.layout true
179− -- }
180− {}
181− entry a =
182− Motion.div
183− </ { key: a
184− -- , layout: Motion.layout true
185− -- , variants: Motion.variants itemVariants
186− , onClick: handler_ (modSelectedAuthors (_ `Array.snoc` a))
180+ { placement: Placement Placement.Bottom (Just Placement.End)
181+ , referenceElement
182+ , style:
183+ css
184+ { boxShadow: "0px 2px 4px rgba(40,40,40,0.5)"
185+ , background: colour.interfaceBackground
186+ , borderRadius: "0 0 9px 9px"
187+ , borderTop: "solid 1px " <> colour.interfaceBackgroundHighlight
188+ , borderBottom: "solid 1px " <> colour.interfaceBackgroundShadow
189+ , overflowY: "scroll"
190+ , maxHeight: "400px"
191+ , margin: "0"
192+ , padding: "0"
193+ }
194+ , key: "popover"
195+ , children
187196 }
188− /> [ R.text a ]
189− pure
190− $ fragment
191− [ inputWrapper [ input ]
192− , animatePresence
193− </ {}
194− /> [ guard visible
195− $ pop
196− [ box
197− [ stack (entry <$> matchingAuthors)
198− ]
199− ]
200− ]
201− ]
202− where
203− itemVariants ∷
204− { hidden ∷ CSS
205− , visible ∷ CSS
206− }
207− itemVariants =
208− { visible:
209− css
210− { left: 0
197+ box =
198+ Box.component
199+ </ { padding: E.str "0"
211200 }
212− , hidden:
213− css
214− { left: -20
201+ stack =
202+ motionStack
203+ </ Motion.withMotion
204+ { space: E.str "0"
205+ }
206+ { variants: Motion.variants containerVariants
207+ , initial: Motion.initial containerVariant.hidden
208+ , animate: Motion.animate containerVariant.visible
209+ , exit: Motion.exit containerVariant.exit
210+ }
211+ entry a =
212+ Motion.div
213+ </* { key: a
214+ , variants: Motion.variants itemVariants
215+ , css:
216+ E.css
217+ { "&:hover":
218+ nest
219+ { borderLeft: E.str $ "calc(var(--s-1) / 2) solid " <> colour.highlight
220+ , paddingLeft: E.str "calc(var(--s-1) / 2)"
221+ , background: E.str colour.backgroundLayer1
222+ }
223+ , padding: E.str "var(--s-1)"
224+ , borderBottom: E.str $ "solid 1px " <> colour.backgroundLayer1
225+ , background: E.str colour.backgroundLayer3
226+ , margin: E.str "0"
227+ , fontSize: E.str "calc(var(--s0) * 0.75)"
228+ }
229+ , className: "item"
230+ , onClick:
231+ handler_ do
232+ maybeRefElem <- readRefMaybe inputRef
233+ for_ (maybeRefElem >>= HTMLElement.fromNode) focus
234+ modSelectedAuthors (_ `Array.union` [ a ])
235+ , onMouseDown: handler_ (setClicking true)
236+ , onMouseUp: handler_ (setClicking false)
215237 }
238+ /> [ R.text a ]
239+ pure
240+ $ fragment
241+ [ inputWrapper [ input ]
242+ , animatePresence
243+ </ {}
244+ /> [ guard visible
245+ $ pop
246+ [ box
247+ [ stack (entry <$> matchingAuthors)
248+ ]
249+ ]
250+ ]
251+ ]
252+ where
253+ itemVariants ∷
254+ { hidden ∷ CSS
255+ , visible ∷ CSS
256+ , exit ∷ CSS
257+ }
258+ itemVariants =
259+ { visible:
260+ css
261+ { x: 0
262+ , opacity: 1
216263 }
217−
218− containerVariants ∷
219− { hidden ∷ CSS
220− , visible ∷ CSS
264+ , hidden:
265+ css
266+ { opacity: 0.8
267+ , x: -20
221268 }
222− containerVariants =
223− { visible:
224− css
225− { transition: { when: "beforeChildren" }
226− , staggerChildren: 0.3
227− , opacity: 1
228− }
229− , hidden:
230− css
231− { transition: { when: "afterChildren" }
232− , opacity: 0
233− }
269+ , exit:
270+ css
271+ { opacity: 1.0
234272 }
273+ }
274+
275+ itemVariant ∷
276+ { hidden ∷ Motion.VariantLabel
277+ , visible ∷ Motion.VariantLabel
278+ , exit ∷ Motion.VariantLabel
279+ }
280+ itemVariant = Motion.makeVariantLabels itemVariants
235281
236− containerVariant ∷
237− { hidden ∷ Motion.VariantLabel
238− , visible ∷ Motion.VariantLabel
282+ containerVariants ∷
283+ { hidden ∷ CSS
284+ , visible ∷ CSS
285+ , exit ∷ CSS
286+ }
287+ containerVariants =
288+ { visible:
289+ css
290+ { transition:
291+ { when: "beforeChildren"
292+ , staggerChildren: 0.02
293+ }
294+ , opacity: 1
295+ }
296+ , hidden:
297+ css
298+ { transition:
299+ { when: "afterChildren"
300+ }
301+ , opacity: 0
302+ }
303+ , exit:
304+ css
305+ { opacity: 0
306+ , height: 0
239307 }
240− containerVariant = Motion.makeVariantLabels containerVariants
308+ }
241309
242− authors =
243− [ "William Shakespeare"
244− , "Agatha Christie"
245− , "Barbara Cartland"
246− , "Danielle Steel"
247− , "Harold Robbins"
248− , "Georges Simenon"
249− , "Enid Blyton"
250− , "Sidney Sheldon"
251− , "J. K. Rowling"
252− , "Gilbert Patten"
253− , "Dr. Seuss"
254− , "Eiichiro Oda"
255− , "Leo Tolstoy"
256− , "Corín Tellado"
257− , "Jackie Collins"
258− , "Horatio Alger"
259− , "R. L. Stine"
260− , "Dean Koontz"
261− , "Nora Roberts"
262− , "Alexander Pushkin"
263− , "Stephen King"
264− , "Paulo Coelho"
265− , "Jeffrey Archer"
266− , "Louis L'Amour"
267− , "Jirō Akagawa"
268− , "René Goscinny"
269− , "Erle Stanley Gardner"
270− , "Edgar Wallace"
271− , "Jin Yong"
272− , "Janet Dailey"
273− , "Robert Ludlum"
274− , "Akira Toriyama"
275− , "Osamu Tezuka"
276− , "James Patterson"
277− , "Frédéric Dard"
278− , "Stan and Jan Berenstain"
279− , "Roald Dahl"
280− , "John Grisham"
281− , "Zane Grey"
282− , "Irving Wallace"
283− , "J. R. R. Tolkien"
284− , "Masashi Kishimoto"
285− , "Karl May"
286− , "Carter Brown"
287− , "Mickey Spillane"
288− , "C. S. Lewis"
289− , "Kyotaro Nishimura"
290− , "Mitsuru Adachi"
291− , "Rumiko Takahashi"
292− , "Gosho Aoyama"
293− , "Dan Brown"
294− , "Ann M. Martin"
295− , "Ryōtarō Shiba"
296− , "Arthur Hailey"
297− , "Gérard de Villiers"
298− , "Beatrix Potter"
299− , "Michael Crichton"
300− , "Richard Scarry"
301− , "Clive Cussler"
302− , "Alistair MacLean"
303− , "Ken Follett"
304− , "Astrid Lindgren"
305− , "Debbie Macomber"
306− , "EL James"
307− , "Tite Kubo"
308− , "Eiji Yoshikawa"
309− , "Catherine Cookson"
310− , "Stephenie Meyer"
311− , "Norman Bridwell"
312− , "David Baldacci"
313− , "Nicholas Sparks"
314− , "Hirohiko Araki"
315− , "Evan Hunter"
316− , "Andrew Neiderman"
317− , "Roger Hargreaves"
318− , "Anne Rice"
319− , "Robin Cook"
320− , "Wilbur Smith"
321− , "Erskine Caldwell"
322− , "Judith Krantz"
323− , "Eleanor Hibbert"
324− , "Lewis Carroll"
325− , "Denise Robins"
326− , "Cao Xueqin"
327− , "Ian Fleming"
328− , "Hermann Hesse"
329− , "Rex Stout"
330− , "Anne Golon"
331− , "Frank G. Slaughter"
332− , "Edgar Rice Burroughs"
333− , "John Creasey"
334− , "James A. Michener"
335− , "Yasuo Uchida"
336− , "Seiichi Morimura"
337− , "Mary Higgins Clark"
338− , "Penny Jordan"
339− , "Patricia Cornwell"
340− ]
310+ containerVariant ∷
311+ { hidden ∷ Motion.VariantLabel
312+ , visible ∷ Motion.VariantLabel
313+ , exit ∷ Motion.VariantLabel
314+ }
315+ containerVariant = Motion.makeVariantLabels containerVariants
316+
317+ authors =
318+ Array.sort
319+ [ "William Shakespeare"
320+ , "Agatha Christie"
321+ , "Barbara Cartland"
322+ , "Danielle Steel"
323+ , "Harold Robbins"
324+ , "Georges Simenon"
325+ , "Enid Blyton"
326+ , "Sidney Sheldon"
327+ , "J. K. Rowling"
328+ , "Gilbert Patten"
329+ , "Dr. Seuss"
330+ , "Eiichiro Oda"
331+ , "Leo Tolstoy"
332+ , "Corín Tellado"
333+ , "Jackie Collins"
334+ , "Horatio Alger"
335+ , "R. L. Stine"
336+ , "Dean Koontz"
337+ , "Nora Roberts"
338+ , "Alexander Pushkin"
339+ , "Stephen King"
340+ , "Paulo Coelho"
341+ , "Jeffrey Archer"
342+ , "Louis L'Amour"
343+ , "Jirō Akagawa"
344+ , "René Goscinny"
345+ , "Erle Stanley Gardner"
346+ , "Edgar Wallace"
347+ , "Jin Yong"
348+ , "Janet Dailey"
349+ , "Robert Ludlum"
350+ , "Akira Toriyama"
351+ , "Osamu Tezuka"
352+ , "James Patterson"
353+ , "Frédéric Dard"
354+ , "Stan and Jan Berenstain"
355+ , "Roald Dahl"
356+ , "John Grisham"
357+ , "Zane Grey"
358+ , "Irving Wallace"
359+ , "J. R. R. Tolkien"
360+ , "Masashi Kishimoto"
361+ , "Karl May"
362+ , "Carter Brown"
363+ , "Mickey Spillane"
364+ , "C. S. Lewis"
365+ , "Kyotaro Nishimura"
366+ , "Mitsuru Adachi"
367+ , "Rumiko Takahashi"
368+ , "Gosho Aoyama"
369+ , "Dan Brown"
370+ , "Ann M. Martin"
371+ , "Ryōtarō Shiba"
372+ , "Arthur Hailey"
373+ , "Gérard de Villiers"
374+ , "Beatrix Potter"
375+ , "Michael Crichton"
376+ , "Richard Scarry"
377+ , "Clive Cussler"
378+ , "Alistair MacLean"
379+ , "Ken Follett"
380+ , "Astrid Lindgren"
381+ , "Debbie Macomber"
382+ , "EL James"
383+ , "Tite Kubo"
384+ , "Eiji Yoshikawa"
385+ , "Catherine Cookson"
386+ , "Stephenie Meyer"
387+ , "Norman Bridwell"
388+ , "David Baldacci"
389+ , "Nicholas Sparks"
390+ , "Hirohiko Araki"
391+ , "Evan Hunter"
392+ , "Andrew Neiderman"
393+ , "Roger Hargreaves"
394+ , "Anne Rice"
395+ , "Robin Cook"
396+ , "Wilbur Smith"
397+ , "Erskine Caldwell"
398+ , "Judith Krantz"
399+ , "Eleanor Hibbert"
400+ , "Lewis Carroll"
401+ , "Denise Robins"
402+ , "Cao Xueqin"
403+ , "Ian Fleming"
404+ , "Hermann Hesse"
405+ , "Rex Stout"
406+ , "Anne Golon"
407+ , "Frank G. Slaughter"
408+ , "Edgar Rice Burroughs"
409+ , "John Creasey"
410+ , "James A. Michener"
411+ , "Yasuo Uchida"
412+ , "Seiichi Morimura"
413+ , "Mary Higgins Clark"
414+ , "Penny Jordan"
415+ , "Patricia Cornwell"
416+ ]
src/Yoga/Block/Atom/Popover/View.purs+3−1
@@ -1,12 +1,13 @@
11module Yoga.Block.Atom.Popover.View (component, MandatoryProps, Props, PropsF) where
22
33import Yoga.Prelude.View
4+
45import Effect.Uncurried (mkEffectFn1)
56import React.Basic.Hooks as React
7+import React.Basic.Popper (modifierMatchReferenceSize, modifierOffset, nullRef)
68import React.Basic.Popper.Hook (usePopper)
79import React.Basic.Popper.Placement.Types (Placement)
810import React.Basic.Popper.Placement.Types as Placement
9−import React.Basic.Popper.Types (modifierOffset, nullRef)
1011import Unsafe.Coerce (unsafeCoerce)
1112import Yoga.Block.Atom.Popover.Style as Style
1213
@@ -42,6 +43,7 @@ rawComponent =
4243 usePopper props.referenceElement popperElement
4344 { modifiers:
4445 [ modifierOffset { x: 0.0, y: 0.0 }
46+ , modifierMatchReferenceSize
4547 ]
4648 , placement: props.placement <#> Placement.render # unsafeUnOptional
4749 }
src/Yoga/Block/Atom/Range/Style.purs+1
@@ -4,6 +4,7 @@ import Yoga.Prelude.Style
44import Data.Interpolate (i)
55import Yoga.Block.Container.Style (colour)
66
7+type Props :: forall k. (Type -> k) -> Row k -> Row k
78type Props f r =
89 ( css ∷ f Style
910 | r
src/Yoga/Block/Atom/Segmented/Style.purs+1
@@ -4,6 +4,7 @@ import Yoga.Prelude.Style
44import Data.Interpolate (i)
55import Yoga.Block.Container.Style (colour)
66
7+type Props :: forall k. (Type -> k) -> Row k -> Row k
78type Props f r =
89 ( css ∷ f Style
910 | r
src/Yoga/Block/Atom/Toggle.purs+1−1
@@ -2,4 +2,4 @@ module Yoga.Block.Atom.Toggle
22 ( module Yoga.Block.Atom.Toggle.View
33 ) where
44
5−import Yoga.Block.Atom.Toggle.View (component, Props, MandatoryProps)
5+import Yoga.Block.Atom.Toggle.View (component, Props)
src/Yoga/Block/Atom/Toggle/Spec.purs+2−4
@@ -11,15 +11,13 @@ spec =
1111 it "renders without errors" do
1212 void
1313 $ renderComponent Toggle.component
14− { togglePosition: ToggleIsLeft
15− , setTogglePosition: mempty
14+ { value: ToggleIsLeft
1615 , ariaLabel: "Example Toggle"
1716 }
1817 it "has an aria-label" do
1918 { findByRole } <-
2019 renderComponent Toggle.component
21− { togglePosition: ToggleIsLeft
22− , setTogglePosition: mempty
20+ { value: ToggleIsLeft
2321 , ariaLabel: "Example Toggle"
2422 }
2523 elem <- findByRole "switch"
src/Yoga/Block/Atom/Toggle/Story.purs+4−4
@@ -39,8 +39,8 @@ toggle = do
3939 togglePosition /\ setTogglePosition <- React.useState' ToggleIsRight
4040 pure
4141 $ element Toggle.component
42− { togglePosition
43− , setTogglePosition
42+ { value: togglePosition
43+ , onChange: setTogglePosition
4444 , ariaLabel: "dark-light-toggle"
4545 }
4646
@@ -51,9 +51,9 @@ toggle = do
5151 let
5252 content =
5353 element Toggle.component
54− { togglePosition: togglePosition
54+ { value: togglePosition
5555 , ariaLabel: "dark-light-toggle"
56− , setTogglePosition:
56+ , onChange:
5757 \newTogglePosition -> do
5858 setTogglePosition newTogglePosition
5959 setTheme case newTogglePosition of
src/Yoga/Block/Atom/Toggle/Style.purs+1
@@ -3,6 +3,7 @@ module Yoga.Block.Atom.Toggle.Style where
33import Yoga.Prelude.Style
44import Yoga.Block.Container.Style (colour)
55
6+type Props :: forall k. (Type -> k) -> Row k -> Row k
67type Props f r =
78 ( css ∷ f Style
89 , backgroundLeft ∷ f Color
src/Yoga/Block/Atom/Toggle/View.purs+24−24
@@ -1,6 +1,5 @@
11module Yoga.Block.Atom.Toggle.View
22 ( component
3− , MandatoryProps
43 , Props
54 , PropsF
65 ) where
@@ -28,14 +27,10 @@ type PropsF f =
2827 ( className ∷ f String
2928 , left ∷ f JSX
3029 , right ∷ f JSX
31− | Style.Props f (MandatoryProps InputWritableProps)
32− )
33−
34−type MandatoryProps r =
35− ( togglePosition ∷ TogglePosition
36− , setTogglePosition ∷ TogglePosition -> Effect Unit
37− , ariaLabel ∷ String
38− | r
30+ , ariaLabel ∷ f String
31+ , value ∷ f TogglePosition
32+ , onChange ∷ f (TogglePosition -> Effect Unit)
33+ | Style.Props f (InputWritableProps)
3934 )
4035
4136data TappingState
@@ -61,7 +56,7 @@ type Props =
6156type PropsOptional =
6257 PropsF OptionalProp
6358
64−component ∷ ∀ p p_. Union p p_ Props => ReactComponent { | MandatoryProps p }
59+component ∷ ∀ p p_. Union p p_ Props => ReactComponent { | p }
6560component = rawComponent
6661
6762rawComponent ∷ ∀ p. ReactComponent (Record p)
@@ -69,6 +64,11 @@ rawComponent =
6964 mkForwardRefComponent "Toggle" do
7065 \(props ∷ { | PropsOptional }) ref -> React.do
7166 let disabled = props.disabled
67+ togglePosition /\ setTogglePositionState <- useState' (props.value ?|| ToggleIsLeft)
68+ let
69+ setTogglePosition newPos = do
70+ for_ props.onChange (\f -> f newPos)
71+ setTogglePositionState newPos
7272 buttonRef <- useRef null
7373 toggleRef <- useRef null
7474 tapState <- useRef TapNotAllowed
@@ -82,9 +82,9 @@ rawComponent =
8282 maybeBbox <- getBoundingBoxFromRef buttonRef
8383 for_ maybeBbox \bbox -> do
8484 if endX - startX <= (bbox.left - startX) + (bbox.width / 2.0) then do
85− props.setTogglePosition ToggleIsLeft
85+ setTogglePosition ToggleIsLeft
8686 else do
87− props.setTogglePosition ToggleIsRight
87+ setTogglePosition ToggleIsRight
8888 mempty
8989 let
9090 buttonVariants =
@@ -99,14 +99,14 @@ rawComponent =
9999 [ animateTextPresence
100100 [ textContainer
101101 [ textOnContainer
102− [ guard (props.togglePosition == ToggleIsRight)
102+ [ guard (togglePosition == ToggleIsRight)
103103 $ textOn
104104 [ props.left
105105 ?|| (Icon.component </> { icon: SVGIcon.on, stroke: Style.successTextColour })
106106 ]
107107 ]
108108 , textOffContainer
109− [ guard (props.togglePosition == ToggleIsLeft)
109+ [ guard (togglePosition == ToggleIsLeft)
110110 $ textOff
111111 [ props.right
112112 ?|| (Icon.component </> { icon: SVGIcon.off, stroke: Style.disabledTextColour })
@@ -124,22 +124,22 @@ rawComponent =
124124 , transition: Motion.transition { type: "tween", duration: 0.33, ease: "easeOut" }
125125 , variants: Motion.variants buttonVariants
126126 , animate:
127− Motion.animate case props.togglePosition of
127+ Motion.animate case togglePosition of
128128 ToggleIsRight -> buttonVariant.right
129129 ToggleIsLeft -> buttonVariant.left
130− , value: show props.togglePosition
131− , onClick: handler preventDefault \_ -> props.setTogglePosition (flipToggle props.togglePosition)
130+ , value: show togglePosition
131+ , onClick: handler preventDefault \_ -> setTogglePosition (flipToggle togglePosition)
132132 , style: props.style
133133 , _data: Object.singleton "testid" "toggle-testid"
134134 , role: "switch"
135135 , _aria:
136136 Object.fromHomogeneous
137137 { checked:
138− case props.togglePosition of
138+ case togglePosition of
139139 ToggleIsLeft -> "false"
140140 ToggleIsRight -> "true"
141− , label: props.ariaLabel
142141 }
142+ <>? (props.ariaLabel <#> Object.singleton "label")
143143 , ref: buttonRef
144144 , children
145145 }
@@ -179,8 +179,8 @@ rawComponent =
179179 toggleCircle
180180 </> { buttonRef
181181 , toggleRef
182− , togglePosition: props.togglePosition
183− , setTogglePosition: props.setTogglePosition
182+ , togglePosition
183+ , onChange: setTogglePosition
184184 , dragState
185185 , setDragState
186186 , tapState
@@ -189,7 +189,7 @@ rawComponent =
189189
190190toggleCircle ∷
191191 ReactComponent
192− { setTogglePosition ∷ TogglePosition -> Effect Unit
192+ { onChange ∷ TogglePosition -> Effect Unit
193193 , toggleRef ∷ NodeRef
194194 , buttonRef ∷ NodeRef
195195 , togglePosition ∷ TogglePosition
@@ -201,7 +201,7 @@ toggleCircle =
201201 unsafePerformEffect
202202 $ reactComponent "ToggleCircle" do
203203 \( { togglePosition
204− , setTogglePosition
204+ , onChange
205205 , toggleRef
206206 , buttonRef
207207 , dragState
@@ -264,7 +264,7 @@ toggleCircle =
264264 Motion.onTap \_ pi -> do
265265 ts <- readRef tapState
266266 case ts of
267− TapAllowed -> setTogglePosition (flipToggle togglePosition)
267+ TapAllowed -> onChange (flipToggle togglePosition)
268268 _ -> mempty
269269 mempty
270270 , onTapCancel:
src/Yoga/Block/Atom/Tooltip/Style.purs+1
@@ -3,6 +3,7 @@ module Yoga.Block.Atom.Tooltip.Style where
33import Yoga.Prelude.Style
44import Yoga.Block.Container.Style (colour)
55
6+type Props :: forall k. (Type -> k) -> Row k -> Row k
67type Props f r =
78 ( css ∷ f Style
89 , backgroundColour ∷ f Color
src/Yoga/Block/Atom/Tooltip/View.purs+1−1
@@ -9,7 +9,7 @@ import React.Basic.DOM as R
99import React.Basic.Hooks as React
1010import React.Basic.Hooks.Aff (useAff)
1111import React.Basic.Popper.Hook (usePopper)
12−import React.Basic.Popper.Types (modifierArrow, modifierOffset, nullRef)
12+import React.Basic.Popper (modifierArrow, modifierOffset, nullRef)
1313import Unsafe.Coerce (unsafeCoerce)
1414import Yoga.Block.Atom.Tooltip.Style as Style
1515
src/Yoga/Block/Container/Style.purs+18−5
@@ -2,7 +2,7 @@ module Yoga.Block.Container.Style where
22
33import Yoga.Prelude.Style
44import Color as Color
5−import Data.Symbol (class IsSymbol, SProxy, reflectSymbol)
5+import Data.Symbol (class IsSymbol, reflectSymbol)
66import Effect.Uncurried (EffectFn2, runEffectFn2)
77import Foreign.Object (Object)
88import Foreign.Object as Object
@@ -14,7 +14,7 @@ import Web.Event.Internal.Types (EventTarget)
1414import Web.HTML (Window, window)
1515import Web.HTML.HTMLDocument as HTMLDocument
1616import Web.HTML.Window (document)
17−import Type.Proxy (Proxy(..))
17+import Type.Proxy (Proxy)
1818
1919data DarkOrLightMode
2020 = DarkMode
@@ -222,6 +222,9 @@ defaultColours =
222222 , backgroundLayer4: darken 0.02 >>> rotateHue (0.0) >>> desaturate 0.05 $ lightBg
223223 , backgroundLayer5: lightBg
224224 , highlight
225+ , highlightAlpha33: highlightBase 0.33
226+ , highlightAlpha50: highlightBase 0.50
227+ , highlightAlpha67: highlightBase 0.67
225228 , highlightDarker: withAlpha 0.15 (Color.darken 0.5 highlight)
226229 , highlightDisabled: (desaturate 0.80 >>> lighten 0.28) highlight
227230 , highlightLighter: withAlpha 0.2 (Color.lighten 0.5 highlight)
@@ -256,6 +259,9 @@ defaultColours =
256259 , backgroundLayer4: lighten 0.21 >>> saturate 0.08 $ darkBg
257260 , backgroundLayer5: lighten 0.30 >>> saturate 0.07 $ darkBg
258261 , highlight: highlightDark
262+ , highlightAlpha33: highlightDarkBase 0.33
263+ , highlightAlpha50: highlightDarkBase 0.50
264+ , highlightAlpha67: highlightDarkBase 0.67
259265 , highlightDarker: withAlpha 0.4 (Color.darken 0.5 highlightDark)
260266 , highlightDisabled: (desaturate 0.76 >>> darken 0.32) highlightDark
261267 , highlightLighter: withAlpha 0.2 (Color.lighten 0.5 highlightDark)
@@ -285,9 +291,13 @@ defaultColours =
285291 where
286292 darkBg = Color.hsl 240.0 0.07 0.10
287293
288− highlight = Color.hsl 275.0 0.82 0.4
294+ highlightBase = Color.hsla 275.0 0.82 0.4
289295
290− highlightDark = Color.hsl 265.0 1.00 0.6
296+ highlight = highlightBase 1.0
297+
298+ highlightDarkBase = Color.hsla 265.0 1.00 0.6
299+
300+ highlightDark = Color.hsla 265.0 1.00 0.6 1.0
291301
292302 highlightText = Color.rgb 0xFF 0xFF 0xFF
293303
@@ -323,7 +333,7 @@ defaultColours =
323333
324334 text = Color.rgb 16 16 32
325335
326− textDark = Color.rgb 220 210 220
336+ textDark = Color.rgb 230 230 250
327337
328338type FlatTheme a =
329339 { background ∷ a
@@ -344,6 +354,9 @@ type FlatTheme a =
344354 , inputBorder ∷ a
345355 , link ∷ a
346356 , highlight ∷ a
357+ , highlightAlpha33 ∷ a
358+ , highlightAlpha50 ∷ a
359+ , highlightAlpha67 ∷ a
347360 , highlightRotatedBackwards ∷ a
348361 , highlightRotatedForwards ∷ a
349362 , highlightDarker ∷ a
src/Yoga/Block/Internal.purs+1−1
@@ -59,7 +59,7 @@ import Web.DOM (Node)
5959import Web.HTML.HTMLElement (DOMRect, HTMLElement, getBoundingClientRect, offsetHeight, offsetWidth)
6060import Web.HTML.HTMLElement as HTMLElement
6161import Yoga.Block.Internal.CSS (_0)
62−import Yoga.Block.Internal.OptionalProp (Id, OptionalProp(..), appendIfDefined, asOptional, composeHandler, getOr, getOrFlipped, ifTrue, isTruthy, maybeToOp, opToMaybe, setOrDelete, unsafeUnMaybe, unsafeUnOptional, (<>?), (?||))
62+import Yoga.Block.Internal.OptionalProp (OptionalProp(..), Id, appendIfDefined, asOptional, composeHandler, getOr, getOrFlipped, ifTrue, isTruthy, maybeToOp, opToMaybe, setOrDelete, unsafeUnMaybe, unsafeUnOptional, (<>?), (?||))
6363
6464foreign import mkForwardRefComponent ∷
6565 ∀ inputProps props a hooks.
src/Yoga/Block/Layout/Cluster/Style.purs+17−16
@@ -2,6 +2,7 @@ module Yoga.Block.Layout.Cluster.Style where
22
33import Yoga.Prelude.Style
44
5+type Props :: forall k. (Type -> k) -> Row k -> Row k
56type Props f r =
67 ( css ∷ f Style
78 , space ∷ f String
@@ -13,20 +14,20 @@ type Props f r =
1314cluster ∷ ∀ p. { | Props OptionalProp p } -> Style
1415cluster props = styles <>? props.css
1516 where
16− space = props.space ?|| "var(--s1)"
17+ space = props.space ?|| "var(--s1)"
1718
18− styles =
19− css
20− { "& > *":
21− nest
22− { display: flex
23− , flexWrap: wrap
24− , alignItems: (str <$> props.align) ?|| center
25− , justifyContent: (str <$> props.justify) ?|| flexStart
26− , margin: "calc(" <> space <> "/2 * -1)" # str
27− }
28− , "& > * > *":
29− nest
30− { margin: "calc(" <> space <> "/2)" # str
31− }
32− }
19+ styles =
20+ css
21+ { "& > *":
22+ nest
23+ { display: flex
24+ , flexWrap: wrap
25+ , alignItems: (str <$> props.align) ?|| center
26+ , justifyContent: (str <$> props.justify) ?|| flexStart
27+ , margin: "calc(" <> space <> " / 2 * -1)" # str
28+ }
29+ , "& > * > *":
30+ nest
31+ { margin: "calc(" <> space <> " / 2)" # str
32+ }
33+ }
src/Yoga/Block/Layout/Cluster/View.purs+1−1
@@ -26,5 +26,5 @@ rawComponent =
2626 $ emotionDiv ref props
2727 { className: "ry-cluster " <>? props.className
2828 , css: Style.cluster props
29− , children: [ R.div' </ {} /> props.children ]
29+ , children: [ R.div_ props.children ]
3030 }
src/Yoga/Block/Layout/Switcher/Story.purs+3−3
@@ -46,10 +46,10 @@ switcher =
4646 5
4747 ]
4848 ]
49− , R.h2_ [ R.text "Limit of 5 and 6 children" ]
49+ , R.h2_ [ R.text "Limit of 4 and 5 children" ]
5050 , el Switcher.component
5151 { style: css { backgroundColor: "darkslateblue" }
52− , limit: 200
52+ , limit: 4
5353 , space: "0"
5454 }
5555 [ R.div_
@@ -58,7 +58,7 @@ switcher =
5858 , style: css { backgroundColor: "teal" }
5959 }
6060 `power`
61− 6
61+ 5
6262 ]
6363 ]
6464 ]
src/Yoga/Block/Molecule/TableOfContents/Story.purs+2−11
@@ -3,27 +3,18 @@ module Yoga.Block.Molecule.TableOfContents.Story where
33import Prelude
44import Control.Comonad.Cofree as Cofree
55import Data.Array (foldMap)
6−import Data.Array as Array
7−import Data.Either (Either(..), hush)
86import Data.Foldable (fold)
97import Data.Tuple.Nested ((/\))
10−import Data.FoldableWithIndex (foldMapWithIndex)
11−import Data.List ((:))
12−import Data.List as List
13−import Data.List.Lazy (Step(..))
14−import Data.Maybe (Maybe(..))
158import Data.Monoid (power)
16−import Data.Traversable (for_, sequence, traverse)
9+import Data.Traversable (traverse)
1710import Data.Tree (Forest, Tree, mkLeaf, mkTree)
18−import Data.Tree.Zipper (fromTree)
19−import Data.Tree.Zipper as Zipper
2011import Effect (Effect)
2112import Effect.Unsafe (unsafePerformEffect)
2213import React.Basic (JSX, element, fragment)
2314import React.Basic.DOM as R
2415import React.Basic.Emotion as E
2516import React.Basic.Hooks as React
26−import Yoga ((/>), (</), (</>))
17+import Yoga ((/>), (</))
2718import Yoga.Block.Container.Style as Styles
2819import Yoga.Block.Internal (createRef)
2920import Yoga.Block.Molecule.TableOfContents as TableOfContents
src/Yoga/Block/Molecule/TableOfContents/Style.purs+1
@@ -3,6 +3,7 @@ module Yoga.Block.Molecule.TableOfContents.Style where
33import Yoga.Prelude.Style
44import Yoga.Block.Container.Style (colour)
55
6+type Props :: forall k. (Type -> k) -> Row k -> Row k
67type Props f r =
78 ( css ∷ f Style
89 , backgroundLeft ∷ f Color
src/Yoga/Block/Organism/Form.purs+38−17
@@ -2,7 +2,7 @@ module Yoga.Block.Organism.Form
22 ( module Defaults
33 , module Yoga.Block.Organism.Form.Internal
44 , module Validation
5− , build
5+ -- , build
66 , build'
77 , defaultRenderForm
88 , defaultRenderForest
@@ -11,6 +11,7 @@ module Yoga.Block.Organism.Form
1111 , formState
1212 , inputBox
1313 , static
14+ , toggle
1415 , focus
1516 , match
1617 , match_
@@ -47,6 +48,8 @@ import Yoga.Block as Block
4748import Yoga.Block.Atom.Input as Input
4849import Yoga.Block.Atom.Input.Types (HTMLInputType)
4950import Yoga.Block.Atom.Input.Types as HTMLInputType
51+import Yoga.Block.Atom.Toggle as Toggle
52+import Yoga.Block.Atom.Toggle.Types (TogglePosition(..))
5053import Yoga.Block.Organism.Form.Defaults (formDefaults) as Defaults
5154import Yoga.Block.Organism.Form.Internal (Forest, FormBuilder, FormBuilder'(..), Tree(..), formBuilder, formBuilder_, pruneTree)
5255import Yoga.Block.Organism.Form.Types (RequiredField(..))
@@ -62,7 +65,6 @@ build ∷
6265 ReactComponent
6366 { value ∷ unvalidated
6467 , onChange ∷ (unvalidated -> unvalidated) -> Effect Unit
65− , inlineTable ∷ Boolean
6668 , formProps ∷ { readOnly ∷ Boolean | props }
6769 }
6870build = build' defaultRenderForm
@@ -104,20 +106,18 @@ build' render editor =
104106-- | form fields.
105107defaultRenderForm ∷
106108 ∀ props.
107− { inlineTable ∷ Boolean
108− } ->
109+ {} ->
109110 { readOnly ∷ Boolean
110111 | props
111112 } ->
112113 Forest ->
113114 JSX
114−defaultRenderForm renderProps@{ inlineTable } { readOnly } forest =
115+defaultRenderForm _ { readOnly } forest =
115116 R.div
116117 { className:
117118 String.joinWith " "
118119 $ fold
119120 [ [ "lumi-form" ]
120− , guard inlineTable [ "inline-table" ]
121121 , guard readOnly [ "readOnly" ]
122122 ]
123123 , children:
@@ -165,7 +165,6 @@ useForm ∷
165165 unvalidated
166166 result ->
167167 { initialState ∷ unvalidated
168− , inlineTable ∷ Boolean
169168 , formProps ∷ { readOnly ∷ Boolean | props }
170169 } ->
171170 Hooks.Hook (Hooks.UseState unvalidated)
@@ -177,12 +176,7 @@ useForm ∷
177176 , form ∷ JSX
178177 }
179178useForm editor props = Hooks.do
180− let
181− renderer =
182− defaultRenderForm
183− { inlineTable: props.inlineTable
184− }
185− props.formProps
179+ let renderer = defaultRenderForm {} props.formProps
186180 f <- useForm' editor props.initialState props.formProps
187181 pure f { form = renderer f.form }
188182
@@ -227,8 +221,6 @@ formState ∷
227221 Mapping Validation.ModifyValidated unvalidated unvalidated =>
228222 { initialState ∷ unvalidated
229223 , form ∷ FormBuilder { readOnly ∷ Boolean | props } unvalidated result
230− , inlineTable ∷ Boolean
231− , forceTopLabels ∷ Boolean
232224 , formProps ∷ { readOnly ∷ Boolean | props }
233225 , render ∷
234226 { formData ∷ unvalidated
@@ -247,7 +239,6 @@ formState =
247239 state <-
248240 useForm props.form
249241 { initialState: props.initialState
250− , inlineTable: props.inlineTable
251242 , formProps: props.formProps
252243 }
253244 pure (props.render state)
@@ -455,7 +446,7 @@ inputBox ∷
455446 Union p InputFixedProps q =>
456447 Union q r Input.Props =>
457448 Nub q q =>
458− NonEmptyString -> RequiredField -> (Record p) -> FormBuilder { readOnly ∷ Boolean | more } String String
449+ NonEmptyString -> RequiredField -> Record p -> FormBuilder { readOnly ∷ Boolean | more } String String
459450inputBox label requiredField inputProps =
460451 formBuilder_ \{ readOnly } s onChange ->
461452 element Input.component
@@ -474,3 +465,33 @@ inputBox label requiredField inputProps =
474465 , css: E.css { width: E.percent 100.0 }
475466 }
476467 )
468+
469+type ToggleFixedProps =
470+ ( value ∷ TogglePosition
471+ , onChange ∷ TogglePosition -> Effect Unit
472+ , disabled ∷ Boolean
473+ )
474+
475+-- | A toggle for Boolean values
476+toggle ∷
477+ ∀ p q r more.
478+ Union p ToggleFixedProps q =>
479+ Union q r Toggle.Props =>
480+ Nub q q =>
481+ (Record p) -> FormBuilder { readOnly ∷ Boolean | more } Boolean Boolean
482+toggle toggleProps =
483+ formBuilder_ \{ readOnly } value onChange ->
484+ element Toggle.component
485+ ( toggleProps
486+ `disjointUnion`
487+ { value:
488+ case value of
489+ false -> ToggleIsLeft
490+ true -> ToggleIsRight
491+ , onChange:
492+ case _ of
493+ ToggleIsLeft -> onChange false
494+ ToggleIsRight -> onChange true
495+ , disabled: readOnly
496+ }
497+ )
src/Yoga/Block/Organism/Form/Story.purs+24−15
@@ -1,6 +1,7 @@
11module Yoga.Block.Organism.Form.Story where
22
33import Prelude
4+import Data.Either (Either(..))
45import Data.Lens.Record (prop)
56import Data.Maybe (Maybe(..))
67import Data.String.NonEmpty (NonEmptyString, nes)
@@ -8,7 +9,7 @@ import Data.Tuple.Nested ((/\))
89import Effect (Effect)
910import Effect.Unsafe (unsafePerformEffect)
1011import Foreign.Object as Object
11−import React.Basic (JSX, element)
12+import React.Basic (JSX, element, fragment)
1213import React.Basic.DOM as R
1314import React.Basic.DOM.Events (preventDefault)
1415import React.Basic.Emotion as E
@@ -40,11 +41,13 @@ default =
4041type User =
4142 { firstName ∷ Form.Validated String
4243 , lastName ∷ Form.Validated String
44+ , isAdmin ∷ Boolean
4345 }
4446
4547type ValidUser =
4648 { firstName ∷ NonEmptyString
4749 , lastName ∷ NonEmptyString
50+ , isAdmin ∷ Boolean
4851 }
4952
5053aForm ∷ Effect JSX
@@ -58,24 +61,26 @@ aForm = do
5861 { setModified, reset, validated, form } <-
5962 Form.useForm userForm
6063 { initialState: formDefaults
61− , inlineTable: false
6264 , formProps:
6365 { readOnly: false
6466 }
6567 }
6668 pure
67− $ R.form -- Forms should be enclosed in a single "<form>" element to enable
68− -- default browser behavior, such as the enter key. Use "type=submit"
69− -- on the form's submit button and `preventDefault` to keep the browser
70− -- from reloading the page on submission.
71− { onSubmit:
72− handler preventDefault \_ -> case validated of
73− Nothing -> do
74− setModified
75− Just { firstName, lastName } -> setUserDialog $ Just { firstName, lastName }
76− , style: R.css { alignSelf: "stretch" }
77− , children: [ form ]
78− }
69+ $ fragment
70+ [ R.form -- Forms should be enclosed in a single "<form>" element to enable
71+ -- default browser behavior, such as the enter key. Use "type=submit"
72+ -- on the form's submit button and `preventDefault` to keep the browser
73+ -- from reloading the page on submission.
74+ { onSubmit:
75+ handler preventDefault \_ -> case validated of
76+ Nothing -> do
77+ setModified
78+ Just { firstName, lastName } -> setUserDialog $ Just { firstName, lastName }
79+ , style: R.css { alignSelf: "stretch" }
80+ , children: [ form ]
81+ }
82+ , R.text $ show validated
83+ ]
7984 where
8085 userForm ∷ ∀ props. FormBuilder { readOnly ∷ Boolean | props } User ValidUser
8186 userForm = ado
@@ -90,4 +95,8 @@ aForm = do
9095 $ Form.inputBox (nes (Proxy ∷ _ "Last Name")) Required
9196 { placeholder: "Last name"
9297 }
93− in { firstName, lastName }
98+ isAdmin <-
99+ Form.indent "Admin?" Neither
100+ $ Form.focus (prop (Proxy ∷ _ "isAdmin"))
101+ $ Form.toggle {}
102+ in { firstName, lastName, isAdmin }
src/Yoga/Block/Organism/Form/Validation.purs+1−3
@@ -59,10 +59,8 @@ import Foreign.Generic (class Decode, class Encode, decode, encode)
5959import Heterogeneous.Mapping (class MapRecordWithIndex, class Mapping, ConstMapping, hmap, mapping)
6060import Prim.RowList as RL
6161import React.Basic.DOM as R
62−import Record (disjointUnion)
6362import Yoga.Block as Block
64−import Yoga.Block.Atom.Input as Input
65−import Yoga.Block.Organism.Form.Internal (Forest, FormBuilder, formBuilder_, FormBuilder'(..), Tree(..))
63+import Yoga.Block.Organism.Form.Internal (Forest, FormBuilder, FormBuilder'(..), Tree(..))
6664
6765-- | A `Validator` takes a possibly invalid form `result` and produces
6866-- | a `valid` result, or an error message.