@etsoo/materialui 1.0.1
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/.eslintignore +3 -0
- package/.eslintrc.json +38 -0
- package/.gitattributes +2 -0
- package/.github/workflows/main.yml +48 -0
- package/.prettierignore +5 -0
- package/.prettierrc +6 -0
- package/LICENSE +21 -0
- package/README.md +16 -0
- package/__tests__/ComboBox.tsx +30 -0
- package/__tests__/MUGlobalTests.tsx +58 -0
- package/__tests__/NotifierMUTests.tsx +217 -0
- package/__tests__/SelectEx.tsx +26 -0
- package/__tests__/tsconfig.json +19 -0
- package/babel.config.json +11 -0
- package/lib/AuditDisplay.d.ts +33 -0
- package/lib/AuditDisplay.js +52 -0
- package/lib/AutocompleteExtendedProps.d.ts +64 -0
- package/lib/AutocompleteExtendedProps.js +1 -0
- package/lib/BackButton.d.ts +13 -0
- package/lib/BackButton.js +33 -0
- package/lib/BridgeCloseButton.d.ts +23 -0
- package/lib/BridgeCloseButton.js +32 -0
- package/lib/ButtonLink.d.ts +17 -0
- package/lib/ButtonLink.js +19 -0
- package/lib/ComboBox.d.ts +38 -0
- package/lib/ComboBox.js +108 -0
- package/lib/CountdownButton.d.ts +23 -0
- package/lib/CountdownButton.js +81 -0
- package/lib/CustomFabProps.d.ts +27 -0
- package/lib/CustomFabProps.js +1 -0
- package/lib/DataGridEx.d.ts +94 -0
- package/lib/DataGridEx.js +329 -0
- package/lib/DataGridRenderers.d.ts +22 -0
- package/lib/DataGridRenderers.js +99 -0
- package/lib/DialogButton.d.ts +54 -0
- package/lib/DialogButton.js +45 -0
- package/lib/DnDList.d.ts +87 -0
- package/lib/DnDList.js +153 -0
- package/lib/DraggablePaperComponent.d.ts +8 -0
- package/lib/DraggablePaperComponent.js +12 -0
- package/lib/EmailInput.d.ts +11 -0
- package/lib/EmailInput.js +15 -0
- package/lib/FabBox.d.ts +21 -0
- package/lib/FabBox.js +31 -0
- package/lib/FlexBox.d.ts +14 -0
- package/lib/FlexBox.js +18 -0
- package/lib/GridDataFormat.d.ts +10 -0
- package/lib/GridDataFormat.js +43 -0
- package/lib/IconButtonLink.d.ts +17 -0
- package/lib/IconButtonLink.js +16 -0
- package/lib/InputField.d.ts +21 -0
- package/lib/InputField.js +39 -0
- package/lib/ItemList.d.ts +56 -0
- package/lib/ItemList.js +69 -0
- package/lib/ListItemRightIcon.d.ts +4 -0
- package/lib/ListItemRightIcon.js +8 -0
- package/lib/ListMoreDisplay.d.ts +35 -0
- package/lib/ListMoreDisplay.js +99 -0
- package/lib/LoadingButton.d.ts +16 -0
- package/lib/LoadingButton.js +41 -0
- package/lib/MUGlobal.d.ts +102 -0
- package/lib/MUGlobal.js +184 -0
- package/lib/MaskInput.d.ts +34 -0
- package/lib/MaskInput.js +43 -0
- package/lib/MobileListItemRenderer.d.ts +17 -0
- package/lib/MobileListItemRenderer.js +35 -0
- package/lib/MoreFab.d.ts +45 -0
- package/lib/MoreFab.js +95 -0
- package/lib/NotifierMU.d.ts +47 -0
- package/lib/NotifierMU.js +387 -0
- package/lib/NotifierPromptProps.d.ts +22 -0
- package/lib/NotifierPromptProps.js +1 -0
- package/lib/OptionGroup.d.ts +58 -0
- package/lib/OptionGroup.js +81 -0
- package/lib/PList.d.ts +15 -0
- package/lib/PList.js +12 -0
- package/lib/ProgressCount.d.ts +44 -0
- package/lib/ProgressCount.js +79 -0
- package/lib/PullToRefreshUI.d.ts +9 -0
- package/lib/PullToRefreshUI.js +18 -0
- package/lib/RLink.d.ts +14 -0
- package/lib/RLink.js +37 -0
- package/lib/ResponsibleContainer.d.ts +87 -0
- package/lib/ResponsibleContainer.js +156 -0
- package/lib/ScrollTopFab.d.ts +7 -0
- package/lib/ScrollTopFab.js +25 -0
- package/lib/ScrollerListEx.d.ts +81 -0
- package/lib/ScrollerListEx.js +167 -0
- package/lib/SearchBar.d.ts +29 -0
- package/lib/SearchBar.js +260 -0
- package/lib/SearchField.d.ts +21 -0
- package/lib/SearchField.js +39 -0
- package/lib/SearchOptionGroup.d.ts +9 -0
- package/lib/SearchOptionGroup.js +14 -0
- package/lib/SelectBool.d.ts +13 -0
- package/lib/SelectBool.js +22 -0
- package/lib/SelectEx.d.ts +50 -0
- package/lib/SelectEx.js +156 -0
- package/lib/ShowDataComparison.d.ts +20 -0
- package/lib/ShowDataComparison.js +58 -0
- package/lib/Switch.d.ts +29 -0
- package/lib/Switch.js +34 -0
- package/lib/SwitchAnt.d.ts +25 -0
- package/lib/SwitchAnt.js +40 -0
- package/lib/TabBox.d.ts +54 -0
- package/lib/TabBox.js +31 -0
- package/lib/TableEx.d.ts +65 -0
- package/lib/TableEx.js +270 -0
- package/lib/TextFieldEx.d.ts +101 -0
- package/lib/TextFieldEx.js +126 -0
- package/lib/Tiplist.d.ts +18 -0
- package/lib/Tiplist.js +157 -0
- package/lib/TooltipClick.d.ts +15 -0
- package/lib/TooltipClick.js +40 -0
- package/lib/UserAvatar.d.ts +24 -0
- package/lib/UserAvatar.js +25 -0
- package/lib/UserAvatarEditor.d.ts +53 -0
- package/lib/UserAvatarEditor.js +129 -0
- package/lib/app/CommonApp.d.ts +38 -0
- package/lib/app/CommonApp.js +149 -0
- package/lib/app/IServiceAppSettings.d.ts +11 -0
- package/lib/app/IServiceAppSettings.js +1 -0
- package/lib/app/IServicePage.d.ts +6 -0
- package/lib/app/IServicePage.js +1 -0
- package/lib/app/IServiceUser.d.ts +14 -0
- package/lib/app/IServiceUser.js +1 -0
- package/lib/app/ISmartERPUser.d.ts +14 -0
- package/lib/app/ISmartERPUser.js +1 -0
- package/lib/app/Labels.d.ts +65 -0
- package/lib/app/Labels.js +62 -0
- package/lib/app/ReactApp.d.ts +195 -0
- package/lib/app/ReactApp.js +296 -0
- package/lib/app/ServiceApp.d.ts +78 -0
- package/lib/app/ServiceApp.js +244 -0
- package/lib/index.d.ts +74 -0
- package/lib/index.js +74 -0
- package/lib/pages/CommonPage.d.ts +11 -0
- package/lib/pages/CommonPage.js +60 -0
- package/lib/pages/CommonPageProps.d.ts +59 -0
- package/lib/pages/CommonPageProps.js +1 -0
- package/lib/pages/DataGridPage.d.ts +9 -0
- package/lib/pages/DataGridPage.js +79 -0
- package/lib/pages/DataGridPageProps.d.ts +17 -0
- package/lib/pages/DataGridPageProps.js +1 -0
- package/lib/pages/EditPage.d.ts +33 -0
- package/lib/pages/EditPage.js +29 -0
- package/lib/pages/FixedListPage.d.ts +15 -0
- package/lib/pages/FixedListPage.js +70 -0
- package/lib/pages/ListPage.d.ts +9 -0
- package/lib/pages/ListPage.js +50 -0
- package/lib/pages/ListPageProps.d.ts +7 -0
- package/lib/pages/ListPageProps.js +1 -0
- package/lib/pages/ResponsivePage.d.ts +9 -0
- package/lib/pages/ResponsivePage.js +45 -0
- package/lib/pages/ResponsivePageProps.d.ts +39 -0
- package/lib/pages/ResponsivePageProps.js +1 -0
- package/lib/pages/SearchPageProps.d.ts +30 -0
- package/lib/pages/SearchPageProps.js +1 -0
- package/lib/pages/TablePage.d.ts +9 -0
- package/lib/pages/TablePage.js +69 -0
- package/lib/pages/TablePageProps.d.ts +7 -0
- package/lib/pages/TablePageProps.js +1 -0
- package/lib/pages/ViewPage.d.ts +66 -0
- package/lib/pages/ViewPage.js +105 -0
- package/lib/texts/DateText.d.ts +34 -0
- package/lib/texts/DateText.js +25 -0
- package/lib/texts/MoneyText.d.ts +21 -0
- package/lib/texts/MoneyText.js +14 -0
- package/lib/texts/NumberText.d.ts +25 -0
- package/lib/texts/NumberText.js +14 -0
- package/package.json +97 -0
- package/src/AuditDisplay.tsx +114 -0
- package/src/AutocompleteExtendedProps.ts +83 -0
- package/src/BackButton.tsx +55 -0
- package/src/BridgeCloseButton.tsx +69 -0
- package/src/ButtonLink.tsx +32 -0
- package/src/ComboBox.tsx +251 -0
- package/src/CountdownButton.tsx +119 -0
- package/src/CustomFabProps.ts +32 -0
- package/src/DataGridEx.tsx +713 -0
- package/src/DataGridRenderers.tsx +140 -0
- package/src/DialogButton.tsx +163 -0
- package/src/DnDList.tsx +344 -0
- package/src/DraggablePaperComponent.tsx +19 -0
- package/src/EmailInput.tsx +24 -0
- package/src/FabBox.tsx +51 -0
- package/src/FlexBox.tsx +20 -0
- package/src/GridDataFormat.tsx +77 -0
- package/src/IconButtonLink.tsx +29 -0
- package/src/InputField.tsx +82 -0
- package/src/ItemList.tsx +204 -0
- package/src/ListItemRightIcon.tsx +9 -0
- package/src/ListMoreDisplay.tsx +205 -0
- package/src/LoadingButton.tsx +75 -0
- package/src/MUGlobal.ts +220 -0
- package/src/MaskInput.tsx +107 -0
- package/src/MobileListItemRenderer.tsx +79 -0
- package/src/MoreFab.tsx +211 -0
- package/src/NotifierMU.tsx +654 -0
- package/src/NotifierPromptProps.ts +24 -0
- package/src/OptionGroup.tsx +223 -0
- package/src/PList.tsx +27 -0
- package/src/ProgressCount.tsx +166 -0
- package/src/PullToRefreshUI.tsx +21 -0
- package/src/RLink.tsx +64 -0
- package/src/ResponsibleContainer.tsx +394 -0
- package/src/ScrollTopFab.tsx +34 -0
- package/src/ScrollerListEx.tsx +387 -0
- package/src/SearchBar.tsx +396 -0
- package/src/SearchField.tsx +82 -0
- package/src/SearchOptionGroup.tsx +31 -0
- package/src/SelectBool.tsx +33 -0
- package/src/SelectEx.tsx +290 -0
- package/src/ShowDataComparison.tsx +106 -0
- package/src/Switch.tsx +94 -0
- package/src/SwitchAnt.tsx +95 -0
- package/src/TabBox.tsx +118 -0
- package/src/TableEx.tsx +558 -0
- package/src/TextFieldEx.tsx +249 -0
- package/src/Tiplist.tsx +303 -0
- package/src/TooltipClick.tsx +84 -0
- package/src/UserAvatar.tsx +64 -0
- package/src/UserAvatarEditor.tsx +287 -0
- package/src/app/CommonApp.ts +223 -0
- package/src/app/IServiceAppSettings.ts +13 -0
- package/src/app/IServicePage.ts +6 -0
- package/src/app/IServiceUser.ts +17 -0
- package/src/app/ISmartERPUser.ts +16 -0
- package/src/app/Labels.ts +77 -0
- package/src/app/ReactApp.ts +504 -0
- package/src/app/ServiceApp.ts +352 -0
- package/src/index.ts +77 -0
- package/src/pages/CommonPage.tsx +128 -0
- package/src/pages/CommonPageProps.ts +70 -0
- package/src/pages/DataGridPage.tsx +140 -0
- package/src/pages/DataGridPageProps.ts +24 -0
- package/src/pages/EditPage.tsx +114 -0
- package/src/pages/FixedListPage.tsx +141 -0
- package/src/pages/ListPage.tsx +90 -0
- package/src/pages/ListPageProps.ts +12 -0
- package/src/pages/ResponsivePage.tsx +68 -0
- package/src/pages/ResponsivePageProps.ts +57 -0
- package/src/pages/SearchPageProps.ts +39 -0
- package/src/pages/TablePage.tsx +126 -0
- package/src/pages/TablePageProps.ts +12 -0
- package/src/pages/ViewPage.tsx +282 -0
- package/src/texts/DateText.tsx +74 -0
- package/src/texts/MoneyText.tsx +49 -0
- package/src/texts/NumberText.tsx +40 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { Button, ButtonGroup, Slider, Stack } from '@mui/material';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import AvatarEditor from 'react-avatar-editor';
|
|
4
|
+
import RotateLeftIcon from '@mui/icons-material/RotateLeft';
|
|
5
|
+
import RotateRightIcon from '@mui/icons-material/RotateRight';
|
|
6
|
+
import ClearAllIcon from '@mui/icons-material/ClearAll';
|
|
7
|
+
import ComputerIcon from '@mui/icons-material/Computer';
|
|
8
|
+
import DoneIcon from '@mui/icons-material/Done';
|
|
9
|
+
import pica from 'pica';
|
|
10
|
+
import { Labels } from './app/Labels';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* User avatar editor to Blob helper
|
|
14
|
+
*/
|
|
15
|
+
export interface UserAvatarEditorToBlob {
|
|
16
|
+
(
|
|
17
|
+
canvas: HTMLCanvasElement,
|
|
18
|
+
mimeType?: string,
|
|
19
|
+
quality?: number
|
|
20
|
+
): Promise<Blob>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* User avatar editor on done handler
|
|
25
|
+
*/
|
|
26
|
+
export interface UserAvatarEditorOnDoneHandler {
|
|
27
|
+
(canvas: HTMLCanvasElement, toBlob: UserAvatarEditorToBlob): void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* User avatar editor props
|
|
32
|
+
*/
|
|
33
|
+
export interface UserAvatarEditorProps {
|
|
34
|
+
/**
|
|
35
|
+
* Cropping border size
|
|
36
|
+
*/
|
|
37
|
+
border?: number;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Image source
|
|
41
|
+
*/
|
|
42
|
+
image?: string | File;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Max width to save
|
|
46
|
+
*/
|
|
47
|
+
maxWidth?: number;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* On done handler
|
|
51
|
+
*/
|
|
52
|
+
onDone: UserAvatarEditorOnDoneHandler;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Return scaled result?
|
|
56
|
+
*/
|
|
57
|
+
scaledResult?: boolean;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Width of the editor
|
|
61
|
+
*/
|
|
62
|
+
width?: number;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Height of the editor
|
|
66
|
+
*/
|
|
67
|
+
height?: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface EditorState {
|
|
71
|
+
scale: number;
|
|
72
|
+
rotate: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const defaultState: EditorState = {
|
|
76
|
+
scale: 1,
|
|
77
|
+
rotate: 0
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* User avatar editor
|
|
82
|
+
* https://github.com/mosch/react-avatar-editor
|
|
83
|
+
* @param props Props
|
|
84
|
+
* @returns Component
|
|
85
|
+
*/
|
|
86
|
+
export function UserAvatarEditor(props: UserAvatarEditorProps) {
|
|
87
|
+
// Destruct
|
|
88
|
+
const {
|
|
89
|
+
border = 30,
|
|
90
|
+
image,
|
|
91
|
+
maxWidth,
|
|
92
|
+
onDone,
|
|
93
|
+
scaledResult = false,
|
|
94
|
+
width = 200,
|
|
95
|
+
height = 200
|
|
96
|
+
} = props;
|
|
97
|
+
|
|
98
|
+
// Container width
|
|
99
|
+
const containerWidth = width + 2 * border + 44 + 4;
|
|
100
|
+
|
|
101
|
+
// Calculated max width
|
|
102
|
+
const maxWidthCalculated =
|
|
103
|
+
maxWidth == null || maxWidth < 200 ? 3 * width : maxWidth;
|
|
104
|
+
|
|
105
|
+
// Labels
|
|
106
|
+
const labels = Labels.UserAvatarEditor;
|
|
107
|
+
|
|
108
|
+
// Ref
|
|
109
|
+
const ref = React.createRef<AvatarEditor>();
|
|
110
|
+
|
|
111
|
+
// Button ref
|
|
112
|
+
const buttonRef = React.createRef<HTMLButtonElement>();
|
|
113
|
+
|
|
114
|
+
// Preview image state
|
|
115
|
+
const [previewImage, setPreviewImage] = React.useState(image);
|
|
116
|
+
|
|
117
|
+
// Is ready state
|
|
118
|
+
const [ready, setReady] = React.useState(false);
|
|
119
|
+
|
|
120
|
+
// Editor states
|
|
121
|
+
const [editorState, setEditorState] = React.useState(defaultState);
|
|
122
|
+
|
|
123
|
+
// Handle zoom
|
|
124
|
+
const handleZoom = (
|
|
125
|
+
_event: Event,
|
|
126
|
+
value: number | number[],
|
|
127
|
+
_activeThumb: number
|
|
128
|
+
) => {
|
|
129
|
+
const scale = typeof value === 'number' ? value : value[0];
|
|
130
|
+
const newState = { ...editorState, scale };
|
|
131
|
+
setEditorState(newState);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Handle image load
|
|
135
|
+
const handleLoad = () => {
|
|
136
|
+
setReady(true);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// Handle file change
|
|
140
|
+
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
141
|
+
const files = event.target.files;
|
|
142
|
+
if (files == null || files.length == 0) return;
|
|
143
|
+
|
|
144
|
+
// Reset all settings
|
|
145
|
+
handleReset();
|
|
146
|
+
|
|
147
|
+
// Set new preview image
|
|
148
|
+
setPreviewImage(files[0]);
|
|
149
|
+
|
|
150
|
+
// Set ready state
|
|
151
|
+
setReady(false);
|
|
152
|
+
|
|
153
|
+
// Make the submit button visible
|
|
154
|
+
buttonRef.current?.scrollIntoView(false);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Handle reset
|
|
158
|
+
const handleReset = () => {
|
|
159
|
+
setEditorState({ ...defaultState });
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Handle rotate
|
|
163
|
+
const handleRotate = (r: number) => {
|
|
164
|
+
let rotate = editorState.rotate + r;
|
|
165
|
+
if (rotate >= 360 || rotate <= -360) rotate = 0;
|
|
166
|
+
|
|
167
|
+
const newState = { ...editorState, rotate };
|
|
168
|
+
setEditorState(newState);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Handle done
|
|
172
|
+
const handleDone = () => {
|
|
173
|
+
// Data
|
|
174
|
+
var data = scaledResult
|
|
175
|
+
? ref.current?.getImageScaledToCanvas()
|
|
176
|
+
: ref.current?.getImage();
|
|
177
|
+
if (data == null) return;
|
|
178
|
+
|
|
179
|
+
// pica
|
|
180
|
+
const picaInstance = pica();
|
|
181
|
+
|
|
182
|
+
// toBlob helper
|
|
183
|
+
// Convenience method, similar to canvas.toBlob(), but with promise interface & polyfill for old browsers.
|
|
184
|
+
const toBlob = (
|
|
185
|
+
canvas: HTMLCanvasElement,
|
|
186
|
+
mimeType: string = 'image/jpeg',
|
|
187
|
+
quality: number = 1
|
|
188
|
+
) => {
|
|
189
|
+
return picaInstance.toBlob(canvas, mimeType, quality);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
if (data.width > maxWidthCalculated) {
|
|
193
|
+
// Target height
|
|
194
|
+
const heightCalculated = (height * maxWidthCalculated) / width;
|
|
195
|
+
|
|
196
|
+
// Target
|
|
197
|
+
const to = document.createElement('canvas');
|
|
198
|
+
to.width = maxWidthCalculated;
|
|
199
|
+
to.height = heightCalculated;
|
|
200
|
+
|
|
201
|
+
// Large photo, resize it
|
|
202
|
+
// https://github.com/nodeca/pica
|
|
203
|
+
picaInstance
|
|
204
|
+
.resize(data, to, {
|
|
205
|
+
unsharpAmount: 160,
|
|
206
|
+
unsharpRadius: 0.6,
|
|
207
|
+
unsharpThreshold: 1
|
|
208
|
+
})
|
|
209
|
+
.then((result) => onDone(result, toBlob));
|
|
210
|
+
} else {
|
|
211
|
+
onDone(data, toBlob);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<Stack direction="column" spacing={0.5} width={containerWidth}>
|
|
217
|
+
<Button
|
|
218
|
+
variant="outlined"
|
|
219
|
+
size="medium"
|
|
220
|
+
component="label"
|
|
221
|
+
startIcon={<ComputerIcon />}
|
|
222
|
+
fullWidth
|
|
223
|
+
>
|
|
224
|
+
{labels.upload}
|
|
225
|
+
<input
|
|
226
|
+
id="fileInput"
|
|
227
|
+
type="file"
|
|
228
|
+
accept="image/png, image/jpeg"
|
|
229
|
+
multiple={false}
|
|
230
|
+
hidden
|
|
231
|
+
onChange={handleFileChange}
|
|
232
|
+
/>
|
|
233
|
+
</Button>
|
|
234
|
+
<Stack direction="row" spacing={0.5}>
|
|
235
|
+
<AvatarEditor
|
|
236
|
+
ref={ref}
|
|
237
|
+
border={border}
|
|
238
|
+
width={width}
|
|
239
|
+
height={height}
|
|
240
|
+
onLoadSuccess={handleLoad}
|
|
241
|
+
image={previewImage ?? ''}
|
|
242
|
+
scale={editorState.scale}
|
|
243
|
+
rotate={editorState.rotate}
|
|
244
|
+
/>
|
|
245
|
+
<ButtonGroup
|
|
246
|
+
size="small"
|
|
247
|
+
orientation="vertical"
|
|
248
|
+
disabled={!ready}
|
|
249
|
+
>
|
|
250
|
+
<Button
|
|
251
|
+
onClick={() => handleRotate(90)}
|
|
252
|
+
title={labels.rotateRight}
|
|
253
|
+
>
|
|
254
|
+
<RotateRightIcon />
|
|
255
|
+
</Button>
|
|
256
|
+
<Button
|
|
257
|
+
onClick={() => handleRotate(-90)}
|
|
258
|
+
title={labels.rotateLeft}
|
|
259
|
+
>
|
|
260
|
+
<RotateLeftIcon />
|
|
261
|
+
</Button>
|
|
262
|
+
<Button onClick={handleReset} title={labels.reset}>
|
|
263
|
+
<ClearAllIcon />
|
|
264
|
+
</Button>
|
|
265
|
+
</ButtonGroup>
|
|
266
|
+
</Stack>
|
|
267
|
+
<Slider
|
|
268
|
+
title={labels.zoom}
|
|
269
|
+
disabled={!ready}
|
|
270
|
+
min={1}
|
|
271
|
+
max={5}
|
|
272
|
+
step={0.01}
|
|
273
|
+
value={editorState.scale}
|
|
274
|
+
onChange={handleZoom}
|
|
275
|
+
/>
|
|
276
|
+
<Button
|
|
277
|
+
ref={buttonRef}
|
|
278
|
+
variant="contained"
|
|
279
|
+
startIcon={<DoneIcon />}
|
|
280
|
+
disabled={!ready}
|
|
281
|
+
onClick={handleDone}
|
|
282
|
+
>
|
|
283
|
+
{labels.done}
|
|
284
|
+
</Button>
|
|
285
|
+
</Stack>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IActionResult,
|
|
3
|
+
IApiPayload,
|
|
4
|
+
IAppSettings,
|
|
5
|
+
IUser,
|
|
6
|
+
RefreshTokenProps,
|
|
7
|
+
RefreshTokenResult
|
|
8
|
+
} from '@etsoo/appscript';
|
|
9
|
+
import { CoreConstants, IPageData, RefreshTokenRQ } from '@etsoo/react';
|
|
10
|
+
import { ReactApp } from './ReactApp';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Common independent application
|
|
14
|
+
* 通用独立程序
|
|
15
|
+
*/
|
|
16
|
+
export abstract class CommonApp<
|
|
17
|
+
U extends IUser = IUser,
|
|
18
|
+
P extends IPageData = IPageData,
|
|
19
|
+
S extends IAppSettings = IAppSettings
|
|
20
|
+
> extends ReactApp<S, U, P> {
|
|
21
|
+
/**
|
|
22
|
+
* Override persistedFields
|
|
23
|
+
*/
|
|
24
|
+
protected override get persistedFields() {
|
|
25
|
+
return [...super.persistedFields, CoreConstants.FieldUserIdSaved];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Init call update fields in local storage
|
|
30
|
+
* @returns Fields
|
|
31
|
+
*/
|
|
32
|
+
protected override initCallEncryptedUpdateFields(): string[] {
|
|
33
|
+
const fields = super.initCallEncryptedUpdateFields();
|
|
34
|
+
fields.push(CoreConstants.FieldUserIdSaved);
|
|
35
|
+
return fields;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Do user login
|
|
40
|
+
* @param data User data
|
|
41
|
+
* @param refreshToken Refresh token
|
|
42
|
+
* @param keep Keep login
|
|
43
|
+
* @returns Success data
|
|
44
|
+
*/
|
|
45
|
+
protected doUserLogin(
|
|
46
|
+
data: U,
|
|
47
|
+
refreshToken: string,
|
|
48
|
+
keep: boolean
|
|
49
|
+
): string | undefined {
|
|
50
|
+
this.userLogin(data, refreshToken, keep);
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Refresh token
|
|
56
|
+
* @param props Props
|
|
57
|
+
*/
|
|
58
|
+
override async refreshToken<D extends object = RefreshTokenRQ>(
|
|
59
|
+
props?: RefreshTokenProps<D>
|
|
60
|
+
) {
|
|
61
|
+
// Destruct
|
|
62
|
+
const {
|
|
63
|
+
callback,
|
|
64
|
+
data,
|
|
65
|
+
relogin = false,
|
|
66
|
+
showLoading = false
|
|
67
|
+
} = props ?? {};
|
|
68
|
+
|
|
69
|
+
// Token
|
|
70
|
+
const token = this.getCacheToken();
|
|
71
|
+
if (token == null || token === '') {
|
|
72
|
+
if (callback) callback(false);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Reqest data
|
|
77
|
+
const rq: RefreshTokenRQ = {
|
|
78
|
+
deviceId: this.deviceId,
|
|
79
|
+
timezone: this.getTimeZone(),
|
|
80
|
+
...data
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Login result type
|
|
84
|
+
type LoginResult = IActionResult<U>;
|
|
85
|
+
|
|
86
|
+
// Payload
|
|
87
|
+
const payload: IApiPayload<LoginResult, any> = {
|
|
88
|
+
// No loading bar needed to avoid screen flicks
|
|
89
|
+
showLoading,
|
|
90
|
+
config: { headers: { [CoreConstants.TokenHeaderRefresh]: token } },
|
|
91
|
+
onError: (error) => {
|
|
92
|
+
if (callback) callback(error);
|
|
93
|
+
|
|
94
|
+
// Prevent further processing
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Success callback
|
|
100
|
+
const success = (
|
|
101
|
+
result: LoginResult,
|
|
102
|
+
failCallback?: (
|
|
103
|
+
result: RefreshTokenResult,
|
|
104
|
+
serviceToken?: string
|
|
105
|
+
) => void
|
|
106
|
+
) => {
|
|
107
|
+
// Token
|
|
108
|
+
const refreshToken = this.getResponseToken(payload.response);
|
|
109
|
+
if (refreshToken == null || result.data == null) {
|
|
110
|
+
if (failCallback) failCallback(this.get('noData')!);
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Keep
|
|
115
|
+
const keep = this.storage.getData(
|
|
116
|
+
CoreConstants.FieldLoginKeep,
|
|
117
|
+
false
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// User login
|
|
121
|
+
var successData = this.doUserLogin(result.data, refreshToken, keep);
|
|
122
|
+
|
|
123
|
+
// Callback
|
|
124
|
+
if (failCallback) failCallback(true, successData);
|
|
125
|
+
|
|
126
|
+
return true;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Call API
|
|
130
|
+
const result = await this.api.put<LoginResult>(
|
|
131
|
+
'Auth/RefreshToken',
|
|
132
|
+
rq,
|
|
133
|
+
payload
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
if (result == null) return false;
|
|
137
|
+
|
|
138
|
+
if (!result.ok) {
|
|
139
|
+
if (result.type === 'TokenExpired' && relogin) {
|
|
140
|
+
// Try login
|
|
141
|
+
// Dialog to receive password
|
|
142
|
+
var labels = this.getLabels('reloginTip', 'login');
|
|
143
|
+
this.notifier.prompt(
|
|
144
|
+
labels.reloginTip,
|
|
145
|
+
async (pwd) => {
|
|
146
|
+
if (pwd == null) {
|
|
147
|
+
this.toLoginPage();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Set password for the action
|
|
152
|
+
rq.pwd = this.encrypt(this.hash(pwd));
|
|
153
|
+
|
|
154
|
+
// Submit again
|
|
155
|
+
const result = await this.api.put<LoginResult>(
|
|
156
|
+
'Auth/RefreshToken',
|
|
157
|
+
rq,
|
|
158
|
+
payload
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (result == null) return;
|
|
162
|
+
|
|
163
|
+
if (result.ok) {
|
|
164
|
+
success(
|
|
165
|
+
result,
|
|
166
|
+
(loginResult: RefreshTokenResult) => {
|
|
167
|
+
if (loginResult === true) {
|
|
168
|
+
if (callback) callback(true);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const message =
|
|
173
|
+
this.formatRefreshTokenResult(
|
|
174
|
+
loginResult
|
|
175
|
+
);
|
|
176
|
+
if (message) this.notifier.alert(message);
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Popup message
|
|
183
|
+
this.alertResult(result);
|
|
184
|
+
return false;
|
|
185
|
+
},
|
|
186
|
+
labels.login,
|
|
187
|
+
{ type: 'password' }
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// Fake truth to avoid reloading
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (callback) callback(result);
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return success(result, callback);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Try login
|
|
203
|
+
* @param data Additional data
|
|
204
|
+
* @param showLoading Show loading bar or not
|
|
205
|
+
* @returns Result
|
|
206
|
+
*/
|
|
207
|
+
override async tryLogin<D extends object = RefreshTokenRQ>(
|
|
208
|
+
data?: D,
|
|
209
|
+
showLoading?: boolean
|
|
210
|
+
) {
|
|
211
|
+
// Reset user state
|
|
212
|
+
const result = await super.tryLogin(data);
|
|
213
|
+
if (!result) return false;
|
|
214
|
+
|
|
215
|
+
// Refresh token
|
|
216
|
+
return await this.refreshToken({
|
|
217
|
+
callback: (result) => this.doRefreshTokenResult(result),
|
|
218
|
+
data,
|
|
219
|
+
showLoading,
|
|
220
|
+
relogin: true
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IAppSettings } from '@etsoo/appscript';
|
|
2
|
+
import { DataTypes } from '@etsoo/shared';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Service app settings interface
|
|
6
|
+
*/
|
|
7
|
+
export interface IServiceAppSettings<S extends DataTypes.IdType = number>
|
|
8
|
+
extends IAppSettings {
|
|
9
|
+
/**
|
|
10
|
+
* Service id
|
|
11
|
+
*/
|
|
12
|
+
readonly serviceId: S;
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { IActionResult, IUser } from '@etsoo/appscript';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Service user interface
|
|
5
|
+
*/
|
|
6
|
+
export interface IServiceUser extends IUser {
|
|
7
|
+
/**
|
|
8
|
+
* Service device id
|
|
9
|
+
*/
|
|
10
|
+
serviceDeviceId: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Service user login result
|
|
15
|
+
*/
|
|
16
|
+
export type ServiceLoginResult<U extends IServiceUser = IServiceUser> =
|
|
17
|
+
IActionResult<U>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { IActionResult, IUser } from '@etsoo/appscript';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SmartERP user interface
|
|
5
|
+
*/
|
|
6
|
+
export interface ISmartERPUser extends IUser {
|
|
7
|
+
/**
|
|
8
|
+
* Service refresh token
|
|
9
|
+
*/
|
|
10
|
+
readonly serviceToken?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* SmartERP user login result
|
|
15
|
+
*/
|
|
16
|
+
export type SmartERPLoginResult = IActionResult<ISmartERPUser>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { DataTypes, Utils } from '@etsoo/shared';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Labels
|
|
5
|
+
*/
|
|
6
|
+
export namespace Labels {
|
|
7
|
+
/**
|
|
8
|
+
* Common page labels
|
|
9
|
+
*/
|
|
10
|
+
export const CommonPage = {
|
|
11
|
+
add: 'Add',
|
|
12
|
+
back: 'Back',
|
|
13
|
+
delete: 'Delete',
|
|
14
|
+
more: 'More',
|
|
15
|
+
pullToRefresh: 'Pull down to refresh',
|
|
16
|
+
refresh: 'Refresh',
|
|
17
|
+
refreshing: 'Refreshing',
|
|
18
|
+
releaseToRefresh: 'Release to refresh',
|
|
19
|
+
reset: 'Reset',
|
|
20
|
+
save: 'Save',
|
|
21
|
+
scrollTop: 'Scroll to top',
|
|
22
|
+
submit: 'Submit'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Notification MU labels
|
|
27
|
+
*/
|
|
28
|
+
export const NotificationMU = {
|
|
29
|
+
alertTitle: 'Warning',
|
|
30
|
+
alertOK: 'OK',
|
|
31
|
+
confirmTitle: 'Confirm',
|
|
32
|
+
confirmYes: 'OK',
|
|
33
|
+
confirmNo: 'Cancel',
|
|
34
|
+
promptTitle: 'Input',
|
|
35
|
+
promptCancel: 'Cancel',
|
|
36
|
+
promptOK: 'OK',
|
|
37
|
+
loading: 'Loading',
|
|
38
|
+
success: 'Success',
|
|
39
|
+
warning: 'Warning',
|
|
40
|
+
info: 'Information'
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* UserAvatarEditor labels
|
|
45
|
+
*/
|
|
46
|
+
export const UserAvatarEditor = {
|
|
47
|
+
done: 'Done',
|
|
48
|
+
reset: 'Reset',
|
|
49
|
+
rotateLeft: 'Rotate left 90°',
|
|
50
|
+
rotateRight: 'Rotate right 90°',
|
|
51
|
+
upload: 'Upload',
|
|
52
|
+
zoom: 'Zoom'
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* setLabelReference key reference
|
|
57
|
+
*/
|
|
58
|
+
export interface setLabelsReference {
|
|
59
|
+
commonPage?: DataTypes.StringDictionary;
|
|
60
|
+
notificationMU?: DataTypes.StringDictionary;
|
|
61
|
+
userAvatarEditor?: DataTypes.StringDictionary;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Set components' labels
|
|
66
|
+
* @param labels Labels
|
|
67
|
+
* @param reference Key reference
|
|
68
|
+
*/
|
|
69
|
+
export const setLabels = (
|
|
70
|
+
labels: DataTypes.StringRecord,
|
|
71
|
+
reference: setLabelsReference = {}
|
|
72
|
+
) => {
|
|
73
|
+
Utils.setLabels(CommonPage, labels, reference.commonPage);
|
|
74
|
+
Utils.setLabels(NotificationMU, labels, reference.notificationMU);
|
|
75
|
+
Utils.setLabels(UserAvatarEditor, labels, reference.userAvatarEditor);
|
|
76
|
+
};
|
|
77
|
+
}
|