You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/01-GettingStartedGuide.md
+89-99Lines changed: 89 additions & 99 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,15 +21,15 @@ In this guide, we will build simple flat list with draggable items.
21
21
22
22
First, let's shape up a state. This is going to be an array of ints:
23
23
24
-
```reason
25
-
type item = int;
26
-
type state = array(item);
24
+
```rescript
25
+
type item = int
26
+
type state = array<item>
27
27
```
28
28
29
29
To create components that handle all drag & drop business, we need to call `Dnd.Make()` functor:
30
30
31
-
```reason
32
-
module Items = Dnd.Make(Item, Container);
31
+
```rescript
32
+
module Items = Dnd.Make(Item, Container)
33
33
```
34
34
35
35
This code wouldn't compile yet because we need to provide two modules to the `Dnd.Make` functor:
@@ -39,10 +39,10 @@ This code wouldn't compile yet because we need to provide two modules to the `Dn
39
39
40
40
Both of these modules has the same signature:
41
41
42
-
```reason
43
-
type t;
44
-
let eq: (t, t) => bool;
45
-
let cmp: (t, t) => int;
42
+
```rescript
43
+
type t
44
+
let eq: (t, t) => bool
45
+
let cmp: (t, t) => int
46
46
```
47
47
48
48
Basically, functor asks you to answer the following questions:
@@ -52,35 +52,35 @@ Basically, functor asks you to answer the following questions:
52
52
53
53
Let's start with very simple (and in general not 100% safe) implementation of `Item` container:
54
54
55
-
```reason
55
+
```rescript
56
56
module Item = {
57
-
type t = item; // `item` is a type alias we defined above which is resolved to `int`
58
-
let eq = (x1, x2) => x1 == x2; // or more concise: let eq = (==);
59
-
let cmp = compare; // default comparator from Pervasives module
57
+
type t = item // `item` is a type alias we defined above which is resolved to `int`
58
+
let eq = (x1, x2) => x1 == x2// or more concise: let eq = (==)
59
+
let cmp = compare // default comparator from Pervasives module
60
60
}
61
61
```
62
62
63
63
Regarding `Container` type, there is no specific entity in the app domain which can be associated with this single abstract box that holds our flat list of items in UI. So we need to keep its configuration abstract, e.g.:
64
64
65
-
```reason
65
+
```rescript
66
66
module Container = {
67
-
type t; // abstract type
68
-
external id: unit => t = "%identity"; // `Container.id()` would produce value of abstract type `t`
69
-
let eq = (_, _) => true; // since `Container` is singleton, it's always equal to self
70
-
let cmp = (_, _) => 0; // same logic applies
67
+
type t // abstract type
68
+
external id: unit => t = "%identity" // `Container.id()` would produce value of abstract type `t`
69
+
let eq = (_, _) => true // since `Container` is singleton, it's always equal to self
70
+
let cmp = (_, _) => 0 // same logic applies
71
71
}
72
72
```
73
73
74
74
For convenience, `re-dnd` exposes functor which would create such singleton for you, so you don't have to type this boilerplate yourself:
75
75
76
-
```reason
77
-
module Container = Dnd.MakeSingletonContainer();
76
+
```rescript
77
+
module Container = Dnd.MakeSingletonContainer()
78
78
```
79
79
80
80
Now, when we have complete configuration defined, we can create module which holds React components:
81
81
82
-
```reason
83
-
module Items = Dnd.Make(Item, Container);
82
+
```rescript
83
+
module Items = Dnd.Make(Item, Container)
84
84
```
85
85
86
86
Module `Items` holds 3 components (each link below leads to component's api):
@@ -90,52 +90,48 @@ Module `Items` holds 3 components (each link below leads to component's api):
90
90
91
91
Let's render those:
92
92
93
-
```reason
94
-
let (state, dispatch) = reducer->React.useReducer(initialState);
93
+
```rescript
94
+
let (state, dispatch) = reducer->React.useReducer(initialState)
Even though render tree looks good, to finish this component we still need to implement handler that would persist result of reordering when item gets dropped.
114
111
115
112
This is how corresponding `action` constructor type looks like:
116
113
117
-
```reason
118
-
type action =
119
-
| ReorderItems(Dnd.result(Item.t, Container.t));
114
+
```rescript
115
+
type action = ReorderItems(Dnd.result<Item.t, Container.t>)
120
116
```
121
117
122
118
What `Dnd.result` type is?
123
119
124
-
```reason
125
-
type result('item, 'container) = option(reorderResult('item, 'container))
120
+
```rescript
121
+
type rec result<'item, 'container> = option<reorderResult<'item, 'container>>
// Item has landed in the new position of the same container,
165
+
// so it should be reinserted from the old position
166
+
// in the array into the new one.
167
+
// `ArrayExt.reinsert` is a helper which does just this.
168
+
state->ArrayExt.reinsert(
169
+
~value=item,
170
+
~place=switch placement {
171
+
| Before(id) => #Before(id)
172
+
| Last => #Last
177
173
},
178
-
)
174
+
)
179
175
180
-
// not possible since we have only one container
181
-
| ReorderItems(Some(NewContainer(_)))
182
-
| ReorderItems(None) => state
183
-
}
176
+
// not possible since we have only one container
177
+
| ReorderItems(Some(NewContainer(_)))
178
+
| ReorderItems(None) => state
179
+
}
184
180
```
185
181
186
182
> `ArrayExt.reinsert` is not a part of the public API since usually in a real-world app reordering is handled differently. If you want to inspect it or use it in your own code, you can find its definition in the [examples](../examples/libs/ArrayExt.re).
187
183
188
184
Looks like we have everything in place. This is how the final module looks like:
189
185
190
-
```reason
191
-
type item = int;
186
+
```rescript
187
+
type item = int
192
188
193
189
module Item = {
194
-
type t = item;
195
-
let eq = (x1, x2) => x1 == x2;
196
-
let cmp = compare;
197
-
};
190
+
type t = item
191
+
let eq = (x1, x2) => x1 == x2
192
+
let cmp = compare
193
+
}
198
194
199
-
module Container =
200
-
Dnd.MakeSingletonContainer({});
195
+
module Container = Dnd.MakeSingletonContainer()
201
196
202
-
module Items = Dnd.Make(Item, Container);
197
+
module Items = Dnd.Make(Item, Container)
203
198
204
-
type state = array(item);
199
+
type state = array<item>
205
200
206
-
type action =
207
-
| ReorderItems(Dnd.result(Item.t, Container.t));
201
+
type action = ReorderItems(Dnd.result<Item.t, Container.t>)
_Source code of the final module for this guide: [`GettingStartedGuide.re`](../examples/guides/GettingStartedGuide.re)_
238
+
_Source code of the final module for this guide: [`GettingStartedGuide.res`](../examples/guides/GettingStartedGuide.res)_
249
239
250
240
---
251
241
This guide gives base overview of how `re-dnd` works. To find out more about how to make it safer and how to deal with multiple containers—proceed to the [next guide](./02-SaferIdentifiersAndMultipleContainersGuide.md).
0 commit comments