@etsoo/react 1.3.91 → 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.
@@ -11,7 +11,7 @@ import { RefreshTokenRQ } from './RefreshTokenRQ';
11
11
  * Use the acess token to the service api, get a service access token
12
12
  * Use the new acess token and refresh token to login
13
13
  */
14
- export declare class ServiceApp<P extends IServicePageData = IServicePageData, U extends IServiceUser = IServiceUser, S extends IServiceAppSettings = IServiceAppSettings> extends ReactApp<S, U, P> {
14
+ export declare class ServiceApp<U extends IServiceUser = IServiceUser, P extends IServicePageData = IServicePageData, S extends IServiceAppSettings = IServiceAppSettings> extends ReactApp<S, U, P> {
15
15
  /**
16
16
  * Service API
17
17
  */
@@ -48,8 +48,6 @@ export class ServiceApp extends ReactApp {
48
48
  coreUrl +
49
49
  '?serviceId=' +
50
50
  this.settings.serviceId +
51
- '&serviceDeviceId=' +
52
- encodeURIComponent(this.deviceId) +
53
51
  '&' +
54
52
  DomUtils.CultureField +
55
53
  '=' +
package/lib/index.d.ts CHANGED
@@ -38,6 +38,7 @@ export * from './mu/CustomFabProps';
38
38
  export * from './mu/DataGridEx';
39
39
  export * from './mu/DataGridRenderers';
40
40
  export * from './mu/DialogButton';
41
+ export * from './mu/DnDList';
41
42
  export * from './mu/DraggablePaperComponent';
42
43
  export * from './mu/EmailInput';
43
44
  export * from './mu/FabBox';
package/lib/index.js CHANGED
@@ -41,6 +41,7 @@ export * from './mu/CustomFabProps';
41
41
  export * from './mu/DataGridEx';
42
42
  export * from './mu/DataGridRenderers';
43
43
  export * from './mu/DialogButton';
44
+ export * from './mu/DnDList';
44
45
  export * from './mu/DraggablePaperComponent';
45
46
  export * from './mu/EmailInput';
46
47
  export * from './mu/FabBox';
@@ -0,0 +1,49 @@
1
+ import React, { CSSProperties } from 'react';
2
+ /**
3
+ * DnD list props
4
+ */
5
+ export interface DnDListProps<D extends {}, L extends keyof D, E extends React.ElementType> {
6
+ /**
7
+ * Item renderer
8
+ */
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>;
19
+ /**
20
+ * Get list item style callback
21
+ */
22
+ getItemStyle?: (isDragging: boolean) => CSSProperties;
23
+ /**
24
+ * Get list style callback
25
+ */
26
+ getListStyle?: (isDraggingOver: boolean) => CSSProperties;
27
+ /**
28
+ * Label field
29
+ */
30
+ labelField: L;
31
+ /**
32
+ * Load data
33
+ */
34
+ loadData: (name: string) => PromiseLike<D[]>;
35
+ /**
36
+ * Top and bottom sides renderer
37
+ */
38
+ sideRenderer?: (top: boolean, onAdd: (item: D) => boolean) => React.ReactNode;
39
+ /**
40
+ * Name for hidden form input
41
+ */
42
+ name: string;
43
+ }
44
+ /**
45
+ * Drag and drop list
46
+ * @param props Props
47
+ * @returns Component
48
+ */
49
+ export declare function DnDList<D extends {}, L extends keyof D, E extends React.ElementType = React.ElementType>(props: DnDListProps<D, L, E>): JSX.Element;
@@ -0,0 +1,74 @@
1
+ import { DataTypes } from '@etsoo/shared';
2
+ import React from 'react';
3
+ import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
4
+ /**
5
+ * Drag and drop list
6
+ * @param props Props
7
+ * @returns Component
8
+ */
9
+ export function DnDList(props) {
10
+ // Destruct
11
+ const { children, Component = 'div', componentProps, getItemStyle = (_isDragging) => ({}), getListStyle = () => undefined, labelField, loadData, name, sideRenderer } = props;
12
+ // State
13
+ const [items, setItems] = React.useState([]);
14
+ // Drag end handler
15
+ const onDragEnd = (result) => {
16
+ console.log(result);
17
+ // Dropped outside the list
18
+ if (!result.destination) {
19
+ return;
20
+ }
21
+ // Clone
22
+ const newItems = [...items];
23
+ // Removed item
24
+ const [removed] = newItems.splice(result.source.index, 1);
25
+ // Insert to the destination index
26
+ newItems.splice(result.destination.index, 0, removed);
27
+ // Update the state
28
+ setItems(newItems);
29
+ };
30
+ // Add handler
31
+ const onAdd = (newItem) => {
32
+ // Existence check
33
+ if (items.some((item) => item[labelField] == newItem[labelField])) {
34
+ return false;
35
+ }
36
+ // Clone
37
+ const newItems = [newItem, ...items];
38
+ // Update the state
39
+ setItems(newItems);
40
+ return true;
41
+ };
42
+ // Delete handler
43
+ const onDelete = (index) => {
44
+ // Clone
45
+ const newItems = [...items];
46
+ // Remove the item
47
+ newItems.splice(index, 1);
48
+ // Update the state
49
+ setItems(newItems);
50
+ };
51
+ React.useEffect(() => {
52
+ loadData(name).then((items) => setItems(items));
53
+ }, [name]);
54
+ // Layout
55
+ return (React.createElement(React.Fragment, null,
56
+ sideRenderer && sideRenderer(true, onAdd),
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))))),
73
+ sideRenderer && sideRenderer(false, onAdd)));
74
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/react",
3
- "version": "1.3.91",
3
+ "version": "1.3.95",
4
4
  "description": "TypeScript ReactJs framework",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -50,17 +50,18 @@
