@@ -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+ ]