git.iko.soy/rowtype-yoga/psfp
on master help sign in
Magic!
commitc8302a64705dc9ab88c164c6cf80e30bd6fbc3a7 authorMark Eibes <mark.eibes@gmail.com> dateWed 6 May 2020, 00:27 (6 years ago) parent4669fb5
3 files changed+107−16
components/src/React/Basic/Hooks/UseGesture.purs+5−4 components/src/Yoga/Grimoire/Component.purs+89−12 components/src/Yoga/Grimoire/Stories.purs+13
components/src/React/Basic/Hooks/UseGesture.purs+5−4
@@ -5,7 +5,6 @@ import Data.Array ((!!))
55import Data.Maybe (Maybe, fromMaybe')
66import Data.Nullable (Nullable)
77import Data.Tuple.Nested ((/\), type (/\))
8−import Debug.Trace (spy)
98import Effect (Effect)
109import Effect.Aff.Compat (runEffectFn2)
1110import Effect.Uncurried (EffectFn2)
@@ -22,10 +21,10 @@ import Yoga.Helpers ((?||))
2221foreign import data UseDrag ∷ Type -> Type -> Type
2322
2423type DragHandler a
25− = { arg ∷ a, down ∷ Boolean, movement ∷ Number /\ Number } -> Effect Unit
24+ = { arg ∷ a, down ∷ Boolean, movement ∷ Number /\ Number, xy ∷ Number /\ Number } -> Effect Unit
2625
2726type DragHandlerImpl a
28− = { args ∷ Array a, down ∷ Boolean, movement ∷ Array Number } -> Unit
27+ = { args ∷ Array a, down ∷ Boolean, movement ∷ Array Number, xy ∷ Array Number } -> Unit
2928
3029type DragProps
3130 = { onMouseDown ∷ EventHandler, onTouchStart ∷ EventHandler }
@@ -53,12 +52,14 @@ useDrag dragOptions dragHandler = unsafeHook (runEffectFn2 useDragImpl dragHandl
5352 dragHandlerImpl ∷ DragHandlerImpl a
5453 dragHandlerImpl x =
5554 (unsafePerformEffect <<< dragHandler)
56− { arg, down: x.down, movement: mx /\ my
55+ { arg, down: x.down, movement: mx /\ my, xy: xyX /\ xyY
5756 }
5857 where
5958 arg = x.args !! 0 # fromMaybe' (\_ -> unsafeCrashWith "Bollox")
6059 mx = x.movement !! 0 ?|| 0.0
6160 my = x.movement !! 1 ?|| 0.0
61+ xyX = x.xy !! 0 ?|| 0.0
62+ xyY = x.xy !! 1 ?|| 0.0
6263
6364withDragProps ∷
6465 ∀ attrs.
components/src/Yoga/Grimoire/Component.purs+89−12
@@ -1,27 +1,35 @@
11module Yoga.Grimoire.Component where
22
33import Prelude
4−import Data.Array (mapWithIndex, zip)
4+import Data.Array (mapWithIndex, zip, (!!))
55import Data.Array as Array
6−import Data.Maybe (Maybe(..))
6+import Data.FoldableWithIndex (findWithIndex)
7+import Data.Lens (set)
8+import Data.Lens.Index (ix)
9+import Data.Maybe (Maybe(..), fromMaybe, isJust)
710import Data.Nullable (Nullable)
8−import Data.Tuple.Nested ((/\))
11+import Data.Nullable as Nullable
12+import Data.Traversable (for, sequence)
13+import Data.Tuple.Nested ((/\), type (/\))
914import Debug.Trace (spy)
1015import Effect (Effect)
1116import Justifill (justifill)
1217import React.Basic (ReactComponent)
1318import React.Basic.DOM (css)
1419import React.Basic.Helpers (jsx)
15−import React.Basic.Hooks (Ref, component, element)
20+import React.Basic.Hooks (Ref, component, element, readRef, useLayoutEffect, useRef, writeRef)
1621import React.Basic.Hooks as React
1722import React.Basic.Hooks.Spring (animatedDiv, useSprings)
1823import React.Basic.Hooks.UseGesture (useDrag, withDragProps)
1924import Record.Extra (pick)
2025import Unsafe.Coerce (unsafeCoerce)
2126import Web.DOM (Node)
27+import Web.HTML.HTMLElement (DOMRect, getBoundingClientRect)
28+import Web.HTML.HTMLElement as HTMLElement
2229import Yoga.Grid.Component as Grid
2330import Yoga.Grimoire.Spell.Component as GrimoireSpell
2431import Yoga.Grimoire.Styles (styles)
32+import Yoga.Helpers ((?||))
2533import Yoga.Spell.Types (Spell)
2634import Yoga.Theme.Styles (makeStylesJSS, useTheme)
2735import Yoga.Theme.Types (CSSTheme)
@@ -30,6 +38,40 @@ type Props
3038 = { spells ∷ Array Spell
3139 }
3240
41+getRects ref = do
42+ refNodes <- readRef ref
43+ for
44+ refNodes \nullinger ->
45+ sequence do
46+ Nullable.toMaybe nullinger >>= HTMLElement.fromNode <#> getBoundingClientRect
47+
48+center ∷ DOMRect -> (Number /\ Number)
49+center { left, top, width, height } = (left + width / 2.0) /\ (top + height / 2.0)
50+
51+overlaps ∷ DOMRect -> DOMRect -> Boolean
52+overlaps r1 { left, top, right, bottom } = between left right x && between top bottom y
53+ where
54+ x /\ y = center r1
55+
56+emptyDomRect ∷ DOMRect
57+emptyDomRect = { top: 0.0, right: 0.0, bottom: 0.0, left: 0.0, width: 0.0, height: 0.0 }
58+
59+translate ∷ (Number /\ Number) -> DOMRect -> DOMRect
60+translate (x /\ y) { top, right, bottom, left, width, height } =
61+ { top: top + y
62+ , right: right + x
63+ , bottom: bottom + y
64+ , left: left + x
65+ , width
66+ , height
67+ }
68+
69+swap ∷ ∀ a. Int -> Int -> Array a -> Maybe (Array a)
70+swap i j arr = do
71+ valueI <- arr !! i
72+ valueJ <- arr !! j
73+ pure $ (set (ix i) valueJ <<< set (ix j) valueI) arr
74+
3375makeComponent ∷ Effect (ReactComponent Props)
3476makeComponent = do
3577 grid <- Grid.makeComponent
@@ -38,21 +80,56 @@ makeComponent = do
3880 component "Grimoire" \(props ∷ Props) -> React.do
3981 {} <- useStyles (pick props)
4082 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
83+ nodeRefs <- useRef (props.spells $> (Nullable.null ∷ (_ Node)))
84+ positionsRef <- useRef ([] ∷ _ DOMRect)
85+ originalPositionsRef <- useRef ([] ∷ _ DOMRect)
86+ useLayoutEffect unit do
87+ rects <- getRects nodeRefs
88+ writeRef positionsRef (rects <#> fromMaybe emptyDomRect)
89+ writeRef originalPositionsRef (rects <#> fromMaybe emptyDomRect)
90+ mempty
4391 theme ∷ CSSTheme <- useTheme
4492 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 }
93+ useDrag (justifill {}) \{ arg, down, movement: mx /\ my, xy: x /\ y } -> do
94+ rects <- getRects nodeRefs
95+ originalPositions <- readRef originalPositionsRef
96+ positionsBefore <- readRef positionsRef
97+ let
98+ originalPosDraggedBefore = positionsBefore !! arg ?|| emptyDomRect
99+
100+ currentPosDraggedBefore = originalPosDraggedBefore # translate (mx /\ my)
101+
102+ overlapsOtherBefore = positionsBefore # findWithIndex \i' pos -> i' /= arg && overlaps currentPosDraggedBefore pos
103+ case down, overlapsOtherBefore of
104+ false, Just { index, value } -> writeRef positionsRef (swap arg index positionsBefore ?|| positionsBefore)
105+ _, _ -> mempty
106+ positions <- readRef positionsRef
107+ let
108+ originalPosDragged = positions !! arg ?|| emptyDomRect
109+
110+ currentPosDragged = originalPosDragged # translate (mx /\ my)
111+
112+ overlapsOther = positions # findWithIndex \i' pos -> i' /= arg && overlaps currentPosDragged pos
113+ springs.set \i -> do
114+ let
115+ iPos = positions !! i ?|| emptyDomRect
116+
117+ origIPos = originalPositions !! i ?|| emptyDomRect
118+
119+ leftOffset = if iPos == origIPos then 0.0 else iPos.left - origIPos.left
120+
121+ topOffset = if iPos == origIPos then 0.0 else iPos.top - origIPos.top
122+ case i == arg, down, overlapsOther of
123+ false, true, Just { index, value }
124+ | index == i -> { x: originalPosDragged.left - value.left + leftOffset, y: originalPosDragged.top - value.top + topOffset, zIndex: 0, transform: "scale3d(1.0, 1.0, 1.0)", immediate: const false }
125+ true, true, _ -> { x: mx + leftOffset, y: my + topOffset, zIndex: 1, transform: "scale3d(1.1, 1.1, 1.1)", immediate: \n -> n == "x" || n == "y" || n == "zIndex" }
126+ _, _, _ -> { x: leftOffset, y: topOffset, zIndex: 0, transform: "scale3d(1.0, 1.0, 1.0)", immediate: const false }
51127 let
52128 renderSpells =
53129 mapWithIndex \i (spell /\ style) ->
54130 animatedDiv
55131 $ { style: css style
132+ , ref: unsafeCoerce (unsafeUpdateRefs nodeRefs i)
56133 , children:
57134 [ element
58135 spellComponent
components/src/Yoga/Grimoire/Stories.purs+13
@@ -21,4 +21,17 @@ spells =
2121 [ { name: "cast", signature: "String -> Effect Unit", description: "Casts an incantation" }
2222 , { name: "take", signature: "Int -> String -> String", description: "Takes the first characters of a string" }
2323 , { name: "append", signature: "a -> a -> a", description: "Takes two values and produces one" }
24+ , { name: "drop", signature: "Int -> String -> String", description: "Removes the first characters of a string" }
25+ , { name: "cast", signature: "String -> Effect Unit", description: "Casts an incantation" }
26+ , { name: "take", signature: "Int -> String -> String", description: "Takes the first characters of a string" }
27+ , { name: "append", signature: "a -> a -> a", description: "Takes two values and produces one" }
28+ , { name: "drop", signature: "Int -> String -> String", description: "Removes the first characters of a string" }
29+ , { name: "cast", signature: "String -> Effect Unit", description: "Casts an incantation" }
30+ , { name: "take", signature: "Int -> String -> String", description: "Takes the first characters of a string" }
31+ , { name: "append", signature: "a -> a -> a", description: "Takes two values and produces one" }
32+ , { name: "drop", signature: "Int -> String -> String", description: "Removes the first characters of a string" }
33+ , { name: "cast", signature: "String -> Effect Unit", description: "Casts an incantation" }
34+ , { name: "take", signature: "Int -> String -> String", description: "Takes the first characters of a string" }
35+ , { name: "append", signature: "a -> a -> a", description: "Takes two values and produces one" }
36+ , { name: "drop", signature: "Int -> String -> String", description: "Removes the first characters of a string" }
2437 ]