@etsoo/react 1.5.61 → 1.5.64

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/index.d.ts CHANGED
@@ -42,6 +42,7 @@ export * from './mu/CustomFabProps';
42
42
  export * from './mu/DataGridEx';
43
43
  export * from './mu/DataGridRenderers';
44
44
  export * from './mu/DialogButton';
45
+ export * from './mu/DnDList';
45
46
  export * from './mu/DraggablePaperComponent';
46
47
  export * from './mu/EmailInput';
47
48
  export * from './mu/FabBox';
package/lib/index.js CHANGED
@@ -45,6 +45,7 @@ export * from './mu/CustomFabProps';
45
45
  export * from './mu/DataGridEx';
46
46
  export * from './mu/DataGridRenderers';
47
47
  export * from './mu/DialogButton';
48
+ export * from './mu/DnDList';
48
49
  export * from './mu/DraggablePaperComponent';
49
50
  export * from './mu/EmailInput';
50
51
  export * from './mu/FabBox';
@@ -0,0 +1,74 @@
1
+ import { UniqueIdentifier } from '@dnd-kit/core';
2
+ import { DataTypes } from '@etsoo/shared';
3
+ import React, { CSSProperties } from 'react';
4
+ /**
5
+ * Scroller list forward ref
6
+ */
7
+ export interface DnDListRef<D extends {}> {
8
+ /**
9
+ * Add item
10
+ * @param item New item
11
+ */
12
+ addItem(item: D): void;
13
+ /**
14
+ * Add items
15
+ * @param items items
16
+ */
17
+ addItems(items: D[]): void;
18
+ /**
19
+ * Delete item
20
+ * @param index Item index
21
+ */
22
+ deleteItem(index: number): void;
23
+ /**
24
+ * Edit item
25
+ * @param newItem New item
26
+ * @param index Index
27
+ */
28
+ editItem(newItem: D, index: number): boolean;
29
+ }
30
+ /**
31
+ * DnD sortable list properties
32
+ */
33
+ export interface DnDListPros<D extends {}, K extends DataTypes.Keys<D>> {
34
+ /**
35
+ * Get list item style callback
36
+ */
37
+ getItemStyle?: (index: number, isDragging: boolean) => CSSProperties;
38
+ /**
39
+ * Item renderer
40
+ */
41
+ itemRenderer: (item: D, index: number, nodeRef: React.ComponentProps<any>, actionNodeRef: React.ComponentProps<any>) => React.ReactElement;
42
+ /**
43
+ * List items
44
+ */
45
+ items: D[];
46
+ /**
47
+ * Unique key field
48
+ */
49
+ keyField: K;
50
+ /**
51
+ * Label field
52
+ */
53
+ labelField: K;
54
+ /**
55
+ * Methods ref
56
+ */
57
+ mRef?: React.Ref<DnDListRef<D>>;
58
+ /**
59
+ * Data change handler
60
+ */
61
+ onChange?: (items: D[]) => void;
62
+ /**
63
+ * Drag end handler
64
+ */
65
+ onDragEnd?: (items: D[]) => void;
66
+ }
67
+ /**
68
+ * DnD (Drag and Drop) sortable list
69
+ * @param props Props
70
+ * @returns Component
71
+ */
72
+ export declare function DnDList<D extends {
73
+ id: UniqueIdentifier;
74
+ }, K extends DataTypes.Keys<D, UniqueIdentifier> = DataTypes.Keys<D, UniqueIdentifier>>(props: DnDListPros<D, K>): JSX.Element;
@@ -0,0 +1,133 @@
1
+ import { DndContext } from '@dnd-kit/core';
2
+ import { SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
3
+ import { CSS } from '@dnd-kit/utilities';
4
+ import React from 'react';
5
+ function SortableItem(props) {
6
+ // Destruct
7
+ const { id, itemRenderer, style = {} } = props;
8
+ // Use sortable
9
+ const { attributes, listeners, setNodeRef, transform, transition, setActivatorNodeRef } = useSortable({ id });
10
+ const allStyle = {
11
+ ...style,
12
+ transform: CSS.Transform.toString(transform),
13
+ transition
14
+ };
15
+ const nodeRef = {
16
+ style: allStyle,
17
+ ref: setNodeRef,
18
+ ...attributes
19
+ };
20
+ const actionNodeRef = {
21
+ ...listeners,
22
+ ref: setActivatorNodeRef
23
+ };
24
+ return itemRenderer(nodeRef, actionNodeRef);
25
+ }
26
+ /**
27
+ * DnD (Drag and Drop) sortable list
28
+ * @param props Props
29
+ * @returns Component
30
+ */
31
+ export function DnDList(props) {
32
+ // Destruct
33
+ const { getItemStyle, keyField, itemRenderer, labelField, mRef, onChange, onDragEnd } = props;
34
+ // States
35
+ const [items, setItems] = React.useState([]);
36
+ const [activeId, setActiveId] = React.useState();
37
+ const changeItems = (newItems) => {
38
+ // Possible to alter items with the handler
39
+ if (onChange)
40
+ onChange(newItems);
41
+ // Update state
42
+ setItems(newItems);
43
+ };
44
+ // Drag event handlers
45
+ function handleDragStart(event) {
46
+ const { active } = event;
47
+ setActiveId(active.id);
48
+ }
49
+ function handleDragEnd(event) {
50
+ const { active, over } = event;
51
+ if (over && active.id !== over.id) {
52
+ // Indices
53
+ const oldIndex = items.findIndex((item) => item.id === active.id);
54
+ const newIndex = items.findIndex((item) => item.id === over.id);
55
+ // Clone
56
+ const newItems = [...items];
57
+ // Removed item
58
+ const [removed] = newItems.splice(oldIndex, 1);
59
+ // Insert to the destination index
60
+ newItems.splice(newIndex, 0, removed);
61
+ changeItems(newItems);
62
+ // Drag end handler
63
+ if (onDragEnd)
64
+ onDragEnd(newItems);
65
+ }
66
+ setActiveId(undefined);
67
+ }
68
+ // Methods
69
+ React.useImperativeHandle(mRef, () => {
70
+ return {
71
+ addItem(newItem) {
72
+ // Existence check
73
+ if (items.some((item) => item[labelField] === newItem[labelField])) {
74
+ return false;
75
+ }
76
+ // Clone
77
+ const newItems = [newItem, ...items];
78
+ // Update the state
79
+ changeItems(newItems);
80
+ return true;
81
+ },
82
+ addItems(inputItems) {
83
+ // Clone
84
+ const newItems = [...items];
85
+ // Insert items
86
+ inputItems.forEach((newItem) => {
87
+ // Existence check
88
+ if (newItems.some((item) => item[labelField] === newItem[labelField])) {
89
+ return;
90
+ }
91
+ newItems.push(newItem);
92
+ });
93
+ // Update the state
94
+ changeItems(newItems);
95
+ return newItems.length - items.length;
96
+ },
97
+ editItem(newItem, index) {
98
+ // Existence check
99
+ const newIndex = items.findIndex((item) => item[labelField] === newItem[labelField]);
100
+ if (newIndex >= 0 && newIndex !== index) {
101
+ // Label field is the same with a different item
102
+ return false;
103
+ }
104
+ // Clone
105
+ const newItems = [...items];
106
+ // Remove the item
107
+ newItems.splice(index, 1, newItem);
108
+ // Update the state
109
+ changeItems(newItems);
110
+ return true;
111
+ },
112
+ deleteItem(index) {
113
+ // Clone
114
+ const newItems = [...items];
115
+ // Remove the item
116
+ newItems.splice(index, 1);
117
+ // Update the state
118
+ changeItems(newItems);
119
+ }
120
+ };
121
+ }, [items]);
122
+ React.useEffect(() => {
123
+ setItems(props.items);
124
+ }, [props.items]);
125
+ return (React.createElement(DndContext, { onDragStart: handleDragStart, onDragEnd: handleDragEnd },
126
+ React.createElement(SortableContext, { items: items, strategy: verticalListSortingStrategy }, items.map((item, index) => {
127
+ var id = item[keyField];
128
+ var itemStyle = getItemStyle == null
129
+ ? undefined
130
+ : getItemStyle(index, id === activeId);
131
+ return (React.createElement(SortableItem, { id: id, key: id, style: itemStyle, itemRenderer: (nodeRef, actionNodeRef) => itemRenderer(item, index, nodeRef, actionNodeRef) }));
132
+ }))));
133
+ }
@@ -12,7 +12,7 @@ export function OptionGroup(props) {
12
12
  // Get option value
13
13
  // D type should be the source id type
14
14
  const getOptionValue = (option) => {
15
- const value = DataTypes.getIdValue(option, idField);
15
+ const value = DataTypes.getValue(option, idField);
16
16
  if (value == null)
17
17
  return null;
18
18
  return value;
@@ -112,7 +112,7 @@ export function ScrollerListEx(props) {
112
112
  return selected;
113
113
  };
114
114
  // Destruct
115
- const { alternatingColors = [undefined, undefined], className, idField = 'id', innerItemRenderer, itemSize, itemKey = (index, data) => { var _a; return (_a = DataTypes.getIdValue(data, idField)) !== null && _a !== void 0 ? _a : index; }, itemRenderer = (itemProps) => {
115
+ const { alternatingColors = [undefined, undefined], className, idField = 'id', innerItemRenderer, itemSize, itemKey = (index, data) => { var _a; return (_a = DataTypes.getValue(data, idField)) !== null && _a !== void 0 ? _a : index; }, itemRenderer = (itemProps) => {
116
116
  const [itemHeight, space, margins] = calculateItemSize(itemProps.index);
117
117
  return defaultItemRenderer({
118
118
  itemHeight,
package/lib/mu/TableEx.js CHANGED
@@ -229,7 +229,7 @@ export function TableEx(props) {
229
229
  ? rows[rowIndex]
230
230
  : undefined;
231
231
  // Row id field value
232
- const rowId = (_a = DataTypes.getIdValue(row, idField)) !== null && _a !== void 0 ? _a : rowIndex;
232
+ const rowId = (_a = DataTypes.getValue(row, idField)) !== null && _a !== void 0 ? _a : rowIndex;
233
233
  // Selected or not
234
234
  const isItemSelected = selectable
235
235
  ? selectedItems.some((item) => item[idField] === rowId)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/react",
3
- "version": "1.5.61",
3
+ "version": "1.5.64",
4
4
  "description": "TypeScript ReactJs framework",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -46,14 +46,23 @@
46
46
  },
47
47
  "homepage": "https://github.com/ETSOO/AppReact#readme",
48
48
  "dependencies": {
49
+ "@dnd-kit/core": "^6.0.5",
50
+ "@dnd-kit/sortable": "^7.0.1",
49
51
  "@emotion/css": "^11.10.0",
50
52
  "@emotion/react": "^11.10.0",
51
53
  "@emotion/styled": "^11.10.0",
52
- "@etsoo/appscript": "^1.2.72",
54
+ "@etsoo/appscript": "^1.2.73",
53
55
  "@etsoo/notificationbase": "^1.1.5",
54
- "@etsoo/shared": "^1.1.41",
56
+ "@etsoo/shared": "^1.1.42",
55
57
  "@mui/icons-material": "^5.8.4",
56
58
  "@mui/material": "^5.9.3",
59
+ "@types/pica": "^9.0.1",
60
+ "@types/pulltorefreshjs": "^0.1.5",
61
+ "@types/react": "^18.0.15",
62
+ "@types/react-avatar-editor": "^13.0.0",
63
+ "@types/react-dom": "^18.0.6",
64
+ "@types/react-input-mask": "^3.0.1",
65
+ "@types/react-window": "^1.8.5",
57
66
  "pica": "^9.0.1",
58
67
  "pulltorefreshjs": "^0.1.22",
59
68
  "react": "^18.2.0",
@@ -70,13 +79,6 @@
70
79
  "@babel/plugin-transform-runtime": "^7.18.10",
71
80
  "@babel/preset-env": "^7.18.10",
72
81
  "@babel/runtime-corejs3": "^7.18.9",
73
- "@types/pica": "^9.0.1",
74
- "@types/pulltorefreshjs": "^0.1.5",
75
- "@types/react": "^18.0.15",
76
- "@types/react-avatar-editor": "^12.0.0",
77
- "@types/react-dom": "^18.0.6",
78
- "@types/react-input-mask": "^3.0.1",
79
- "@types/react-window": "^1.8.5",
80
82
  "@types/jest": "^28.1.6",
81
83
  "@types/react-test-renderer": "^18.0.0",
82
84
  "@typescript-eslint/eslint-plugin": "^5.32.0",
package/src/index.ts CHANGED
@@ -49,6 +49,7 @@ export * from './mu/CustomFabProps';
49
49
  export * from './mu/DataGridEx';
50
50
  export * from './mu/DataGridRenderers';
51
51
  export * from './mu/DialogButton';
52
+ export * from './mu/DnDList';
52
53
  export * from './mu/DraggablePaperComponent';
53
54
  export * from './mu/EmailInput';
54
55
  export * from './mu/FabBox';
@@ -0,0 +1,325 @@
1
+ import {
2
+ DndContext,
3
+ DragEndEvent,
4
+ DragStartEvent,
5
+ UniqueIdentifier
6
+ } from '@dnd-kit/core';
7
+ import {
8
+ SortableContext,
9
+ useSortable,
10
+ verticalListSortingStrategy
11
+ } from '@dnd-kit/sortable';
12
+ import { CSS } from '@dnd-kit/utilities';
13
+ import { DataTypes } from '@etsoo/shared';
14
+ import React, { CSSProperties } from 'react';
15
+
16
+ function SortableItem(props: {
17
+ id: UniqueIdentifier;
18
+ itemRenderer: (
19
+ nodeRef: React.ComponentProps<any>,
20
+ actionNodeRef: React.ComponentProps<any>
21
+ ) => React.ReactElement;
22
+ style?: React.CSSProperties;
23
+ }) {
24
+ // Destruct
25
+ const { id, itemRenderer, style = {} } = props;
26
+
27
+ // Use sortable
28
+ const {
29
+ attributes,
30
+ listeners,
31
+ setNodeRef,
32
+ transform,
33
+ transition,
34
+ setActivatorNodeRef
35
+ } = useSortable({ id });
36
+
37
+ const allStyle = {
38
+ ...style,
39
+ transform: CSS.Transform.toString(transform),
40
+ transition
41
+ };
42
+
43
+ const nodeRef = {
44
+ style: allStyle,
45
+ ref: setNodeRef,
46
+ ...attributes
47
+ };
48
+
49
+ const actionNodeRef = {
50
+ ...listeners,
51
+ ref: setActivatorNodeRef
52
+ };
53
+
54
+ return itemRenderer(nodeRef, actionNodeRef);
55
+ }
56
+
57
+ /**
58
+ * Scroller list forward ref
59
+ */
60
+ export interface DnDListRef<D extends {}> {
61
+ /**
62
+ * Add item
63
+ * @param item New item
64
+ */
65
+ addItem(item: D): void;
66
+
67
+ /**
68
+ * Add items
69
+ * @param items items
70
+ */
71
+ addItems(items: D[]): void;
72
+
73
+ /**
74
+ * Delete item
75
+ * @param index Item index
76
+ */
77
+ deleteItem(index: number): void;
78
+
79
+ /**
80
+ * Edit item
81
+ * @param newItem New item
82
+ * @param index Index
83
+ */
84
+ editItem(newItem: D, index: number): boolean;
85
+ }
86
+
87
+ /**
88
+ * DnD sortable list properties
89
+ */
90
+ export interface DnDListPros<D extends {}, K extends DataTypes.Keys<D>> {
91
+ /**
92
+ * Get list item style callback
93
+ */
94
+ getItemStyle?: (index: number, isDragging: boolean) => CSSProperties;
95
+
96
+ /**
97
+ * Item renderer
98
+ */
99
+ itemRenderer: (
100
+ item: D,
101
+ index: number,
102
+ nodeRef: React.ComponentProps<any>,
103
+ actionNodeRef: React.ComponentProps<any>
104
+ ) => React.ReactElement;
105
+
106
+ /**
107
+ * List items
108
+ */
109
+ items: D[];
110
+
111
+ /**
112
+ * Unique key field
113
+ */
114
+ keyField: K;
115
+
116
+ /**
117
+ * Label field
118
+ */
119
+ labelField: K;
120
+
121
+ /**
122
+ * Methods ref
123
+ */
124
+ mRef?: React.Ref<DnDListRef<D>>;
125
+
126
+ /**
127
+ * Data change handler
128
+ */
129
+ onChange?: (items: D[]) => void;
130
+
131
+ /**
132
+ * Drag end handler
133
+ */
134
+ onDragEnd?: (items: D[]) => void;
135
+ }
136
+
137
+ /**
138
+ * DnD (Drag and Drop) sortable list
139
+ * @param props Props
140
+ * @returns Component
141
+ */
142
+ export function DnDList<
143
+ D extends { id: UniqueIdentifier },
144
+ K extends DataTypes.Keys<D, UniqueIdentifier> = DataTypes.Keys<
145
+ D,
146
+ UniqueIdentifier
147
+ >
148
+ >(props: DnDListPros<D, K>) {
149
+ // Destruct
150
+ const {
151
+ getItemStyle,
152
+ keyField,
153
+ itemRenderer,
154
+ labelField,
155
+ mRef,
156
+ onChange,
157
+ onDragEnd
158
+ } = props;
159
+
160
+ // States
161
+ const [items, setItems] = React.useState<D[]>([]);
162
+ const [activeId, setActiveId] = React.useState<UniqueIdentifier>();
163
+
164
+ const changeItems = (newItems: D[]) => {
165
+ // Possible to alter items with the handler
166
+ if (onChange) onChange(newItems);
167
+
168
+ // Update state
169
+ setItems(newItems);
170
+ };
171
+
172
+ // Drag event handlers
173
+ function handleDragStart(event: DragStartEvent) {
174
+ const { active } = event;
175
+ setActiveId(active.id);
176
+ }
177
+
178
+ function handleDragEnd(event: DragEndEvent) {
179
+ const { active, over } = event;
180
+
181
+ if (over && active.id !== over.id) {
182
+ // Indices
183
+ const oldIndex = items.findIndex((item) => item.id === active.id);
184
+ const newIndex = items.findIndex((item) => item.id === over.id);
185
+
186
+ // Clone
187
+ const newItems = [...items];
188
+
189
+ // Removed item
190
+ const [removed] = newItems.splice(oldIndex, 1);
191
+
192
+ // Insert to the destination index
193
+ newItems.splice(newIndex, 0, removed);
194
+
195
+ changeItems(newItems);
196
+
197
+ // Drag end handler
198
+ if (onDragEnd) onDragEnd(newItems);
199
+ }
200
+
201
+ setActiveId(undefined);
202
+ }
203
+
204
+ // Methods
205
+ React.useImperativeHandle(
206
+ mRef,
207
+ () => {
208
+ return {
209
+ addItem(newItem: D) {
210
+ // Existence check
211
+ if (
212
+ items.some(
213
+ (item) => item[labelField] === newItem[labelField]
214
+ )
215
+ ) {
216
+ return false;
217
+ }
218
+
219
+ // Clone
220
+ const newItems = [newItem, ...items];
221
+
222
+ // Update the state
223
+ changeItems(newItems);
224
+
225
+ return true;
226
+ },
227
+
228
+ addItems(inputItems: D[]) {
229
+ // Clone
230
+ const newItems = [...items];
231
+
232
+ // Insert items
233
+ inputItems.forEach((newItem) => {
234
+ // Existence check
235
+ if (
236
+ newItems.some(
237
+ (item) =>
238
+ item[labelField] === newItem[labelField]
239
+ )
240
+ ) {
241
+ return;
242
+ }
243
+
244
+ newItems.push(newItem);
245
+ });
246
+
247
+ // Update the state
248
+ changeItems(newItems);
249
+
250
+ return newItems.length - items.length;
251
+ },
252
+
253
+ editItem(newItem: D, index: number) {
254
+ // Existence check
255
+ const newIndex = items.findIndex(
256
+ (item) => item[labelField] === newItem[labelField]
257
+ );
258
+ if (newIndex >= 0 && newIndex !== index) {
259
+ // Label field is the same with a different item
260
+ return false;
261
+ }
262
+
263
+ // Clone
264
+ const newItems = [...items];
265
+
266
+ // Remove the item
267
+ newItems.splice(index, 1, newItem);
268
+
269
+ // Update the state
270
+ changeItems(newItems);
271
+
272
+ return true;
273
+ },
274
+
275
+ deleteItem(index: number) {
276
+ // Clone
277
+ const newItems = [...items];
278
+
279
+ // Remove the item
280
+ newItems.splice(index, 1);
281
+
282
+ // Update the state
283
+ changeItems(newItems);
284
+ }
285
+ };
286
+ },
287
+ [items]
288
+ );
289
+
290
+ React.useEffect(() => {
291
+ setItems(props.items);
292
+ }, [props.items]);
293
+
294
+ return (
295
+ <DndContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
296
+ <SortableContext
297
+ items={items}
298
+ strategy={verticalListSortingStrategy}
299
+ >
300
+ {items.map((item, index) => {
301
+ var id = item[keyField] as unknown as UniqueIdentifier;
302
+ var itemStyle =
303
+ getItemStyle == null
304
+ ? undefined
305
+ : getItemStyle(index, id === activeId);
306
+ return (
307
+ <SortableItem
308
+ id={id}
309
+ key={id}
310
+ style={itemStyle}
311
+ itemRenderer={(nodeRef, actionNodeRef) =>
312
+ itemRenderer(
313
+ item,
314
+ index,
315
+ nodeRef,
316
+ actionNodeRef
317
+ )
318
+ }
319
+ />
320
+ );
321
+ })}
322
+ </SortableContext>
323
+ </DndContext>
324
+ );
325
+ }
@@ -104,7 +104,7 @@ export function OptionGroup<
104
104
  // Get option value
105
105
  // D type should be the source id type
106
106
  const getOptionValue = (option: T): D | null => {
107
- const value = DataTypes.getIdValue(option, idField);
107
+ const value = DataTypes.getValue(option, idField);
108
108
  if (value == null) return null;
109
109
  return value as D;
110
110
  };
@@ -299,7 +299,7 @@ export function ScrollerListEx<T extends Record<string, unknown>>(
299
299
  innerItemRenderer,
300
300
  itemSize,
301
301
  itemKey = (index: number, data: T) =>
302
- DataTypes.getIdValue(data, idField) ?? index,
302
+ DataTypes.getValue(data, idField) ?? index,
303
303
  itemRenderer = (itemProps) => {
304
304
  const [itemHeight, space, margins] = calculateItemSize(
305
305
  itemProps.index
@@ -441,7 +441,7 @@ export function TableEx<T extends Record<string, unknown>>(
441
441
 
442
442
  // Row id field value
443
443
  const rowId =
444
- DataTypes.getIdValue(row, idField) ?? rowIndex;
444
+ DataTypes.getValue(row, idField) ?? rowIndex;
445
445
 
446
446
  // Selected or not
447
447
  const isItemSelected = selectable