50
50
  "@emotion/react": "^11.7.1",
51
51
  "@emotion/style": "^0.8.0",
52
52
  "@emotion/styled": "^11.6.0",
53
- "@etsoo/appscript": "^1.2.8",
53
+ "@etsoo/appscript": "^1.2.11",
54
54
  "@etsoo/notificationbase": "^1.0.95",
55
- "@etsoo/shared": "^1.0.94",
55
+ "@etsoo/shared": "^1.0.95",
56
56
  "@mui/icons-material": "^5.2.5",
57
- "@mui/material": "^5.2.5",
57
+ "@mui/material": "^5.2.6",
58
58
  "@reach/router": "^1.3.4",
59
59
  "@types/pica": "^5.1.3",
60
60
  "@types/pulltorefreshjs": "^0.1.5",
61
61
  "@types/reach__router": "^1.3.10",
62
62
  "@types/react": "^17.0.38",
63
63
  "@types/react-avatar-editor": "^10.3.6",
64
+ "@types/react-beautiful-dnd": "^13.1.2",
64
65
  "@types/react-dom": "^17.0.11",
65
66
  "@types/react-input-mask": "^3.0.1",
66
67
  "@types/react-window": "^1.8.5",
@@ -68,6 +69,7 @@
68
69
  "pulltorefreshjs": "^0.1.22",
69
70
  "react": "^17.0.2",
70
71
  "react-avatar-editor": "^12.0.0",
72
+ "react-beautiful-dnd": "^13.1.0",
71
73
  "react-dom": "^17.0.2",
72
74
  "react-draggable": "^4.4.4",
73
75
  "react-imask": "^6.2.2",
@@ -81,8 +83,8 @@
81
83
  "@babel/runtime-corejs3": "^7.16.5",
82
84
  "@types/jest": "^27.0.3",
83
85
  "@types/react-test-renderer": "^17.0.1",
84
- "@typescript-eslint/eslint-plugin": "^5.8.0",
85
- "@typescript-eslint/parser": "^5.8.0",
86
+ "@typescript-eslint/eslint-plugin": "^5.8.1",
87
+ "@typescript-eslint/parser": "^5.8.1",
86
88
  "eslint": "^8.5.0",
87
89
  "eslint-config-airbnb-base": "^15.0.0",
88
90
  "eslint-plugin-import": "^2.25.3",
@@ -21,8 +21,8 @@ import { RefreshTokenRQ } from './RefreshTokenRQ';
21
21
  * Use the new acess token and refresh token to login
22
22
  */
23
23
  export class ServiceApp<
24
- P extends IServicePageData = IServicePageData,
25
24
  U extends IServiceUser = IServiceUser,
25
+ P extends IServicePageData = IServicePageData,
26
26
  S extends IServiceAppSettings = IServiceAppSettings
