Input work

This commit is contained in:
Mark Eibes 2020-12-20 23:44:24 +01:00
parent 774db186ef
commit c32928933f
47 changed files with 1331 additions and 275 deletions

1
.psci_modules/index.js Normal file
View File

@ -0,0 +1 @@
require('$PSCI')['$main']();

8
convert-more.fish Normal file
View File

@ -0,0 +1,8 @@
for f in (ls src/Yoga/Block/Icon/SVG/*.svg)
npx svgo -i $f
end
node convert-svgs.js
for f in (ls src/Yoga/Block/Icon/SVG/*.purs)
sed -i "" 's/stroke: "#333"/stroke: "var(--stroke-colour)"/g' $f
end

View File

@ -1,6 +1,6 @@
const path = require('path');
const svg2psreact = require("svg2psreact");
const assetsDir = path.join(__dirname, 'src', 'Assets');
const assetsDir = path.join(__dirname, 'src', 'Yoga', 'Block', 'Icon', 'SVG');
svg2psreact.convertAllSVGsInDirectory("Assets", assetsDir)
svg2psreact.convertAllSVGsInDirectory("Yoga.Block.Icon.SVG", assetsDir)

View File

@ -17,7 +17,7 @@
"babel-loader": "^8.1.0",
"clean-webpack-plugin": "^3.0.0",
"compression-webpack-plugin": "^6.0.3",
"framer-motion": "^2.7.0",
"framer-motion": "^3.0.0",
"html-webpack-plugin": "^4.5.0",
"jsdom": "^16.3.0",
"jsdom-global": "^3.0.2",

View File

@ -119,7 +119,7 @@ let additions =
let upstream =
https://github.com/purescript/package-sets/releases/download/psc-0.13.8-20201021/packages.dhall sha256:55ebdbda1bd6ede4d5307fbc1ef19988c80271b4225d833c8d6fb9b6fb1aa6d8
https://github.com/purescript/package-sets/releases/download/psc-0.13.8-20201217/packages.dhall sha256:f46d45e29977f3b57717b56d20a5ceac12532224516eea3012a4688f22ac1539
let overrides =
{ spec-discovery = upstream.spec-discovery // { version = "master" }

View File

@ -18,6 +18,7 @@ You can edit this file as you like.
, "routing-duplex"
, "spec-discovery"
, "untagged-union"
, "web-uievents"
]
, packages = ./packages.dhall
, sources = [ "src/**/*.purs", "test/**/*.purs" ]

View File

@ -6,6 +6,14 @@ module Framer.Motion
, callback
, class EffectFnMaker
, toEffectFn
, OnHoverStart
, onHoverStart
, customProp
, OnHoverEnd
, onHoverEnd
, whileHover
, WhileHover
, EventInfo
, OnTap
, TapInfo
, OnTapStart
@ -80,6 +88,7 @@ import Data.Nullable (Nullable)
import Data.Symbol (class IsSymbol, SProxy, reflectSymbol)
import Effect (Effect)
import Effect.Uncurried (EffectFn1, EffectFn2, mkEffectFn1, mkEffectFn2)
import Foreign (Foreign, unsafeToForeign)
import Foreign.Object (Object)
import Heterogeneous.Mapping (class HMapWithIndex, class MappingWithIndex, hmapWithIndex)
import Literals.Undefined (Undefined)
@ -97,6 +106,7 @@ import Untagged.Castable (class Castable, cast)
import Untagged.Union (type (|+|))
import Web.DOM (Node)
import Web.Event.Internal.Types (Event)
import Web.UIEvent.MouseEvent (MouseEvent)
import Yoga.Block.Internal (Id)
foreign import divImpl ∷ ∀ a. ReactComponent { | a }
@ -216,6 +226,28 @@ onTap fn2 = cast (mkEffectFn2 fn2)
onTapCancel ∷ (Event -> TapInfo -> Effect Unit) -> OnTap
onTapCancel fn2 = cast (mkEffectFn2 fn2)
type EventInfo =
{ point ∷ { x ∷ Number, y ∷ Number }
}
type WhileHover =
(EffectFn2 MouseEvent EventInfo Unit |+| Undefined)
type OnHoverEnd =
(EffectFn2 MouseEvent EventInfo Unit |+| Undefined)
type OnHoverStart =
(EffectFn2 MouseEvent EventInfo Unit |+| Undefined)
onHoverStart ∷ (MouseEvent -> EventInfo -> Effect Unit) -> OnHoverStart
onHoverStart = cast <<< toEffectFn
onHoverEnd ∷ (MouseEvent -> EventInfo -> Effect Unit) -> OnHoverEnd
onHoverEnd = cast <<< toEffectFn
whileHover ∷ ∀ c. Castable c WhileHover => c -> WhileHover
whileHover = cast
type TapInfo =
{ x ∷ Number, y ∷ Number }
@ -250,9 +282,13 @@ onDragEnd = cast <<< toEffectFn
onDrag ∷ (Event -> PanInfo -> Effect Unit) -> OnDrag
onDrag fn2 = cast (mkEffectFn2 fn2)
customProp ∷ ∀ a. a -> Foreign
customProp = unsafeToForeign
type MotionPropsF f r =
( initial ∷ f Initial
, animate ∷ f Animate
, custom ∷ f Foreign
, drag ∷ f Drag
, dragMomentum ∷ f DragMomentum
, dragElastic ∷ f DragElastic
@ -270,6 +306,9 @@ type MotionPropsF f r =
, onTapStart ∷ f OnTapStart
, onTapEnd ∷ f OnTapEnd
, onTapCancel ∷ f OnTapCancel
, whileHover ∷ f WhileHover
, onHoverStart ∷ f OnHoverStart
, onHoverEnd ∷ f OnHoverEnd
, exit ∷ f Exit
| r
)

View File

@ -7,43 +7,11 @@ import Effect.Uncurried (EffectFn3, runEffectFn3)
import Foreign.Object (Object)
import Foreign.Object as Object
import Prim.Row (class Union)
import React.Basic.DOM (CSS)
import Unsafe.Coerce (unsafeCoerce)
import Yoga.Prelude.View (Hook, NodeRef, unsafeHook)
import Yoga.Prelude.View (Hook, unsafeHook)
import React.Basic.Popper.Types (Options, PopperData, PopperElement, ReferenceElement)
foreign import data UsePopper ∷ Type -> Type -> Type
type ReferenceElement =
NodeRef
type PopperElement =
NodeRef
type ArrowElement =
NodeRef
type DataAttributes =
{ popper ∷ Object String, arrow ∷ Object String }
type Styles =
{ popper ∷ CSS, arrow ∷ CSS }
type PopperData =
{ styles ∷ Styles, attributes ∷ DataAttributes }
foreign import data Modifier ∷ Type
modifierArrow ∷ ArrowElement -> Modifier
modifierArrow element = unsafeCoerce { name: "arrow", options: { element } }
modifierOffset ∷ { x ∷ Number, y ∷ Number } -> Modifier
modifierOffset { x, y } = unsafeCoerce { name: "offset", options: { offset: [ x, y ] } }
type Options =
( modifiers ∷ Array Modifier
, strategy ∷ String
)
foreign import usePopperImpl ∷ ∀ opts. EffectFn3 ReferenceElement PopperElement { | opts } PopperData
toDataAttributes ∷ Object String -> Object String

View File

@ -1,27 +1,24 @@
module React.Basic.Popper.Story where
import Prelude
import Color as Color
import Data.Maybe (Maybe(..))
import Data.Interpolate (i)
import Data.Nullable (null)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Uncurried (mkEffectFn1)
import Effect.Unsafe (unsafePerformEffect)
import Framer.Motion (onTap)
import Framer.Motion as Motion
import React.Basic (JSX, element, fragment)
import React.Basic.DOM as R
import React.Basic.Emotion as E
import React.Basic.Hooks as React
import React.Basic.Popper.Hook (modifierArrow, modifierOffset, usePopper)
import React.Basic.Popper.Hook (usePopper)
import React.Basic.Popper.Types (modifierArrow, modifierOffset)
import Unsafe.Coerce (unsafeCoerce)
import Yoga.Block as Block
import Yoga.Block.Atom.Toggle as Toggle
import Yoga.Block.Container.Style (DarkOrLightMode(..))
import Yoga.Block.Container.Style as Styles
import Yoga.Block.Internal.CSS (nest)
import Yoga.Prelude.View (NodeRef, el, styled, styledLeaf)
import Yoga.Prelude.View (NodeRef, el, guard, handler, preventDefault, styled, styledLeaf)
default ∷
{ decorators ∷ Array (Effect JSX -> JSX)
@ -136,6 +133,7 @@ animatedPopper = do
mkBasicExample =
React.reactComponent "Popper example" \p -> React.do
-- Hooks
referenceElement /\ setReferenceElement <- React.useState' nullRef
popperElement /\ setPopperElement <- React.useState' nullRef
arrowElement /\ setArrowElement <- React.useState' nullRef
@ -143,63 +141,105 @@ animatedPopper = do
usePopper referenceElement popperElement
{ modifiers:
[ modifierArrow arrowElement
, modifierOffset { x: 0.0, y: 8.0 }
, modifierOffset { x: 0.0, y: 12.0 }
]
}
pure
$ fragment
$ [ element R.div'
{ ref: unsafeCoerce (mkEffectFn1 setReferenceElement)
, children: [ R.text "Reference element" ]
}
, el Motion.animatePresence {}
[ styled Motion.div
{ className: "popper-element"
, key: "heinz"
, css:
E.css
{ background: E.str "darkslateblue"
, borderRadius: E.str "8px"
, display: E.str "block"
, padding: E.str "4px 8px"
, "&[data-popper-placement^='top'] > .popper-arrow":
nest { bottom: E.str "-4px" }
, "&[data-popper-placement^='bottom'] > .popper-arrow":
nest { top: E.str "-4px" }
, "&[data-popper-placement^='left'] > .popper-arrow":
nest { right: E.str "-4px" }
, "&[data-popper-placement^='right'] > .popper-arrow":
nest { left: E.str "-4px" }
}
, ref: unsafeCoerce (mkEffectFn1 setPopperElement)
, style: styles.popper
, _data: attributes.popper
}
[ R.text "Popper Element"
, styledLeaf R.div'
{ className: "popper-arrow"
, id: "arrow"
, css:
E.css
{ position: E.str "absolute"
, width: E.str "8px"
, height: E.str "8px"
, zIndex: E.str "-1"
, "&::before":
nest
{ position: E.str "absolute"
, width: E.str "8px"
, height: E.str "8px"
, zIndex: E.str "-1"
, content: E.str "''"
, transform: E.str "rotate(45deg)"
, background: E.str "darkslateblue"
}
}
, ref: unsafeCoerce (mkEffectFn1 setArrowElement)
, style: styles.arrow
, _data: attributes.arrow
}
on /\ setOn <- React.useState' true
-- Handlers
let buttonClicked = setOn (not on)
-- Elements
let
result =
fragment
$ [ refElem
, popperEl
[ animatePresence
$ guard on
[ content
[ el Block.box {} [ R.text "Ich bin Stinky Bill" ]
, arrow
]
]
]
]
]
animatePresence =
el Motion.animatePresence
{ initial: false
}
content =
styled Motion.div
{ className: "popper-element-content"
, css: contentCss
, initial: Motion.initial $ R.css { opacity: [ 1.0, 0.8, 0.0 ], scale: [ 1.0, 0.85 ] }
, animate: Motion.animate $ R.css { opacity: [ 0.0, 0.3, 1.0 ], scale: [ 0.0, 1.05, 1.0, 0.98, 1.01, 1.0 ] }
, exit: Motion.exit $ R.css { opacity: [ 1.0, 0.8, 0.0 ], scale: [ 1.0, 0.85 ] }
, transition: Motion.transition { duration: 0.2 }
, key: "container"
}
popperEl =
styled R.div'
{ className: "popper-element"
, css: popperCss
, ref: unsafeCoerce (mkEffectFn1 setPopperElement)
, style: styles.popper
, _data: attributes.popper
}
arrow =
styledLeaf R.div'
{ className: "popper-arrow"
, id: "arrow"
, css: arrowCss
, ref: unsafeCoerce (mkEffectFn1 setArrowElement)
, style: styles.arrow
, _data: attributes.arrow
}
refElem =
element R.button'
{ ref: unsafeCoerce (mkEffectFn1 setReferenceElement)
, children: [ R.text "Reference element" ]
, onClick: handler preventDefault (const buttonClicked)
}
pure result
backgroundColour = "var(--highlight)"
backgroundColour2 = "rgb(100, 160, 240)"
arrowCss =
E.css
{ position: E.str "absolute"
, width: E.str "8px"
, height: E.str "8px"
, zIndex: E.str "0"
, "&::before":
nest
{ position: E.str "absolute"
, width: E.str "8px"
, height: E.str "8px"
, content: E.str "''"
, transform: E.str "rotate(45deg)"
, background: E.str backgroundColour
}
}
contentCss =
E.css
{ background: E.str $ i "linear-gradient(0deg," backgroundColour "," backgroundColour2 "," backgroundColour ")"
, fontSize: E.str "14px"
, zIndex: E.str "-1"
, color: E.str $ "white"
, boxShadow: E.str "0 1px 8px rgba(0,0,0,0.5)"
, borderRadius: E.str "12px"
}
popperCss =
E.css
{ "&[data-popper-placement^='top'] > * > .popper-arrow":
nest { bottom: E.str "-4px" }
, "&[data-popper-placement^='bottom'] > * > .popper-arrow":
nest { top: E.str "-4px" }
, "&[data-popper-placement^='left'] > * > .popper-arrow":
nest { right: E.str "-4px" }
, "&[data-popper-placement^='right'] > * > .popper-arrow":
nest { left: E.str "-4px" }
}

View File

@ -0,0 +1,40 @@
module React.Basic.Popper.Types where
import Foreign.Object (Object)
import React.Basic.DOM (CSS)
import Unsafe.Coerce (unsafeCoerce)
import Yoga.Prelude.View (NodeRef, null)
nullRef ∷ NodeRef
nullRef = unsafeCoerce null
type ReferenceElement =
NodeRef
type PopperElement =
NodeRef
type ArrowElement =
NodeRef
type DataAttributes =
{ popper ∷ Object String, arrow ∷ Object String }
type Styles =
{ popper ∷ CSS, arrow ∷ CSS }
type PopperData =
{ styles ∷ Styles, attributes ∷ DataAttributes }
foreign import data Modifier ∷ Type
modifierArrow ∷ ArrowElement -> Modifier
modifierArrow element = unsafeCoerce { name: "arrow", options: { element } }
modifierOffset ∷ { x ∷ Number, y ∷ Number } -> Modifier
modifierOffset { x, y } = unsafeCoerce { name: "offset", options: { offset: [ x, y ] } }
type Options =
( modifiers ∷ Array Modifier
, strategy ∷ String
)

View File

@ -21,7 +21,7 @@ codeInput props = styles <>? cast props.css
, background: str colour.inputBackground
, border: str $ i "solid 1px " colour.inputBorder
, borderRadius: str "var(--s-2)"
, fontFamily: str "var(--monoFont)"
, fontFamily: str "var(--mono-font)"
, fontSize: str "var(--s0)"
, lineHeight: str "var(--s0)"
, width: str $ i "calc(" (cast props.maxLength ?|| 10) "ch + 4.3 * var(--s-5))"

View File

@ -0,0 +1,5 @@
module Yoga.Block.Atom.Icon
( module Yoga.Block.Atom.Icon.View
) where
import Yoga.Block.Atom.Icon.View (component, Props)

View File

@ -0,0 +1,37 @@
module Yoga.Block.Atom.Icon.Story where
import Prelude
import Effect (Effect)
import Effect.Unsafe (unsafePerformEffect)
import React.Basic (JSX, element, fragment)
import React.Basic.DOM as R
import React.Basic.Emotion as E
import Yoga.Block.Atom.Icon as Icon
import Yoga.Block.Container.Style as Styles
import Yoga.Block.Icon.SVG as SVGIcon
default ∷
{ decorators ∷ Array (Effect JSX -> JSX)
, title ∷ String
}
default =
{ title: "Atom/Icon"
, decorators:
[ \storyFn ->
R.div_
[ element E.global { styles: Styles.global }
, unsafePerformEffect storyFn
]
]
}
icon ∷ Effect JSX
icon = do
pure
$ fragment
[ R.div_
[ R.h2_ [ R.text "Icon" ]
, element Icon.component { icon: SVGIcon.on }
, element Icon.component { icon: SVGIcon.off }
]
]

View File

@ -0,0 +1,27 @@
module Yoga.Block.Atom.Icon.Style where
import Yoga.Prelude.Style
import Yoga.Block.Container.Style (colour)
type Props f r =
( css ∷ f Style
, colour ∷ f StyleProperty
, stroke ∷ f StyleProperty
, fill ∷ f StyleProperty
, size ∷ f StyleProperty
, width ∷ f StyleProperty
, height ∷ f StyleProperty
| r
)
span ∷ ∀ r. { | Props OptionalProp r } -> Style
span props =
css
{ "--stroke-colour": (props.stroke <|> props.colour) ?|| (str colour.text)
, "--fill-colour": (props.fill <|> props.colour) ?|| (str "transparent")
, "& > svg":
nest
{ width: (props.width <|> props.size) ?|| (str "auto")
, height: (props.height <|> props.size) ?|| (str "1.2ch")
}
}

View File

@ -0,0 +1,32 @@
module Yoga.Block.Atom.Icon.View where
import Yoga.Prelude.View
import React.Basic.DOM as R
import Yoga.Block.Atom.Icon.Style as Style
type Props =
PropsF Id
type MandatoryProps r =
( icon ∷ JSX
| r
)
type PropsF f =
( className ∷ f String
| Style.Props f (MandatoryProps ())
)
component ∷ ∀ p p_. Union p p_ Props => ReactComponent { | MandatoryProps p }
component = rawComponent
rawComponent ∷ ∀ p. ReactComponent { | p }
rawComponent =
mkForwardRefComponent "Yoga Icon" \(props ∷ { | PropsF OptionalProp }) ref -> React.do
pure
$ styled R.span'
{ className: "ry-icon" <>? props.className
, css: Style.span props
, ref
}
[ props.icon ]

View File

@ -0,0 +1,5 @@
module Yoga.Block.Atom.Input
( module Yoga.Block.Atom.Input.View
) where
import Yoga.Block.Atom.Input.View (component, Props)

View File

@ -0,0 +1,56 @@
module Yoga.Block.Atom.Input.Story where
import Prelude
import Effect (Effect)
import Effect.Unsafe (unsafePerformEffect)
import React.Basic (JSX, element, fragment)
import React.Basic.DOM as R
import React.Basic.Emotion as E
import React.Basic.Events (handler_)
import Yoga.Block.Atom.Input as Input
import Yoga.Block.Container.Style as Styles
default ∷
{ decorators ∷ Array (Effect JSX -> JSX)
, title ∷ String
}
default =
{ title: "Atom/Input"
, decorators:
[ \storyFn ->
R.div_
[ element E.global { styles: Styles.global }
, unsafePerformEffect storyFn
]
]
}
input ∷ Effect JSX
input = do
pure
$ fragment
[ R.div_
[ R.h2_ [ R.text "Generic Input" ]
, element Input.component { value: "A Generic Input", onChange: handler_ mempty }
, R.h2_ [ R.text "Password" ]
, element Input.component { type: "password" }
, R.h2_ [ R.text "Text Input" ]
, element Input.component { type: "text", value: "Some text", onChange: handler_ mempty }
, R.h2_ [ R.text "Search Input" ]
, element Input.component { type: "search", value: "Search...", onChange: handler_ mempty }
, R.h2_ [ R.text "Button" ]
, element Input.component { type: "button", value: "A button", onChange: handler_ mempty }
, R.h2_ [ R.text "Submit" ]
, element Input.component { type: "submit" }
, R.h2_ [ R.text "Radio" ]
, element Input.component { type: "radio" }
, R.h2_ [ R.text "Checkbox" ]
, element Input.component { type: "checkbox" }
, R.h2_ [ R.text "File" ]
, element Input.component { type: "file" }
, R.h2_ [ R.text "Image" ]
, element Input.component { type: "image" }
, R.h2_ [ R.text "Number" ]
, element Input.component { type: "number" }
]
]

View File

@ -0,0 +1,91 @@
module Yoga.Block.Atom.Input.Style where
import Yoga.Prelude.Style
import Yoga.Block.Container.Style (colour)
import Yoga.Prelude.Style as Color
type Props f r =
( css ∷ f Style
| r
)
leftIconSize ∷ StyleProperty
leftIconSize = var "--left-icon-size"
rightIconSize ∷ StyleProperty
rightIconSize = var "--right-icon-size"
leftIconStyle ∷ Style
leftIconStyle =
css
{ "--stroke-colour": str colour.interfaceTextDisabled
, marginTop: str "4px"
, marginLeft: str "-4px"
}
rightIconStyle ∷ Style
rightIconStyle =
css
{ "--stroke-colour": str colour.interfaceTextDisabled
, paddingTop: str "6px"
, marginRight: str "6px"
}
textWrapper ∷ ∀ r. { | Props OptionalProp r } -> Style
textWrapper props =
css
{ fontFamily: var "--main-font"
, boxSizing: borderBox
, backgroundColor: str $ colour.background07
, display: inlineFlex
, "--left-icon-size": var "--s0"
, "--right-icon-size": str "calc(var(--s0) * 1.2)"
, alignItems: center
, justifyContent: flexStart
, paddingLeft: var "--s-1"
, gap: var "--s-3"
, "--border-width": var "--s-5"
, border: str $ "var(--border-width) solid " <> colour.inputBorder
, borderRadius: var "--s-1"
, "&:focus-within":
nest
{}
}
input ∷ ∀ r. { | Props OptionalProp r } -> Style
input props =
css
{ "&[type=text],&[type=search],&[type=password],&[type=number],&:not([type])":
nest
{ color: str colour.text
, background: str "transparent"
, alignSelf: stretch
, "--padding-top": str "var(--s-1)"
, "--padding-bottom": str "calc(var(--padding-top) * 0.85)"
, paddingTop: var "--padding-top"
, paddingBottom: var "--padding-bottom"
, paddingLeft: _0
, paddingRight: _0
-- , background: str colour.inputBackground
, border: none
}
, "&[type=search]":
nest
{ "&::-webkit-search-decoration, &::-webkit-search-cancel-button, &::-webkit-serch-results-button, &::-webkit-search-results-decoration":
nest
{ "WebkitAppearance": none
}
}
, "&:focus":
nest
{ outline: none
}
, "&[type=button], &[type=submit]":
nest
{ background: str colour.highlight
, color: str "white"
, boxShadow: str "0 1px 4px rgba(0,0,0,0.5)"
, borderColour: str colour.highlight
, height: str "auto"
}
}

View File

@ -0,0 +1,142 @@
module Yoga.Block.Atom.Input.View where
import Yoga.Prelude.View
import Framer.Motion as M
import React.Basic.DOM (css)
import React.Basic.DOM as R
import React.Basic.DOM.SVG as SVG
import React.Basic.Hooks as React
import Record.Extra (pick)
import Unsafe.Coerce (unsafeCoerce)
import Yoga.Block.Atom.Icon as Icon
import Yoga.Block.Atom.Input.Style as Style
import Yoga.Block.Atom.Tooltip as Tooltip
import Yoga.Block.Icon.SVG as SVGIcon
type PropsF f =
( iconLeft ∷ f JSX
, iconRight ∷ f JSX
| Style.Props f InputProps
)
type Props =
PropsF Id
type PropsOptional =
PropsF OptionalProp
component ∷ ∀ p p_. Union p p_ Props => ReactComponent { | p }
component = rawComponent
mkLeftIcon ∷ JSX -> JSX
mkLeftIcon icon =
styledLeaf
Icon.component
{ size: Style.leftIconSize
, className: "ry-input-right-icon"
, css: Style.leftIconStyle
, icon
}
mkRightIcon ∷ JSX -> JSX
mkRightIcon icon =
styledLeaf Icon.component
{ size: Style.rightIconSize
, className: "ry-input-left-icon"
, css: Style.rightIconStyle
, icon
}
rawComponent ∷ ∀ p. ReactComponent { | p }
rawComponent =
mkForwardRefComponent "Input" do
\(props ∷ { | PropsOptional }) ref -> React.do
let
iconLeft =
props.iconLeft
?|| if (((cast props.type) ?|| "") == "search") then mkLeftIcon SVGIcon.magnifyingGlass else mempty
iconRight =
props.iconRight
?|| if (((cast props.type) ?|| "") == "password") then mkRightIcon SVGIcon.eyeOpen else mempty
pure
$ case props.type # cast # opToMaybe of
Just "password" -> el_ password props
_ ->
styled R.div'
{ className: "ry-input-wrapper"
, css: Style.textWrapper props
}
[ iconLeft
, emotionInput
ref
props
{ className: "ry-input"
, css: Style.input props
}
, iconRight
]
password ∷ ∀ p. ReactComponent { | p }
password =
mkForwardRefComponent "Password" do
\(props ∷ { | PropsOptional }) ref -> React.do
hidePassword /\ modifyHidePassword <- useState true
let
eyeCon =
el_ Tooltip.component
{ target:
el R.div'
{ onClick: handler preventDefault \_ -> modifyHidePassword not
}
[ el M.animatePresence
{ exitBeforeEnter: true
}
[ if hidePassword then
el M.div
{ key: "eyeOpen"
, initial: M.initial $ css { scaleY: 0 }
, animate: M.animate $ css { scaleY: 1 }
, exit: M.exit $ css { scaleY: 0.9 }
, transition: M.transition { duration: 0.15 }
}
[ styledLeaf Icon.component
{ size: Style.rightIconSize
, className: "ry-input-left-icon"
, css: Style.rightIconStyle
, icon: SVGIcon.eyeOpen
}
]
else
el M.div
{ key: "eyeClosed"
, initial: M.initial $ css { scaleY: 0.0 }
, animate: M.animate $ css { scaleY: 1 }
, exit: M.exit $ css { scaleY: 0.9 }
, transition: M.transition { duration: 0.15 }
}
[ styledLeaf Icon.component
{ size: Style.rightIconSize
, className: "ry-input-left-icon"
, css: Style.rightIconStyle
, icon: SVGIcon.eyeClosed
}
]
]
]
, theTip: R.text if hidePassword then "Show password" else "Hide password"
}
let iconRight = props.iconRight ?|| eyeCon
pure
$ styled R.div'
{ className: "ry-input-wrapper"
, css: Style.textWrapper props
}
[ emotionInput
ref
props
{ className: "ry-input"
, css: Style.input props
, type: if hidePassword then "password" else "text"
}
, iconRight
]

View File

@ -6,8 +6,6 @@ import Yoga.Block.Container.Style (colour)
type Props f r =
( css ∷ f Style
, space ∷ f StyleProperty
, splitAfter ∷ f Int
| r
)

View File

@ -1,13 +1,15 @@
module Yoga.Block.Atom.Range.View (component, Props, PropsF) where
import Yoga.Prelude.View
import Yoga.Block.Atom.Range.Style as Style
import Data.Int as Int
import Foreign.Object as Object
import Framer.Motion as M
import React.Basic.DOM (css)
import React.Basic.DOM as R
import React.Basic.DOM.Events as Event
import React.Basic.Hooks as React
import Yoga.Block.Atom.Range.Style as Style
import Yoga.Block.Atom.Tooltip as Tooltip
type PropsF f =
( className ∷ f String
@ -58,11 +60,11 @@ rawComponent =
{ className: "ry-range-focus-circle"
, css: Style.focusCircle
}
, emotionInput ref (props { max = show max, min = show min, value = show value })
, emotionInput
ref
(props { max = show max, min = show min, value = show value })
{ className: "ry-range-thumb " <>? props.className
, css: Style.range props <> guard props.disabled Style.inputDisabled <>? props.css
, style: props.style
, value: show value
, type: "range"
, onChange: handler Event.targetValue ((_ >>= Int.fromString) >>> (foldMap setValue))
}

View File

@ -2,6 +2,7 @@ module Yoga.Block.Atom.Toggle.Spec where
import Yoga.Prelude.Spec
import Yoga.Block.Atom.Toggle as Toggle
import Yoga.Block.Atom.Toggle.Types (TogglePosition(..))
spec ∷ Spec Unit
spec =
@ -10,6 +11,6 @@ spec =
it "renders without errors" do
void
$ renderComponent Toggle.component
{ value: true
, onToggle: mempty
{ togglePosition: ToggleIsLeft
, setTogglePosition: mempty
}

View File

@ -10,6 +10,7 @@ import React.Basic.DOM as R
import React.Basic.Hooks as React
import Yoga.Block as Block
import Yoga.Block.Atom.Toggle as Toggle
import Yoga.Block.Atom.Toggle.Types (TogglePosition(..))
import Yoga.Block.Container.Style (DarkOrLightMode(..))
default ∷
@ -35,30 +36,32 @@ toggle = do
where
mkBasicExample =
React.reactComponent "Toggle example" \p -> React.do
isOn /\ turnOnOrOff <- React.useState' false
togglePosition /\ setTogglePosition <- React.useState' ToggleIsRight
pure
$ element Toggle.component
{ value: isOn
, onToggle: turnOnOrOff
{ togglePosition
, setTogglePosition
}
mkDarkLightToggle =
React.reactComponent "Toggle dark night example" \p -> React.do
isOn /\ turnOnOrOff <- React.useState' false
togglePosition /\ setTogglePosition <- React.useState' ToggleIsLeft
theme /\ setTheme <- React.useState' Nothing
let
content =
element Toggle.component
{ value: isOn
, onToggle:
\b -> do
turnOnOrOff b
setTheme (Just if b then DarkMode else LightMode)
, on: R.text "🌒"
, off: R.text "🌞"
, backgroundOn:
Color.hsl 205.0 1.0 0.93
, backgroundOff:
{ togglePosition: togglePosition
, setTogglePosition:
\newTogglePosition -> do
setTogglePosition newTogglePosition
setTheme case newTogglePosition of
ToggleIsRight -> Just DarkMode
ToggleIsLeft -> Just LightMode
, left: R.text "🌒"
, right: R.text "🌞"
, backgroundLeft:
Color.hsl 205.0 1.0 0.83
, backgroundRight:
Color.hsl 260.0 0.7 0.45
}
pure

View File

@ -5,8 +5,8 @@ import Yoga.Block.Container.Style (colour)
type Props f r =
( css ∷ f Style
, backgroundOn ∷ f Color
, backgroundOff ∷ f Color
, backgroundLeft ∷ f Color
, backgroundRight ∷ f Color
| r
)
@ -23,17 +23,17 @@ button =
, padding: _0
}
theToggle ∷ ∀ p. { | Props OptionalProp p } -> Style
theToggle props =
theToggle ∷ Style
theToggle =
css
{ width: var "--s2"
, height: var "--s2"
{ width: str "calc(var(--s2) * 0.8)"
, height: str "calc(var(--s2) * 0.8)"
, background: str $ colour.interfaceBackground
, border: none
, borderRadius: str $ "calc(var(--s2) / 2)"
, position: absolute
, top: str "-1px"
, left: _0
, top: str "3px"
, left: str "3px"
, margin: _0
, boxShadow: str "0 0.5px 3px rgba(0,0,0,0.50)"
}
@ -46,25 +46,33 @@ toggleTextContainer =
, fontWeight: str "bold"
, position: absolute
, top: _0
, fontSize: str "calc(0.6 * var(--s1))"
, lineHeight: var "--s2"
, height: var "--s2"
, display: flex
}
successTextColour ∷ StyleProperty
successTextColour = str colour.successText
disabledTextColour ∷ StyleProperty
disabledTextColour = str colour.interfaceTextDisabled
toggleText ∷ Style
toggleText =
css
{ textAlign: str "left"
, margin: _0
, padding: _0
, width: str "50%"
, height: str "100%"
, display: flex
, justifyContent: center
, alignItems: center
, color: str colour.interfaceTextDisabled
, "&:first-child":
, color: disabledTextColour
, "& > *":
nest
{ color: str colour.successText
{ color: successTextColour
}
}

View File

@ -0,0 +1,19 @@
module Yoga.Block.Atom.Toggle.Types where
import Prelude
data TogglePosition
= ToggleIsLeft
| ToggleIsRight
derive instance eqTogglePosition ∷ Eq TogglePosition
instance showTogglePosition ∷ Show TogglePosition where
show = case _ of
ToggleIsLeft -> "ToggleIsLeft"
ToggleIsRight -> "ToggleIsRight"
flipToggle ∷ TogglePosition -> TogglePosition
flipToggle = case _ of
ToggleIsLeft -> ToggleIsRight
ToggleIsRight -> ToggleIsLeft

View File

@ -3,26 +3,33 @@ module Yoga.Block.Atom.Toggle.View (component, MandatoryProps, Props, PropsF) wh
import Yoga.Prelude.View
import Color as Color
import Data.Interpolate (i)
import Data.Maybe (isNothing)
import Effect.Class.Console as Console
import Effect.Unsafe (unsafePerformEffect)
import Foreign.Object as Object
import Framer.Motion as Motion
import Partial.Unsafe (unsafeCrashWith)
import React.Basic.DOM (css)
import React.Basic.DOM as R
import React.Basic.Emotion as Emotion
import React.Basic.Hooks (reactComponent)
import React.Basic.Hooks as React
import Yoga.Block.Atom.Icon as Icon
import Yoga.Block.Atom.Toggle.Style as Style
import Yoga.Block.Atom.Toggle.Types (TogglePosition(..), flipToggle)
import Yoga.Block.Container.Style (colour)
import Yoga.Block.Icon.SVG as SVGIcon
type PropsF f =
( className ∷ f String
, on ∷ f JSX
, off ∷ f JSX
, left ∷ f JSX
, right ∷ f JSX
| Style.Props f (MandatoryProps InputProps)
)
type MandatoryProps r =
( value ∷ Boolean
, onToggle ∷ Boolean -> Effect Unit
( togglePosition ∷ TogglePosition
, setTogglePosition ∷ TogglePosition -> Effect Unit
| r
)
@ -32,7 +39,7 @@ data TappingState
data DragState
= NotDragging
| Dragging { startX ∷ Number }
| Dragging { startX ∷ Number, currentX ∷ Number }
| DragDone { startX ∷ Number, endX ∷ Number }
derive instance eqDragState ∷ Eq DragState
@ -40,8 +47,8 @@ derive instance eqDragState ∷ Eq DragState
instance showDragState ∷ Show DragState where
show = case _ of
NotDragging -> "NotDragging"
Dragging x -> "Dragging " <> (show x)
DragDone x -> "DragDone " <> (show x)
Dragging x -> "Dragging " <> show x
DragDone x -> "DragDone " <> show x
type Props =
PropsF Id
@ -57,147 +64,213 @@ rawComponent =
mkForwardRefComponent "Toggle" do
\(props ∷ { | PropsOptional }) ref -> React.do
let disabled = props.disabled
tapState <- useRef TapNotAllowed
dragState /\ setDragState <- React.useState' NotDragging
maxLeft /\ setMaxLeft <- useState' 0.0
buttonRef <- useRef null
toggleRef <- useRef null
leftState /\ setLeftState <- React.useState' 0.0
let
getWidth aRef = do
bbox <- getBoundingBoxFromRef aRef
pure $ bbox <#> _.width # fromMaybe 0.0
buttonWidth = getWidth buttonRef
toggleWidth = getWidth toggleRef
useLayoutEffectAlways do
when (maxLeft == 0.0) do
bw <- buttonWidth
tw <- toggleWidth
setMaxLeft (bw - tw)
mempty
let
toggleVariants =
{ off: { x: 0.0 }
, on: { x: maxLeft }
}
toggleVariant = Motion.makeVariantLabels toggleVariants
buttonVariants =
{ off: { backgroundColor: (Emotion.str <<< Color.cssStringRGBA <$> props.backgroundOn) ?|| Emotion.str colour.inputBackground }
, on: { backgroundColor: (Emotion.str <<< Color.cssStringRGBA <$> props.backgroundOff) ?|| Emotion.str colour.success }
}
buttonVariant = Motion.makeVariantLabels buttonVariants
hasFocus /\ setHasFocus <- useState' false
tapState <- useRef TapNotAllowed
dragState /\ setDragState <- React.useState' NotDragging
useEffect dragState do
case dragState of
NotDragging -> mempty
Dragging { startX } -> writeRef tapState TapNotAllowed
Dragging { startX, currentX } -> do
writeRef tapState TapNotAllowed
DragDone { startX, endX } -> do
maybeBbox <- getBoundingBoxFromRef buttonRef
for_ maybeBbox \bbox -> do
if endX - startX <= (bbox.left - startX) + (bbox.width / 2.0) then do
props.onToggle false
props.setTogglePosition ToggleIsLeft
else do
props.onToggle true
props.setTogglePosition ToggleIsRight
mempty
pure
$ styled Motion.button
let
buttonVariants =
{ left: { backgroundColor: (Emotion.str <<< Color.cssStringRGBA <$> props.backgroundLeft) ?|| Emotion.str colour.inputBackground }
, right: { backgroundColor: (Emotion.str <<< Color.cssStringRGBA <$> props.backgroundRight) ?|| Emotion.str colour.success }
}
buttonVariant = Motion.makeVariantLabels buttonVariants
-- components
let
result =
container
[ animateTextPresence
[ textContainer
[ textOnContainer
[ guard (props.togglePosition == ToggleIsRight)
$ textOn
[ props.left
?|| el_ Icon.component { icon: SVGIcon.on, stroke: Style.successTextColour }
]
]
, textOffContainer
[ guard (props.togglePosition == ToggleIsLeft)
$ textOff
[ props.right
?|| el_ Icon.component { icon: SVGIcon.off, stroke: Style.disabledTextColour }
]
]
]
]
, toggle
]
container =
styled Motion.button
{ className: "ry-toggle"
, css: Style.button <> guard props.disabled Style.inputDisabled
, onFocus: handler_ $ setHasFocus true
, onBlur: handler_ $ setHasFocus false
, css: Style.button
, transition: Motion.transition { type: "tween", duration: 0.33, ease: "easeOut" }
, variants: Motion.variants buttonVariants
, animate: Motion.animate if props.value then buttonVariant.on else buttonVariant.off
, value: show props.value
, onClick: handler preventDefault \_ -> props.onToggle (not props.value)
, animate:
Motion.animate case props.togglePosition of
ToggleIsRight -> buttonVariant.right
ToggleIsLeft -> buttonVariant.left
, value: show props.togglePosition
, onClick: handler preventDefault \_ -> props.setTogglePosition (flipToggle props.togglePosition)
, style: props.style
, _data: Object.singleton "testid" "toggle-testid"
, role: "switch"
, _aria: Object.singleton "checked" "switch"
, ref: buttonRef
}
[ styled R.div'
{ className: "ry-toggle-text"
, css: Style.toggleTextContainer
}
[ styled R.div'
{ className: "ry-toggle-text-on"
, css: Style.toggleText
}
[ el Motion.animatePresence {}
[ guard (props.value)
$ styled Motion.div
{ className: "ry-toggle-text-on-container"
, css: Style.toggleOnText
, key: "ry-toggle-text-on-container"
, initial: Motion.initial $ css { scale: 0, opacity: 0 }
, animate: Motion.animate $ css { scale: 1, opacity: 1 }
, exit: Motion.exit $ css { scale: 0, opacity: 0 }
}
[ props.on ?|| R.text "I" ]
]
]
, styled R.div'
{ className: "ry-toggle-text-off"
, css: Style.toggleText
}
[ el Motion.animatePresence {}
[ guard (not props.value)
$ styled Motion.div
{ className: "ry-toggle-text-on-container"
, css: Style.toggleOnText
, key: "ry-toggle-text-on-container"
, initial: Motion.initial $ css { scale: 0, opacity: 0 }
, animate: Motion.animate $ css { scale: 1, opacity: 1 }
, exit: Motion.exit $ css { scale: 0, opacity: 0 }
}
[ props.off ?|| R.text "O" ]
]
]
]
, styled Motion.div
textContainer =
styled R.div'
{ className: "ry-toggle-text"
, css: Style.toggleTextContainer
}
textOnContainer =
styled R.div'
{ className: "ry-toggle-text-on"
, css: Style.toggleText
}
textOffContainer =
styled R.div'
{ className: "ry-toggle-text-off"
, css: Style.toggleText
}
textOn =
el Motion.div
{ className: "ry-toggle-text-container"
, key: "ry-toggle-text-on-container"
, initial: Motion.initial $ css { scale: 0, opacity: 0 }
, animate: Motion.animate $ css { scale: 1, opacity: 1 }
, exit: Motion.exit $ css { scale: 0, opacity: 0 }
}
textOff =
el Motion.div
{ className: "ry-toggle-text-container"
, key: "ry-toggle-text-off-container"
, initial: Motion.initial $ css { scale: 0, opacity: 0 }
, animate: Motion.animate $ css { scale: 1, opacity: 1 }
, exit: Motion.exit $ css { scale: 0, opacity: 0 }
}
animateTextPresence = el Motion.animatePresence {}
toggle =
el_ toggleCircle
{ buttonRef
, toggleRef
, togglePosition: props.togglePosition
, setTogglePosition: props.setTogglePosition
, dragState
, setDragState
, tapState
}
pure result
toggleCircle ∷
ReactComponent
{ setTogglePosition ∷ TogglePosition -> Effect Unit
, toggleRef ∷ NodeRef
, buttonRef ∷ NodeRef
, togglePosition ∷ TogglePosition
, dragState ∷ DragState
, setDragState ∷ DragState -> Effect Unit
, tapState ∷ Ref TappingState
}
toggleCircle =
unsafePerformEffect
$ reactComponent "ToggleCircle" do
\( { togglePosition
, setTogglePosition
, toggleRef
, buttonRef
, dragState
, setDragState
, tapState
}
) -> React.do
maxLeft /\ setMaxLeft <- useState' Nothing
useEffectAlways do
when (maxLeft == Nothing) do
runMaybeT_ do
b <- getBoundingBoxFromRef buttonRef # MaybeT
t <- getBoundingBoxFromRef toggleRef # MaybeT
let ml = b.width - t.width - (2.0 * (t.left - b.left))
setMaxLeft (Just ml) # lift
mempty
let
toggleVariants =
{ left: { x: 0.0 }
, right: { x: maxLeft # fromMaybe 0.0 }
}
toggleVariant = Motion.makeVariantLabels toggleVariants
pure
$ styledLeaf Motion.div
{ className: "ry-toggle-toggle"
, css: Style.theToggle props
, layout: Motion.layout true
, onClick: handler stopPropagation mempty
, onTouchStart: handler stopPropagation mempty
, onTouchEnd: handler stopPropagation mempty
, css: Style.theToggle
, drag: Motion.drag "x"
, dragMomentum: Motion.dragMomentum false
, key: if isNothing maxLeft then "initialising" else "ready"
, dragElastic: Motion.dragElastic false
, dragConstraints:
Motion.dragConstraints
{ left: if props.value then negate maxLeft else zero
, right: if props.value then zero else maxLeft
{ left:
case togglePosition of
ToggleIsLeft -> zero
ToggleIsRight -> negate (maxLeft # fromMaybe 0.0)
, right:
case togglePosition of
ToggleIsRight -> zero
ToggleIsLeft -> maxLeft # fromMaybe 0.0
}
, variants: Motion.variants toggleVariants
, transition: Motion.transition { type: "tween", duration: 0.33, ease: "easeOut" }
, whileTap: Motion.prop $ css { scale: 0.85, transition: { type: "tween", duration: 0.10, ease: "easeInOut" } }
, whileTap: Motion.prop $ css { scale: 1.1, transition: { type: "tween", duration: 0.10, ease: "easeInOut" } }
, onTapStart: Motion.onTapStart \_ _ -> writeRef tapState TapAllowed
, onTap:
Motion.onTap \_ pi -> do
ts <- readRef tapState
case ts of
TapAllowed -> props.onToggle $ not props.value
TapAllowed -> setTogglePosition (flipToggle togglePosition)
_ -> mempty
mempty
, onTapCancel:
Motion.onTapCancel \_ pi -> do
writeRef tapState TapNotAllowed
mempty
, animate: Motion.animate if props.value then toggleVariant.on else toggleVariant.off
, initial: Motion.initial false
, animate:
Motion.animate case togglePosition of
ToggleIsLeft -> toggleVariant.left
ToggleIsRight -> toggleVariant.right
, onDragStart:
Motion.onDragStart \_ pi -> do
maybeBBox <- getBoundingBoxFromRef toggleRef
let x = maybeBBox <#> \bbox -> bbox.left + (bbox.width / 2.0)
setDragState $ Dragging { startX: x # fromMaybe pi.point.x }
setDragState $ Dragging { startX: x # fromMaybe pi.point.x, currentX: pi.point.x }
, onDrag:
Motion.onDrag \_ pi -> do
case dragState of
Dragging { startX } -> do
setDragState $ Dragging { startX, currentX: pi.point.x }
other -> Console.warn $ i "Unexpected drag state " (show other) " in onDragEvent"
, onDragEnd:
Motion.onDragEnd \_ pi -> do
case dragState of
Dragging { startX } -> do
maybeBBox <- getBoundingBoxFromRef toggleRef
let x = maybeBBox <#> \bbox -> bbox.left + (bbox.width / 2.0)
setDragState (DragDone { startX, endX: x # fromMaybe pi.point.x })
setDragState (DragDone { startX, endX: x # fromMaybe' \_ -> unsafeCrashWith "shit" })
other -> Console.warn $ i "Unexpected drag state " (show other) " in onDragEvent"
, ref: toggleRef
}
[]
]

View File

@ -0,0 +1,5 @@
module Yoga.Block.Atom.Tooltip
( module Yoga.Block.Atom.Tooltip.View
) where
import Yoga.Block.Atom.Tooltip.View (component, Props)

View File

@ -0,0 +1,16 @@
module Yoga.Block.Atom.Tooltip.Spec where
import Yoga.Prelude.Spec
import React.Basic.DOM as R
import Yoga.Block.Atom.Tooltip as Tooltip
spec ∷ Spec Unit
spec =
after_ cleanup do
describe "The tooltip" do
it "renders without errors" do
void
$ renderComponent Tooltip.component
{ theTip: R.text "Tip"
, target: R.text "Target"
}

View File

@ -0,0 +1,39 @@
module Yoga.Block.Atom.Tooltip.Story where
import Prelude
import Effect (Effect)
import Effect.Unsafe (unsafePerformEffect)
import React.Basic (JSX, element, fragment)
import React.Basic.DOM as R
import React.Basic.Emotion as E
import Yoga (el)
import Yoga.Block.Atom.Tooltip as Tooltip
import Yoga.Block.Container.Style as Styles
default ∷
{ decorators ∷ Array (Effect JSX -> JSX)
, title ∷ String
}
default =
{ title: "Atom/Tooltip"
, decorators:
[ \storyFn ->
R.div_
[ element E.global { styles: Styles.global }
, unsafePerformEffect storyFn
]
]
}
tooltip ∷ Effect JSX
tooltip = do
pure
$ fragment
[ R.div_
[ R.h2_ [ R.text "Basics" ]
, element Tooltip.component
{ theTip: R.text "Hi"
, target: el R.button' {} [ R.text "Holzkop" ]
}
]
]

View File

@ -0,0 +1,57 @@
module Yoga.Block.Atom.Tooltip.Style where
import Yoga.Prelude.Style
import Yoga.Block.Container.Style (colour)
type Props f r =
( css ∷ f Style
, backgroundColour ∷ f Color
| r
)
backgroundColour ∷ String
backgroundColour = colour.text
arrow ∷ ∀ r. { | Props OptionalProp r } -> Style
arrow props =
css
{ position: str "absolute"
, width: str "12px"
, height: str "12px"
, zIndex: str "0"
, "&::before":
nest
{ position: str "absolute"
, width: str "12px"
, height: str "12px"
, borderRadius: str "2px"
, content: str "''"
, transform: str "rotate(45deg)"
, background: str $ (cssStringRGBA <$> props.backgroundColour) ?|| backgroundColour
}
}
content ∷ ∀ r. { | Props OptionalProp r } -> Style
content props =
css
{ background: str $ (cssStringRGBA <$> props.backgroundColour) ?|| backgroundColour
, minWidth: str "50px"
, padding: str "6px"
, zIndex: str "-1"
, color: str colour.background0
, boxShadow: str "0 1px 8px rgba(0,0,0,0.33)"
, borderRadius: var "--s-2"
}
popper ∷ Style
popper =
css
{ "&[data-popper-placement^='top'] > * > .popper-arrow":
nest { bottom: str "-4px" }
, "&[data-popper-placement^='bottom'] > * > .popper-arrow":
nest { top: str "-4px" }
, "&[data-popper-placement^='left'] > * > .popper-arrow":
nest { right: str "-4px" }
, "&[data-popper-placement^='right'] > * > .popper-arrow":
nest { left: str "-4px" }
}

View File

@ -0,0 +1,129 @@
module Yoga.Block.Atom.Tooltip.View (component, MandatoryProps, Props, PropsF) where
import Yoga.Prelude.View
import Debug.Trace (spy)
import Effect.Aff (Milliseconds(..), delay)
import Effect.Uncurried (mkEffectFn1)
import Framer.Motion as Motion
import React.Basic.DOM (css)
import React.Basic.DOM as R
import React.Basic.Hooks as React
import React.Basic.Hooks.Aff (useAff)
import React.Basic.Popper.Hook (usePopper)
import React.Basic.Popper.Types (modifierArrow, modifierOffset, nullRef)
import Unsafe.Coerce (unsafeCoerce)
import Yoga.Block.Atom.Tooltip.Style as Style
type PropsF f =
( className ∷ f String
, hideDelay ∷ f Milliseconds
| Style.Props f (MandatoryProps ())
)
type MandatoryProps r =
( theTip ∷ JSX
, target ∷ JSX
| r
)
type Props =
PropsF Id
type PropsOptional =
PropsF OptionalProp
component ∷ ∀ p p_. Union p p_ Props => ReactComponent { | MandatoryProps p }
component = rawComponent
rawComponent ∷ ∀ p. ReactComponent (Record p)
rawComponent =
mkForwardRefComponent "Tooltip" do
\(props ∷ { | PropsOptional }) ref -> React.do
-- Hooks
referenceElement /\ setReferenceElement <- React.useState' nullRef
popperElement /\ setPopperElement <- React.useState' nullRef
arrowElement /\ setArrowElement <- React.useState' nullRef
{ styles, attributes } <-
usePopper referenceElement popperElement
{ modifiers:
[ modifierArrow arrowElement
, modifierOffset { x: 0.0, y: 12.0 }
]
}
visible /\ setVisible <- React.useState' false
touching /\ setTouching <- React.useState' false
hovering /\ setHovering <- React.useState' false
useAff hovering do
if hovering then
setVisible true # liftEffect
else do
delay (props.hideDelay ?|| (0.0 # Milliseconds))
setVisible false # liftEffect
useAff touching do
if touching then do
let _ = spy "sausage" touching
delay (1000.0 # Milliseconds)
setVisible true # liftEffect
else do
delay (props.hideDelay ?|| (450.0 # Milliseconds))
setVisible false # liftEffect
-- Handlers
let
hoveredIn = setHovering true
hoveredOut = setHovering false
touchStarted = setTouching true
touchEnded = setTouching false
-- Elements
let
result =
fragment
$ [ refElem
, popperEl
[ animatePresence
$ guard visible
[ content
[ props.theTip
, arrow
]
]
]
]
animatePresence = el Motion.animatePresence { initial: false }
content =
styled Motion.div
{ className: "popper-element-content"
, css: Style.content props
, initial: Motion.initial $ R.css { opacity: [ 1.0, 0.8, 0.0 ], scale: [ 1.0, 0.85 ] }
, animate: Motion.animate $ R.css { opacity: [ 0.0, 0.3, 1.0 ], scale: [ 0.0, 1.05, 1.0, 0.98, 1.01, 1.0 ] }
, exit: Motion.exit $ R.css { opacity: [ 1.0, 0.8, 0.0 ], scale: [ 1.0, 0.85 ] }
, transition: Motion.transition { duration: 0.2 }
, key: "container"
}
popperEl =
styled R.div'
{ className: "popper-element"
, css: Style.popper
, ref: unsafeCoerce (mkEffectFn1 setPopperElement)
, style: styles.popper
, _data: attributes.popper
}
arrow =
styledLeaf R.div'
{ className: "popper-arrow"
, id: "arrow"
, css: Style.arrow props
, ref: unsafeCoerce (mkEffectFn1 setArrowElement)
, style: styles.arrow
, _data: attributes.arrow
}
refElem =
element Motion.div
{ ref: unsafeCoerce (mkEffectFn1 setReferenceElement)
, style: css { display: "inline-block" }
, children: [ props.target ]
, onHoverStart: Motion.onHoverStart \_ _ -> hoveredIn
, onHoverEnd: Motion.onHoverEnd \_ _ -> hoveredOut
, onTouchStart: handler_ touchStarted
, onTouchEnd: handler_ touchEnded
}
pure result

View File

@ -50,7 +50,7 @@ mkGlobal maybeMode =
, body:
nested
$ css
{ fontFamily: str "var(--mainFont)"
{ fontFamily: str "var(--main-font)"
, background: str colour.background0
, color: str colour.text
, margin: str "0"
@ -61,7 +61,7 @@ mkGlobal maybeMode =
Just LightMode -> lightModeStyle
, "pre,code":
nest
{ fontFamily: str "var(--monoFont)"
{ fontFamily: str "var(--mono-font)"
}
, "h1,h2,h3,h4,h5":
nest
@ -76,7 +76,6 @@ mkGlobal maybeMode =
$ css
{ boxSizing: str "inherit"
}
, input
, form:
nested
$ css
@ -155,7 +154,7 @@ defaultColours =
-- highlight = Color.rgb 0x10 0x45 0x4A
darkBg = Color.rgb 0 0 0
lightBg = Color.rgb 255 255 255
lightBg = Color.rgb 250 250 250
type FlatTheme a =
{ background0 ∷ a
@ -262,22 +261,8 @@ variables =
fontVariables ∷ { main ∷ String, mono ∷ String } -> Style
fontVariables { main, mono } =
css
{ "--mainFont": str $ main <> """, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol""""
, "--monoFont": str $ mono <> ", monospace, monospace"
}
input ∷ StyleProperty
input =
nest
{ backgroundColor: "var(--input-bg-col)" # str
, padding: "5px" # str
, border: str "1px solid var(--bg-col)"
, borderRadius: "5px" # str
, fontSize: "12px" # str
, paddingBottom: "4px" # str
, paddingLeft: "8px" # str
, "&:disabled": nest { color: str "var(--input-text-col-disabled)" }
, "&:focus": nested inputFocus
{ "--main-font": str $ main <> """, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol""""
, "--mono-font": str $ mono <> ", monospace, monospace"
}
-- Standalone style for storybook

View File

@ -0,0 +1,32 @@
module Yoga.Block.Icon.SVG.EyeClosed where
import React.Basic (JSX)
import React.Basic.DOM as R
import React.Basic.DOM.SVG as SVG
eyeClosed ∷ JSX
eyeClosed =
SVG.svg
{ viewBox: "0 0 100 100"
, xmlns: "http://www.w3.org/2000/svg"
, fillRule: "evenodd"
, clipRule: "evenodd"
, strokeLinejoin: "round"
, strokeMiterlimit: "1.5"
, children:
[ SVG.path
{ fill: "none"
, d: "M0 0h100v100H0z"
}
, SVG.path
{ d: "M5.492 63.902c-1.539-2.426-2.587-11.465-1.593-13.076.352.369 4.353-.86 4.732-.54 24.519 20.777 51.057 22.859 83.889.676.892-.603 2.312-1.948 3.567-.637.892 2.137 2.479 6.187 2.439 10.011-.858 3.046-4.013 8.107-6.554 10.666.091-3.033-1.654-9.054-2.787-11.919-1.169 1.053-4.237 3.361-5.557 4.507 2.021 6.439 3.704 10.343 4.342 13.77-1.418 2.716-6.433 6.382-8.172 6.837-.695-3.519-3.589-12.626-4.791-16.545-1.721 1.014-7.746 2.85-7.739 2.967.294 5.01 1.269 11.753 3.159 17.431-2.331 1.521-7.403 2.045-10.644 2.233-1.477-3.05-1.902-13.039-2.221-17.74-1.768.532-6.838.5-8.865.679-.682 5.829-.99 11.62-.47 17.094-2.634-.024-8.846-1.614-10.179-2.127-.34-.886.675-13.622.808-16.23-1.442-.074-7.906-2.268-8.801-2.513-.501 3.058-1.153 12.152-.578 14.598-1.516-.601-8.651-6.109-9.148-6.3-.617-3.543-.097-9.17.23-12.361-.919-.313-5.78-3.571-6.551-4.211-.87 8.919-.1 11.254.106 11.869-1.502-1.338-7.45-7.873-8.622-9.139z"
, fill: "var(--stroke-colour)"
}
, SVG.path
{ d: "M50.026 26.996c23.346 0 42.3 24.595 42.3 24.595s-18.954 17.592-42.3 17.592c-23.347 0-42.301-17.592-42.301-17.592s18.954-24.595 42.301-24.595z"
, fill: "none"
, stroke: "var(--stroke-colour)"
, strokeWidth: "8"
}
]
}

View File

@ -0,0 +1,44 @@
module Yoga.Block.Icon.SVG.EyeOpen where
import React.Basic (JSX)
import React.Basic.DOM as R
import React.Basic.DOM.SVG as SVG
eyeOpen ∷ JSX
eyeOpen =
SVG.svg
{ viewBox: "0 0 100 100"
, xmlns: "http://www.w3.org/2000/svg"
, fillRule: "evenodd"
, clipRule: "evenodd"
, strokeLinejoin: "round"
, strokeMiterlimit: "1.5"
, children:
[ SVG.path
{ fill: "none"
, d: "M0 0h100v100H0z"
}
, SVG.path
{ d: "M50.173 21.996c23.346 0 42.3 27.978 42.3 27.978s-18.954 27.979-42.3 27.979c-23.346 0-42.301-27.979-42.301-27.979s18.955-27.978 42.301-27.978z"
, fill: "none"
, stroke: "var(--stroke-colour)"
, strokeWidth: "8"
}
, SVG.circle
{ cx: "49.963"
, cy: "50.087"
, r: "24.205"
, fill: "none"
, stroke: "var(--stroke-colour)"
, strokeWidth: "6"
}
, SVG.circle
{ cx: "49.963"
, cy: "50.087"
, r: "9.748"
, fill: "var(--stroke-colour)"
, stroke: "var(--stroke-colour)"
, strokeWidth: "8"
}
]
}

View File

@ -0,0 +1,13 @@
module Yoga.Block.Icon.SVG
( module Yoga.Block.Icon.SVG.On
, module Yoga.Block.Icon.SVG.Off
, module Yoga.Block.Icon.SVG.MagnifyingGlass
, module Yoga.Block.Icon.SVG.EyeClosed
, module Yoga.Block.Icon.SVG.EyeOpen
) where
import Yoga.Block.Icon.SVG.On (on)
import Yoga.Block.Icon.SVG.Off (off)
import Yoga.Block.Icon.SVG.MagnifyingGlass (magnifyingGlass)
import Yoga.Block.Icon.SVG.EyeClosed (eyeClosed)
import Yoga.Block.Icon.SVG.EyeOpen (eyeOpen)

View File

@ -0,0 +1,36 @@
module Yoga.Block.Icon.SVG.MagnifyingGlass where
import React.Basic (JSX)
import React.Basic.DOM as R
import React.Basic.DOM.SVG as SVG
magnifyingGlass ∷ JSX
magnifyingGlass =
SVG.svg
{ viewBox: "0 0 100 100"
, xmlns: "http://www.w3.org/2000/svg"
, fillRule: "evenodd"
, clipRule: "evenodd"
, strokeLinejoin: "round"
, strokeMiterlimit: "1.5"
, children:
[ SVG.path
{ fill: "none"
, d: "M0 0h100v100H0z"
}
, SVG.circle
{ cx: "42.994"
, cy: "42.837"
, r: "30.313"
, fill: "none"
, stroke: "var(--stroke-colour)"
, strokeWidth: "10"
}
, SVG.path
{ d: "M66.723 66.665l24.062 24.062"
, fill: "none"
, stroke: "var(--stroke-colour)"
, strokeWidth: "8"
}
]
}

View File

@ -0,0 +1,30 @@
module Yoga.Block.Icon.SVG.Off where
import React.Basic (JSX)
import React.Basic.DOM as R
import React.Basic.DOM.SVG as SVG
off ∷ JSX
off =
SVG.svg
{ viewBox: "0 0 100 100"
, xmlns: "http://www.w3.org/2000/svg"
, fillRule: "evenodd"
, clipRule: "evenodd"
, strokeLinejoin: "round"
, strokeMiterlimit: "1.5"
, children:
[ SVG.path
{ fill: "none"
, d: "M0 0h100v100H0z"
}
, SVG.circle
{ cx: "50"
, cy: "50"
, r: "37.808"
, fill: "none"
, stroke: "var(--stroke-colour)"
, strokeWidth: "20"
}
]
}

View File

@ -0,0 +1,41 @@
module Yoga.Block.Icon.SVG.On where
import React.Basic (JSX)
-- import React.Basic.DOM as R
import React.Basic.DOM.SVG as SVG
on ∷ JSX
on =
SVG.svg
{ viewBox: "0 0 100 100"
, xmlns: "http://www.w3.org/2000/svg"
, fillRule: "evenodd"
, clipRule: "evenodd"
, strokeLinejoin: "round"
, strokeMiterlimit: "1.5"
, children:
[ SVG.path
{ fill: "none"
, d: "M0 0h100v100H0z"
}
, SVG.clipPath
{ id: "a"
, children:
[ SVG.path
{ d: "M0 0h100v100H0z"
}
]
}
, SVG.g
{ clipPath: "url(#a)"
, children:
[ SVG.path
{ d: "M50 1.921v96.261"
, fill: "none"
, stroke: "var(--stroke-colour)"
, strokeWidth: "20"
}
]
}
]
}

View File

@ -0,0 +1,25 @@
module Yoga.Block.Icon.SVG.QuestionMark where
import React.Basic (JSX)
import React.Basic.DOM as R
import React.Basic.DOM.SVG as SVG
questionMark :: JSX
questionMark = SVG.svg
{ viewBox: "0 0 100 100"
, xmlns: "http://www.w3.org/2000/svg"
, fillRule: "evenodd"
, clipRule: "evenodd"
, strokeLinejoin: "round"
, strokeMiterlimit: "2"
, children:
[ SVG.path
{ fill: "none"
, d: "M0 0h100v100H0z"
}
, SVG.path
{ d: "M50 10.037c22.056 0 39.963 17.907 39.963 39.963S72.056 89.963 50 89.963 10.037 72.056 10.037 50 27.944 10.037 50 10.037zM48.871 77.03c2.87 0 5.326-2.384 5.351-5.351-.025-2.918-2.481-5.301-5.351-5.301-2.967 0-5.374 2.383-5.35 5.301-.024 2.967 2.383 5.351 5.35 5.351zm-4.255-15.176h8.098v-.632c.049-5.156 1.727-7.515 5.691-9.947 4.474-2.675 7.271-6.323 7.271-11.892 0-8.22-6.396-13.375-15.686-13.375-8.512 0-15.418 4.718-15.661 13.959h8.657c.219-4.572 3.526-6.761 6.955-6.761 3.721 0 6.713 2.481 6.713 6.299 0 3.405-2.262 5.739-5.205 7.587-4.304 2.676-6.785 5.375-6.833 14.13v.632z"
, fill: "#333"
}
]
}

View File

@ -0,0 +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="1.5"><path fill="none" d="M0 0h100v100H0z"/><path d="M5.492 63.902c-1.539-2.426-2.587-11.465-1.593-13.076.352.369 4.353-.86 4.732-.54 24.519 20.777 51.057 22.859 83.889.676.892-.603 2.312-1.948 3.567-.637.892 2.137 2.479 6.187 2.439 10.011-.858 3.046-4.013 8.107-6.554 10.666.091-3.033-1.654-9.054-2.787-11.919-1.169 1.053-4.237 3.361-5.557 4.507 2.021 6.439 3.704 10.343 4.342 13.77-1.418 2.716-6.433 6.382-8.172 6.837-.695-3.519-3.589-12.626-4.791-16.545-1.721 1.014-7.746 2.85-7.739 2.967.294 5.01 1.269 11.753 3.159 17.431-2.331 1.521-7.403 2.045-10.644 2.233-1.477-3.05-1.902-13.039-2.221-17.74-1.768.532-6.838.5-8.865.679-.682 5.829-.99 11.62-.47 17.094-2.634-.024-8.846-1.614-10.179-2.127-.34-.886.675-13.622.808-16.23-1.442-.074-7.906-2.268-8.801-2.513-.501 3.058-1.153 12.152-.578 14.598-1.516-.601-8.651-6.109-9.148-6.3-.617-3.543-.097-9.17.23-12.361-.919-.313-5.78-3.571-6.551-4.211-.87 8.919-.1 11.254.106 11.869-1.502-1.338-7.45-7.873-8.622-9.139z" fill="#333"/><path d="M50.026 26.996c23.346 0 42.3 24.595 42.3 24.595s-18.954 17.592-42.3 17.592c-23.347 0-42.301-17.592-42.301-17.592s18.954-24.595 42.301-24.595z" fill="none" stroke="#333" stroke-width="8"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +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="1.5"><path fill="none" d="M0 0h100v100H0z"/><path d="M50.173 21.996c23.346 0 42.3 27.978 42.3 27.978s-18.954 27.979-42.3 27.979c-23.346 0-42.301-27.979-42.301-27.979s18.955-27.978 42.301-27.978z" fill="none" stroke="#333" stroke-width="8"/><circle cx="49.963" cy="50.087" r="24.205" fill="none" stroke="#333" stroke-width="6"/><circle cx="49.963" cy="50.087" r="9.748" fill="#333" stroke="#333" stroke-width="8"/></svg>

After

Width:  |  Height:  |  Size: 564 B

View File

@ -0,0 +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="1.5"><path fill="none" d="M0 0h100v100H0z"/><circle cx="42.994" cy="42.837" r="30.313" fill="none" stroke="#333" stroke-width="10"/><path d="M66.723 66.665l24.062 24.062" fill="none" stroke="#333" stroke-width="8"/></svg>

After

Width:  |  Height:  |  Size: 366 B

View File

@ -0,0 +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="1.5"><path fill="none" d="M0 0h100v100H0z"/><circle cx="50" cy="50" r="37.808" fill="none" stroke="#333" stroke-width="20"/></svg>

After

Width:  |  Height:  |  Size: 275 B

View File

@ -0,0 +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="1.5"><path fill="none" d="M0 0h100v100H0z"/><clipPath id="a"><path d="M0 0h100v100H0z"/></clipPath><g clip-path="url(#a)"><path d="M50 1.921v96.261" fill="none" stroke="#333" stroke-width="20"/></g></svg>

After

Width:  |  Height:  |  Size: 349 B

View File

@ -0,0 +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 10.037c22.056 0 39.963 17.907 39.963 39.963S72.056 89.963 50 89.963 10.037 72.056 10.037 50 27.944 10.037 50 10.037zM48.871 77.03c2.87 0 5.326-2.384 5.351-5.351-.025-2.918-2.481-5.301-5.351-5.301-2.967 0-5.374 2.383-5.35 5.301-.024 2.967 2.383 5.351 5.35 5.351zm-4.255-15.176h8.098v-.632c.049-5.156 1.727-7.515 5.691-9.947 4.474-2.675 7.271-6.323 7.271-11.892 0-8.22-6.396-13.375-15.686-13.375-8.512 0-15.418 4.718-15.661 13.959h8.657c.219-4.572 3.526-6.761 6.955-6.761 3.721 0 6.713 2.481 6.713 6.299 0 3.405-2.262 5.739-5.205 7.587-4.304 2.676-6.785 5.375-6.833 14.13v.632z" fill="#333"/></svg>

After

Width:  |  Height:  |  Size: 796 B

View File

@ -11,6 +11,7 @@ module Yoga.Prelude.Default
, module Data.Foldable
, module Data.FoldableWithIndex
, module Data.FunctorWithIndex
, runMaybeT_
) where
import Prelude
@ -25,3 +26,6 @@ import Data.Maybe (Maybe(..), fromMaybe, fromMaybe', isJust, maybe)
import Data.Monoid (guard)
import Effect (Effect)
import Effect.Class (liftEffect)
runMaybeT_ ∷ ∀ f a. Functor f => MaybeT f a -> f Unit
runMaybeT_ = void <<< runMaybeT

View File

@ -4854,25 +4854,23 @@ fragment-cache@^0.2.1:
dependencies:
map-cache "^0.2.2"
framer-motion@^2.7.0:
version "2.9.5"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-2.9.5.tgz#bbb185325d531c57f494cf3f6cf7719fc2c225c7"
integrity sha512-epSX4Co1YbDv0mjfHouuY0q361TpHE7WQzCp/xMTilxy4kXd+Z23uJzPVorfzbm1a/9q1Yu8T5bndaw65NI4Tg==
framer-motion@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-3.1.1.tgz#a8a779501213b7ce02cc35beb27621d73cc2f1e7"
integrity sha512-Gm1QSb0xUxuhcPar5FIs5Ws+STrhLZ6XZf2Io8dVwFofe1OzwkL9asGFVu7z3y6WqC4Hvnxm7wsW5SBHlxZDYw==
dependencies:
framesync "^4.1.0"
framesync "^5.0.0"
hey-listen "^1.0.8"
popmotion "9.0.0-rc.20"
style-value-types "^3.1.9"
popmotion "^9.0.2"
style-value-types "^3.2.0"
tslib "^1.10.0"
optionalDependencies:
"@emotion/is-prop-valid" "^0.8.2"
framesync@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/framesync/-/framesync-4.1.0.tgz#69a8db3ca432dc70d6a76ba882684a1497ef068a"
integrity sha512-MmgZ4wCoeVxNbx2xp5hN/zPDCbLSKiDt4BbbslK7j/pM2lg5S0vhTNv1v8BCVb99JPIo6hXBFdwzU7Q4qcAaoQ==
dependencies:
hey-listen "^1.0.5"
framesync@5.0.0, framesync@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/framesync/-/framesync-5.0.0.tgz#7de8caedf53ac441118e79680f1beb7391c328b6"
integrity sha512-wd8t+JsQGisluSv1twiEeDv0aNGpavGb9q7xgIk9fGbcIWkNXF/KVtrjnOrCwBWJuiXxlJfNkcvGudsI32FxYA==
fresh@0.5.2:
version "0.5.2"
@ -5315,7 +5313,7 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
hey-listen@^1.0.5, hey-listen@^1.0.8:
hey-listen@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68"
integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==
@ -7489,14 +7487,14 @@ polished@^3.4.4:
dependencies:
"@babel/runtime" "^7.9.2"
popmotion@9.0.0-rc.20:
version "9.0.0-rc.20"
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-9.0.0-rc.20.tgz#f3550042ae31957b5416793ae8723200951ad39d"
integrity sha512-f98sny03WuA+c8ckBjNNXotJD4G2utG/I3Q23NU69OEafrXtxxSukAaJBxzbtxwDvz3vtZK69pu9ojdkMoBNTg==
popmotion@^9.0.2:
version "9.0.2"
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-9.0.2.tgz#477650c3b4af97161011809223d9ca6860f3a2b5"
integrity sha512-WfSg8IfoUwYIP9uqeqbgncIsMHLAKWqebT2IP1aGAI6gdSJqTPy/H8NvP4ZyDtDCUCx5Yh3Pth/7iUJjIwR7LA==
dependencies:
framesync "^4.1.0"
framesync "5.0.0"
hey-listen "^1.0.8"
style-value-types "^3.1.9"
style-value-types "3.2.0"
tslib "^1.10.0"
portfinder@^1.0.26:
@ -9087,10 +9085,10 @@ style-loader@^1.2.1:
loader-utils "^2.0.0"
schema-utils "^2.7.0"
style-value-types@^3.1.9:
version "3.1.9"
resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-3.1.9.tgz#faf7da660d3f284ed695cff61ea197d85b9122cc"
integrity sha512-050uqgB7WdvtgacoQKm+4EgKzJExVq0sieKBQQtJiU3Muh6MYcCp4T3M8+dfl6VOF2LR0NNwXBP1QYEed8DfIw==
style-value-types@3.2.0, style-value-types@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-3.2.0.tgz#eb89cab1340823fa7876f3e289d29d99c92111bb"
integrity sha512-ih0mGsrYYmVvdDi++/66O6BaQPRPRMQHoZevNNdMMcPlP/cH28Rnfsqf1UEba/Bwfuw9T8BmIMwbGdzsPwQKrQ==
dependencies:
hey-listen "^1.0.8"
tslib "^1.10.0"