@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 +1 -0
- package/lib/index.js +1 -0
- package/lib/mu/DnDList.d.ts +74 -0
- package/lib/mu/DnDList.js +133 -0
- package/lib/mu/OptionGroup.js +1 -1
- package/lib/mu/ScrollerListEx.js +1 -1
- package/lib/mu/TableEx.js +1 -1
- package/package.json +12 -10
- package/src/index.ts +1 -0
- package/src/mu/DnDList.tsx +325 -0
- package/src/mu/OptionGroup.tsx +1 -1
- package/src/mu/ScrollerListEx.tsx +1 -1
- package/src/mu/TableEx.tsx +1 -1
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
|
+
}
|
package/lib/mu/OptionGroup.js
CHANGED
|
@@ -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.
|
|
15
|
+
const value = DataTypes.getValue(option, idField);
|
|
16
16
|
if (value == null)
|
|
17
17
|
return null;
|
|
18
18
|
return value;
|
package/lib/mu/ScrollerListEx.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
54
|
+
"@etsoo/appscript": "^1.2.73",
|
|
53
55
|
"@etsoo/notificationbase": "^1.1.5",
|
|
54
|
-
"@etsoo/shared": "^1.1.
|
|
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
|
+
}
|
package/src/mu/OptionGroup.tsx
CHANGED
|
@@ -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.
|
|
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.
|
|
302
|
+
DataTypes.getValue(data, idField) ?? index,
|
|
303
303
|
itemRenderer = (itemProps) => {
|
|
304
304
|
const [itemHeight, space, margins] = calculateItemSize(
|
|
305
305
|
itemProps.index
|
package/src/mu/TableEx.tsx
CHANGED
|
@@ -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.
|
|
444
|
+
DataTypes.getValue(row, idField) ?? rowIndex;
|
|
445
445
|
|
|
446
446
|
// Selected or not
|
|
447
447
|
const isItemSelected = selectable
|