@etsoo/react 1.3.94 → 1.3.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/mu/DnDList.d.ts +21 -5
- package/lib/mu/DnDList.js +21 -13
- package/package.json +1 -1
- package/src/mu/DnDList.tsx +97 -56
package/lib/mu/DnDList.d.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
|
-
import { DataTypes } from '@etsoo/shared';
|
|
2
1
|
import React, { CSSProperties } from 'react';
|
|
3
2
|
/**
|
|
4
3
|
* DnD list props
|
|
5
4
|
*/
|
|
6
|
-
export interface DnDListProps {
|
|
5
|
+
export interface DnDListProps<D extends {}, L extends keyof D, E extends React.ElementType> {
|
|
7
6
|
/**
|
|
8
7
|
* Item renderer
|
|
9
8
|
*/
|
|
10
|
-
children: (item:
|
|
9
|
+
children: (item: D, index: number, onDelete: (index: number) => void) => React.ReactNode;
|
|
10
|
+
/**
|
|
11
|
+
* List container component
|
|
12
|
+
* https://javascript.plainenglish.io/building-a-polymorphic-component-in-react-and-typescript-d9f236950af4
|
|
13
|
+
*/
|
|
14
|
+
Component?: E;
|
|
15
|
+
/**
|
|
16
|
+
* List container props
|
|
17
|
+
*/
|
|
18
|
+
componentProps?: React.ComponentProps<E>;
|
|
11
19
|
/**
|
|
12
20
|
* Get list item style callback
|
|
13
21
|
*/
|
|
@@ -16,10 +24,18 @@ export interface DnDListProps {
|
|
|
16
24
|
* Get list style callback
|
|
17
25
|
*/
|
|
18
26
|
getListStyle?: (isDraggingOver: boolean) => CSSProperties;
|
|
27
|
+
/**
|
|
28
|
+
* Label field
|
|
29
|
+
*/
|
|
30
|
+
labelField: L;
|
|
31
|
+
/**
|
|
32
|
+
* Load data
|
|
33
|
+
*/
|
|
34
|
+
loadData: (name: string) => PromiseLike<D[]>;
|
|
19
35
|
/**
|
|
20
36
|
* Top and bottom sides renderer
|
|
21
37
|
*/
|
|
22
|
-
sideRenderer?: (top: boolean, onAdd: (item:
|
|
38
|
+
sideRenderer?: (top: boolean, onAdd: (item: D) => boolean) => React.ReactNode;
|
|
23
39
|
/**
|
|
24
40
|
* Name for hidden form input
|
|
25
41
|
*/
|
|
@@ -30,4 +46,4 @@ export interface DnDListProps {
|
|
|
30
46
|
* @param props Props
|
|
31
47
|
* @returns Component
|
|
32
48
|
*/
|
|
33
|
-
export declare function DnDList(props: DnDListProps): JSX.Element;
|
|
49
|
+
export declare function DnDList<D extends {}, L extends keyof D, E extends React.ElementType = React.ElementType>(props: DnDListProps<D, L, E>): JSX.Element;
|
package/lib/mu/DnDList.js
CHANGED
|
@@ -8,7 +8,7 @@ import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
|
|
|
8
8
|
*/
|
|
9
9
|
export function DnDList(props) {
|
|
10
10
|
// Destruct
|
|
11
|
-
const { children, getItemStyle = (_isDragging) => ({}), getListStyle = () => undefined, name, sideRenderer } = props;
|
|
11
|
+
const { children, Component = 'div', componentProps, getItemStyle = (_isDragging) => ({}), getListStyle = () => undefined, labelField, loadData, name, sideRenderer } = props;
|
|
12
12
|
// State
|
|
13
13
|
const [items, setItems] = React.useState([]);
|
|
14
14
|
// Drag end handler
|
|
@@ -30,7 +30,7 @@ export function DnDList(props) {
|
|
|
30
30
|
// Add handler
|
|
31
31
|
const onAdd = (newItem) => {
|
|
32
32
|
// Existence check
|
|
33
|
-
if (items.some((item) =>
|
|
33
|
+
if (items.some((item) => item[labelField] == newItem[labelField])) {
|
|
34
34
|
return false;
|
|
35
35
|
}
|
|
36
36
|
// Clone
|
|
@@ -48,19 +48,27 @@ export function DnDList(props) {
|
|
|
48
48
|
// Update the state
|
|
49
49
|
setItems(newItems);
|
|
50
50
|
};
|
|
51
|
+
React.useEffect(() => {
|
|
52
|
+
loadData(name).then((items) => setItems(items));
|
|
53
|
+
}, [name]);
|
|
51
54
|
// Layout
|
|
52
55
|
return (React.createElement(React.Fragment, null,
|
|
53
56
|
sideRenderer && sideRenderer(true, onAdd),
|
|
54
|
-
React.createElement(
|
|
55
|
-
React.createElement(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
React.createElement(Component, { ...componentProps },
|
|
58
|
+
React.createElement(DragDropContext, { onDragEnd: onDragEnd },
|
|
59
|
+
React.createElement(Droppable, { droppableId: name }, (provided, snapshot) => (React.createElement("div", { ...provided.droppableProps, ref: provided.innerRef, style: getListStyle(snapshot.isDraggingOver) },
|
|
60
|
+
items.map((item, index) => {
|
|
61
|
+
// Id
|
|
62
|
+
const id = DataTypes.convert(item[labelField], 'string');
|
|
63
|
+
if (id == null)
|
|
64
|
+
return;
|
|
65
|
+
return (React.createElement(Draggable, { key: id, draggableId: id, index: index }, (provided, snapshot) => (React.createElement("div", { ref: provided.innerRef, ...provided.draggableProps, ...provided.dragHandleProps, style: {
|
|
66
|
+
...getItemStyle(snapshot.isDragging),
|
|
67
|
+
...provided
|
|
68
|
+
.draggableProps
|
|
69
|
+
.style
|
|
70
|
+
} }, children(item, index, onDelete)))));
|
|
71
|
+
}),
|
|
72
|
+
provided.placeholder))))),
|
|
65
73
|
sideRenderer && sideRenderer(false, onAdd)));
|
|
66
74
|
}
|
package/package.json
CHANGED
package/src/mu/DnDList.tsx
CHANGED
|
@@ -10,16 +10,31 @@ import {
|
|
|
10
10
|
/**
|
|
11
11
|
* DnD list props
|
|
12
12
|
*/
|
|
13
|
-
export interface DnDListProps
|
|
13
|
+
export interface DnDListProps<
|
|
14
|
+
D extends {},
|
|
15
|
+
L extends keyof D,
|
|
16
|
+
E extends React.ElementType
|
|
17
|
+
> {
|
|
14
18
|
/**
|
|
15
19
|
* Item renderer
|
|
16
20
|
*/
|
|
17
21
|
children: (
|
|
18
|
-
item:
|
|
22
|
+
item: D,
|
|
19
23
|
index: number,
|
|
20
24
|
onDelete: (index: number) => void
|
|
21
25
|
) => React.ReactNode;
|
|
22
26
|
|
|
27
|
+
/**
|
|
28
|
+
* List container component
|
|
29
|
+
* https://javascript.plainenglish.io/building-a-polymorphic-component-in-react-and-typescript-d9f236950af4
|
|
30
|
+
*/
|
|
31
|
+
Component?: E;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* List container props
|
|
35
|
+
*/
|
|
36
|
+
componentProps?: React.ComponentProps<E>;
|
|
37
|
+
|
|
23
38
|
/**
|
|
24
39
|
* Get list item style callback
|
|
25
40
|
*/
|
|
@@ -30,12 +45,22 @@ export interface DnDListProps {
|
|
|
30
45
|
*/
|
|
31
46
|
getListStyle?: (isDraggingOver: boolean) => CSSProperties;
|
|
32
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Label field
|
|
50
|
+
*/
|
|
51
|
+
labelField: L;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Load data
|
|
55
|
+
*/
|
|
56
|
+
loadData: (name: string) => PromiseLike<D[]>;
|
|
57
|
+
|
|
33
58
|
/**
|
|
34
59
|
* Top and bottom sides renderer
|
|
35
60
|
*/
|
|
36
61
|
sideRenderer?: (
|
|
37
62
|
top: boolean,
|
|
38
|
-
onAdd: (item:
|
|
63
|
+
onAdd: (item: D) => boolean
|
|
39
64
|
) => React.ReactNode;
|
|
40
65
|
|
|
41
66
|
/**
|
|
@@ -49,18 +74,26 @@ export interface DnDListProps {
|
|
|
49
74
|
* @param props Props
|
|
50
75
|
* @returns Component
|
|
51
76
|
*/
|
|
52
|
-
export function DnDList
|
|
77
|
+
export function DnDList<
|
|
78
|
+
D extends {},
|
|
79
|
+
L extends keyof D,
|
|
80
|
+
E extends React.ElementType = React.ElementType
|
|
81
|
+
>(props: DnDListProps<D, L, E>) {
|
|
53
82
|
// Destruct
|
|
54
83
|
const {
|
|
55
84
|
children,
|
|
85
|
+
Component = 'div',
|
|
86
|
+
componentProps,
|
|
56
87
|
getItemStyle = (_isDragging) => ({}),
|
|
57
88
|
getListStyle = () => undefined,
|
|
89
|
+
labelField,
|
|
90
|
+
loadData,
|
|
58
91
|
name,
|
|
59
92
|
sideRenderer
|
|
60
93
|
} = props;
|
|
61
94
|
|
|
62
95
|
// State
|
|
63
|
-
const [items, setItems] = React.useState<
|
|
96
|
+
const [items, setItems] = React.useState<D[]>([]);
|
|
64
97
|
|
|
65
98
|
// Drag end handler
|
|
66
99
|
const onDragEnd = (result: DropResult) => {
|
|
@@ -84,14 +117,9 @@ export function DnDList(props: DnDListProps) {
|
|
|
84
117
|
};
|
|
85
118
|
|
|
86
119
|
// Add handler
|
|
87
|
-
const onAdd = (newItem:
|
|
120
|
+
const onAdd = (newItem: D) => {
|
|
88
121
|
// Existence check
|
|
89
|
-
if (
|
|
90
|
-
items.some(
|
|
91
|
-
(item) =>
|
|
92
|
-
DataTypes.getItemId(item) === DataTypes.getItemId(newItem)
|
|
93
|
-
)
|
|
94
|
-
) {
|
|
122
|
+
if (items.some((item) => item[labelField] == newItem[labelField])) {
|
|
95
123
|
return false;
|
|
96
124
|
}
|
|
97
125
|
|
|
@@ -116,54 +144,67 @@ export function DnDList(props: DnDListProps) {
|
|
|
116
144
|
setItems(newItems);
|
|
117
145
|
};
|
|
118
146
|
|
|
147
|
+
React.useEffect(() => {
|
|
148
|
+
loadData(name).then((items) => setItems(items));
|
|
149
|
+
}, [name]);
|
|
150
|
+
|
|
119
151
|
// Layout
|
|
120
152
|
return (
|
|
121
153
|
<React.Fragment>
|
|
122
154
|
{sideRenderer && sideRenderer(true, onAdd)}
|
|
123
|
-
<
|
|
124
|
-
<
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
155
|
+
<Component {...componentProps}>
|
|
156
|
+
<DragDropContext onDragEnd={onDragEnd}>
|
|
157
|
+
<Droppable droppableId={name}>
|
|
158
|
+
{(provided, snapshot) => (
|
|
159
|
+
<div
|
|
160
|
+
{...provided.droppableProps}
|
|
161
|
+
ref={provided.innerRef}
|
|
162
|
+
style={getListStyle(snapshot.isDraggingOver)}
|
|
163
|
+
>
|
|
164
|
+
{items.map((item, index) => {
|
|
165
|
+
// Id
|
|
166
|
+
const id = DataTypes.convert(
|
|
167
|
+
item[labelField],
|
|
168
|
+
'string'
|
|
169
|
+
);
|
|
170
|
+
if (id == null) return;
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<Draggable
|
|
174
|
+
key={id}
|
|
175
|
+
draggableId={id}
|
|
176
|
+
index={index}
|
|
177
|
+
>
|
|
178
|
+
{(provided, snapshot) => (
|
|
179
|
+
<div
|
|
180
|
+
ref={provided.innerRef}
|
|
181
|
+
{...provided.draggableProps}
|
|
182
|
+
{...provided.dragHandleProps}
|
|
183
|
+
style={{
|
|
184
|
+
...getItemStyle(
|
|
185
|
+
snapshot.isDragging
|
|
186
|
+
),
|
|
187
|
+
...provided
|
|
188
|
+
.draggableProps
|
|
189
|
+
.style
|
|
190
|
+
}}
|
|
191
|
+
>
|
|
192
|
+
{children(
|
|
193
|
+
item,
|
|
194
|
+
index,
|
|
195
|
+
onDelete
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
)}
|
|
199
|
+
</Draggable>
|
|
200
|
+
);
|
|
201
|
+
})}
|
|
202
|
+
{provided.placeholder}
|
|
203
|
+
</div>
|
|
204
|
+
)}
|
|
205
|
+
</Droppable>
|
|
206
|
+
</DragDropContext>
|
|
207
|
+
</Component>
|
|
167
208
|
{sideRenderer && sideRenderer(false, onAdd)}
|
|
168
209
|
</React.Fragment>
|
|
169
210
|
);
|