git.iko.soy/rowtype-yoga/psfp
on master help sign in
Draggable Spells
commit4669fb5dc73c35c750a77882af17e84a1db88d8a authorMark Eibes <mark.eibes@gmail.com> dateMon 4 May 2020, 23:44 (6 years ago) parent4321e4c
23 files changed+379−81
assets/icons/elements.afdesign components/src/React/Basic/Helpers.purs+14−7 components/src/React/Basic/Hooks/Spring.js+5 components/src/React/Basic/Hooks/Spring.purs+23−3 components/src/React/Basic/Hooks/Spring.ts+15−1 components/src/React/Basic/Hooks/Spring/Stories.purs+4−3 components/src/React/Basic/Hooks/UseGesture.purs+37−16 components/src/Storybook/Decorator/FullScreen.purs+10−1 components/src/Yoga/Card/Component.purs+13−9 components/src/Yoga/Cover/Component.purs+9−19 components/src/Yoga/Cover/Spec.purs+6−4 components/src/Yoga/Cover/Stories.purs+12−8 components/src/Yoga/Grid/Spec.purs+2−1 components/src/Yoga/Grid/Stories.purs+1−3 components/src/Yoga/Grimoire/Component.js+4 components/src/Yoga/Grimoire/Component.purs+52−5 components/src/Yoga/Grimoire/Component.ts+4 components/src/Yoga/Grimoire/Spell/Component.purs+66 components/src/Yoga/Grimoire/Spell/Spec.purs+19 components/src/Yoga/Grimoire/Spell/Stories.purs+20 components/src/Yoga/Grimoire/Spell/Styles.purs+49 components/src/Yoga/Grimoire/Stories.purs+7−1 components/src/Yoga/Spell/Types.purs+7
assets/icons/elements.afdesignnew file

Binary file not shown.

