@etsoo/react 1.3.98 → 1.4.2

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.
@@ -6,7 +6,7 @@ export interface DnDListProps<D extends {}, E extends React.ElementType> {
6
6
  /**
7
7
  * Item renderer
8
8
  */
9
- children: (item: D, index: number, deleteItem: (index: number) => void) => React.ReactNode;
9
+ children: (item: D, index: number, deleteItem: (index: number) => void, editItem: (newItem: D, index: number) => boolean) => React.ReactNode;
10
10
  /**
11
11
  * List container component
12
12
  * https://javascript.plainenglish.io/building-a-polymorphic-component-in-react-and-typescript-d9f236950af4
@@ -19,7 +19,7 @@ export interface DnDListProps<D extends {}, E extends React.ElementType> {
19
19
  /**
20
20
  * Get list item style callback
21
21
  */
22
- getItemStyle?: (isDragging: boolean) => CSSProperties;
22
+ getItemStyle?: (isDragging: boolean, index: number) => CSSProperties;
23
23
  /**
24
24
  * Get list style callback
25
25
  */
package/lib/mu/DnDList.js CHANGED
@@ -30,7 +30,7 @@ export function DnDList(props) {
30
30
  // Add item
31
31
  const addItem = (newItem) => {
32
32
  // Existence check
33
- if (items.some((item) => item[labelField] == newItem[labelField])) {
33
+ if (items.some((item) => item[labelField] === newItem[labelField])) {
34
34
  return false;
35
35
  }
36
36
  // Clone
@@ -39,6 +39,20 @@ export function DnDList(props) {
39
39
  setItems(newItems);
40
40
  return true;
41
41
  };
42
+ // Edit item
43
+ const editItem = (newItem, index) => {
44
+ // Existence check
45
+ if (items.some((item) => item[labelField] === newItem[labelField])) {
46
+ return false;
47
+ }
48
+ // Clone
49
+ const newItems = [...items];
50
+ // Remove the item
51
+ newItems.splice(index, 1, newItem);
52
+ // Update the state
53
+ setItems(newItems);
54
+ return true;
55
+ };
42
56
  // Add items
43
57
  const addItems = (inputItems) => {
44
58
  // Clone
@@ -79,11 +93,11 @@ export function DnDList(props) {
79
93
  if (id == null)
80
94
  return;
81
95
  return (React.createElement(Draggable, { key: id, draggableId: id, index: index }, (provided, snapshot) => (React.createElement("div", { ref: provided.innerRef, ...provided.draggableProps, ...provided.dragHandleProps, style: {
82
- ...getItemStyle(snapshot.isDragging),
96
+ ...getItemStyle(snapshot.isDragging, index),
83
97
  ...provided
84
98
  .draggableProps
85
99
  .style
86
- } }, children(item, index, deleteItem)))));
100
+ } }, children(item, index, deleteItem, editItem)))));
87
101
  }),
88
102
  provided.placeholder))))),
89
103
  sideRenderer && sideRenderer(false, addItem, addItems)));
@@ -1,5 +1,5 @@
1
1
  import { NotificationAlign, NotificationMessageType, NotificationType } from '@etsoo/notificationbase';
2
- import { Alert, AlertTitle, Backdrop, Box, Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Fade, Slider, Snackbar, styled, Switch, TextField } from '@mui/material';
2
+ import { Alert, AlertTitle, Backdrop, Box, Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Fade, Slider, Snackbar, styled, Switch, TextField, Typography } from '@mui/material';
3
3
  import { Error, Info, Help, Warning, Done } from '@mui/icons-material';
4
4
  import React from 'react';
5
5
  import { Labels } from '../app/Labels';
