@etsoo/materialui 1.2.52 → 1.2.53

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.
@@ -9,7 +9,7 @@ export interface UserAvatarEditorToBlob {
9
9
  * User avatar editor on done handler
10
10
  */
11
11
  export interface UserAvatarEditorOnDoneHandler {
12
- (canvas: HTMLCanvasElement, toBlob: UserAvatarEditorToBlob): void;
12
+ (canvas: HTMLCanvasElement, toBlob: UserAvatarEditorToBlob, type: string): void;
13
13
  }
14
14
  /**
15
15
  * User avatar editor props
@@ -43,6 +43,10 @@ export interface UserAvatarEditorProps {
43
43
  * Height of the editor
44
44
  */
45
45
  height?: number;
46
+ /**
47
+ * Value range
48
+ */
49
+ range?: [number, number, number];
46
50
  }
47
51
  /**
48
52
  * User avatar editor
@@ -1,10 +1,12 @@
1
- import { Button, ButtonGroup, Skeleton, Slider, Stack } from "@mui/material";
1
+ import { Button, ButtonGroup, IconButton, Skeleton, Slider, Stack } from "@mui/material";
2
2
  import React from "react";
3
3
  import RotateLeftIcon from "@mui/icons-material/RotateLeft";
4
4
  import RotateRightIcon from "@mui/icons-material/RotateRight";
5
5
  import ClearAllIcon from "@mui/icons-material/ClearAll";
6
6
  import ComputerIcon from "@mui/icons-material/Computer";
7
7
  import DoneIcon from "@mui/icons-material/Done";
8
+ import RemoveIcon from "@mui/icons-material/Remove";
9
+ import AddIcon from "@mui/icons-material/Add";
8
10
  import { Labels } from "./app/Labels";
9
11
  const defaultState = {
10
12
  scale: 1,
@@ -18,7 +20,7 @@ const defaultState = {
18
20
  */
