mirror of
https://github.com/rowtype-yoga/psfp.git
synced 2024-09-11 16:25:36 +03:00
Magic!
This commit is contained in:
parent
4669fb5dc7
commit
c8302a6470
@ -5,7 +5,6 @@ import Data.Array ((!!))
|
||||
import Data.Maybe (Maybe, fromMaybe')
|
||||
import Data.Nullable (Nullable)
|
||||
import Data.Tuple.Nested ((/\), type (/\))
|
||||
import Debug.Trace (spy)
|
||||
import Effect (Effect)
|
||||
import Effect.Aff.Compat (runEffectFn2)
|
||||
import Effect.Uncurried (EffectFn2)
|
||||
@ -22,10 +21,10 @@ import Yoga.Helpers ((?||))
|
||||
foreign import data UseDrag ∷ Type -> Type -> Type
|
||||
|
||||
type DragHandler a
|
||||
= { arg ∷ a, down ∷ Boolean, movement ∷ Number /\ Number } -> Effect Unit
|
||||
= { arg ∷ a, down ∷ Boolean, movement ∷ Number /\ Number, xy ∷ Number /\ Number } -> Effect Unit
|
||||
|
||||
type DragHandlerImpl a
|
||||
= { args ∷ Array a, down ∷ Boolean, movement ∷ Array Number } -> Unit
|
||||
= { args ∷ Array a, down ∷ Boolean, movement ∷ Array Number, xy ∷ Array Number } -> Unit
|
||||
|
||||
type DragProps
|
||||
= { onMouseDown ∷ EventHandler, onTouchStart ∷ EventHandler }
|
||||
@ -53,12 +52,14 @@ useDrag dragOptions dragHandler = unsafeHook (runEffectFn2 useDragImpl dragHandl
|
||||
dragHandlerImpl ∷ DragHandlerImpl a
|
||||
dragHandlerImpl x =
|
||||
(unsafePerformEffect <<< dragHandler)
|
||||
{ arg, down: x.down, movement: mx /\ my
|
||||
{ arg, down: x.down, movement: mx /\ my, xy: xyX /\ xyY
|
||||
}
|
||||
where
|
||||
arg = x.args !! 0 # fromMaybe' (\_ -> unsafeCrashWith "Bollox")
|
||||
mx = x.movement !! 0 ?|| 0.0
|
||||
my = x.movement !! 1 ?|| 0.0
|
||||
xyX = x.xy !! 0 ?|| 0.0
|
||||
xyY = x.xy !! 1 ?|| 0.0
|
||||
|
||||
withDragProps ∷
|
||||
∀ attrs.
|
||||
|
@ -1,27 +1,35 @@
|
||||
module Yoga.Grimoire.Component where
|
||||
|
||||
import Prelude
|
||||
import Data.Array (mapWithIndex, zip)
|
||||
import Data.Array (mapWithIndex, zip, (!!))
|
||||
import Data.Array as Array
|
||||
import Data.Maybe (Maybe(..))
|
||||
import Data.FoldableWithIndex (findWithIndex)
|
||||
import Data.Lens (set)
|
||||
import Data.Lens.Index (ix)
|
||||
import Data.Maybe (Maybe(..), fromMaybe, isJust)
|
||||
import Data.Nullable (Nullable)
|
||||
import Data.Tuple.Nested ((/\))
|
||||
import Data.Nullable as Nullable
|
||||
import Data.Traversable (for, sequence)
|
||||
import Data.Tuple.Nested ((/\), type (/\))
|
||||
import Debug.Trace (spy)
|
||||
import Effect (Effect)
|
||||
import Justifill (justifill)
|
||||
import React.Basic (ReactComponent)
|
||||
import React.Basic.DOM (css)
|
||||
import React.Basic.Helpers (jsx)
|
||||
import React.Basic.Hooks (Ref, component, element)
|
||||
import React.Basic.Hooks (Ref, component, element, readRef, useLayoutEffect, useRef, writeRef)
|
||||
import React.Basic.Hooks as React
|
||||
import React.Basic.Hooks.Spring (animatedDiv, useSprings)
|
||||
import React.Basic.Hooks.UseGesture (useDrag, withDragProps)
|
||||
import Record.Extra (pick)
|
||||
import Unsafe.Coerce (unsafeCoerce)
|
||||
import Web.DOM (Node)
|
||||
import Web.HTML.HTMLElement (DOMRect, getBoundingClientRect)
|
||||
import Web.HTML.HTMLElement as HTMLElement
|
||||
import Yoga.Grid.Component as Grid
|
||||
import Yoga.Grimoire.Spell.Component as GrimoireSpell
|
||||
import Yoga.Grimoire.Styles (styles)
|
||||
import Yoga.Helpers ((?||))
|
||||
import Yoga.Spell.Types (Spell)
|
||||
import Yoga.Theme.Styles (makeStylesJSS, useTheme)
|
||||
import Yoga.Theme.Types (CSSTheme)
|
||||
@ -30,6 +38,40 @@ type Props
|
||||
= { spells ∷ Array Spell
|
||||
}
|
||||
|
||||
getRects ref = do
|
||||
refNodes <- readRef ref
|
||||
for
|
||||
refNodes \nullinger ->
|
||||
sequence do
|
||||
Nullable.toMaybe nullinger >>= HTMLElement.fromNode <#> getBoundingClientRect
|
||||
|
||||
center ∷ DOMRect -> (Number /\ Number)
|
||||
center { left, top, width, height } = (left + width / 2.0) /\ (top + height / 2.0)
|
||||
|
||||
overlaps ∷ DOMRect -> DOMRect -> Boolean
|
||||
overlaps r1 { left, top, right, bottom } = between left right x && between top bottom y
|
||||
where
|
||||
x /\ y = center r1
|
||||
|
||||
emptyDomRect ∷ DOMRect
|
||||
emptyDomRect = { top: 0.0, right: 0.0, bottom: 0.0, left: 0.0, width: 0.0, height: 0.0 }
|
||||
|
||||
translate ∷ (Number /\ Number) -> DOMRect -> DOMRect
|
||||
translate (x /\ y) { top, right, bottom, left, width, height } =
|
||||
{ top: top + y
|
||||
, right: right + x
|
||||
, bottom: bottom + y
|
||||
, left: left + x
|
||||
, width
|
||||
, height
|
||||
}
|
||||
|
||||
swap ∷ ∀ a. Int -> Int -> Array a -> Maybe (Array a)
|
||||
swap i j arr = do
|
||||
valueI <- arr !! i
|
||||
valueJ <- arr !! j
|
||||
pure $ (set (ix i) valueJ <<< set (ix j) valueI) arr
|
||||
|
||||
makeComponent ∷ Effect (ReactComponent Props)
|
||||
makeComponent = do
|
||||
grid <- Grid.makeComponent
|
||||
@ -38,21 +80,56 @@ makeComponent = do
|
||||
component "Grimoire" \(props ∷ Props) -> React.do
|
||||
{} <- useStyles (pick props)
|
||||
springs <- useSprings (Array.length props.spells) \_ -> { x: 0.0, y: 0.0, zIndex: 0, immediate: false, transform: "scale3d(1.0,1.0,1.0)" }
|
||||
let
|
||||
_ = spy "springs" springs
|
||||
nodeRefs <- useRef (props.spells $> (Nullable.null ∷ (_ Node)))
|
||||
positionsRef <- useRef ([] ∷ _ DOMRect)
|
||||
originalPositionsRef <- useRef ([] ∷ _ DOMRect)
|
||||
useLayoutEffect unit do
|
||||
rects <- getRects nodeRefs
|
||||
writeRef positionsRef (rects <#> fromMaybe emptyDomRect)
|
||||
writeRef originalPositionsRef (rects <#> fromMaybe emptyDomRect)
|
||||
mempty
|
||||
theme ∷ CSSTheme <- useTheme
|
||||
bindDragProps <-
|
||||
useDrag (justifill {}) \{ arg, down, movement: mx /\ my } -> do
|
||||
springs.set \i ->
|
||||
if i == arg && down then
|
||||
{ x: mx, y: my, zIndex: 1, transform: "scale3d(1.1, 1.1, 1.1)", immediate: \n -> n == "x" || n == "y" || n == "zIndex" }
|
||||
else
|
||||
{ x: 0.0, y: 0.0, zIndex: 0, transform: "scale3d(1.0, 1.0, 1.0)", immediate: const false }
|
||||
useDrag (justifill {}) \{ arg, down, movement: mx /\ my, xy: x /\ y } -> do
|
||||
rects <- getRects nodeRefs
|
||||
originalPositions <- readRef originalPositionsRef
|
||||
positionsBefore <- readRef positionsRef
|
||||
let
|
||||
originalPosDraggedBefore = positionsBefore !! arg ?|| emptyDomRect
|
||||
|
||||
currentPosDraggedBefore = originalPosDraggedBefore # translate (mx /\ my)
|
||||
|
||||
overlapsOtherBefore = positionsBefore # findWithIndex \i' pos -> i' /= arg && overlaps currentPosDraggedBefore pos
|
||||
case down, overlapsOtherBefore of
|
||||
false, Just { index, value } -> writeRef positionsRef (swap arg index positionsBefore ?|| positionsBefore)
|
||||
_, _ -> mempty
|
||||
positions <- readRef positionsRef
|
||||
let
|
||||
originalPosDragged = positions !! arg ?|| emptyDomRect
|
||||
|
||||
currentPosDragged = originalPosDragged # translate (mx /\ my)
|
||||
|
||||
overlapsOther = positions # findWithIndex \i' pos -> i' /= arg && overlaps currentPosDragged pos
|
||||
springs.set \i -> do
|
||||
let
|
||||
iPos = positions !! i ?|| emptyDomRect
|
||||
|
||||
origIPos = originalPositions !! i ?|| emptyDomRect
|
||||
|
||||
leftOffset = if iPos == origIPos then 0.0 else iPos.left - origIPos.left
|
||||
|
||||
topOffset = if iPos == origIPos then 0.0 else iPos.top - origIPos.top
|
||||
case i == arg, down, overlapsOther of
|
||||
false, true, Just { index, value }
|
||||
| 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 }
|
||||
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" }
|
||||
_, _, _ -> { x: leftOffset, y: topOffset, zIndex: 0, transform: "scale3d(1.0, 1.0, 1.0)", immediate: const false }
|
||||
let
|
||||
renderSpells =
|
||||
mapWithIndex \i (spell /\ style) ->
|
||||
animatedDiv
|
||||
$ { style: css style
|
||||
, ref: unsafeCoerce (unsafeUpdateRefs nodeRefs i)
|
||||
, children:
|
||||
[ element
|
||||
spellComponent
|
||||
|
@ -21,4 +21,17 @@ spells =
|
||||
[ { name: "cast", signature: "String -> Effect Unit", description: "Casts an incantation" }
|
||||
, { name: "take", signature: "Int -> String -> String", description: "Takes the first characters of a string" }
|
||||
, { name: "append", signature: "a -> a -> a", description: "Takes two values and produces one" }
|
||||
, { name: "drop", signature: "Int -> String -> String", description: "Removes the first characters of a string" }
|
||||
, { name: "cast", signature: "String -> Effect Unit", description: "Casts an incantation" }
|
||||
, { name: "take", signature: "Int -> String -> String", description: "Takes the first characters of a string" }
|
||||
, { name: "append", signature: "a -> a -> a", description: "Takes two values and produces one" }
|
||||
, { name: "drop", signature: "Int -> String -> String", description: "Removes the first characters of a string" }
|
||||
, { name: "cast", signature: "String -> Effect Unit", description: "Casts an incantation" }
|
||||
, { name: "take", signature: "Int -> String -> String", description: "Takes the first characters of a string" }
|
||||
, { name: "append", signature: "a -> a -> a", description: "Takes two values and produces one" }
|
||||
, { name: "drop", signature: "Int -> String -> String", description: "Removes the first characters of a string" }
|
||||
, { name: "cast", signature: "String -> Effect Unit", description: "Casts an incantation" }
|
||||
, { name: "take", signature: "Int -> String -> String", description: "Takes the first characters of a string" }
|
||||
, { name: "append", signature: "a -> a -> a", description: "Takes two values and produces one" }
|
||||
, { name: "drop", signature: "Int -> String -> String", description: "Removes the first characters of a string" }
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user