components/src/React/Basic/Helpers.purs+14−7
@@ -1,6 +1,7 @@
11module React.Basic.Helpers where
22
3−import Data.Maybe (Maybe, fromMaybe, fromMaybe')
3+import Prelude
4+import Data.Maybe (Maybe, fromMaybe')
45import Data.Symbol (SProxy(..))
56import Justifill.Fillable (class Fillable, class FillableFields, fill)
67import Justifill.Justifiable (class Justifiable, justify)
@@ -8,6 +9,7 @@ import Literals.Undefined (undefined)
89import Prim.Row (class Lacks, class Nub)
910import Prim.RowList (class RowToList)
1011import React.Basic (JSX)
12+import React.Basic.DOM as R
1113import React.Basic.Hooks (ReactComponent, element)
1214import Record (disjointUnion, insert)
1315import Unsafe.Coerce (unsafeCoerce)
@@ -57,13 +59,18 @@ jsx x partialProps kids = element x propsWithKids
5759 where
5860 propsWithKids ∷ Record (Kids to)
5961 propsWithKids = disjointUnion { kids } props
62+ fill' ∷ { | thru } -> { | to }
63+ fill' = fill
64+ justify' ∷ { | from } -> { | thru }
65+ justify' = justify
6066 props ∷ Record to
61− props =
62− ( (fill ∷ { | thru } -> { | to })
63− ( (justify ∷ { | from } -> { | thru })
64− partialProps
65− )
66− )
67+ props = fill' (justify' partialProps)
6768
6869orUndefined ∷ ∀ a. Maybe a -> a
6970orUndefined = fromMaybe' \_ -> unsafeCoerce undefined
71+
72+classSpan ∷ String -> Array JSX -> JSX
73+classSpan className children = R.span { className, children }
74+
75+classDiv ∷ String -> Array JSX -> JSX
76+classDiv className children = R.div { className, children }
components/src/React/Basic/Hooks/Spring.js+5
@@ -5,6 +5,11 @@ exports.useSpringImpl = function (mkStyles) { return function () {
55 var result = react_spring_1.useSpring(mkStyles);
66 return { style: result[0], set: result[1], stop: result[2] };
77}; };
8+exports.useSpringsImpl = function (n) { return function (fn) { return function () {
9+ var result = react_spring_1.useSprings(n, fn);
10+ console.log("REEEEEEEEESLUT", result);
11+ return { styles: result[0], set: result[1], stop: result[2] };
12+}; }; };
813exports.useTransitionImpl = react_spring_1.useTransition;
914exports.animatedComponentImpl = function (name) { return react_spring_1.animated[name]; };
1015exports.animatedImpl = react_spring_1.animated;
components/src/React/Basic/Hooks/Spring.purs+23−3
@@ -6,14 +6,12 @@ import Data.Nullable (Nullable, toNullable)
66import Data.Nullable as Nullable
77import Effect (Effect)
88import Effect.Uncurried (EffectFn1, EffectFn3, runEffectFn1, runEffectFn3)
9−import Foreign (Foreign)
109import Prim.Row (class Union)
11−import React.Basic (JSX, ReactComponent, Ref, element)
10+import React.Basic (JSX, ReactComponent, element)
1211import React.Basic.DOM (CSS, Props_div, Props_span)
1312import React.Basic.DOM.SVG (Props_svg, Props_path)
1413import React.Basic.Hooks (Hook, unsafeHook)
1514import React.Basic.Hooks as React
16−import Simple.JSON (class WriteForeign)
1715
1816foreign import data UseSpring ∷ Type -> Type -> Type
1917
@@ -33,6 +31,28 @@ useSpring f = React.do
3331 res <- unsafeHook (useSpringImpl f)
3432 pure $ res { set = runEffectFn1 res.set }
3533
34+foreign import data UseSprings ∷ Type -> Type -> Type
35+
36+foreign import useSpringsImpl ∷
37+ ∀ r s.
38+ Int ->
39+ (Int -> { | r }) ->
40+ Effect { styles ∷ Array { | r }, set ∷ SetSpringsImpl s, stop ∷ StopSpring }
41+
42+type SetSpringsImpl r
43+ = EffectFn1 (Int -> { | r }) Unit
44+
45+type SetSprings r
46+ = (Int -> { | r }) -> Effect Unit
47+
48+useSprings ∷
49+ ∀ r s.
50+ Int ->
51+ (Int -> { | r }) -> Hook (UseSprings { | r }) { styles ∷ Array { | r }, set ∷ SetSprings s, stop ∷ StopSpring }
52+useSprings n f = React.do
53+ res <- unsafeHook (useSpringsImpl n f)
54+ pure $ res { set = runEffectFn1 res.set }
55+
3656foreign import data UseTransition ∷ Type -> Type -> Type
3757
3858foreign import useTransitionImpl ∷ ∀ a. EffectFn3 (Array a) (Nullable (a -> String)) CSS (Array { item ∷ Nullable a, key ∷ Nullable String, props ∷ CSS })
components/src/React/Basic/Hooks/Spring.ts+15−1
@@ -1,10 +1,24 @@
1−import { useChain, useSpring, useTransition, animated } from "react-spring";
1+import {
2+ useChain,
3+ useSpring,
4+ useSprings,
5+ useTransition,
6+ interpolate,
7+ animated,
8+} from "react-spring";
9+
10+exports.interpolateImpl = interpolate;
211
312exports.useSpringImpl = (mkStyles) => () => {
413 const result = useSpring(mkStyles);
514 return { style: result[0], set: result[1], stop: result[2] };
615};
716
17+exports.useSpringsImpl = (n: number) => (fn) => () => {
18+ const result = useSprings(n, fn);
19+ return { styles: result[0], set: result[1], stop: result[2] };
20+};
21+
822exports.useTransitionImpl = useTransition;
923
1024exports.animatedComponentImpl = (name: string) => animated[name];
components/src/React/Basic/Hooks/Spring/Stories.purs+4−3
@@ -7,6 +7,7 @@ import Data.Monoid (guard)
77import Data.Tuple.Nested ((/\))
88import Effect (Effect)
99import JSS (jssClasses)
10+import Justifill (justifill)
1011import React.Basic.DOM (css)
1112import React.Basic.DOM as R
1213import React.Basic.Events (handler_)
@@ -129,8 +130,8 @@ mkDragAnimated = do
129130 component "Draggable Example" \{} -> React.do
130131 { style, set } <- useSpring $ const { x: 0.0, y: 0.0, config: { mass: 1, tension: 210, friction: 20 } }
131132 classes <- useStyles {}
132− mkDragProps <-
133− useDrag \{ down, movement: mx /\ my } ->
133+ bindDragProps <-
134+ useDrag (justifill {}) \{ down, movement: mx /\ my } ->
134135 set { x: if down then mx else 0.0, y: if down then my else 0.0 }
135136 pure
136137 $ animatedDiv
@@ -140,4 +141,4 @@ mkDragAnimated = do
140141 ]
141142 }
142143 `withDragProps`
143− mkDragProps
144+ (bindDragProps unit)
components/src/React/Basic/Hooks/UseGesture.purs+37−16
@@ -2,40 +2,61 @@ module React.Basic.Hooks.UseGesture where
22
33import Prelude
44import Data.Array ((!!))
5−import Data.Foldable (fold)
6−import Data.Monoid.Additive (Additive(..))
7−import Data.Newtype (ala)
5+import Data.Maybe (Maybe, fromMaybe')
6+import Data.Nullable (Nullable)
87import Data.Tuple.Nested ((/\), type (/\))
8+import Debug.Trace (spy)
99import Effect (Effect)
10−import Effect.Uncurried (EffectFn1, runEffectFn1)
10+import Effect.Aff.Compat (runEffectFn2)
11+import Effect.Uncurried (EffectFn2)
1112import Effect.Unsafe (unsafePerformEffect)
13+import Partial.Unsafe (unsafeCrashWith)
1214import Prim.Row (class Lacks, class Nub)
1315import React.Basic.Events (EventHandler)
14−import React.Basic.Hooks (Hook, unsafeHook)
16+import React.Basic.Helpers (orUndefined)
17+import React.Basic.Hooks (Hook, Ref, unsafeHook)
1518import Record (disjointUnion)
19+import Web.DOM (Node)
1620import Yoga.Helpers ((?||))
1721
1822foreign import data UseDrag ∷ Type -> Type -> Type
1923
20−type DragHandler
21− = { down ∷ Boolean, movement ∷ Number /\ Number } -> Effect Unit
24+type DragHandler a
25+ = { arg ∷ a, down ∷ Boolean, movement ∷ Number /\ Number } -> Effect Unit
2226
23−type DragHandlerImpl
24− = { down ∷ Boolean, movement ∷ Array Number } -> Unit
27+type DragHandlerImpl a
28+ = { args ∷ Array a, down ∷ Boolean, movement ∷ Array Number } -> Unit
2529
2630type DragProps
2731 = { onMouseDown ∷ EventHandler, onTouchStart ∷ EventHandler }
2832
29−foreign import useDragImpl ∷ EffectFn1 DragHandlerImpl (Effect DragProps)
33+type GenericOptionsImpl r
34+ = { domTarget ∷ Ref (Nullable Node) | r }
3035
31−useDrag ∷ DragHandler -> Hook (UseDrag Unit) (Effect DragProps)
32−useDrag dragHandler = unsafeHook (runEffectFn1 useDragImpl f)
36+type GenericOptions r
37+ = { domTarget ∷ Maybe (Ref (Nullable Node)) | r }
38+
39+type DragOptions
40+ = GenericOptions ()
41+
42+type DragOptionsImpl
43+ = GenericOptionsImpl ()
44+
45+dragOptionsToImpl ∷ DragOptions -> DragOptionsImpl
46+dragOptionsToImpl { domTarget } = { domTarget: domTarget # orUndefined }
47+
48+foreign import useDragImpl ∷ ∀ a. EffectFn2 (DragHandlerImpl a) DragOptionsImpl (a -> DragProps)
49+
50+useDrag ∷ ∀ a. DragOptions -> DragHandler a -> Hook (UseDrag Unit) (a -> DragProps)
51+useDrag dragOptions dragHandler = unsafeHook (runEffectFn2 useDragImpl dragHandlerImpl (dragOptionsToImpl dragOptions))
3352 where
34− f x =
53+ dragHandlerImpl ∷ DragHandlerImpl a
54+ dragHandlerImpl x =
3555 (unsafePerformEffect <<< dragHandler)
36− { down: x.down, movement: mx /\ my
56+ { arg, down: x.down, movement: mx /\ my
3757 }
3858 where
59+ arg = x.args !! 0 # fromMaybe' (\_ -> unsafeCrashWith "Bollox")
3960 mx = x.movement !! 0 ?|| 0.0
4061 my = x.movement !! 1 ?|| 0.0
4162
@@ -47,6 +68,6 @@ withDragProps ∷
4768 ( onMouseDown ∷ EventHandler, onTouchStart ∷ EventHandler | attrs )
4869 ( onMouseDown ∷ EventHandler, onTouchStart ∷ EventHandler | attrs ) =>
4970 { | attrs } ->
50− Effect DragProps ->
71+ DragProps ->
5172 { onMouseDown ∷ EventHandler, onTouchStart ∷ EventHandler | attrs }
52−withDragProps x (y ∷ Effect DragProps) = disjointUnion (unsafePerformEffect y) x
73+withDragProps x (y ∷ DragProps) = disjointUnion y x
components/src/Storybook/Decorator/FullScreen.purs+10−1
@@ -112,5 +112,14 @@ mkThemeSwitcher = do
112112 pure
113113 $ element themeProvider
114114 { theme
115− , children: [ element baseline { kids: [ R.div_ [ themeSelect, child ] ] } ]
115+ , children:
116+ [ element baseline
117+ { kids:
118+ [ R.div
119+ { style: css { width: "100%" }
120+ , children: [ themeSelect, child ]
121+ }
122+ ]
123+ }
124+ ]
116125 }
components/src/Yoga/Card/Component.purs+13−9
@@ -6,13 +6,16 @@ import Color as Color
66import Data.Foldable (fold)
77import Data.Interpolate (i)
88import Data.Maybe (Maybe)
9+import Data.Nullable (Nullable)
910import Effect (Effect)
1011import JSS (JSSClasses, JSSElem, jssClasses)
1112import React.Basic (JSX)
1213import React.Basic.DOM as R
13−import React.Basic.Hooks (ReactComponent, component)
14+import React.Basic.Helpers (orUndefined)
15+import React.Basic.Hooks (ReactComponent, Ref, component)
1416import React.Basic.Hooks as React
1517import Record.Extra (pick)
18+import Web.DOM (Node)
1619import Yoga.Box.Component as Box
1720import Yoga.Helpers (ifJustTrue)
1821import Yoga.Theme (withAlpha)
@@ -44,24 +47,25 @@ styles =
4447 }
4548 }
4649
47−type Props r
50+type PropsR r
4851 = ( kids ∷ Array JSX
4952 , className ∷ Maybe String
53+ , divRef ∷ Maybe (Ref (Nullable Node))
5054 | r
5155 )
5256
53−mkCard ∷
54− Effect
55− ( ReactComponent
56− { | Props (StyleProps) }
57− )
57+type Props
58+ = { | PropsR (StyleProps) }
59+
60+mkCard ∷ Effect (ReactComponent Props)
5861mkCard = do
5962 useStyles <- makeStylesJSS styles
6063 box <- Box.makeComponent
61− component "Card" \props@{ kids, className } -> React.do
64+ component "Card" \(props@{ kids, className } ∷ Props) -> React.do
6265 classes <- useStyles (pick props)
6366 pure
6467 $ R.div
65− { className: classes.card <> " " <> fold className
68+ { ref: props.divRef # orUndefined
69+ , className: classes.card <> " " <> fold className
6670 , children: kids
6771 }
components/src/Yoga/Cover/Component.purs+9−19
@@ -2,15 +2,14 @@ module Yoga.Cover.Component where
22
33import Prelude
44import Data.Array (foldMap)
5+import Data.Array as Array
56import Data.Maybe (Maybe)
67import Effect (Effect)
7−import Yoga.Cover.Styles as Cover
8−import Prim.Row (class Nub, class Union)
98import React.Basic (JSX)
109import React.Basic.DOM as R
1110import React.Basic.Hooks (ReactComponent, component)
1211import React.Basic.Hooks as React
13−import Record (disjointUnion)
12+import Yoga.Cover.Styles as Cover
1413import Yoga.Theme.Styles (makeStylesJSS)
1514
1615type Props
@@ -20,33 +19,24 @@ type PropsR
2019 = OptionalProps ()
2120
2221type OptionalProps r
23− = ( header ∷ JSX
24− , footer ∷ JSX
25− , principal ∷ JSX
22+ = ( header ∷ Maybe JSX
23+ , footer ∷ Maybe JSX
24+ , kids ∷ Array JSX
2625 , className ∷ Maybe String
2726 | r
2827 )
2928
30−withDefaults ∷
31− ∀ r missing props.
32− Union r missing props =>
33− Nub props props =>
34− Monoid { | missing } =>
35− { | r } ->
36− { | props }
37−withDefaults x = disjointUnion x mempty
38−
3929makeComponent ∷ Effect (ReactComponent Props)
4030makeComponent = do
4131 useStyles <- makeStylesJSS Cover.styles
42− component "Cover" \{ header, footer, principal, className } -> React.do
32+ component "Cover" \{ header, footer, kids, className } -> React.do
4333 classes <- useStyles {}
4434 pure
4535 $ R.div
4636 { className: classes.cover <> foldMap (" " <> _) className
4737 , children:
48− [ R.div { className: classes.header, children: pure header }
49− , R.div { className: classes.principal, children: pure principal }
50− , R.div { className: classes.footer, children: pure footer }
38+ [ R.div { className: classes.header, children: Array.fromFoldable header }
39+ , R.div { className: classes.principal, children: kids }
40+ , R.div { className: classes.footer, children: Array.fromFoldable footer }
5141 ]
5242 }
components/src/Yoga/Cover/Spec.purs+6−4
@@ -1,14 +1,16 @@
11module Yoga.Cover.Spec where
22
33import Prelude
4−import Yoga.Cover.Component as Cover
4+import Justifill (justifill)
5+import React.Basic.DOM as R
56import React.TestingLibrary (describeComponent, renderComponent)
67import Test.Spec (Spec, it)
8+import Yoga.Cover.Component as Cover
79
8−spec :: Spec Unit
10+spec ∷ Spec Unit
911spec =
1012 describeComponent Cover.makeComponent
1113 "The Cover Component" do
12− it "renders without problems" \panel -> do
13− _ <- renderComponent panel (Cover.withDefaults {})
14+ it "renders without problems" \cover -> do
15+ _ <- renderComponent cover (justifill { kids: [ R.text "hi" ] })
1416 pure unit
components/src/Yoga/Cover/Stories.purs+12−8
@@ -1,30 +1,34 @@
11module Yoga.Cover.Stories where
22
33import Prelude hiding (add)
4−import Storybook.Decorator.FullScreen (fullScreenDecorator)
54import Effect (Effect)
6−import Yoga.Cover.Component as Cover
5+import Justifill (justifill)
6+import React.Basic (JSX)
77import React.Basic.DOM as R
8+import Storybook.Decorator.FullScreen (fullScreenDecorator)
89import Storybook.React (Storybook, add, addDecorator, storiesOf)
10+import Yoga.Cover.Component as Cover
911
1012stories ∷ Effect Storybook
1113stories = do
1214 storiesOf "Cover" do
1315 addDecorator fullScreenDecorator
1416 add "The Cover" Cover.makeComponent
15− [ Cover.withDefaults
16− { principal: R.h1_ [ R.text "Main thing" ]
17+ [ justifill
18+ { kids: [ R.h1_ [ R.text "Main thing" ] ]
1719 , header: R.text "Header"
1820 , footer: R.text "Footer"
1921 }
20− , Cover.withDefaults
21− { principal: R.h1_ [ R.text "Only main" ]
22+ , justifill
23+ { kids: [ R.h1_ [ R.text "Only main" ] ]
2224 }
23− , Cover.withDefaults
25+ , justifill
2426 { header: R.h1_ [ R.text "Only header" ]
27+ , kids: [] ∷ Array JSX
2528 }
26− , Cover.withDefaults
29+ , justifill
2730 { header: R.h1_ [ R.text "Header" ]
2831 , footer: R.h1_ [ R.text "Footer" ]
32+ , kids: [] ∷ Array JSX
2933 }
3034 ]
components/src/Yoga/Grid/Spec.purs+2−1
@@ -3,6 +3,7 @@ module Yoga.Grid.Spec where
33import Prelude
44import Justifill (justifill)
55import React.Basic.DOM as R
6+import React.Basic.Hooks (JSX)
67import React.TestingLibrary (describeComponent, renderComponent)
78import Test.Spec (Spec, it)
89import Yoga.Grid.Component as Grid
@@ -16,5 +17,5 @@ spec =
1617 _ <-
1718 renderComponent grid
1819 $ justifill
19− {}
20+ { kids: [] ∷ _ JSX }
2021 pure unit
components/src/Yoga/Grid/Stories.purs+1−3
@@ -1,9 +1,8 @@
11module Yoga.Grid.Stories where
22
33import Prelude hiding (add)
4−import Data.Array (mapWithIndex, replicate, (..))
4+import Data.Array ((..))
55import Effect (Effect)
6−import Effect.Unsafe (unsafePerformEffect)
76import Justifill (justifill)
87import React.Basic.DOM (css)
98import React.Basic.DOM as R
@@ -15,7 +14,6 @@ import Yoga.Box.Component as Box
1514import Yoga.Card.Component (mkCard)
1615import Yoga.Centre.Component as Centre
1716import Yoga.Grid.Component as Grid
18−import Yoga.Stack.Component as Stack
1917
2018stories ∷ Effect Storybook
2119stories = do
components/src/Yoga/Grimoire/Component.jsnew file+4
@@ -0,0 +1,4 @@
1+exports.unsafeArraySetAt = function (i) { return function (v) { return function (arr) {
2+ arr[i] = v;
3+ return arr;
4+}; }; };
components/src/Yoga/Grimoire/Component.purs+52−5
@@ -1,25 +1,72 @@
11module Yoga.Grimoire.Component where
22
33import Prelude
4+import Data.Array (mapWithIndex, zip)
5+import Data.Array as Array
6+import Data.Maybe (Maybe(..))
7+import Data.Nullable (Nullable)
8+import Data.Tuple.Nested ((/\))
9+import Debug.Trace (spy)
410import Effect (Effect)
11+import Justifill (justifill)
512import React.Basic (ReactComponent)
6−import React.Basic.DOM as R
7−import React.Basic.Hooks (component)
13+import React.Basic.DOM (css)
14+import React.Basic.Helpers (jsx)
15+import React.Basic.Hooks (Ref, component, element)
816import React.Basic.Hooks as React
17+import React.Basic.Hooks.Spring (animatedDiv, useSprings)
18+import React.Basic.Hooks.UseGesture (useDrag, withDragProps)
919import Record.Extra (pick)
20+import Unsafe.Coerce (unsafeCoerce)
21+import Web.DOM (Node)
22+import Yoga.Grid.Component as Grid
23+import Yoga.Grimoire.Spell.Component as GrimoireSpell
1024import Yoga.Grimoire.Styles (styles)
25+import Yoga.Spell.Types (Spell)
1126import Yoga.Theme.Styles (makeStylesJSS, useTheme)
1227import Yoga.Theme.Types (CSSTheme)
1328
1429type Props
15− = {
30+ = { spells ∷ Array Spell
1631 }
1732
1833makeComponent ∷ Effect (ReactComponent Props)
1934makeComponent = do
35+ grid <- Grid.makeComponent
36+ spellComponent <- GrimoireSpell.makeComponent
2037 useStyles <- makeStylesJSS styles
21− component "Grimoire" \(props@{} ∷ Props) -> React.do
38+ component "Grimoire" \(props ∷ Props) -> React.do
2239 {} <- useStyles (pick props)
40+ springs <- useSprings (Array.length props.spells) \_ -> { x: 0.0, y: 0.0, zIndex: 0, immediate: false, transform: "scale3d(1.0,1.0,1.0)" }
41+ let
42+ _ = spy "springs" springs
2343 theme ∷ CSSTheme <- useTheme
44+ bindDragProps <-
45+ useDrag (justifill {}) \{ arg, down, movement: mx /\ my } -> do
46+ springs.set \i ->
47+ if i == arg && down then
48+ { x: mx, y: my, zIndex: 1, transform: "scale3d(1.1, 1.1, 1.1)", immediate: \n -> n == "x" || n == "y" || n == "zIndex" }
49+ else
50+ { x: 0.0, y: 0.0, zIndex: 0, transform: "scale3d(1.0, 1.0, 1.0)", immediate: const false }
51+ let
52+ renderSpells =
53+ mapWithIndex \i (spell /\ style) ->
54+ animatedDiv
55+ $ { style: css style
56+ , children:
57+ [ element
58+ spellComponent
59+ { spell
60+ , cardRef: Nothing
61+ }
62+ ]
63+ }
64+ `withDragProps`
65+ bindDragProps i
2466 pure
25− $ R.div_ [ R.text "WriteMe" ]
67+ $ jsx grid {} (renderSpells (props.spells `zip` springs.styles))
68+
69+foreign import unsafeArraySetAt ∷ ∀ a. Int -> a -> Array a -> Array a
70+
71+unsafeUpdateRefs ∷ Ref (Array (Nullable Node)) -> Int -> Nullable Node -> Array (Nullable Node)
72+unsafeUpdateRefs refsRef i el = unsafeArraySetAt i el ((unsafeCoerce refsRef).current)
components/src/Yoga/Grimoire/Component.tsnew file+4
@@ -0,0 +1,4 @@
1+exports.unsafeArraySetAt = <A>(i: number) => (v: A) => (arr: Array<A>) => {
2+ arr[i] = v;
3+ return arr;
4+};
components/src/Yoga/Grimoire/Spell/Component.pursnew file+66
@@ -0,0 +1,66 @@
1+module Yoga.Grimoire.Spell.Component where
2+
3+import Prelude
4+import CSS (AlignItemsValue, JustifyContentValue, spaceBetween)
5+import CSS.Common (baseline)
6+import Data.Maybe (Maybe)
7+import Data.Nullable (Nullable)
8+import Effect (Effect)
9+import React.Basic (ReactComponent)
10+import React.Basic.DOM as R
11+import React.Basic.Helpers (classSpan, jsx)
12+import React.Basic.Hooks (Ref, component)
13+import React.Basic.Hooks as React
14+import Record.Extra (pick)
15+import Web.DOM (Node)
16+import Yoga.Box.Component as Box
17+import Yoga.Card.Component (mkCard)
18+import Yoga.Centre.Component as Centre
19+import Yoga.Cluster.Component as Cluster
20+import Yoga.Cover.Component as Cover
21+import Yoga.Grimoire.Spell.Styles (styles)
22+import Yoga.Grimoire.Spell.Styles as Style
23+import Yoga.Spell.Types (Spell)
24+import Yoga.Stack.Component as Stack
25+import Yoga.Theme.Styles (makeStylesJSS, useTheme)
26+import Yoga.Theme.Types (CSSTheme)
27+
28+type Props
29+ = { spell ∷ Spell
30+ , cardRef ∷ Maybe (Ref (Nullable Node))
31+ | Style.PropsR
32+ }
33+
34+makeComponent ∷ Effect (ReactComponent Props)
35+makeComponent = do
36+ box <- Box.makeComponent
37+ centre <- Centre.makeComponent
38+ stack <- Stack.makeComponent
39+ cluster <- Cluster.makeComponent
40+ card <- mkCard
41+ cover <- Cover.makeComponent
42+ useStyles <- makeStylesJSS styles
43+ component "Spell" \(props@{ spell } ∷ Props) -> React.do
44+ style <- useStyles (pick props)
45+ theme ∷ CSSTheme <- useTheme
46+ let
47+ headerCluster =
48+ jsx cluster
49+ { space: "var(--s-5)"
50+ , align: baseline ∷ AlignItemsValue
51+ , justify: spaceBetween ∷ JustifyContentValue
52+ }
53+ [ R.div_
54+ [ classSpan style.name [ R.text spell.name ]
55+ , classSpan style.signature [ R.text spell.signature ]
56+ ]
57+ ]
58+ pure
59+ $ jsx card { divRef: props.cardRef }
60+ [ jsx box { className: style.container, padding: "var(--s-1)" }
61+ [ jsx stack { space: "var(--s-3)" }
62+ [ headerCluster
63+ , classSpan style.description [ R.text spell.description ]
64+ ]
65+ ]
66+ ]
components/src/Yoga/Grimoire/Spell/Spec.pursnew file+19
@@ -0,0 +1,19 @@
1+module Yoga.Grimoire.Spell.Spec where
2+
3+import Prelude
4+import Justifill (justifill)
5+import React.Basic.DOM as R
6+import React.TestingLibrary (describeComponent, renderComponent)
7+import Test.Spec (Spec, it)
8+import Yoga.Grimoire.Spell.Component as Spell
9+import Yoga.Spec.Helpers (withSpecTheme)
10+
11+spec ∷ Spec Unit
12+spec =
13+ describeComponent (withSpecTheme Spell.makeComponent)
14+ "The Spell Component" do
15+ it "renders without problems" \spell -> do
16+ _ <-
17+ renderComponent spell
18+ $ justifill { spell: { description: "bla", name: "Heinz", signature: "Any -> Unit" } }
19+ pure unit
components/src/Yoga/Grimoire/Spell/Stories.pursnew file+20
@@ -0,0 +1,20 @@
1+module Yoga.Grimoire.Spell.Stories where
2+
3+import Prelude hiding (add)
4+import Effect (Effect)
5+import Justifill (justifill)
6+import React.Basic.DOM as R
7+import Storybook.Decorator.FullScreen (fullScreenDecorator)
8+import Storybook.React (Storybook, add, addDecorator, storiesOf)
9+import Yoga.Grimoire.Spell.Component as Spell
10+
11+stories ∷ Effect Storybook
12+stories = do
13+ storiesOf "Spell" do
14+ addDecorator fullScreenDecorator
15+ add "The Spell" Spell.makeComponent
16+ [ justifill
17+ { spell:
18+ { name: "append", signature: "a -> a -> a", description: "Takes two values and produces one" }
19+ }
20+ ]
components/src/Yoga/Grimoire/Spell/Styles.pursnew file+49
@@ -0,0 +1,49 @@
1+module Yoga.Grimoire.Spell.Styles where
2+
3+import Prelude hiding (top)
4+import CSS (ColorSpace(..), backgroundColor, border, borderRadius, color, fontFamily, fontSize, fontStyle, height, mix, pct, sansSerif, solid, width)
5+import CSS.FontStyle (italic)
6+import CSS.TextAlign (rightTextAlign, textAlign)
7+import Data.Array (fromFoldable)
8+import Data.NonEmpty (NonEmpty(..))
9+import Data.NonEmpty as NonEmpty
10+import JSS (JSSClasses, JSSElem, jss, jssClasses)
11+import Yoga.Theme.Types (YogaTheme)
12+
13+type PropsR
14+ = ()
15+
16+type Props
17+ = Record PropsR
18+
19+type Classes a
20+ = ( container ∷ a
21+ , signature ∷ a
22+ , description ∷ a
23+ , name ∷ a
24+ )
25+
26+styles ∷ JSSClasses YogaTheme Props (Classes (JSSElem Props))
27+styles =
28+ jssClasses \theme@{ s0, s1, s4, s5, s_5 } ->
29+ { container:
30+ jss { userSelect: "none" }
31+ <> jss do
32+ backgroundColor theme.backgroundColourLighter
33+ height (100.0 # pct)
34+ , signature:
35+ do
36+ color theme.textColour
37+ textAlign rightTextAlign
38+ fontFamily (fromFoldable theme.codeFontFamily) (NonEmpty.singleton sansSerif)
39+ fontStyle italic
40+ , name:
41+ do
42+ color theme.highlightColour
43+ fontFamily (fromFoldable theme.codeFontFamily) (NonEmpty.singleton sansSerif)
44+ fontSize s1
45+ , description:
46+ do
47+ color theme.grey
48+ fontStyle italic
49+ }
components/src/Yoga/Grimoire/Stories.purs+7−1
@@ -14,5 +14,11 @@ stories = do
1414 addDecorator fullScreenDecorator
1515 add "The Grimoire" Grimoire.makeComponent
1616 [ justifill
17− {}
17+ { spells }
1818 ]
19+
20+spells =
21+ [ { name: "cast", signature: "String -> Effect Unit", description: "Casts an incantation" }
22+ , { name: "take", signature: "Int -> String -> String", description: "Takes the first characters of a string" }
23+ , { name: "append", signature: "a -> a -> a", description: "Takes two values and produces one" }
24+ ]
components/src/Yoga/Spell/Types.pursnew file+7
@@ -0,0 +1,7 @@
1+module Yoga.Spell.Types where
2+
3+type Spell
4+ = { name ∷ String
5+ , signature ∷ String
6+ , description ∷ String
7+ }