19
21
  export function UserAvatarEditor(props) {
20
22
  // Destruct
21
- const { border = 30, image, maxWidth, onDone, scaledResult = false, width = 200, height = 200 } = props;
23
+ const { border = 30, image, maxWidth, onDone, scaledResult = false, width = 200, height = 200, range = [0.1, 2, 0.1] } = props;
22
24
  // Container width
23
25
  const containerWidth = width + 2 * border + 44 + 4;
24
26
  // Calculated max width
@@ -27,6 +29,8 @@ export function UserAvatarEditor(props) {
27
29
  const labels = Labels.UserAvatarEditor;
28
30
  // Ref
29
31
  const ref = React.createRef();
32
+ // Image type
33
+ const type = React.useRef("image/jpeg");
30
34
  // Button ref
31
35
  const buttonRef = React.createRef();
32
36
  // Preview image state
@@ -35,12 +39,33 @@ export function UserAvatarEditor(props) {
35
39
  const [ready, setReady] = React.useState(false);
36
40
  // Editor states
37
41
  const [editorState, setEditorState] = React.useState(defaultState);
42
+ // Range
43
+ const [min, max, step] = range;
44
+ const marks = [
45
+ {
46
+ value: min,
47
+ label: min.toString()
48
+ },
49
+ {
50
+ value: max,
51
+ label: max.toString()
52
+ }
53
+ ];
54
+ if (min < 1) {
55
+ marks.splice(1, 0, { value: 1, label: "1" });
56
+ }
38
57
  // Handle zoom
39
58
  const handleZoom = (_event, value, _activeThumb) => {
40
59
  const scale = typeof value === "number" ? value : value[0];
60
+ setScale(scale);
61
+ };
62
+ const setScale = (scale) => {
41
63
  const newState = { ...editorState, scale };
42
64
  setEditorState(newState);
43
65
  };
66
+ const adjustScale = (isAdd) => {
67
+ setScale(editorState.scale + (isAdd ? step : -step));
68
+ };
44
69
  // Handle image load
45
70
  const handleLoad = () => {
46
71
  setReady(true);
@@ -54,7 +79,9 @@ export function UserAvatarEditor(props) {
54
79
  // Reset all settings
55
80
  handleReset();
56
81
  // Set new preview image
57
- setPreviewImage(files[0]);
82
+ const file = files[0];
83
+ type.current = file.type;
84
+ setPreviewImage(file);
58
85
  // Set ready state
59
86
  setReady(false);
60
87
  // Make the submit button visible
@@ -86,7 +113,7 @@ export function UserAvatarEditor(props) {
86
113
  const picaInstance = pica();
87
114
  // toBlob helper
88
115
  // Convenience method, similar to canvas.toBlob(), but with promise interface & polyfill for old browsers.
89
- const toBlob = (canvas, mimeType = "image/jpeg", quality = 1) => {
116
+ const toBlob = (canvas, mimeType = type.current, quality = 1) => {
90
117
  return picaInstance.toBlob(canvas, mimeType, quality);
91
118
  };
92
119
  if (data.width > maxWidthCalculated) {
@@ -104,10 +131,10 @@ export function UserAvatarEditor(props) {
104
131
  unsharpRadius: 0.6,
105
132
  unsharpThreshold: 1
106
133
  })
107
- .then((result) => onDone(result, toBlob));
134
+ .then((result) => onDone(result, toBlob, type.current));
108
135
  }
109
136
  else {
110
- onDone(data, toBlob);
137
+ onDone(data, toBlob, type.current);
111
138
  }
112
139
  };
113
140
  // Load the component
@@ -126,6 +153,11 @@ export function UserAvatarEditor(props) {
126
153
  React.createElement(RotateLeftIcon, null)),
127
154
  React.createElement(Button, { onClick: handleReset, title: labels.reset },
128
155
  React.createElement(ClearAllIcon, null)))),
129
- React.createElement(Slider, { title: labels.zoom, disabled: !ready, min: 1, max: 5, step: 0.01, value: editorState.scale, onChange: handleZoom }),
156
+ React.createElement(Stack, { spacing: 0.5, direction: "row", sx: { paddingBottom: 2 }, alignItems: "center" },
157
+ React.createElement(IconButton, { size: "small", disabled: !ready || editorState.scale <= min, onClick: () => adjustScale(false) },
158
+ React.createElement(RemoveIcon, null)),
159
+ React.createElement(Slider, { title: labels.zoom, disabled: !ready, min: min, max: max, step: step, value: editorState.scale, valueLabelDisplay: "auto", valueLabelFormat: (value) => `${Math.round(100 * value) / 100}`, marks: marks, onChange: handleZoom }),
160
+ React.createElement(IconButton, { size: "small", disabled: !ready || editorState.scale >= max, onClick: () => adjustScale(true) },
161
+ React.createElement(AddIcon, null))),
130
162
  React.createElement(Button, { ref: buttonRef, variant: "contained", startIcon: React.createElement(DoneIcon, null), disabled: !ready, onClick: handleDone }, labels.done)));
131
163
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.2.52",
3
+ "version": "1.2.53",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -59,7 +59,7 @@
59
59
  "@mui/x-data-grid": "^6.7.0",
60
60
  "@types/pica": "^9.0.1",
61
61
  "@types/pulltorefreshjs": "^0.1.5",
62
- "@types/react": "^18.2.9",
62
+ "@types/react": "^18.2.11",
63
63
  "@types/react-avatar-editor": "^13.0.0",
64
64
  "@types/react-dom": "^18.2.4",
65
65
  "@types/react-input-mask": "^3.0.2",
@@ -1,4 +1,11 @@
1
- import { Button, ButtonGroup, Skeleton, Slider, Stack } from "@mui/material";
1
+ import {
2
+ Button,
3
+ ButtonGroup,
4
+ IconButton,
5
+ Skeleton,
6
+ Slider,
7
+ Stack
8
+ } from "@mui/material";
2
9
  import React from "react";
3
10
  import type AvatarEditor from "react-avatar-editor";
4
11
  import RotateLeftIcon from "@mui/icons-material/RotateLeft";
@@ -6,6 +13,8 @@ import RotateRightIcon from "@mui/icons-material/RotateRight";
6
13
  import ClearAllIcon from "@mui/icons-material/ClearAll";
7
14
  import ComputerIcon from "@mui/icons-material/Computer";
8
15
  import DoneIcon from "@mui/icons-material/Done";
16
+ import RemoveIcon from "@mui/icons-material/Remove";
17
+ import AddIcon from "@mui/icons-material/Add";
9
18
  import { Labels } from "./app/Labels";
10
19
 
11
20
  /**
@@ -23,7 +32,11 @@ export interface UserAvatarEditorToBlob {
23
32
  * User avatar editor on done handler
24
33
  */
25
34
  export interface UserAvatarEditorOnDoneHandler {
26
- (canvas: HTMLCanvasElement, toBlob: UserAvatarEditorToBlob): void;
35
+ (
36
+ canvas: HTMLCanvasElement,
37
+ toBlob: UserAvatarEditorToBlob,
38
+ type: string
39
+ ): void;
27
40
  }
28
41
 
29
42
  /**
@@ -64,6 +77,11 @@ export interface UserAvatarEditorProps {
64
77
  * Height of the editor
65
78
  */
66
79
  height?: number;
80
+
81
+ /**
82
+ * Value range
83
+ */
84
+ range?: [number, number, number];
67
85
  }
68
86
 
