@etsoo/materialui 1.2.90 → 1.2.92

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.
@@ -2,6 +2,7 @@ import { Autocomplete } from "@mui/material";
2
2
  import React from "react";
3
3
  import { globalApp } from "./app/ReactApp";
4
4
  import { InputField } from "./InputField";
5
+ import { MUUtils } from "./MUUtils";
5
6
  export function ComboBoxPro(props) {
6
7
  var _a;
7
8
  // Labels
@@ -52,11 +53,7 @@ export function ComboBoxPro(props) {
52
53
  if (!localValue && localValue != value)
53
54
  onChange(event, value, "blur", undefined);
54
55
  }
55
- } })), getOptionLabel: (item) => typeof item === "object"
56
- ? "label" in item
57
- ? item.label
58
- : item.name
59
- : item, isOptionEqualToValue: (option, value) => option.id === value.id, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, onChange: (event, value, reason, details) => {
56
+ } })), getOptionLabel: (item) => typeof item === "object" ? MUUtils.getListItemLabel(item) : item, isOptionEqualToValue: (option, value) => option.id === value.id, noOptionsText: noOptionsText, loadingText: loadingText, openText: openText, onChange: (event, value, reason, details) => {
60
57
  setValue(value);
61
58
  if (onChange)
62
59
  onChange(event, value, reason, details);
@@ -4,10 +4,24 @@ import React from "react";
4
4
  * File upload button props
5
5
  */
6
6
  export type FileUploadButtonProps = ButtonProps<"label"> & {
7
+ /**
8
+ * Max files allowed
9
+ */
10
+ maxFiles?: number;
11
+ /**
12
+ * Max file size
13
+ */
14
+ maxFileSize?: number;
7
15
  /**
8
16
  * Input field attributes
9
17
  */
10
18
  inputProps?: Omit<React.InputHTMLAttributes<HTMLInputElement>, "type" | "hidden">;
19
+ /**
20
+ * File invalid handler
21
+ * @param values [expected, actual]
22
+ * @param file File
23
+ */
24
+ onFileInvalid?: (values: [number, number], file?: File) => void;
11
25
  /**
12
26
  * Upload files callback
13
27
  * @param files Files
@@ -7,7 +7,7 @@ import React from "react";
7
7
  */
8
8
  export function FileUploadButton(props) {
9
9
  // Destruct
10
- const { inputProps, onUploadFiles, children, ...rest } = props;
10
+ const { maxFiles, maxFileSize, inputProps, onFileInvalid, onUploadFiles, children, ...rest } = props;
11
11
  const { onChange } = inputProps !== null && inputProps !== void 0 ? inputProps : {};
12
12
  // Layout
13
13
  return (React.createElement(Button, { component: "label", ...rest },
@@ -19,8 +19,26 @@ export function FileUploadButton(props) {
19
19
  return;
20
20
  if (onUploadFiles) {
21
21
  const files = event.target.files;
22
- if (files == null || files.length == 0)
22
+ if (files == null)
23
23
  return;
24
+ const fl = files.length;
25
+ if (fl === 0)
26
+ return;
27
+ if (maxFiles && maxFiles > 0 && fl > maxFiles) {
28
+ if (onFileInvalid)
29
+ onFileInvalid([maxFiles, fl]);
30
+ return;
31
+ }
32
+ if (maxFileSize && maxFileSize > 0) {
33
+ for (let f = 0; f < fl; f++) {
34
+ const file = files[f];
35
+ if (file.size > maxFileSize) {
36
+ if (onFileInvalid)
37
+ onFileInvalid([maxFileSize, file.size], file);
38
+ return;
39
+ }
40
+ }
41
+ }
24
42
  onUploadFiles(files);
25
43
  }
26
44
  }, ...inputProps })));
@@ -0,0 +1,12 @@
1
+ import { ListType2 } from "@etsoo/shared";
2
+ /**
3
+ * MU utilities
4
+ */
5
+ export declare namespace MUUtils {
6
+ /**
7
+ * Get ListType2 item label
8
+ * @param item Item
9
+ * @returns Result
10
+ */
11
+ function getListItemLabel(item: ListType2): string;
12
+ }
package/lib/MUUtils.js ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * MU utilities
3
+ */
4
+ export var MUUtils;
5
+ (function (MUUtils) {
6
+ /**
7
+ * Get ListType2 item label
8
+ * @param item Item
9
+ * @returns Result
10
+ */
11
+ function getListItemLabel(item) {
12
+ return "label" in item
13
+ ? item.label
14
+ : "name" in item
15
+ ? item.name
16
+ : item.title;
17
+ }
18
+ MUUtils.getListItemLabel = getListItemLabel;
19
+ })(MUUtils || (MUUtils = {}));
package/lib/MenuButton.js CHANGED
@@ -27,29 +27,31 @@ export function MenuButton(props) {
27
27
  };
28
28
  return (React.createElement(React.Fragment, null,
29
29
  typeof button === "function" ? (button(handleMenuOpen)) : (React.createElement(Button, { onClick: handleMenuOpen, ...button })),
30
- React.createElement(Menu, { PaperProps: {
31
- elevation: 0,
32
- sx: {
33
- overflow: "visible",
34
- filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
35
- "& .MuiAvatar-root": {
36
- width: 32,
37
- height: 32
38
- },
39
- "&:before": {
40
- content: '""',
41
- display: "block",
42
- position: "absolute",
43
- top: isTop ? 0 : undefined,
44
- bottom: isTop ? undefined : -10,
45
- right: 14,
46
- width: 10,
47
- height: 10,
48
- bgcolor: "background.paper",
49
- transform: "translateY(-50%) rotate(45deg)",
50
- zIndex: 0
51
- },
52
- ...{ sx }
30
+ React.createElement(Menu, { slotProps: {
31
+ paper: {
32
+ elevation: 0,
33
+ sx: {
34
+ overflow: "visible",
35
+ filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
36
+ "& .MuiAvatar-root": {
37
+ width: 32,
38
+ height: 32
39
+ },
40
+ "&:before": {
41
+ content: '""',
42
+ display: "block",
43
+ position: "absolute",
44
+ top: isTop ? 0 : undefined,
45
+ bottom: isTop ? undefined : -10,
46
+ right: 14,
47
+ width: 10,
48
+ height: 10,
49
+ bgcolor: "background.paper",
50
+ transform: "translateY(-50%) rotate(45deg)",
51
+ zIndex: 0
52
+ },
53
+ ...{ sx }
54
+ }
53
55
  }
54
56
  }, disableScrollLock: true, anchorEl: anchorEl, anchorOrigin: anchorOrigin, keepMounted: true, transformOrigin: transformOrigin, open: isMenuOpen, transitionDuration: 0, onClose: handleMenuClose, ...rest }, items.map((item) => {
55
57
  const label = item[labelField];
package/lib/MoreFab.js CHANGED
@@ -86,10 +86,10 @@ export function MoreFab(props) {
86
86
  : Array.isArray(action)
87
87
  ? { component: Link, to: action[0], state: action[1] }
88
88
  : {
89
- onClick: () => {
89
+ onClick: (event) => {
90
90
  handleClose();
91
91
  if (typeof action === "function")
92
- action();
92
+ action(event);
93
93
  }
94
94
  }) },
95
95
  icon != null && React.createElement(ListItemIcon, null, icon),
@@ -10,6 +10,7 @@ export declare class NotificationMU extends NotificationReact {
10
10
  private createMessageColor;
11
11
  private createMessage;
12
12
  private createPrompt;
13
+ private createPopup;
13
14
  private createLoading;
14
15
  /**
15
16
  * Render method
package/lib/NotifierMU.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { NotificationAlign, NotificationMessageType, NotificationType } from "@etsoo/notificationbase";
2
2
  import { DomUtils } from "@etsoo/shared";
3
- import { Alert, AlertTitle, Backdrop, Box, Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Fade, IconButton, Slider, Snackbar, styled, Switch, TextField, Typography } from "@mui/material";
3
+ import { Alert, AlertTitle, Backdrop, Box, Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Fade, IconButton, Popover, Slider, Snackbar, styled, Switch, TextField, Typography } from "@mui/material";
4
4
  import { Error, Info, Help, Warning, Done } from "@mui/icons-material";
5
5
  import CloseIcon from "@mui/icons-material/Close";
6
6
  import React from "react";
@@ -57,14 +57,13 @@ export class NotificationMU extends NotificationReact {
57
57
  color: "primary"
58
58
  };
59
59
  // Setup callback
60
- if (this.renderSetup)
61
- this.renderSetup(setupProps);
60
+ const options = this.renderSetup ? this.renderSetup(setupProps) : undefined;
62
61
  // Callback
63
62
  const callback = async (_event) => {
64
63
  await this.returnValue(undefined);
65
64
  return true;
66
65
  };
67
- return (React.createElement(Dialog, { key: this.id, open: this.open, PaperComponent: DraggablePaperComponent, className: className, fullWidth: fullWidth, maxWidth: maxWidth, fullScreen: fullScreen },
66
+ return (React.createElement(Dialog, { key: this.id, open: this.open, PaperComponent: DraggablePaperComponent, className: className, fullWidth: fullWidth, maxWidth: maxWidth, fullScreen: fullScreen, ...options },
68
67
  React.createElement(IconDialogTitle, { className: "draggable-dialog-title" },
69
68
  icon,
70
69
  React.createElement("span", { className: "dialogTitle" }, title),
@@ -81,11 +80,13 @@ export class NotificationMU extends NotificationReact {
81
80
  const labels = Labels.NotificationMU;
82
81
  const title = (_a = this.title) !== null && _a !== void 0 ? _a : labels.confirmTitle;
83
82
  const { buttons, okLabel = labels.confirmYes, cancelLabel = labels.confirmNo, cancelButton = true, inputs, fullScreen, fullWidth = true, maxWidth, primaryButton, closable = false } = (_b = this.inputProps) !== null && _b !== void 0 ? _b : {};
83
+ // Setup callback
84
+ const options = this.renderSetup ? this.renderSetup({}) : undefined;
84
85
  const callback = async (_event, value) => {
85
86
  await this.returnValue(value);
86
87
  return true;
87
88
  };
88
- return (React.createElement(Dialog, { key: this.id, open: this.open, PaperComponent: DraggablePaperComponent, className: className, fullWidth: fullWidth, maxWidth: maxWidth, fullScreen: fullScreen },
89
+ return (React.createElement(Dialog, { key: this.id, open: this.open, PaperComponent: DraggablePaperComponent, className: className, fullWidth: fullWidth, maxWidth: maxWidth, fullScreen: fullScreen, ...options },
89
90
  React.createElement(IconDialogTitle, { className: "draggable-dialog-title" },
90
91
  React.createElement(Help, { color: "action" }),
91
92
  React.createElement("span", { className: "dialogTitle" }, title),
@@ -117,10 +118,9 @@ export class NotificationMU extends NotificationReact {
117
118
  variant: "filled"
118
119
  };
119
120
  // Setup callback
120
- if (this.renderSetup)
121
- this.renderSetup(setupProps);
121
+ const options = this.renderSetup ? this.renderSetup(setupProps) : undefined;
122
122
  return (React.createElement(Fade, { in: true, key: this.id },
123
- React.createElement(Alert, { ...setupProps, action: closable ? (React.createElement(IconButton, { size: "small", onClick: () => this.returnValue("CLOSE") },
123
+ React.createElement(Alert, { ...setupProps, ...options, action: closable ? (React.createElement(IconButton, { size: "small", onClick: () => this.returnValue("CLOSE") },
124
124
  React.createElement(CloseIcon, null))) : undefined, onClose: () => this.dismiss(), className: className },
125
125
  this.title && React.createElement(AlertTitle, null, this.title),
126
126
  this.content)));
@@ -195,7 +195,9 @@ export class NotificationMU extends NotificationReact {
195
195
  else {
196
196
  localInputs = inputs;
197
197
  }
198
- return (React.createElement(Dialog, { key: this.id, open: this.open, PaperComponent: DraggablePaperComponent, className: className, fullWidth: fullWidth, maxWidth: maxWidth, fullScreen: fullScreen },
198
+ // Setup callback
199
+ const options = this.renderSetup ? this.renderSetup({}) : undefined;
200
+ return (React.createElement(Dialog, { key: this.id, open: this.open, PaperComponent: DraggablePaperComponent, className: className, fullWidth: fullWidth, maxWidth: maxWidth, fullScreen: fullScreen, ...options },
199
201
  React.createElement("form", { onSubmit: (event) => {
200
202
  var _a;
201
203
  event.preventDefault();
@@ -219,6 +221,13 @@ export class NotificationMU extends NotificationReact {
219
221
  } }, cancelLabel)),
220
222
  React.createElement(LoadingButton, { color: "primary", autoFocus: true, onClick: handleSubmit, name: "okButton", ...primaryButton }, okLabel)))))));
221
223
  }
224
+ createPopup(_props, className) {
225
+ // Destruct
226
+ const { content, open, renderSetup } = this;
227
+ // Setup callback
228
+ const options = this.renderSetup ? this.renderSetup({}) : undefined;
229
+ return (React.createElement(Popover, { open: open, className: className, ...options }, content));
230
+ }
222
231
  // Create loading
223
232
  createLoading(_props, className) {
224
233
  var _a;
@@ -232,12 +241,11 @@ export class NotificationMU extends NotificationReact {
232
241
  if (content === "@")
233
242
  content = labels.loading.toString();
234
243
  // Setup callback
235
- if (this.renderSetup)
236
- this.renderSetup(setupProps);
244
+ const options = this.renderSetup ? this.renderSetup(setupProps) : undefined;
237
245
  return (React.createElement(Backdrop, { key: this.id, className: className, sx: {
238
246
  color: "#fff",
239
247
  zIndex: (theme) => theme.zIndex.modal + 1
240
- }, open: this.open },
248
+ }, open: this.open, ...options },
241
249
  React.createElement(Box, { display: "flex", flexDirection: "column", flexWrap: "nowrap", alignItems: "center", sx: {
242
250
  "& > :not(style) + :not(style)": {
243
251
  marginTop: (theme) => theme.spacing(1)
@@ -263,6 +271,9 @@ export class NotificationMU extends NotificationReact {
263
271
  else if (this.type === NotificationType.Prompt) {
264
272
  return this.createPrompt(props, className);
265
273
  }
274
+ else if (this.type === NotificationType.Popup) {
275
+ return this.createPopup(props, className);
276
+ }
266
277
  else if (this.type === NotificationType.Error ||
267
278
  (this.modal && this.type in NotificationMessageType)) {
268
279
  // Alert or modal message
package/lib/QuickList.js CHANGED
@@ -3,6 +3,7 @@ import React from "react";
3
3
  import { InputField } from "./InputField";
4
4
  import { globalApp } from "./app/ReactApp";
5
5
  import { VBox } from "./FlexBox";
6
+ import { MUUtils } from "./MUUtils";
6
7
  /**
7
8
  * Quick list
8
9
  * @param props Props
@@ -10,7 +11,7 @@ import { VBox } from "./FlexBox";
10
11
  */
11
12
  export function QuickList(props) {
12
13
  // Destruct
13
- const { buttonProps = {}, label, inputProps, itemLabel = (item) => ("label" in item ? item.label : item.name), itemRenderer = (item) => itemLabel(item), itemProps, loadData, noMatchesLabel = globalApp === null || globalApp === void 0 ? void 0 : globalApp.get("noMatches"), gap = 1, height = "480px", onItemClick, ...rest } = props;
14
+ const { buttonProps = {}, label, inputProps, itemLabel = MUUtils.getListItemLabel, itemRenderer = (item) => itemLabel(item), itemProps, loadData, noMatchesLabel = globalApp === null || globalApp === void 0 ? void 0 : globalApp.get("noMatches"), gap = 1, height = "480px", onItemClick, ...rest } = props;
14
15
  const { onClick, ...buttonRest } = buttonProps;
15
16
  // States
16
17
  const [loading, setLoading] = React.useState(false);
package/lib/TiplistPro.js CHANGED
@@ -3,6 +3,7 @@ import { Autocomplete } from "@mui/material";
3
3
  import React from "react";
4
4
  import { InputField } from "./InputField";
5
5
  import { globalApp } from "./app/ReactApp";
6
+ import { MUUtils } from "./MUUtils";
6
7
  /**
7
8
  * TiplistPro
8
9
  * @param props Props
@@ -195,7 +196,7 @@ export function TiplistPro(props) {
195
196
  if (item["id"] === -1)
196
197
  return (more !== null && more !== void 0 ? more : "More") + "...";
197
198
  if (getOptionLabel == null)
198
- return "label" in item ? item.label : item.name;
199
+ return MUUtils.getListItemLabel(item);
199
200
  return getOptionLabel(item);
200
201
  }, ...rest })));
201
202
  }
package/lib/index.d.ts CHANGED
@@ -74,6 +74,7 @@ export * from "./MobileListItemRenderer";
74
74
  export * from "./MoneyInputField";
75
75
  export * from "./MoreFab";
76
76
  export * from "./MUGlobal";
77
+ export * from "./MUUtils";
77
78
  export * from "./NotifierMU";
78
79
  export * from "./OptionBool";
79
80
  export * from "./OptionGroup";
package/lib/index.js CHANGED
@@ -74,6 +74,7 @@ export * from "./MobileListItemRenderer";
74
74
  export * from "./MoneyInputField";
75
75
  export * from "./MoreFab";
76
76
  export * from "./MUGlobal";
77
+ export * from "./MUUtils";
77
78
  export * from "./NotifierMU";
78
79
  export * from "./OptionBool";
79
80
  export * from "./OptionGroup";
@@ -50,29 +50,31 @@ export function UserMenu(props) {
50
50
  return (React.createElement(React.Fragment, null,
51
51
  React.createElement(IconButton, { edge: "end", "aria-haspopup": "true", onClick: handleUserMenuOpen, color: "inherit" },
52
52
  React.createElement(UserAvatar, { title: name, src: avatar })),
53
- React.createElement(Menu, { PaperProps: {
54
- elevation: 0,
55
- sx: {
56
- overflow: "visible",
57
- filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
58
- mt: -0.4,
59
- "& .MuiAvatar-root": {
60
- width: 32,
61
- height: 32,
62
- ml: -0.5,
63
- mr: 1
64
- },
65
- "&:before": {
66
- content: '""',
67
- display: "block",
68
- position: "absolute",
69
- top: 0,
70
- right: 14,
71
- width: 10,
72
- height: 10,
73
- bgcolor: "background.paper",
74
- transform: "translateY(-50%) rotate(45deg)",
75
- zIndex: 0
53
+ React.createElement(Menu, { slotProps: {
54
+ paper: {
55
+ elevation: 0,
56
+ sx: {
57
+ overflow: "visible",
58
+ filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
59
+ mt: -0.4,
60
+ "& .MuiAvatar-root": {
61
+ width: 32,
62
+ height: 32,
63
+ ml: -0.5,
64
+ mr: 1
65
+ },
66
+ "&:before": {
67
+ content: '""',
68
+ display: "block",
69
+ position: "absolute",
70
+ top: 0,
71
+ right: 14,
72
+ width: 10,
73
+ height: 10,
74
+ bgcolor: "background.paper",
75
+ transform: "translateY(-50%) rotate(45deg)",
76
+ zIndex: 0
77
+ }
76
78
  }
77
79
  }
78
80
  }, disableScrollLock: true, anchorEl: anchorEl, anchorOrigin: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.2.90",
3
+ "version": "1.2.92",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -50,13 +50,13 @@
50
50
  "@emotion/css": "^11.11.2",
51
51
  "@emotion/react": "^11.11.1",
52
52
  "@emotion/styled": "^11.11.0",
53
- "@etsoo/appscript": "^1.4.32",
54
- "@etsoo/notificationbase": "^1.1.26",
55
- "@etsoo/react": "^1.7.3",
56
- "@etsoo/shared": "^1.2.8",
57
- "@mui/icons-material": "^5.14.6",
58
- "@mui/material": "^5.14.6",
59
- "@mui/x-data-grid": "^6.11.2",
53
+ "@etsoo/appscript": "^1.4.34",
54
+ "@etsoo/notificationbase": "^1.1.28",
55
+ "@etsoo/react": "^1.7.6",
56
+ "@etsoo/shared": "^1.2.10",
57
+ "@mui/icons-material": "^5.14.7",
58
+ "@mui/material": "^5.14.7",
59
+ "@mui/x-data-grid": "^6.12.0",
60
60
  "@types/pica": "^9.0.1",
61
61
  "@types/pulltorefreshjs": "^0.1.5",
62
62
  "@types/react": "^18.2.21",
@@ -78,15 +78,15 @@
78
78
  "@babel/cli": "^7.22.10",
79
79
  "@babel/core": "^7.22.11",
80
80
  "@babel/plugin-transform-runtime": "^7.22.10",
81
- "@babel/preset-env": "^7.22.10",
81
+ "@babel/preset-env": "^7.22.14",
82
82
  "@babel/preset-react": "^7.22.5",
83
83
  "@babel/preset-typescript": "^7.22.11",
84
84
  "@babel/runtime-corejs3": "^7.22.11",
85
85
  "@testing-library/jest-dom": "^6.1.2",
86
86
  "@testing-library/react": "^14.0.0",
87
87
  "@types/jest": "^29.5.4",
88
- "@typescript-eslint/eslint-plugin": "^6.4.1",
89
- "@typescript-eslint/parser": "^6.4.1",
88
+ "@typescript-eslint/eslint-plugin": "^6.5.0",
89
+ "@typescript-eslint/parser": "^6.5.0",
90
90
  "jest": "^29.6.4",
91
91
  "jest-environment-jsdom": "^29.6.4",
92
92
  "typescript": "^5.2.2"
@@ -3,6 +3,7 @@ import React from "react";
3
3
  import { globalApp } from "./app/ReactApp";
4
4
  import { InputField, InputFieldProps } from "./InputField";
5
5
  import { ListType2 } from "@etsoo/shared";
6
+ import { MUUtils } from "./MUUtils";
6
7
 
7
8
  export type ComboBoxProProps<D extends ListType2 = ListType2> = Omit<
8
9
  AutocompleteProps<D, false, false, true>,
@@ -130,11 +131,7 @@ export function ComboBoxPro<D extends ListType2 = ListType2>(
130
131
  />
131
132
  )}
132
133
  getOptionLabel={(item) =>
133
- typeof item === "object"
134
- ? "label" in item
135
- ? item.label
136
- : item.name
137
- : item
134
+ typeof item === "object" ? MUUtils.getListItemLabel(item) : item
138
135
  }
139
136
  isOptionEqualToValue={(option, value) => option.id === value.id}
140
137
  noOptionsText={noOptionsText}
@@ -5,6 +5,16 @@ import React from "react";
5
5
  * File upload button props
6
6
  */
7
7
  export type FileUploadButtonProps = ButtonProps<"label"> & {
8
+ /**
9
+ * Max files allowed
10
+ */
11
+ maxFiles?: number;
12
+
13
+ /**
14
+ * Max file size
15
+ */
16
+ maxFileSize?: number;
17
+
8
18
  /**
9
19
  * Input field attributes
10
20
  */
@@ -13,6 +23,13 @@ export type FileUploadButtonProps = ButtonProps<"label"> & {
13
23
  "type" | "hidden"
14
24
  >;
15
25
 
26
+ /**
27
+ * File invalid handler
28
+ * @param values [expected, actual]
29
+ * @param file File
30
+ */
31
+ onFileInvalid?: (values: [number, number], file?: File) => void;
32
+
16
33
  /**
17
34
  * Upload files callback
18
35
  * @param files Files
@@ -27,7 +44,15 @@ export type FileUploadButtonProps = ButtonProps<"label"> & {
27
44
  */
28
45
  export function FileUploadButton(props: FileUploadButtonProps) {
29
46
  // Destruct
30
- const { inputProps, onUploadFiles, children, ...rest } = props;
47
+ const {
48
+ maxFiles,
49
+ maxFileSize,
50
+ inputProps,
51
+ onFileInvalid,
52
+ onUploadFiles,
53
+ children,
54
+ ...rest
55
+ } = props;
31
56
 
32
57
  const { onChange } = inputProps ?? {};
33
58
 
@@ -44,7 +69,27 @@ export function FileUploadButton(props: FileUploadButtonProps) {
44
69
 
45
70
  if (onUploadFiles) {
46
71
  const files = event.target.files;
47
- if (files == null || files.length == 0) return;
72
+ if (files == null) return;
73
+
74
+ const fl = files.length;
75
+ if (fl === 0) return;
76
+
77
+ if (maxFiles && maxFiles > 0 && fl > maxFiles) {
78
+ if (onFileInvalid) onFileInvalid([maxFiles, fl]);
79
+ return;
80
+ }
81
+
82
+ if (maxFileSize && maxFileSize > 0) {
83
+ for (let f = 0; f < fl; f++) {
84
+ const file = files[f];
85
+ if (file.size > maxFileSize) {
86
+ if (onFileInvalid)
87
+ onFileInvalid([maxFileSize, file.size], file);
88
+ return;
89
+ }
90
+ }
91
+ }
92
+
48
93
  onUploadFiles(files);
49
94
  }
50
95
  }}
package/src/MUUtils.ts ADDED
@@ -0,0 +1,19 @@
1
+ import { ListType2 } from "@etsoo/shared";
2
+
3
+ /**
4
+ * MU utilities
5
+ */
6
+ export namespace MUUtils {
7
+ /**
8
+ * Get ListType2 item label
9
+ * @param item Item
10
+ * @returns Result
11
+ */
12
+ export function getListItemLabel(item: ListType2) {
13
+ return "label" in item
14
+ ? item.label
15
+ : "name" in item
16
+ ? item.name
17
+ : item.title;
18
+ }
19
+ }
@@ -66,29 +66,31 @@ export function MenuButton<T extends DataTypes.IdItem>(
66
66
  )}
67
67
 
68
68
  <Menu
69
- PaperProps={{
70
- elevation: 0,
71
- sx: {
72
- overflow: "visible",
73
- filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
74
- "& .MuiAvatar-root": {
75
- width: 32,
76
- height: 32
77
- },
78
- "&:before": {
79
- content: '""',
80
- display: "block",
81
- position: "absolute",
82
- top: isTop ? 0 : undefined,
83
- bottom: isTop ? undefined : -10,
84
- right: 14,
85
- width: 10,
86
- height: 10,
87
- bgcolor: "background.paper",
88
- transform: "translateY(-50%) rotate(45deg)",
89
- zIndex: 0
90
- },
91
- ...{ sx }
69
+ slotProps={{
70
+ paper: {
71
+ elevation: 0,
72
+ sx: {
73
+ overflow: "visible",
74
+ filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
75
+ "& .MuiAvatar-root": {
76
+ width: 32,
77
+ height: 32
78
+ },
79
+ "&:before": {
80
+ content: '""',
81
+ display: "block",
82
+ position: "absolute",
83
+ top: isTop ? 0 : undefined,
84
+ bottom: isTop ? undefined : -10,
85
+ right: 14,
86
+ width: 10,
87
+ height: 10,
88
+ bgcolor: "background.paper",
89
+ transform: "translateY(-50%) rotate(45deg)",
90
+ zIndex: 0
91
+ },
92
+ ...{ sx }
93
+ }
92
94
  }
93
95
  }}
94
96
  disableScrollLock
package/src/MoreFab.tsx CHANGED
@@ -186,9 +186,9 @@ export function MoreFab(props: MoreFabProps) {
186
186
  : Array.isArray(action)
187
187
  ? { component: Link, to: action[0], state: action[1] }
188
188
  : {
189
- onClick: () => {
189
+ onClick: (event: React.MouseEvent) => {
190
190
  handleClose();
191
- if (typeof action === "function") action();
191
+ if (typeof action === "function") action(event);
192
192
  }
193
193
  })}
194
194
  >
@@ -22,6 +22,7 @@ import {
22
22
  DialogTitle,
23
23
  Fade,
24
24
  IconButton,
25
+ Popover,
25
26
  Slider,
26
27
  Snackbar,
27
28
  styled,
@@ -101,7 +102,7 @@ export class NotificationMU extends NotificationReact {
101
102
  };
102
103
 
103
104
  // Setup callback
104
- if (this.renderSetup) this.renderSetup(setupProps);
105
+ const options = this.renderSetup ? this.renderSetup(setupProps) : undefined;
105
106
 
106
107
  // Callback
107
108
  const callback = async (_event: React.MouseEvent<HTMLButtonElement>) => {
@@ -118,6 +119,7 @@ export class NotificationMU extends NotificationReact {
118
119
  fullWidth={fullWidth}
119
120
  maxWidth={maxWidth}
120
121
  fullScreen={fullScreen}
122
+ {...options}
121
123
  >
122
124
  <IconDialogTitle className="draggable-dialog-title">
123
125
  {icon}
@@ -172,6 +174,9 @@ export class NotificationMU extends NotificationReact {
172
174
  closable = false
173
175
  } = this.inputProps ?? {};
174
176
 
177
+ // Setup callback
178
+ const options = this.renderSetup ? this.renderSetup({}) : undefined;
179
+
175
180
  const callback = async (
176
181
  _event: React.MouseEvent<HTMLButtonElement>,
177
182
  value: boolean
@@ -189,6 +194,7 @@ export class NotificationMU extends NotificationReact {
189
194
  fullWidth={fullWidth}
190
195
  maxWidth={maxWidth}
191
196
  fullScreen={fullScreen}
197
+ {...options}
192
198
  >
193
199
  <IconDialogTitle className="draggable-dialog-title">
194
200
  <Help color="action" />
@@ -254,12 +260,13 @@ export class NotificationMU extends NotificationReact {
254
260
  };
255
261
 
256
262
  // Setup callback
257
- if (this.renderSetup) this.renderSetup(setupProps);
263
+ const options = this.renderSetup ? this.renderSetup(setupProps) : undefined;
258
264
 
259
265
  return (
260
266
  <Fade in={true} key={this.id}>
261
267
  <Alert
262
268
  {...setupProps}
269
+ {...options}
263
270
  action={
264
271
  closable ? (
265
272
  <IconButton
@@ -390,6 +397,9 @@ export class NotificationMU extends NotificationReact {
390
397
  localInputs = inputs;
391
398
  }
392
399
 
400
+ // Setup callback
401
+ const options = this.renderSetup ? this.renderSetup({}) : undefined;
402
+
393
403
  return (
394
404
  <Dialog
395
405
  key={this.id}
@@ -399,6 +409,7 @@ export class NotificationMU extends NotificationReact {
399
409
  fullWidth={fullWidth}
400
410
  maxWidth={maxWidth}
401
411
  fullScreen={fullScreen}
412
+ {...options}
402
413
  >
403
414
  <form
404
415
  onSubmit={(event) => {
@@ -467,6 +478,20 @@ export class NotificationMU extends NotificationReact {
467
478
  );
468
479
  }
469
480
 
481
+ private createPopup(_props: NotificationRenderProps, className?: string) {
482
+ // Destruct
483
+ const { content, open, renderSetup } = this;
484
+
485
+ // Setup callback
486
+ const options = this.renderSetup ? this.renderSetup({}) : undefined;
487
+
488
+ return (
489
+ <Popover open={open} className={className} {...options}>
490
+ {content}
491
+ </Popover>
492
+ );
493
+ }
494
+
470
495
  // Create loading
471
496
  private createLoading(_props: NotificationRenderProps, className?: string) {
472
497
  // Properties
@@ -482,7 +507,7 @@ export class NotificationMU extends NotificationReact {
482
507
  if (content === "@") content = labels.loading.toString();
483
508
 
484
509
  // Setup callback
485
- if (this.renderSetup) this.renderSetup(setupProps);
510
+ const options = this.renderSetup ? this.renderSetup(setupProps) : undefined;
486
511
 
487
512
  return (
488
513
  <Backdrop
@@ -493,6 +518,7 @@ export class NotificationMU extends NotificationReact {
493
518
  zIndex: (theme) => theme.zIndex.modal + 1
494
519
  }}
495
520
  open={this.open}
521
+ {...options}
496
522
  >
497
523
  <Box
498
524
  display="flex"
@@ -530,6 +556,8 @@ export class NotificationMU extends NotificationReact {
530
556
  return this.createConfirm(props, className);
531
557
  } else if (this.type === NotificationType.Prompt) {
532
558
  return this.createPrompt(props, className);
559
+ } else if (this.type === NotificationType.Popup) {
560
+ return this.createPopup(props, className);
533
561
  } else if (
534
562
  this.type === NotificationType.Error ||
535
563
  (this.modal && this.type in NotificationMessageType)
package/src/QuickList.tsx CHANGED
@@ -13,6 +13,7 @@ import React from "react";
13
13
  import { InputField, InputFieldProps } from "./InputField";
14
14
  import { globalApp } from "./app/ReactApp";
15
15
  import { VBox } from "./FlexBox";
16
+ import { MUUtils } from "./MUUtils";
16
17
 
17
18
  /**
18
19
  * Quick list props
@@ -82,7 +83,7 @@ export function QuickList<T extends ListType2 = ListType2>(
82
83
  buttonProps = {},
83
84
  label,
84
85
  inputProps,
85
- itemLabel = (item: T) => ("label" in item ? item.label : item.name),
86
+ itemLabel = MUUtils.getListItemLabel,
86
87
  itemRenderer = (item: T) => itemLabel(item),
87
88
  itemProps,
88
89
  loadData,
@@ -4,6 +4,7 @@ import { Autocomplete, AutocompleteProps } from "@mui/material";
4
4
  import React, { ChangeEventHandler } from "react";
5
5
  import { InputField, InputFieldProps } from "./InputField";
6
6
  import { globalApp } from "./app/ReactApp";
7
+ import { MUUtils } from "./MUUtils";
7
8
 
8
9
  /**
9
10
  * TiplistPro props
@@ -369,8 +370,7 @@ export function TiplistPro<T extends ListType2 = ListType2>(
369
370
  getOptionLabel={(item) => {
370
371
  if (typeof item === "string") return item;
371
372
  if (item["id"] === -1) return (more ?? "More") + "...";
372
- if (getOptionLabel == null)
373
- return "label" in item ? item.label : item.name;
373
+ if (getOptionLabel == null) return MUUtils.getListItemLabel(item);
374
374
  return getOptionLabel(item);
375
375
  }}
376
376
  {...rest}
package/src/index.ts CHANGED
@@ -78,6 +78,7 @@ export * from "./MobileListItemRenderer";
78
78
  export * from "./MoneyInputField";
79
79
  export * from "./MoreFab";
80
80
  export * from "./MUGlobal";
81
+ export * from "./MUUtils";
81
82
  export * from "./NotifierMU";
82
83
  export * from "./OptionBool";
83
84
  export * from "./OptionGroup";
@@ -101,29 +101,31 @@ export function UserMenu(props: UserMenuProps) {
101
101
  <UserAvatar title={name} src={avatar} />
102
102
  </IconButton>
103
103
  <Menu
104
- PaperProps={{
105
- elevation: 0,
106
- sx: {
107
- overflow: "visible",
108
- filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
109
- mt: -0.4,
110
- "& .MuiAvatar-root": {
111
- width: 32,
112
- height: 32,
113
- ml: -0.5,
114
- mr: 1
115
- },
116
- "&:before": {
117
- content: '""',
118
- display: "block",
119
- position: "absolute",
120
- top: 0,
121
- right: 14,
122
- width: 10,
123
- height: 10,
124
- bgcolor: "background.paper",
125
- transform: "translateY(-50%) rotate(45deg)",
126
- zIndex: 0
104
+ slotProps={{
105
+ paper: {
106
+ elevation: 0,
107
+ sx: {
108
+ overflow: "visible",
109
+ filter: "drop-shadow(0px 2px 8px rgba(0,0,0,0.32))",
110
+ mt: -0.4,
111
+ "& .MuiAvatar-root": {
112
+ width: 32,
113
+ height: 32,
114
+ ml: -0.5,
115
+ mr: 1
116
+ },
117
+ "&:before": {
118
+ content: '""',
119
+ display: "block",
120
+ position: "absolute",
121
+ top: 0,
122
+ right: 14,
123
+ width: 10,
124
+ height: 10,
125
+ bgcolor: "background.paper",
126
+ transform: "translateY(-50%) rotate(45deg)",
127
+ zIndex: 0
128
+ }
127
129
  }
128
130
  }
129
131
  }}