@@ -120,14 +120,21 @@ export class NotificationMU extends NotificationReact {
120
120
  const labels = Labels.NotificationMU;
121
121
  const title = (_a = this.title) !== null && _a !== void 0 ? _a : labels.promptTitle;
122
122
  const { cancelLabel = labels.promptCancel, okLabel = labels.promptOK, inputs, type, fullScreen, fullWidth = true, maxWidth, primaryButton, ...rest } = (_b = this.inputProps) !== null && _b !== void 0 ? _b : {};
123
+ const inputRef = React.createRef();
124
+ const errorRef = React.createRef();
125
+ const setError = (error) => {
126
+ if (errorRef.current == null)
127
+ return;
128
+ errorRef.current.innerText = error !== null && error !== void 0 ? error : '';
129
+ };
123
130
  const handleSubmit = async (event) => {
124
131
  // Result
125
132
  let result = undefined;
133
+ const input = inputRef.current;
126
134
  if (this.onReturn) {
127
135
  // Inputs case, no HTMLForm set to value, set the current form
128
136
  if (inputs && value == null)
129
137
  value = event.currentTarget.form;
130
- const input = inputRef.current;
131
138
  if (input) {
132
139
  if (type === 'date') {
133
140
  const dateValue = input.valueAsDate;
@@ -165,12 +172,18 @@ export class NotificationMU extends NotificationReact {
165
172
  // Get the value
166
173
  // returns false to prevent default dismiss
167
174
  const v = await result;
168
- if (v === false)
175
+ if (v === false) {
176
+ input === null || input === void 0 ? void 0 : input.focus();
169
177
  return;
178
+ }
179
+ if (typeof v === 'string') {
180
+ setError(v);
181
+ input === null || input === void 0 ? void 0 : input.focus();
182
+ return;
183
+ }
170
184
  this.dismiss();
171
185
  };
172
186
  let localInputs;
173
- let inputRef = React.createRef();
174
187
  let value = undefined;
175
188
  if (inputs == null) {
176
189
  if (type === 'switch') {
@@ -180,7 +193,7 @@ export class NotificationMU extends NotificationReact {
180
193
  localInputs = React.createElement(Slider, { onChange: (_e, v) => (value = v) });
181
194
  }
182
195
  else {
183
- localInputs = (React.createElement(TextField, { inputRef: inputRef, autoFocus: true, margin: "dense", fullWidth: true, type: type, required: true, ...rest }));
196
+ localInputs = (React.createElement(TextField, { inputRef: inputRef, onChange: () => setError(undefined), autoFocus: true, margin: "dense", fullWidth: true, type: type, required: true, ...rest }));
184
197
  }
185
198
  }
186
199
  else {
@@ -193,7 +206,8 @@ export class NotificationMU extends NotificationReact {
193
206
  React.createElement("span", { className: "dialogTitle" }, title)),
194
207
  React.createElement(DialogContent, null,
195
208
  React.createElement(DialogContentText, null, this.content),
196
- localInputs),
209
+ localInputs,
210
+ React.createElement(Typography, { variant: "caption", display: "block", ref: errorRef, color: (theme) => theme.palette.error.main })),
197
211
  React.createElement(DialogActions, null,
198
212
  React.createElement(Button, { color: "secondary", onClick: () => {
199
213
  if (this.onReturn)
@@ -3,7 +3,7 @@ import React from 'react';
3
3
  import { Labels } from '../../app/Labels';
4
4
  import { MUGlobal } from '../MUGlobal';
5
5
  import { CommonPage, CommonPageScrollContainer } from './CommonPage';
6
- import EditIcon from '@mui/icons-material/Edit';
6
+ import SaveIcon from '@mui/icons-material/Save';
7
7
  /**
8
8
  * Edit page
9
9
  * @param props Props
@@ -21,5 +21,5 @@ export function EditPage(props) {
21
21
  bottom: (theme) => MUGlobal.updateWithTheme(paddings, theme.spacing),
22
22
  paddingTop: paddings
23
23
  } },
24
- React.createElement(Button, { variant: "contained", type: "submit", fullWidth: true, startIcon: React.createElement(EditIcon, null) }, labels.save)))));
24
+ React.createElement(Button, { variant: "contained", type: "submit", fullWidth: true, startIcon: React.createElement(SaveIcon, null) }, labels.save)))));
25
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/react",
3
- "version": "1.3.98",
3
+ "version": "1.4.2",
4
4
  "description": "TypeScript ReactJs framework",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -50,8 +50,8 @@
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.11",
54
- "@etsoo/notificationbase": "^1.0.95",
53
+ "@etsoo/appscript": "^1.2.15",
54
+ "@etsoo/notificationbase": "^1.1.0",
55
55
  "@etsoo/shared": "^1.0.97",
56
56
  "@mui/icons-material": "^5.2.5",
57
57
  "@mui/material": "^5.2.6",
@@ -76,12 +76,12 @@
76
76
  "react-window": "^1.8.6"
77
77
  },
78
78
  "devDependencies": {
79
- "@babel/cli": "^7.16.0",
80
- "@babel/core": "^7.16.5",
81
- "@babel/plugin-transform-runtime": "^7.16.5",
82
- "@babel/preset-env": "^7.16.5",
83
- "@babel/runtime-corejs3": "^7.16.5",
84
- "@types/jest": "^27.0.3",
79
+ "@babel/cli": "^7.16.7",
80
+ "@babel/core": "^7.16.7",
81
+ "@babel/plugin-transform-runtime": "^7.16.7",
82
+ "@babel/preset-env": "^7.16.7",
83
+ "@babel/runtime-corejs3": "^7.16.7",
84
+ "@types/jest": "^27.4.0",
85
85
  "@types/react-test-renderer": "^17.0.1",
86
86
  "@typescript-eslint/eslint-plugin": "^5.8.1",
87
87
  "@typescript-eslint/parser": "^5.8.1",
@@ -17,7 +17,8 @@ export interface DnDListProps<D extends {}, E extends React.ElementType> {
17
17
  children: (
18
18
  item: D,
19
19
  index: number,
20
- deleteItem: (index: number) => void
20
+ deleteItem: (index: number) => void,
21
+ editItem: (newItem: D, index: number) => boolean
21
22
  ) => React.ReactNode;
22
23
 
23
24
  /**
@@ -34,7 +35,7 @@ export interface DnDListProps<D extends {}, E extends React.ElementType> {
34
35
  /**
35
36
  * Get list item style callback
36
37
  */
37
- getItemStyle?: (isDragging: boolean) => CSSProperties;
38
+ getItemStyle?: (isDragging: boolean, index: number) => CSSProperties;
38
39
 
39
40
  /**
40
41
  * Get list style callback
@@ -115,7 +116,7 @@ export function DnDList<
115
116
  // Add item
116
117
  const addItem = (newItem: D) => {
117
118
  // Existence check
118
- if (items.some((item) => item[labelField] == newItem[labelField])) {
119
+ if (items.some((item) => item[labelField] === newItem[labelField])) {
119
120
  return false;
120
121
  }
121
122
 
@@ -128,6 +129,25 @@ export function DnDList<
128
129
  return true;
129
130
  };
130
131
 
132
+ // Edit item
133
+ const editItem = (newItem: D, index: number) => {
134
+ // Existence check
135
+ if (items.some((item) => item[labelField] === newItem[labelField])) {
136
+ return false;
137
+ }
138
+
139
+ // Clone
140
+ const newItems = [...items];
141
+
142
+ // Remove the item
143
+ newItems.splice(index, 1, newItem);
144
+
145
+ // Update the state
146
+ setItems(newItems);
147
+
148
+ return true;
149
+ };
150
+
131
151
  // Add items
132
152
  const addItems = (inputItems: D[]) => {
133
153
  // Clone
@@ -201,7 +221,8 @@ export function DnDList<
201
221
  {...provided.dragHandleProps}
202
222
  style={{
203
223
  ...getItemStyle(
204
- snapshot.isDragging
224
+ snapshot.isDragging,
225
+ index
205
226
  ),
206
227
  ...provided
207
228
  .draggableProps
@@ -211,7 +232,8 @@ export function DnDList<
211
232
  {children(
212
233
  item,
213
234
  index,
214
- deleteItem
235
+ deleteItem,
236
+ editItem
215
237
  )}
216
238
  </div>
217
239
  )}
@@ -25,7 +25,8 @@ import {
25
25
  Snackbar,
26
26
  styled,
27
27
  Switch,
28
- TextField
28
+ TextField,
29
+ Typography
29
30
  } from '@mui/material';
30
31
  import { Error, Info, Help, Warning, Done } from '@mui/icons-material';
31
32
  import React from 'react';
@@ -238,18 +239,30 @@ export class NotificationMU extends NotificationReact {
238
239
  ...rest
239
240
  } = this.inputProps ?? {};
240
241
 
242
+ const inputRef = React.createRef<HTMLInputElement>();
243
+ const errorRef = React.createRef<HTMLSpanElement>();
244
+
245
+ const setError = (error?: string) => {
246
+ if (errorRef.current == null) return;
247
+ errorRef.current.innerText = error ?? '';
248
+ };
249
+
241
250
  const handleSubmit = async (
242
251
  event: React.MouseEvent<HTMLButtonElement>
243
252
  ) => {
244
253
  // Result
245
- let result: boolean | void | PromiseLike<boolean | void> =
246
- undefined;
254
+ let result:
255
+ | boolean
256
+ | string
257
+ | void
258
+ | PromiseLike<boolean | string | void> = undefined;
259
+
260
+ const input = inputRef.current;
247
261
 
248
262
  if (this.onReturn) {
249
263
  // Inputs case, no HTMLForm set to value, set the current form
250
264
  if (inputs && value == null) value = event.currentTarget.form;
251
265
 
252
- const input = inputRef.current;
253
266
  if (input) {
254
267
  if (type === 'date') {
255
268
  const dateValue = input.valueAsDate;
@@ -284,13 +297,20 @@ export class NotificationMU extends NotificationReact {
284
297
  // Get the value
285
298
  // returns false to prevent default dismiss
286
299
  const v = await result;
287
- if (v === false) return;
300
+ if (v === false) {
301
+ input?.focus();
302
+ return;
303
+ }
304
+ if (typeof v === 'string') {
305
+ setError(v);
306
+ input?.focus();
307
+ return;
308
+ }
288
309
 
289
310
  this.dismiss();
290
311
  };
291
312
 
292
313
  let localInputs: React.ReactNode;
293
- let inputRef = React.createRef<HTMLInputElement>();
294
314
  let value: any = undefined;
295
315
 
296
316
  if (inputs == null) {
@@ -310,6 +330,7 @@ export class NotificationMU extends NotificationReact {
310
330
  localInputs = (
311
331
  <TextField
312
332
  inputRef={inputRef}
333
+ onChange={() => setError(undefined)}
313
334
  autoFocus
314
335
  margin="dense"
315
336
  fullWidth
@@ -341,6 +362,12 @@ export class NotificationMU extends NotificationReact {
341
362
  <DialogContent>
342
363
  <DialogContentText>{this.content}</DialogContentText>
343
364
  {localInputs}
365
+ <Typography
366
+ variant="caption"
367
+ display="block"
368
+ ref={errorRef}
369
+ color={(theme) => theme.palette.error.main}
370
+ />
344
371
  </DialogContent>
345
372
  <DialogActions>
346
373
  <Button
@@ -6,7 +6,7 @@ import { Labels } from '../../app/Labels';
6
6
  import { MUGlobal } from '../MUGlobal';
7
7
  import { CommonPage, CommonPageScrollContainer } from './CommonPage';
8
8
  import { CommonPageProps } from './CommonPageProps';
9
- import EditIcon from '@mui/icons-material/Edit';
9
+ import SaveIcon from '@mui/icons-material/Save';
10
10
 
11
11
  /**
12
12
  * Edit page router props, include 'id' (/:id) query parameter
@@ -70,7 +70,7 @@ export function EditPage(props: EditPageProps) {
70
70
  variant="contained"
71
71
  type="submit"
72
72
  fullWidth
73
- startIcon={<EditIcon />}
73
+ startIcon={<SaveIcon />}
74
74
  >
75
75
  {labels.save}
76
76
  </Button>