27
27
  > extends ReactApp<S, U, P> {
28
28
  /**
@@ -76,8 +76,6 @@ export class ServiceApp<
76
76
  coreUrl +
77
77
  '?serviceId=' +
78
78
  this.settings.serviceId +
79
- '&serviceDeviceId=' +
80
- encodeURIComponent(this.deviceId) +
81
79
  '&' +
82
80
  DomUtils.CultureField +
83
81
  '=' +
package/src/index.ts 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,211 @@
1
+ import { DataTypes } from '@etsoo/shared';
2
+ import React, { CSSProperties } from 'react';
3
+ import {
4
+ DragDropContext,
5
+ Draggable,
6
+ Droppable,
7
+ DropResult
8
+ } from 'react-beautiful-dnd';
9
+
10
+ /**
11
+ * DnD list props
12
+ */
13
+ export interface DnDListProps<
14
+ D extends {},
15
+ L extends keyof D,
16
+ E extends React.ElementType
17
+ > {
18
+ /**
19
+ * Item renderer
20
+ */
21
+ children: (
22
+ item: D,
23
+ index: number,
24
+ onDelete: (index: number) => void
25
+ ) => React.ReactNode;
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
+
38
+ /**
39
+ * Get list item style callback
40
+ */
41
+ getItemStyle?: (isDragging: boolean) => CSSProperties;
42
+
43
+ /**
44
+ * Get list style callback
45
+ */
46
+ getListStyle?: (isDraggingOver: boolean) => CSSProperties;
47
+
48
+ /**
49
+ * Label field
50
+ */
51
+ labelField: L;
52
+
53
+ /**
54
+ * Load data
55
+ */
56
+ loadData: (name: string) => PromiseLike<D[]>;
57
+
58
+ /**
59
+ * Top and bottom sides renderer
60
+ */
61
+ sideRenderer?: (
62
+ top: boolean,
63
+ onAdd: (item: D) => boolean
64
+ ) => React.ReactNode;
65
+
66
+ /**
67
+ * Name for hidden form input
68
+ */
69
+ name: string;
70
+ }
71
+
72
+ /**
73
+ * Drag and drop list
74
+ * @param props Props
75
+ * @returns Component
76
+ */
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>) {
82
+ // Destruct
83
+ const {
84
+ children,
85
+ Component = 'div',
86
+ componentProps,
87
+ getItemStyle = (_isDragging) => ({}),
88
+ getListStyle = () => undefined,
89
+ labelField,
90
+ loadData,
91
+ name,
92
+ sideRenderer
93
+ } = props;
94
+
95
+ // State
96
+ const [items, setItems] = React.useState<D[]>([]);
97
+
98
+ // Drag end handler
99
+ const onDragEnd = (result: DropResult) => {
100
+ console.log(result);
101
+ // Dropped outside the list
102
+ if (!result.destination) {
103
+ return;
104
+ }
105
+
106
+ // Clone
107
+ const newItems = [...items];
108
+
109
+ // Removed item
110
+ const [removed] = newItems.splice(result.source.index, 1);
111
+
112
+ // Insert to the destination index
113
+ newItems.splice(result.destination.index, 0, removed);
114
+
115
+ // Update the state
116
+ setItems(newItems);
117
+ };
118
+
119
+ // Add handler
120
+ const onAdd = (newItem: D) => {
121
+ // Existence check
122
+ if (items.some((item) => item[labelField] == newItem[labelField])) {
123
+ return false;
124
+ }
125
+
126
+ // Clone
127
+ const newItems = [newItem, ...items];
128
+
129
+ // Update the state
130
+ setItems(newItems);
131
+
132
+ return true;
133
+ };
134
+
135
+ // Delete handler
136
+ const onDelete = (index: number) => {
137
+ // Clone
138
+ const newItems = [...items];
139
+
140
+ // Remove the item
141
+ newItems.splice(index, 1);
142
+
143
+ // Update the state
144
+ setItems(newItems);
145
+ };
146
+
147
+ React.useEffect(() => {
148
+ loadData(name).then((items) => setItems(items));
149
+ }, [name]);
150
+
151
+ // Layout
152
+ return (
153
+ <React.Fragment>
154
+ {sideRenderer && sideRenderer(true, onAdd)}
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>
208
+ {sideRenderer && sideRenderer(false, onAdd)}
209
+ </React.Fragment>
210
+ );
211
+ }