|
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