69
87
  interface EditorState {
@@ -91,7 +109,8 @@ export function UserAvatarEditor(props: UserAvatarEditorProps) {
91
109
  onDone,
92
110
  scaledResult = false,
93
111
  width = 200,
94
- height = 200
112
+ height = 200,
113
+ range = [0.1, 2, 0.1]
95
114
  } = props;
96
115
 
97
116
  // Container width
@@ -107,6 +126,9 @@ export function UserAvatarEditor(props: UserAvatarEditorProps) {
107
126
  // Ref
108
127
  const ref = React.createRef<AvatarEditor>();
109
128
 
129
+ // Image type
130
+ const type = React.useRef<string>("image/jpeg");
131
+
110
132
  // Button ref
111
133
  const buttonRef = React.createRef<HTMLButtonElement>();
112
134
 
@@ -119,6 +141,23 @@ export function UserAvatarEditor(props: UserAvatarEditorProps) {
119
141
  // Editor states
120
142
  const [editorState, setEditorState] = React.useState(defaultState);
121
143
 
144
+ // Range
145
+ const [min, max, step] = range;
146
+ const marks = [
147
+ {
148
+ value: min,
149
+ label: min.toString()
150
+ },
151
+ {
152
+ value: max,
153
+ label: max.toString()
154
+ }
155
+ ];
156
+
157
+ if (min < 1) {
158
+ marks.splice(1, 0, { value: 1, label: "1" });
159
+ }
160
+
122
161
  // Handle zoom
123
162
  const handleZoom = (
124
163
  _event: Event,
@@ -126,10 +165,18 @@ export function UserAvatarEditor(props: UserAvatarEditorProps) {
126
165
  _activeThumb: number
127
166
  ) => {
128
167
  const scale = typeof value === "number" ? value : value[0];
168
+ setScale(scale);
169
+ };
170
+
171
+ const setScale = (scale: number) => {
129
172
  const newState = { ...editorState, scale };
130
173
  setEditorState(newState);
131
174
  };
132
175
 
176
+ const adjustScale = (isAdd: boolean) => {
177
+ setScale(editorState.scale + (isAdd ? step : -step));
178
+ };
179
+
133
180
  // Handle image load
134
181
  const handleLoad = () => {
135
182
  setReady(true);
@@ -144,7 +191,9 @@ export function UserAvatarEditor(props: UserAvatarEditorProps) {
144
191
  handleReset();
145
192
 
146
193
  // Set new preview image
147
- setPreviewImage(files[0]);
194
+ const file = files[0];
195
+ type.current = file.type;
196
+ setPreviewImage(file);
148
197
 
149
198
  // Set ready state
150
199
  setReady(false);
@@ -183,7 +232,7 @@ export function UserAvatarEditor(props: UserAvatarEditorProps) {
183
232
  // Convenience method, similar to canvas.toBlob(), but with promise interface & polyfill for old browsers.
184
233
  const toBlob = (
185
234
  canvas: HTMLCanvasElement,
186
- mimeType: string = "image/jpeg",
235
+ mimeType: string = type.current,
187
236
  quality: number = 1
188
237
  ) => {
189
238
  return picaInstance.toBlob(canvas, mimeType, quality);
@@ -206,9 +255,9 @@ export function UserAvatarEditor(props: UserAvatarEditorProps) {
206
255
  unsharpRadius: 0.6,
207
256
  unsharpThreshold: 1
208
257
  })
209
- .then((result) => onDone(result, toBlob));
258
+ .then((result) => onDone(result, toBlob, type.current));
210
259
  } else {
211
- onDone(data, toBlob);
260
+ onDone(data, toBlob, type.current);
212
261
  }
213
262
  };
214
263
 
@@ -263,15 +312,39 @@ export function UserAvatarEditor(props: UserAvatarEditorProps) {
263
312
  </Button>
264
313
  </ButtonGroup>
265
314
  </Stack>
266
- <Slider
267
- title={labels.zoom}
268
- disabled={!ready}
269
- min={1}
270
- max={5}
271
- step={0.01}
272
- value={editorState.scale}
273
- onChange={handleZoom}
274
- />
315
+ <Stack
316
+ spacing={0.5}
317
+ direction="row"
318
+ sx={{ paddingBottom: 2 }}
319
+ alignItems="center"
320
+ >
321
+ <IconButton
322
+ size="small"
323
+ disabled={!ready || editorState.scale <= min}
324
+ onClick={() => adjustScale(false)}
325
+ >
326
+ <RemoveIcon />
327
+ </IconButton>
328
+ <Slider
329
+ title={labels.zoom}
330
+ disabled={!ready}
331
+ min={min}
332
+ max={max}
333
+ step={step}
334
+ value={editorState.scale}
335
+ valueLabelDisplay="auto"
336
+ valueLabelFormat={(value) => `${Math.round(100 * value) / 100}`}
337
+ marks={marks}
338
+ onChange={handleZoom}
339
+ />
340
+ <IconButton
341
+ size="small"
342
+ disabled={!ready || editorState.scale >= max}
343
+ onClick={() => adjustScale(true)}
344
+ >
345
+ <AddIcon />
346
+ </IconButton>
347
+ </Stack>
275
348
  <Button
276
349
  ref={buttonRef}
277
350
  variant="contained"