|
1 | 1 | module Basic where
|
2 | 2 |
|
3 | 3 | import Prelude
|
4 |
| - |
5 | 4 | import Data.Array ((!!), drop, mapWithIndex, take)
|
6 |
| -import Data.Foldable (for_) |
7 |
| -import Data.Maybe (Maybe(Nothing), fromMaybe, maybe) |
8 |
| -import React.Basic (Component, JSX, StateUpdate(..), createComponent, fragment, make, runUpdate) |
| 5 | +import Data.Foldable (traverse_) |
| 6 | +import Data.Int as Int |
| 7 | +import Data.Maybe (fromMaybe, maybe) |
| 8 | +import Effect (Effect) |
9 | 9 | import React.Basic.DOM as R
|
10 | 10 | import React.Basic.DOM.Events (targetChecked)
|
11 | 11 | import React.Basic.Events as Events
|
12 |
| -import React.Basic.ReactDND (DragDrop, DragDropItemType(..), createDragDrop, createDragDropContext) |
| 12 | +import React.Basic.Hooks (Component, component, fragment, mkReducer, useReducer, (/\)) |
| 13 | +import React.Basic.Hooks as React |
| 14 | +import React.Basic.ReactDND (dndProvider, mergeTargets, useDrag, useDrop) |
13 | 15 | import React.Basic.ReactDND.Backends.HTML5Backend (html5Backend)
|
14 | 16 |
|
15 |
| -dndContext :: JSX -> JSX |
16 |
| -dndContext = createDragDropContext html5Backend |
17 |
| - |
18 |
| -dnd :: DragDrop { itemId :: String, index :: Int } |
19 |
| -dnd = createDragDrop (DragDropItemType "TODO_ITEM") |
20 |
| - |
21 | 17 | data Action
|
22 | 18 | = Move { from :: Int, to :: Int }
|
23 | 19 | | SetDone String Boolean
|
24 | 20 |
|
25 |
| -component :: Component Unit |
26 |
| -component = createComponent "TodoExample" |
27 |
| - |
28 |
| -todoExample :: JSX |
29 |
| -todoExample = unit # make component { initialState, render } |
30 |
| - where |
31 |
| - initialState = |
32 |
| - { todos: |
33 |
| - [ { id: "a", text: "PureScript", done: true } |
34 |
| - , { id: "b", text: "React-Basic", done: true } |
35 |
| - , { id: "c", text: "React-DND-Basic", done: false } |
36 |
| - ] |
37 |
| - } |
| 21 | +type Todo |
| 22 | + = { id :: String, text :: String, done :: Boolean } |
38 | 23 |
|
39 |
| - update self = case _ of |
40 |
| - Move { from, to } -> |
41 |
| - Update self.state { todos = moveItem from to self.state.todos } |
42 |
| - |
43 |
| - SetDone id done -> |
44 |
| - Update self.state |
45 |
| - { todos = self.state.todos <#> \t -> |
46 |
| - if t.id == id |
47 |
| - then t { done = done } |
48 |
| - else t |
49 |
| - } |
50 |
| - |
51 |
| - send self = runUpdate update self |
52 |
| - |
53 |
| - render self = |
54 |
| - dndContext $ |
55 |
| - fragment |
| 24 | +mkTodoExample :: Component Unit |
| 25 | +mkTodoExample = do |
| 26 | + todo <- mkTodo |
| 27 | + reducer <- mkReducer update |
| 28 | + React.component "TodoExample" \_ -> React.do |
| 29 | + state /\ dispatch <- useReducer initialState reducer |
| 30 | + pure |
| 31 | + $ dndProvider html5Backend |
| 32 | + $ fragment |
56 | 33 | [ R.h1_ [ R.text "Todos" ]
|
57 | 34 | , R.p_ [ R.text "Drag to reorder the list:" ]
|
58 |
| - , R.section_ (mapWithIndex renderTodo self.state.todos) |
| 35 | + , R.section_ |
| 36 | + $ state.todos |
| 37 | + # mapWithIndex \index t -> todo { index, todo: t, dispatch } |
59 | 38 | ]
|
| 39 | + where |
| 40 | + initialState = |
| 41 | + { todos: |
| 42 | + [ { id: "a", text: "PureScript", done: true } |
| 43 | + , { id: "b", text: "React Basic", done: true } |
| 44 | + , { id: "c", text: "React Basic DND", done: false } |
| 45 | + ] |
| 46 | + } |
| 47 | + |
| 48 | + update state = case _ of |
| 49 | + Move { from, to } -> |
| 50 | + state |
| 51 | + { todos = moveItem from to state.todos |
| 52 | + } |
| 53 | + SetDone id done -> |
| 54 | + state |
| 55 | + { todos = |
| 56 | + state.todos |
| 57 | + <#> \t -> |
| 58 | + if t.id == id then |
| 59 | + t { done = done } |
| 60 | + else |
| 61 | + t |
| 62 | + } |
60 | 63 |
|
61 |
| - where |
62 |
| - renderTodo index todo = |
63 |
| - dnd.dragSource |
64 |
| - { beginDrag: \_ -> pure |
65 |
| - { itemId: todo.id |
66 |
| - , index |
| 64 | +mkTodo :: |
| 65 | + Component |
| 66 | + { index :: Int |
| 67 | + , todo :: Todo |
| 68 | + , dispatch :: Action -> Effect Unit |
| 69 | + } |
| 70 | +mkTodo = do |
| 71 | + let |
| 72 | + todoDND = "todo-dnd" |
| 73 | + component "Todo" \{ index, todo, dispatch } -> React.do |
| 74 | + { isDragging, connectDrag } <- useDrag { type: todoDND, id: show index } |
| 75 | + { id: maybeDragItem, isOver, connectDrop } <- |
| 76 | + useDrop |
| 77 | + { accept: todoDND |
| 78 | + , onDrop: Int.fromString >>> traverse_ \id -> dispatch $ Move { from: id, to: index } |
| 79 | + } |
| 80 | + pure |
| 81 | + $ R.label |
| 82 | + { ref: mergeTargets connectDrag connectDrop |
| 83 | + , style: |
| 84 | + R.css |
| 85 | + { display: "block" |
| 86 | + , padding: "0.3rem 0.8rem" |
| 87 | + , alignItems: "center" |
| 88 | + , borderTop: |
| 89 | + if isOver && maybe false ((_ > show index)) maybeDragItem then |
| 90 | + "0.2rem solid #0044e4" |
| 91 | + else |
| 92 | + "0.2rem solid transparent" |
| 93 | + , borderBottom: |
| 94 | + if isOver && maybe false ((_ < show index)) maybeDragItem then |
| 95 | + "0.2rem solid #0044e4" |
| 96 | + else |
| 97 | + "0.2rem solid transparent" |
| 98 | + , opacity: if isDragging then 0.1 else 1.0 |
67 | 99 | }
|
68 |
| - , endDrag: const (pure unit) |
69 |
| - , canDrag: const (pure true) |
70 |
| - , isDragging: \{ item: draggingItem } -> |
71 |
| - pure $ maybe false (\i -> i.itemId == todo.id) draggingItem |
72 |
| - , render: \{ connectDragSource, isDragging } -> |
73 |
| - dnd.dropTarget |
74 |
| - { drop: \{ item: dragItem } -> do |
75 |
| - for_ (_.index <$> dragItem) \dragItemIndex -> |
76 |
| - send self $ Move { from: dragItemIndex, to: index } |
77 |
| - pure Nothing |
78 |
| - , hover: const (pure unit) |
79 |
| - , canDrop: const (pure true) |
80 |
| - , render: \{ connectDropTarget, isOver, item: maybeDragItem } -> |
81 |
| - connectDragSource $ connectDropTarget $ |
82 |
| - R.div |
83 |
| - { style: R.css |
84 |
| - { padding: "0.3rem 0.8rem" |
85 |
| - , alignItems: "center" |
86 |
| - , borderTop: |
87 |
| - if isOver && (fromMaybe false ((\dragItem -> dragItem.index > index) <$> maybeDragItem)) |
88 |
| - then "0.2rem solid #0044e4" |
89 |
| - else "0.2rem solid transparent" |
90 |
| - , borderBottom: |
91 |
| - if isOver && (fromMaybe false ((\dragItem -> dragItem.index < index) <$> maybeDragItem)) |
92 |
| - then "0.2rem solid #0044e4" |
93 |
| - else "0.2rem solid transparent" |
94 |
| - , opacity: if isDragging then 0.1 else 1.0 |
95 |
| - } |
96 |
| - , children: |
97 |
| - [ R.input |
98 |
| - { "type": "checkbox" |
99 |
| - , checked: todo.done |
100 |
| - , onChange: Events.handler targetChecked \checked -> do |
101 |
| - send self $ SetDone todo.id $ fromMaybe false checked |
102 |
| - } |
103 |
| - , R.text todo.text |
104 |
| - ] |
105 |
| - } |
| 100 | + , children: |
| 101 | + [ R.input |
| 102 | + { type: "checkbox" |
| 103 | + , checked: todo.done |
| 104 | + , onChange: |
| 105 | + Events.handler targetChecked \checked -> do |
| 106 | + dispatch $ SetDone todo.id $ fromMaybe false checked |
106 | 107 | }
|
107 |
| - } |
| 108 | + , R.text todo.text |
| 109 | + ] |
| 110 | + } |
108 | 111 |
|
109 | 112 | moveItem :: forall a. Int -> Int -> Array a -> Array a
|
110 | 113 | moveItem fromIndex toIndex items =
|
111 | 114 | let
|
112 | 115 | item = items !! fromIndex
|
| 116 | + |
113 | 117 | items' = take fromIndex items <> drop (fromIndex + 1) items
|
114 | 118 | in
|
115 | 119 | take toIndex items'
|
|
0 commit comments