I have changed the code files

This commit is contained in:
Mark Eibes 2022-11-21 09:13:57 +01:00
parent c0c9705851
commit 56087af264
12 changed files with 267 additions and 304 deletions

View File

@ -1,6 +1,6 @@
{
"editor.formatOnSave": true,
"workbench.colorTheme": "Espresso Soda",
"workbench.colorTheme": "Halcyon",
"search.useGlobalIgnoreFiles": true,
"files.watcherExclude": {
"**/.spago/**": true,

View File

@ -22,7 +22,7 @@ import Yoga.Block.Layout.Imposter as Imposter
import Yoga.Block.Layout.Sidebar as Sidebar
import Yoga.Block.Layout.Stack as Stack
import Yoga.Block.Layout.Switcher as Switcher
import Yoga.Block.Atom.Modal as Modal
import Yoga.Block.Molecule.Modal as Modal
import Yoga.Block.Molecule.ReadMore as ReadMore
import Yoga.Block.Quark.Layer as Layer
@ -207,4 +207,3 @@ toggle = Toggle.component
readMore ∷ ∀ p q. Union p q ReadMore.Props ⇒ ReactComponent (Record p)
readMore = ReadMore.component

View File

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

View File

@ -1,15 +0,0 @@
module Yoga.Block.Atom.Modal.Style where
import Yoga.Prelude.Style
clickAway ∷ Style
clickAway =
widthScreen
<> heightScreen
<> positionFixed
<> left zero
<> top zero
<> acceptClicks
modal ∷ Style
modal = acceptClicks

View File

@ -1,90 +0,0 @@
module Yoga.Block.Atom.Modal.View where
import Yoga.Prelude.View
import Effect.Unsafe (unsafePerformEffect)
import Fahrtwind.Style.Color.Background (background)
import Fahrtwind.Style.Color.Tailwind as TW
import Fahrtwind.Style.Color.Util (withAlpha)
import Framer.Motion as M
import React.Basic.DOM as R
import React.Basic.Hooks as React
import Yoga.Block.Atom.Modal.Style as Style
import Yoga.Block.Hook.UseRenderInPortal (useRenderInPortal)
import Yoga.Block.Layout.Centre.View as Centre
import Yoga.Block.Layout.Cover.View as Cover
import Yoga.Block.Quark.ClickAway.View as ClickAway
type ModalIds = { clickAwayId ∷ String, modalContainerId ∷ String }
type Props =
{ hide ∷ Effect Unit
, isVisible ∷ Boolean
, content ∷ JSX
, allowClickAway ∷ Boolean
, clickAwayId ∷ String
, modalContainerId ∷ String
}
component ∷ ReactComponent Props
component = unsafePerformEffect do
React.reactComponent "Modal" \props → React.do
let
{ hide
, isVisible
, content
, allowClickAway
, clickAwayId
, modalContainerId
} = props
renderInPortal ← useRenderInPortal modalContainerId
let
child = div "ry-modal" Style.modal [ content ]
pure $ fragment
[ ClickAway.component </>
{ css: background (TW.gray._900 # withAlpha 0.5)
, hide: if allowClickAway then hide else mempty
, isVisible
, clickAwayId
}
, renderInPortal
$ Cover.component
</ {}
/>
[ Centre.component </ {} />
[ M.animatePresence </ {} />
[ guard isVisible
$ M.div
</
{ key: "popOver"
, initial: M.initial
( R.css
{ opacity: 0.0
, scale: 0.3
}
)
, animate: M.animate
( R.css
{ opacity: 1
, scale: 1.0
, transition:
{ duration: 0.4, type: "spring" }
}
)
, exit: M.exit
( R.css
{ opacity: 0
, scale: 0.3
, transition: { duration: 0.2 }
}
)
-- , onAnimationComplete
-- , onAnimationStart
-- , ref: motionRef
}
/>
[ child ]
]
]
]
]

View File

@ -1,79 +0,0 @@
module Yoga.Block.Molecule.Modal.Story where
import Prelude
import Data.Foldable (foldMap)
import Data.Maybe (Maybe(..), isNothing)
import Data.String.NonEmpty.Internal (NonEmptyString(..))
import Data.Tuple.Nested ((/\))
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 React.Basic.Hooks (reactComponent, useEffectAlways)
import React.Basic.Hooks as React
import Web.DOM (Element)
import Web.DOM.NonElementParentNode (getElementById)
import Web.HTML (window)
import Web.HTML.HTMLDocument (toNonElementParentNode)
import Web.HTML.Window (document)
import Yoga as Y
import Yoga.Block as Block
import Yoga.Block.Container.Style (colour)
import Yoga.Block.Container.Style as Styles
import Yoga.Block.Molecule.Modal as Modal
default ∷
{ decorators ∷ Array (Effect JSX -> JSX)
, title ∷ String
}
default =
{ title: "Molecule/Modal"
, decorators:
[ \storyFn ->
R.div_
[ element E.global { styles: Styles.global }
, unsafePerformEffect storyFn
]
]
}
modal ∷ Effect JSX
modal = do
pure $ React.element compo {}
where
compo =
unsafePerformEffect
$ reactComponent "Modal Story" \{} -> React.do
isOpen /\ setIsOpen <- React.useState' true
maybeModalElement /\ setModalElement <- React.useState' Nothing
useEffectAlways do
when (isNothing maybeModalElement) do
container <- getElementById "modal-container" =<< (map toNonElementParentNode $ document =<< window)
setModalElement container
mempty
pure
$ fragment
[ R.h2_ [ R.text "No Options" ]
, Y.el R.button'
{ onClick: handler_ (setIsOpen true)
}
[ R.text "Show Modal"
]
, R.div { id: "modal-container" }
, maybeModalElement
# foldMap \(modalElement ∷ Element) ->
element Modal.component
{ content:
Block.box
{ borderRadius: E.str "var(--s3)"
, background: colour.backgroundLayer1
, padding: E.str "var(--s3) var(--s2)"
}
[ Y.leaf Block.input { label: NonEmptyString "A label that should fit" } ]
, isOpen
, onDismiss: setIsOpen false
, target: modalElement
}
]

View File

@ -2,27 +2,14 @@ module Yoga.Block.Molecule.Modal.Style where
import Yoga.Prelude.Style
type Props :: forall k. (Type -> k) -> Row k -> Row k
type Props f r =
( css ∷ f Style
| r
)
clickAway ∷ Style
clickAway =
widthScreen
<> heightScreen
<> positionFixed
<> left zero
<> top zero
<> acceptClicks
modal ∷ Style
modal = styles
where
styles =
css
{}
clickaway ∷ Style
clickaway =
css
{ width: 100.0 # vw
, height: 100.0 # vh
, position: fixed
, left: _0
, top: _0
, backdropFilter: str "blur(4px) brightness(80%)"
, zIndex: str "3"
}
modal = acceptClicks

View File

@ -1,92 +1,90 @@
module Yoga.Block.Molecule.Modal.View (component, Props) where
module Yoga.Block.Molecule.Modal.View where
import Yoga.Prelude.View
import Data.Nullable as Nullable
import Effect.Unsafe (unsafePerformEffect)
import Framer.Motion (withMotion)
import Framer.Motion as Motion
import React.Basic.DOM (createPortal, css)
import Fahrtwind.Style.Color.Background (background)
import Fahrtwind.Style.Color.Tailwind as TW
import Fahrtwind.Style.Color.Util (withAlpha)
import Framer.Motion as M
import React.Basic.DOM as R
import React.Basic.Emotion as Emotion
import React.Basic.Hooks (reactComponent)
import React.Basic.Hooks as React
import Web.DOM (Element)
import Yoga.Block.Hook.Key as KeyCode
import Yoga.Block.Hook.UseKeyDown (useKeyDown)
import Yoga.Block.Layout.Imposter as Imposter
import Yoga.Block.Molecule.Modal.Style as Style
import Yoga.Block.Hook.UseRenderInPortal (useRenderInPortal)
import Yoga.Block.Layout.Centre.View as Centre
import Yoga.Block.Layout.Cover.View as Cover
import Yoga.Block.Quark.ClickAway.View as ClickAway
type ModalIds = { clickAwayId ∷ String, modalContainerId ∷ String }
type Props =
{ content ∷ JSX
, isOpen ∷ Boolean
, onDismiss ∷ Effect Unit
, target ∷ Element
| ()
{ hide ∷ Effect Unit
, isVisible ∷ Boolean
, content ∷ JSX
, allowClickAway ∷ Boolean
, clickAwayId ∷ String
, modalContainerId ∷ String
}
component ∷ ReactComponent Props
component =
unsafePerformEffect
$ reactComponent "Modal Wrapper" \{ content, isOpen, onDismiss, target } -> React.do
useKeyDown $ \_ _ -> case _ of
KeyCode.Escape -> onDismiss
_ -> mempty
clickAwayRef <- React.useRef Nullable.null
let
toRender ∷ JSX
toRender =
R.div' </ {}
/>
[ Motion.animatePresence </ {} /> [ guard isOpen $ element clickaway { theRef: clickAwayRef, onDismiss } ]
, Motion.animatePresence </ {} /> [ guard isOpen $ element window { clickAwayRef, onDismiss, content } ]
]
pure (createPortal toRender target)
clickaway ∷ ReactComponent { theRef ∷ Ref (Nullable Node), onDismiss ∷ Effect Unit }
clickaway =
unsafePerformEffect
$ reactComponent "Modal Clickaway" \{ theRef, onDismiss } -> React.do
pure $ Emotion.elementKeyed Motion.div
$
{ key: "ry-modal-clickaway"
, onClick: handler_ onDismiss
, className: "ry-modal-clickaway"
, css: Style.clickaway
, initial: Motion.prop $ css { opacity: 0.0 }
, animate: Motion.prop $ css { opacity: 1.0 }
, exit: Motion.prop $ css { opacity: 0.0 }
, ref: theRef
}
window ∷ ReactComponent { clickAwayRef ∷ NodeRef, content ∷ JSX, onDismiss ∷ Effect Unit }
window =
unsafePerformEffect
$ reactComponent "Modal Window" \{ clickAwayRef, content, onDismiss } -> React.do
imposterRef <- useRef null
pure
$ Emotion.element motionImposter
( { className: "ry-modal-window"
, css: Style.modal
, ref: imposterRef
, onClick: handler_ onDismiss
, children:
[ Motion.div
</*
{ className: "ry-modal"
, css: Style.modal
, drag: Motion.prop true
, dragMomentum: Motion.prop false
, dragConstraints: Motion.prop clickAwayRef
, onClick: handler stopPropagation mempty
component = unsafePerformEffect do
React.reactComponent "Modal" \props → React.do
let
{ hide
, isVisible
, content
, allowClickAway
, clickAwayId
, modalContainerId
} = props
renderInPortal ← useRenderInPortal modalContainerId
let
child = div "ry-modal" Style.modal [ content ]
pure $ fragment
[ ClickAway.component </>
{ css: background (TW.gray._900 # withAlpha 0.5)
, hide: if allowClickAway then hide else mempty
, isVisible
, clickAwayId
}
, renderInPortal
$ Cover.component
</ {}
/>
[ Centre.component </ {} />
[ M.animatePresence </ {} />
[ guard isVisible
$ M.div
</
{ key: "popOver"
, initial: M.initial
( R.css
{ opacity: 0.0
, scale: 0.3
}
)
, animate: M.animate
( R.css
{ opacity: 1
, scale: 1.0
, transition:
{ duration: 0.4, type: "spring" }
}
)
, exit: M.exit
( R.css
{ opacity: 0
, scale: 0.3
, transition: { duration: 0.2 }
}
)
-- , onAnimationComplete
-- , onAnimationStart
-- , ref: motionRef
}
/> [ content ]
/>
[ child ]
]
}
`withMotion`
{ initial: css { transform: "translate(-50%, -50%) scale3d(0.1,0.1,0.1)", opacity: 0 }
, animate: css { transform: "translate(-50%, -50%) scale3d(1,1,1)", opacity: 1 }
, exit: css { transform: "translate(-50%, -50%) scale3d(0.1,0.1,0.1)", opacity: 0 }
}
)
where
motionImposter = unsafePerformEffect $ Motion.custom Imposter.component
]
]
]

View File

@ -0,0 +1,53 @@
module Story.Yoga.Block.Atom.Range where
import Prelude
import Yoga.Block.Container.Style as Styles
import Yoga.Block.Atom.Range as Range
import Effect (Effect)
import Effect.Unsafe (unsafePerformEffect)
import React.Basic (JSX, element, fragment)
import React.Basic.DOM (css)
import React.Basic.DOM as R
import React.Basic.Emotion as E
default ∷
{ decorators ∷ Array (Effect JSX → JSX)
, title ∷ String
}
default =
{ title: "Atom/Range"
, decorators:
[ \storyFn →
R.div_
[ element E.global { styles: Styles.global }
, unsafePerformEffect storyFn
]
]
}
range ∷ Effect JSX
range =
pure
$ fragment
[ R.div_
[ R.h2_ [ R.text "No Options" ]
, element Range.component
{}
, R.h2_ [ R.text "Min and max" ]
, element Range.component
{ min: 2
, max: 10
}
, R.h2_ [ R.text "Controlled" ]
, element Range.component
{ value: 88
, style: css { width: "200px" }
}
, R.h2_ [ R.text "Disabled" ]
, element Range.component
{ value: 88
, style: css { width: "200px" }
, disabled: true
}
]
]

View File

@ -0,0 +1,51 @@
module Story.Yoga.Block.Atom.Segmented where
import Prelude
import Data.Tuple.Nested ((/\))
import Data.TwoOrMore (twoOrMore)
import Effect (Effect)
import Effect.Unsafe (unsafePerformEffect)
import React.Basic (JSX, element)
import React.Basic.DOM as R
import React.Basic.Emotion as E
import React.Basic.Hooks (reactComponent, useState')
import React.Basic.Hooks as React
import Yoga.Block.Atom.Segmented as Segmented
import Yoga.Block.Container.Style as Styles
default ∷
{ decorators ∷ Array (Effect JSX → JSX)
, title ∷ String
}
default =
{ title: "Atom/Segmented"
, decorators:
[ \storyFn →
R.div_
[ element E.global { styles: Styles.global }
, unsafePerformEffect storyFn
]
]
}
segmented ∷ Effect JSX
segmented = do
demoComponent ← makeDemoComponent
pure $ element demoComponent {}
where
makeDemoComponent =
reactComponent "Segmented Demo" \{} → React.do
activeIndex /\ setElementIndex ← useState' 2
pure
$ element Segmented.component
{ activeIndex
, updateActiveItem: const setElementIndex
, buttonContents:
twoOrMore
{ id: "gag", value: "2" }
{ id: "nag", value: "1" }
[ { id: "two", value: "Another option" }
, { id: "four", value: "2" }
, { id: "one", value: "1" }
]
}

View File

@ -0,0 +1,69 @@
module Story.Yoga.Block.Atom.Toggle where
import Prelude
import Color as Color
import Data.Maybe (Maybe(..))
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import React.Basic (JSX, element, fragment)
import React.Basic.DOM as R
import React.Basic.Hooks as React
import Yoga ((/>), (</))
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 ∷ { title ∷ String }
default = { title: "Atom/Toggle" }
toggle ∷ Effect JSX
toggle = do
example ← mkBasicExample
darkLight ← mkDarkLightToggle
pure
$ fragment
[ R.div_
[ R.h2_ [ R.text "Basics" ]
, element example {}
, R.h2_ [ R.text "Dark Light toggle" ]
, element darkLight {}
]
]
where
mkBasicExample =
React.reactComponent "Toggle example" \_p → React.do
togglePosition /\ setTogglePosition ← React.useState' ToggleIsRight
pure
$ element Toggle.component
{ value: togglePosition
, onChange: setTogglePosition
, ariaLabel: "dark-light-toggle"
}
mkDarkLightToggle =
React.reactComponent "Toggle dark night example" \_p → React.do
togglePosition /\ setTogglePosition ← React.useState' ToggleIsLeft
theme /\ setTheme ← React.useState' Nothing
let
content =
element Toggle.component
{ value: togglePosition
, ariaLabel: "dark-light-toggle"
, onChange:
\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
$ Block.container
</ { themeVariant: theme }
/> [ content ]

View File

@ -1,32 +1,27 @@
module Story.Yoga.Block.Atom.Modal.Story (default, modal) where
module Story.Yoga.Block.Molecule.Modal.Story (default, modal) where
import Prelude
import Data.Monoid (guard)
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Unsafe (unsafePerformEffect)
import Fahrtwind (background', mXY, pXY, roundedLg, textXl)
import Fahrtwind (background', mXY, roundedLg, textXl)
import React.Basic (JSX, element, fragment)
import React.Basic as React
import React.Basic.DOM as R
import React.Basic.Emotion as E
import React.Basic.Events (handler_)
import React.Basic.Hooks as React
import Record (merge)
import React.Basic.Hooks (bind, component, element, useState') as React
import Storybook (Meta, meta, metaDecorator)
import Storybook.Addon.Actions (action)
import Yoga ((/>), (</*), (</>))
import Yoga.Block as Block
import Yoga.Block.Atom.Modal as Modal
import Yoga.Block.Container.Style (col, colour)
import Yoga.Block.Molecule.Modal as Modal
import Yoga.Block.Container.Style (col)
import Yoga.Block.Container.Style as Styles
import Yoga.Block.Icon.SVG as SVGIcon
import Yoga.Block.Layout.Types (JustifyContent(..))
default ∷ Meta Modal.Props
default = meta
{ title: "Atom/Modal"
{ title: "Molecule/Modal"
, component: (pure <<< React.element) Modal.component
, decorators:
[ metaDecorator \storyFn →