Sortable.purs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. module Sortable (Sortable, Options, Event, MoveEvent, PullMode, create) where
  2. import Control.Alt ((<$>))
  3. import Control.Alternative (pure)
  4. import Control.Bind ((>>=))
  5. import Control.Category ((<<<), (>>>))
  6. import Control.Monad.Except (mapExcept, runExcept, throwError)
  7. import Data.Array (intercalate)
  8. import Data.Boolean (otherwise)
  9. import Data.BooleanAlgebra ((||))
  10. import Data.Either (Either(..))
  11. import Data.Function (($))
  12. import Data.Function.Uncurried (Fn3, runFn3)
  13. import Data.List.NonEmpty (NonEmptyList(..))
  14. import Data.Maybe (Maybe(..))
  15. import Data.Symbol (class IsSymbol)
  16. import Data.Traversable (traverse)
  17. import Data.Unit (Unit)
  18. import Effect (Effect)
  19. import Effect.Exception (throw)
  20. import Foreign (F, Foreign, ForeignError(..), fail, isNull, isUndefined, readInt, readNull, readNullOrUndefined, readNumber, renderForeignError, tagOf, unsafeFromForeign)
  21. import Foreign.Index ((!))
  22. import Heterogeneous.Mapping (class MappingWithIndex)
  23. import Prelude (bind)
  24. import Prim.Row (class Union, class Cons) as Row
  25. import Web.HTML (HTMLElement)
  26. import Web.HTML.Event.DataTransfer (DataTransfer)
  27. import Web.HTML.HTMLElement (DOMRect)
  28. foreign import data Sortable :: Type
  29. foreign import isTrue :: Foreign -> Boolean
  30. foreign import isFalse :: Foreign -> Boolean
  31. foreign import isClone :: Foreign -> Boolean
  32. data PullMode
  33. = Clone
  34. | Bool Boolean
  35. | NotDefined
  36. readPullMode :: Foreign -> F PullMode
  37. readPullMode value
  38. | isNull value || isUndefined value = pure NotDefined
  39. | isTrue value = pure (Bool true)
  40. | isFalse value = pure (Bool false)
  41. | isClone value = pure Clone
  42. | otherwise = fail $ TypeMismatch "PullMode" (tagOf value)
  43. type Event
  44. = { to :: HTMLElement
  45. , from :: HTMLElement
  46. , item :: HTMLElement
  47. , clone :: HTMLElement
  48. , oldIndex :: Maybe Int
  49. , newIndex :: Maybe Int
  50. , oldDraggableIndex :: Maybe Int
  51. , newDraggableIndex :: Maybe Int
  52. , pullMode :: PullMode
  53. }
  54. {-- foreign import data ForeignEvent :: Type --}
  55. readEvent :: Foreign -> F Event
  56. readEvent value = do
  57. to <- value ! "to" >>= (pure <<< unsafeFromForeign)
  58. from <- value ! "from" >>= (pure <<< unsafeFromForeign)
  59. item <- value ! "item" >>= (pure <<< unsafeFromForeign)
  60. clone <- value ! "clone" >>= (pure <<< unsafeFromForeign)
  61. oldIndex <- value ! "oldIndex" >>= readNullOrUndefined >>= traverse readInt
  62. newIndex <- value ! "newIndex" >>= readNullOrUndefined >>= traverse readInt
  63. oldDraggableIndex <- value ! "oldDraggableIndex" >>= readNullOrUndefined >>= traverse readInt
  64. newDraggableIndex <- value ! "newDraggableIndex" >>= readNullOrUndefined >>= traverse readInt
  65. pullMode <- value ! "pullMode" >>= readPullMode
  66. pure { to, from, item, clone, oldIndex, newIndex, oldDraggableIndex, newDraggableIndex, pullMode }
  67. type MoveEvent
  68. = { to :: HTMLElement
  69. , from :: HTMLElement
  70. , dragged :: HTMLElement
  71. , draggedRect :: DOMRect
  72. , related :: HTMLElement
  73. , relatedRect :: DOMRect
  74. , willInsertAfter :: Boolean
  75. }
  76. type Options
  77. = ( group :: String
  78. , sort :: Boolean
  79. , delay :: Int
  80. , delayOnTouchOnly :: Boolean
  81. , touchStartThreshold :: Int
  82. , disabled :: Boolean
  83. , {-- store :: --} animation :: Int
  84. , easing :: String
  85. , handle :: String
  86. , filter :: String
  87. , preventOnFilter :: Boolean
  88. , draggable :: String
  89. , dataIdAttr :: String
  90. , ghostClass :: String
  91. , chosenClass :: String
  92. , dragClass :: String
  93. , swapThreshold :: Int
  94. , invertSwap :: Boolean
  95. , invertedSwapThreshold :: Int
  96. , direction :: String
  97. , forceFallback :: Boolean
  98. , fallbackClass :: String
  99. , fallbackOnBody :: Boolean
  100. , fallbackTolerance :: Int
  101. , dragoverBubble :: Boolean
  102. , removeCloneOnHide :: Boolean
  103. , emptyInsertThreshold :: Number
  104. {-- , setData :: DataTransfer -> HTMLElement -> Effect Unit --}
  105. , onChoose :: Event -> Effect Unit
  106. , onUnchoose :: Event -> Effect Unit
  107. , onStart :: Event -> Effect Unit
  108. , onEnd :: Event -> Effect Unit
  109. , onAdd :: Event -> Effect Unit
  110. , onUpdate :: Event -> Effect Unit
  111. , onSort :: Event -> Effect Unit
  112. , onRemove :: Event -> Effect Unit
  113. , onFilter :: Event -> Effect Unit
  114. , onMove :: MoveEvent -> Effect Unit
  115. , onClone :: Event -> Effect Unit
  116. , onChange :: Event -> Effect Unit
  117. )
  118. foreign import create' ::
  119. forall given.
  120. Fn3
  121. { | given }
  122. HTMLElement
  123. ((Event -> Effect Unit) -> (Foreign -> Effect Unit))
  124. (Effect Sortable)
  125. parseEvent :: (Event -> Effect Unit) -> (Foreign -> Effect Unit)
  126. parseEvent f = wrappedF
  127. where
  128. wrappedF :: Foreign -> Effect Unit
  129. wrappedF =
  130. readEvent >>> runExcept
  131. >>> ( case _ of
  132. Left err -> throw $ formatErr err
  133. Right event -> f event
  134. )
  135. formatErr :: NonEmptyList ForeignError -> String
  136. formatErr err = intercalate ", " $ renderForeignError <$> err
  137. create ::
  138. forall given trash.
  139. Row.Union given trash Options =>
  140. { | given } ->
  141. HTMLElement ->
  142. Effect Sortable
  143. create options elem = runFn3 create' options elem parseEvent