@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.
Files changed (250) hide show
  1. package/.eslintignore +3 -0
  2. package/.eslintrc.json +38 -0
  3. package/.gitattributes +2 -0
  4. package/.github/workflows/main.yml +48 -0
  5. package/.prettierignore +5 -0
  6. package/.prettierrc +6 -0
  7. package/LICENSE +21 -0
  8. package/README.md +16 -0
  9. package/__tests__/ComboBox.tsx +30 -0
  10. package/__tests__/MUGlobalTests.tsx +58 -0
  11. package/__tests__/NotifierMUTests.tsx +217 -0
  12. package/__tests__/SelectEx.tsx +26 -0
  13. package/__tests__/tsconfig.json +19 -0
  14. package/babel.config.json +11 -0
  15. package/lib/AuditDisplay.d.ts +33 -0
  16. package/lib/AuditDisplay.js +52 -0
  17. package/lib/AutocompleteExtendedProps.d.ts +64 -0
  18. package/lib/AutocompleteExtendedProps.js +1 -0
  19. package/lib/BackButton.d.ts +13 -0
  20. package/lib/BackButton.js +33 -0
  21. package/lib/BridgeCloseButton.d.ts +23 -0
  22. package/lib/BridgeCloseButton.js +32 -0
  23. package/lib/ButtonLink.d.ts +17 -0
  24. package/lib/ButtonLink.js +19 -0
  25. package/lib/ComboBox.d.ts +38 -0
  26. package/lib/ComboBox.js +108 -0
  27. package/lib/CountdownButton.d.ts +23 -0
  28. package/lib/CountdownButton.js +81 -0
  29. package/lib/CustomFabProps.d.ts +27 -0
  30. package/lib/CustomFabProps.js +1 -0
  31. package/lib/DataGridEx.d.ts +94 -0
  32. package/lib/DataGridEx.js +329 -0
  33. package/lib/DataGridRenderers.d.ts +22 -0
  34. package/lib/DataGridRenderers.js +99 -0
  35. package/lib/DialogButton.d.ts +54 -0
  36. package/lib/DialogButton.js +45 -0
  37. package/lib/DnDList.d.ts +87 -0
  38. package/lib/DnDList.js +153 -0
  39. package/lib/DraggablePaperComponent.d.ts +8 -0
  40. package/lib/DraggablePaperComponent.js +12 -0
  41. package/lib/EmailInput.d.ts +11 -0
  42. package/lib/EmailInput.js +15 -0
  43. package/lib/FabBox.d.ts +21 -0
  44. package/lib/FabBox.js +31 -0
  45. package/lib/FlexBox.d.ts +14 -0
  46. package/lib/FlexBox.js +18 -0
  47. package/lib/GridDataFormat.d.ts +10 -0
  48. package/lib/GridDataFormat.js +43 -0
  49. package/lib/IconButtonLink.d.ts +17 -0
  50. package/lib/IconButtonLink.js +16 -0
  51. package/lib/InputField.d.ts +21 -0
  52. package/lib/InputField.js +39 -0
  53. package/lib/ItemList.d.ts +56 -0
  54. package/lib/ItemList.js +69 -0
  55. package/lib/ListItemRightIcon.d.ts +4 -0
  56. package/lib/ListItemRightIcon.js +8 -0
  57. package/lib/ListMoreDisplay.d.ts +35 -0
  58. package/lib/ListMoreDisplay.js +99 -0
  59. package/lib/LoadingButton.d.ts +16 -0
  60. package/lib/LoadingButton.js +41 -0
  61. package/lib/MUGlobal.d.ts +102 -0
  62. package/lib/MUGlobal.js +184 -0
  63. package/lib/MaskInput.d.ts +34 -0
  64. package/lib/MaskInput.js +43 -0
  65. package/lib/MobileListItemRenderer.d.ts +17 -0
  66. package/lib/MobileListItemRenderer.js +35 -0
  67. package/lib/MoreFab.d.ts +45 -0
  68. package/lib/MoreFab.js +95 -0
  69. package/lib/NotifierMU.d.ts +47 -0
  70. package/lib/NotifierMU.js +387 -0
  71. package/lib/NotifierPromptProps.d.ts +22 -0
  72. package/lib/NotifierPromptProps.js +1 -0
  73. package/lib/OptionGroup.d.ts +58 -0
  74. package/lib/OptionGroup.js +81 -0
  75. package/lib/PList.d.ts +15 -0
  76. package/lib/PList.js +12 -0
  77. package/lib/ProgressCount.d.ts +44 -0
  78. package/lib/ProgressCount.js +79 -0
  79. package/lib/PullToRefreshUI.d.ts +9 -0
  80. package/lib/PullToRefreshUI.js +18 -0
  81. package/lib/RLink.d.ts +14 -0
  82. package/lib/RLink.js +37 -0
  83. package/lib/ResponsibleContainer.d.ts +87 -0
  84. package/lib/ResponsibleContainer.js +156 -0
  85. package/lib/ScrollTopFab.d.ts +7 -0
  86. package/lib/ScrollTopFab.js +25 -0
  87. package/lib/ScrollerListEx.d.ts +81 -0
  88. package/lib/ScrollerListEx.js +167 -0
  89. package/lib/SearchBar.d.ts +29 -0
  90. package/lib/SearchBar.js +260 -0
  91. package/lib/SearchField.d.ts +21 -0
  92. package/lib/SearchField.js +39 -0
  93. package/lib/SearchOptionGroup.d.ts +9 -0
  94. package/lib/SearchOptionGroup.js +14 -0
  95. package/lib/SelectBool.d.ts +13 -0
  96. package/lib/SelectBool.js +22 -0
  97. package/lib/SelectEx.d.ts +50 -0
  98. package/lib/SelectEx.js +156 -0
  99. package/lib/ShowDataComparison.d.ts +20 -0
  100. package/lib/ShowDataComparison.js +58 -0
  101. package/lib/Switch.d.ts +29 -0
  102. package/lib/Switch.js +34 -0
  103. package/lib/SwitchAnt.d.ts +25 -0
  104. package/lib/SwitchAnt.js +40 -0
  105. package/lib/TabBox.d.ts +54 -0
  106. package/lib/TabBox.js +31 -0
  107. package/lib/TableEx.d.ts +65 -0
  108. package/lib/TableEx.js +270 -0
  109. package/lib/TextFieldEx.d.ts +101 -0
  110. package/lib/TextFieldEx.js +126 -0
  111. package/lib/Tiplist.d.ts +18 -0
  112. package/lib/Tiplist.js +157 -0
  113. package/lib/TooltipClick.d.ts +15 -0
  114. package/lib/TooltipClick.js +40 -0
  115. package/lib/UserAvatar.d.ts +24 -0
  116. package/lib/UserAvatar.js +25 -0
  117. package/lib/UserAvatarEditor.d.ts +53 -0
  118. package/lib/UserAvatarEditor.js +129 -0
  119. package/lib/app/CommonApp.d.ts +38 -0
  120. package/lib/app/CommonApp.js +149 -0
  121. package/lib/app/IServiceAppSettings.d.ts +11 -0
  122. package/lib/app/IServiceAppSettings.js +1 -0
  123. package/lib/app/IServicePage.d.ts +6 -0
  124. package/lib/app/IServicePage.js +1 -0
  125. package/lib/app/IServiceUser.d.ts +14 -0
  126. package/lib/app/IServiceUser.js +1 -0
  127. package/lib/app/ISmartERPUser.d.ts +14 -0
  128. package/lib/app/ISmartERPUser.js +1 -0
  129. package/lib/app/Labels.d.ts +65 -0
  130. package/lib/app/Labels.js +62 -0
  131. package/lib/app/ReactApp.d.ts +195 -0
  132. package/lib/app/ReactApp.js +296 -0
  133. package/lib/app/ServiceApp.d.ts +78 -0
  134. package/lib/app/ServiceApp.js +244 -0
  135. package/lib/index.d.ts +74 -0
  136. package/lib/index.js +74 -0
  137. package/lib/pages/CommonPage.d.ts +11 -0
  138. package/lib/pages/CommonPage.js +60 -0
  139. package/lib/pages/CommonPageProps.d.ts +59 -0
  140. package/lib/pages/CommonPageProps.js +1 -0
  141. package/lib/pages/DataGridPage.d.ts +9 -0
  142. package/lib/pages/DataGridPage.js +79 -0
  143. package/lib/pages/DataGridPageProps.d.ts +17 -0
  144. package/lib/pages/DataGridPageProps.js +1 -0
  145. package/lib/pages/EditPage.d.ts +33 -0
  146. package/lib/pages/EditPage.js +29 -0
  147. package/lib/pages/FixedListPage.d.ts +15 -0
  148. package/lib/pages/FixedListPage.js +70 -0
  149. package/lib/pages/ListPage.d.ts +9 -0
  150. package/lib/pages/ListPage.js +50 -0
  151. package/lib/pages/ListPageProps.d.ts +7 -0
  152. package/lib/pages/ListPageProps.js +1 -0
  153. package/lib/pages/ResponsivePage.d.ts +9 -0
  154. package/lib/pages/ResponsivePage.js +45 -0
  155. package/lib/pages/ResponsivePageProps.d.ts +39 -0
  156. package/lib/pages/ResponsivePageProps.js +1 -0
  157. package/lib/pages/SearchPageProps.d.ts +30 -0
  158. package/lib/pages/SearchPageProps.js +1 -0
  159. package/lib/pages/TablePage.d.ts +9 -0
  160. package/lib/pages/TablePage.js +69 -0
  161. package/lib/pages/TablePageProps.d.ts +7 -0
  162. package/lib/pages/TablePageProps.js +1 -0
  163. package/lib/pages/ViewPage.d.ts +66 -0
  164. package/lib/pages/ViewPage.js +105 -0
  165. package/lib/texts/DateText.d.ts +34 -0
  166. package/lib/texts/DateText.js +25 -0
  167. package/lib/texts/MoneyText.d.ts +21 -0
  168. package/lib/texts/MoneyText.js +14 -0
  169. package/lib/texts/NumberText.d.ts +25 -0
  170. package/lib/texts/NumberText.js +14 -0
  171. package/package.json +97 -0
  172. package/src/AuditDisplay.tsx +114 -0
  173. package/src/AutocompleteExtendedProps.ts +83 -0
  174. package/src/BackButton.tsx +55 -0
  175. package/src/BridgeCloseButton.tsx +69 -0
  176. package/src/ButtonLink.tsx +32 -0
  177. package/src/ComboBox.tsx +251 -0
  178. package/src/CountdownButton.tsx +119 -0
  179. package/src/CustomFabProps.ts +32 -0
  180. package/src/DataGridEx.tsx +713 -0
  181. package/src/DataGridRenderers.tsx +140 -0
  182. package/src/DialogButton.tsx +163 -0
  183. package/src/DnDList.tsx +344 -0
  184. package/src/DraggablePaperComponent.tsx +19 -0
  185. package/src/EmailInput.tsx +24 -0
  186. package/src/FabBox.tsx +51 -0
  187. package/src/FlexBox.tsx +20 -0
  188. package/src/GridDataFormat.tsx +77 -0
  189. package/src/IconButtonLink.tsx +29 -0
  190. package/src/InputField.tsx +82 -0
  191. package/src/ItemList.tsx +204 -0
  192. package/src/ListItemRightIcon.tsx +9 -0
  193. package/src/ListMoreDisplay.tsx +205 -0
  194. package/src/LoadingButton.tsx +75 -0
  195. package/src/MUGlobal.ts +220 -0
  196. package/src/MaskInput.tsx +107 -0
  197. package/src/MobileListItemRenderer.tsx +79 -0
  198. package/src/MoreFab.tsx +211 -0
  199. package/src/NotifierMU.tsx +654 -0
  200. package/src/NotifierPromptProps.ts +24 -0
  201. package/src/OptionGroup.tsx +223 -0
  202. package/src/PList.tsx +27 -0
  203. package/src/ProgressCount.tsx +166 -0
  204. package/src/PullToRefreshUI.tsx +21 -0
  205. package/src/RLink.tsx +64 -0
  206. package/src/ResponsibleContainer.tsx +394 -0
  207. package/src/ScrollTopFab.tsx +34 -0
  208. package/src/ScrollerListEx.tsx +387 -0
  209. package/src/SearchBar.tsx +396 -0
  210. package/src/SearchField.tsx +82 -0
  211. package/src/SearchOptionGroup.tsx +31 -0
  212. package/src/SelectBool.tsx +33 -0
  213. package/src/SelectEx.tsx +290 -0
  214. package/src/ShowDataComparison.tsx +106 -0
  215. package/src/Switch.tsx +94 -0
  216. package/src/SwitchAnt.tsx +95 -0
  217. package/src/TabBox.tsx +118 -0
  218. package/src/TableEx.tsx +558 -0
  219. package/src/TextFieldEx.tsx +249 -0
  220. package/src/Tiplist.tsx +303 -0
  221. package/src/TooltipClick.tsx +84 -0
  222. package/src/UserAvatar.tsx +64 -0
  223. package/src/UserAvatarEditor.tsx +287 -0
  224. package/src/app/CommonApp.ts +223 -0
  225. package/src/app/IServiceAppSettings.ts +13 -0
  226. package/src/app/IServicePage.ts +6 -0
  227. package/src/app/IServiceUser.ts +17 -0
  228. package/src/app/ISmartERPUser.ts +16 -0
  229. package/src/app/Labels.ts +77 -0
  230. package/src/app/ReactApp.ts +504 -0
  231. package/src/app/ServiceApp.ts +352 -0
  232. package/src/index.ts +77 -0
  233. package/src/pages/CommonPage.tsx +128 -0
  234. package/src/pages/CommonPageProps.ts +70 -0
  235. package/src/pages/DataGridPage.tsx +140 -0
  236. package/src/pages/DataGridPageProps.ts +24 -0
  237. package/src/pages/EditPage.tsx +114 -0
  238. package/src/pages/FixedListPage.tsx +141 -0
  239. package/src/pages/ListPage.tsx +90 -0
  240. package/src/pages/ListPageProps.ts +12 -0
  241. package/src/pages/ResponsivePage.tsx +68 -0
  242. package/src/pages/ResponsivePageProps.ts +57 -0
  243. package/src/pages/SearchPageProps.ts +39 -0
  244. package/src/pages/TablePage.tsx +126 -0
  245. package/src/pages/TablePageProps.ts +12 -0
  246. package/src/pages/ViewPage.tsx +282 -0
  247. package/src/texts/DateText.tsx +74 -0
  248. package/src/texts/MoneyText.tsx +49 -0
  249. package/src/texts/NumberText.tsx +40 -0
  250. package/tsconfig.json +19 -0
@@ -0,0 +1,290 @@
1
+ import {
2
+ Checkbox,
3
+ FormControl,
4
+ InputLabel,
5
+ ListItemText,
6
+ MenuItem,
7
+ OutlinedInput,
8
+ Select,
9
+ SelectChangeEvent,
10
+ SelectProps
11
+ } from '@mui/material';
12
+ import React from 'react';
13
+ import { MUGlobal } from './MUGlobal';
14
+ import { ListItemRightIcon } from './ListItemRightIcon';
15
+ import {
16
+ DataTypes,
17
+ IdDefaultType,
18
+ LabelDefaultType,
19
+ ListType,
20
+ Utils
21
+ } from '@etsoo/shared';
22
+ import { ReactUtils } from '@etsoo/react';
23
+
24
+ /**
25
+ * Extended select component props
26
+ */
27
+ export type SelectExProps<
28
+ T extends object,
29
+ D extends DataTypes.Keys<T> = IdDefaultType<T>,
30
+ L extends DataTypes.Keys<T, string> = LabelDefaultType<T>
31
+ > = Omit<SelectProps, 'labelId' | 'input' | 'native'> & {
32
+ /**
33
+ * Auto add blank item
34
+ */
35
+ autoAddBlankItem?: boolean;
36
+
37
+ /**
38
+ * Id field
39
+ */
40
+ idField?: D;
41
+
42
+ /**
43
+ * Item icon renderer
44
+ */
45
+ itemIconRenderer?: (id: T[D]) => React.ReactNode;
46
+
47
+ /**
48
+ * Label field
49
+ */
50
+ labelField?: L | ((option: T) => string);
51
+
52
+ /**
53
+ * Load data callback
54
+ */
55
+ loadData?: () => PromiseLike<T[] | null | undefined>;
56
+
57
+ /**
58
+ * Item click handler
59
+ */
60
+ onItemClick?: (event: React.MouseEvent, option: T) => void;
61
+
62
+ /**
63
+ * On load data handler
64
+ */
65
+ onLoadData?: (options: T[]) => void;
66
+
67
+ /**
68
+ * Array of options.
69
+ */
70
+ options?: ReadonlyArray<T>;
71
+
72
+ /**
73
+ * Is search case?
74
+ */
75
+ search?: boolean;
76
+ };
77
+
78
+ /**
79
+ * Extended select component
80
+ * @param props Props
81
+ * @returns Component
82
+ */
83
+ export function SelectEx<
84
+ T extends object = ListType,
85
+ D extends DataTypes.Keys<T> = IdDefaultType<T>,
86
+ L extends DataTypes.Keys<T, string> = LabelDefaultType<T>
87
+ >(props: SelectExProps<T, D, L>) {
88
+ // Destruct
89
+ const {
90
+ defaultValue,
91
+ idField = 'id' as D,
92
+ itemIconRenderer,
93
+ label,
94
+ labelField = 'label' as L,
95
+ loadData,
96
+ onItemClick,
97
+ onLoadData,
98
+ multiple = false,
99
+ name,
100
+ options = [],
101
+ search = false,
102
+ autoAddBlankItem = search,
103
+ value,
104
+ onChange,
105
+ fullWidth,
106
+ ...rest
107
+ } = props;
108
+
109
+ // Options state
110
+ const [localOptions, setOptions] = React.useState(options);
111
+ const isMounted = React.useRef(true);
112
+
113
+ // When options change
114
+ // [options] will cause infinite loop
115
+ const propertyWay = loadData == null;
116
+ React.useEffect(() => {
117
+ if (propertyWay && options != null) setOptions(options);
118
+ }, [JSON.stringify(options), propertyWay]);
119
+
120
+ // Local value
121
+ const valueSource = defaultValue ?? value ?? '';
122
+ let localValue: unknown | unknown[];
123
+ if (multiple) {
124
+ if (Array.isArray(valueSource)) localValue = valueSource;
125
+ else localValue = [valueSource];
126
+ } else {
127
+ localValue = valueSource;
128
+ }
129
+
130
+ // Value state
131
+ const [valueState, setValueState] = React.useState<unknown>();
132
+
133
+ React.useEffect(() => {
134
+ if (localValue != null) setValueState(localValue);
135
+ }, [localValue]);
136
+
137
+ // Label id
138
+ const labelId = `selectex-label-${name}`;
139
+
140
+ // Item checked or not
141
+ const itemChecked = (id: unknown) => {
142
+ if (Array.isArray(valueState)) return valueState.indexOf(id) !== -1;
143
+ return valueState === id;
144
+ };
145
+
146
+ // Change handler
147
+ const handleChange = (event: SelectChangeEvent<unknown>) => {
148
+ const value = event.target.value;
149
+ if (multiple && !Array.isArray(value)) setItemValue([value]);
150
+ else setItemValue(value);
151
+ };
152
+
153
+ // Set item
154
+ const setItemValue = (id: unknown) => {
155
+ if (id != valueState) {
156
+ setValueState(id);
157
+
158
+ const input = divRef.current?.querySelector('input');
159
+ if (input) {
160
+ // Different value, trigger change event
161
+ ReactUtils.triggerChange(input, id as string, false);
162
+ }
163
+ }
164
+ };
165
+
166
+ // Get option id
167
+ const getId = (option: T) => {
168
+ return option[idField] as unknown as React.Key;
169
+ };
170
+
171
+ // Get option label
172
+ const getLabel = (option: T) => {
173
+ return typeof labelField === 'function'
174
+ ? labelField(option)
175
+ : (option[labelField] as string);
176
+ };
177
+
178
+ // Refs
179
+ const divRef = React.useRef<HTMLDivElement>();
180
+
181
+ // When value change
182
+ React.useEffect(() => {
183
+ if (loadData) {
184
+ loadData().then((result) => {
185
+ if (result == null || !isMounted.current) return;
186
+ if (onLoadData) onLoadData(result);
187
+ if (autoAddBlankItem) {
188
+ Utils.addBlankItem(result, idField, labelField);
189
+ }
190
+ setOptions(result);
191
+ });
192
+ }
193
+ }, [localValue]);
194
+
195
+ // When layout ready
196
+ React.useEffect(() => {
197
+ const input = divRef.current?.querySelector('input');
198
+ const inputChange = (event: Event) => {
199
+ // Reset case
200
+ if (event.cancelable) setValueState(multiple ? [] : '');
201
+ };
202
+ input?.addEventListener('change', inputChange);
203
+
204
+ return () => {
205
+ isMounted.current = false;
206
+ input?.removeEventListener('change', inputChange);
207
+ };
208
+ }, []);
209
+
210
+ // Layout
211
+ return (
212
+ <FormControl
213
+ size={search ? MUGlobal.searchFieldSize : MUGlobal.inputFieldSize}
214
+ fullWidth={fullWidth}
215
+ >
216
+ <InputLabel
217
+ id={labelId}
218
+ shrink={
219
+ search
220
+ ? MUGlobal.searchFieldShrink
221
+ : MUGlobal.inputFieldShrink
222
+ }
223
+ >
224
+ {label}
225
+ </InputLabel>
226
+ <Select
227
+ ref={divRef}
228
+ value={
229
+ localOptions.some((option) => itemChecked(getId(option)))
230
+ ? valueState ?? ''
231
+ : ''
232
+ }
233
+ input={<OutlinedInput notched label={label} />}
234
+ labelId={labelId}
235
+ name={name}
236
+ multiple={multiple}
237
+ onChange={(event, child) => {
238
+ if (onChange) onChange(event, child);
239
+ if (multiple) handleChange(event);
240
+ }}
241
+ renderValue={(selected) => {
242
+ // The text shows up
243
+ return localOptions
244
+ .filter((option) => {
245
+ const id = getId(option);
246
+ return Array.isArray(selected)
247
+ ? selected.indexOf(id) !== -1
248
+ : selected === id;
249
+ })
250
+ .map((option) => getLabel(option))
251
+ .join(', ');
252
+ }}
253
+ sx={{ minWidth: '150px' }}
254
+ fullWidth={fullWidth}
255
+ {...rest}
256
+ >
257
+ {localOptions.map((option) => {
258
+ // Option id
259
+ const id = getId(option);
260
+
261
+ // Option label
262
+ const label = getLabel(option);
263
+
264
+ // Option
265
+ return (
266
+ <MenuItem
267
+ key={id}
268
+ value={id}
269
+ onClick={(event) => {
270
+ if (onItemClick) {
271
+ onItemClick(event, option);
272
+ if (event.defaultPrevented) return;
273
+ }
274
+ if (!multiple) setItemValue(id);
275
+ }}
276
+ >
277
+ {multiple && <Checkbox checked={itemChecked(id)} />}
278
+ <ListItemText primary={label} />
279
+ {itemIconRenderer && (
280
+ <ListItemRightIcon>
281
+ {itemIconRenderer(option[idField])}
282
+ </ListItemRightIcon>
283
+ )}
284
+ </MenuItem>
285
+ );
286
+ })}
287
+ </Select>
288
+ </FormControl>
289
+ );
290
+ }
@@ -0,0 +1,106 @@
1
+ import { IApp } from '@etsoo/appscript/lib/mjs/app/IApp';
2
+ import { NotificationMessageType } from '@etsoo/notificationbase';
3
+ import { Utils } from '@etsoo/shared';
4
+ import {
5
+ Table,
6
+ TableBody,
7
+ TableCell,
8
+ TableHead,
9
+ TableRow
10
+ } from '@mui/material';
11
+ import React from 'react';
12
+ import { globalApp } from './app/ReactApp';
13
+
14
+ /**
15
+ * Audit line update data model
16
+ */
17
+ export interface AuditLineUpdateData {
18
+ oldData: Record<string, unknown>;
19
+ newData: Record<string, unknown>;
20
+ }
21
+
22
+ /**
23
+ * Check obj is instance of AuditLineUpdateData
24
+ * @param obj Input
25
+ * @returns Result
26
+ */
27
+ export function IsAuditLineUpdateData(obj: any): obj is AuditLineUpdateData {
28
+ return (
29
+ typeof obj === 'object' &&
30
+ 'oldData' in obj &&
31
+ typeof obj.oldData === 'object' &&
32
+ 'newData' in obj &&
33
+ typeof obj.newData === 'object'
34
+ );
35
+ }
36
+
37
+ // Format value
38
+ const formatValue = (value: unknown, app: IApp) => {
39
+ if (value == null) return '';
40
+ if (value instanceof Date) return app.formatDate(value, 'ds');
41
+ return `${value}`;
42
+ };
43
+
44
+ /**
45
+ * Show data comparison
46
+ * @param data Data
47
+ * @param modelTitle Model window title
48
+ * @param getLabel Get label callback
49
+ */
50
+ export const ShowDataComparison = (
51
+ data: AuditLineUpdateData,
52
+ modelTitle?: string,
53
+ getLabel?: (field: string) => string
54
+ ) => {
55
+ modelTitle ??= globalApp.get<string>('dataComparison');
56
+ getLabel ??= (key) => {
57
+ return globalApp.get(Utils.formatInitial(key)) ?? key;
58
+ };
59
+
60
+ const keys = new Set([
61
+ ...Object.keys(data.oldData),
62
+ ...Object.keys(data.newData)
63
+ ]);
64
+
65
+ const rows = Array.from(keys).map((field) => ({
66
+ field,
67
+ oldValue: data.oldData[field],
68
+ newValue: data.newData[field]
69
+ }));
70
+
71
+ const inputs = (
72
+ <Table>
73
+ <TableHead>
74
+ <TableRow>
75
+ <TableCell width="18%">{getLabel('field')}</TableCell>
76
+ <TableCell width="41%" align="right">
77
+ {getLabel('oldValue')}
78
+ </TableCell>
79
+ <TableCell width="41%" align="right">
80
+ {getLabel('newValue')}
81
+ </TableCell>
82
+ </TableRow>
83
+ </TableHead>
84
+ <TableBody>
85
+ {rows.map((row) => (
86
+ <TableRow key={row.field}>
87
+ <TableCell>{getLabel!(row.field)}</TableCell>
88
+ <TableCell align="right">
89
+ {formatValue(row.oldValue, globalApp)}
90
+ </TableCell>
91
+ <TableCell align="right">
92
+ {formatValue(row.newValue, globalApp)}
93
+ </TableCell>
94
+ </TableRow>
95
+ ))}
96
+ </TableBody>
97
+ </Table>
98
+ );
99
+
100
+ globalApp.notifier.alert(
101
+ [undefined, modelTitle],
102
+ undefined,
103
+ NotificationMessageType.Info,
104
+ { fullScreen: globalApp.smDown, inputs }
105
+ );
106
+ };
package/src/Switch.tsx ADDED
@@ -0,0 +1,94 @@
1
+ import React from 'react';
2
+ import { FormControlLabel, FormControlLabelProps } from '@mui/material';
3
+ import MuiCheckbox from '@mui/material/Checkbox';
4
+ import MuiSwitch from '@mui/material/Switch';
5
+
6
+ /**
7
+ * Switch props
8
+ */
9
+ export interface SwitchProps extends Omit<FormControlLabelProps, 'control'> {
10
+ /**
11
+ * Value, default 'on'
12
+ */
13
+ value?: string;
14
+
15
+ /**
16
+ * Is the field read only?
17
+ */
18
+ readOnly?: boolean;
19
+
20
+ /**
21
+ * Size
22
+ */
23
+ size?: 'small' | 'medium';
24
+
25
+ /**
26
+ * Display as Checkbox
27
+ */
28
+ checkbox?: boolean;
29
+ }
30
+
31
+ /**
32
+ * Switch
33
+ * @param props Props
34
+ * @returns Component
35
+ */
36
+ export function Switch(props: SwitchProps) {
37
+ // Destruct
38
+ const {
39
+ checked,
40
+ defaultChecked,
41
+ defaultValue,
42
+ onChange,
43
+ readOnly,
44
+ size,
45
+ checkbox = false,
46
+ value = 'true',
47
+ ...rest
48
+ } = props;
49
+
50
+ // Checked state
51
+ const [controlChecked, setControlChecked] = React.useState(
52
+ checked ?? defaultChecked ?? defaultValue == value
53
+ );
54
+
55
+ React.useEffect(() => {
56
+ if (checked) setControlChecked(checked);
57
+ }, [checked]);
58
+
59
+ // Handle change
60
+ const handleChange = (
61
+ event: React.ChangeEvent<HTMLInputElement>,
62
+ checked: boolean
63
+ ) => {
64
+ if (onChange) onChange(event, checked);
65
+ setControlChecked(checked);
66
+ };
67
+
68
+ // Control
69
+ const control = checkbox ? (
70
+ <MuiCheckbox
71
+ readOnly={readOnly}
72
+ checked={controlChecked}
73
+ onChange={handleChange}
74
+ size={size}
75
+ value={value}
76
+ />
77
+ ) : (
78
+ <MuiSwitch
79
+ readOnly={readOnly}
80
+ checked={controlChecked}
81
+ onChange={handleChange}
82
+ size={size}
83
+ value={value}
84
+ />
85
+ );
86
+
87
+ // Default state
88
+ React.useEffect(() => {
89
+ setControlChecked(controlChecked);
90
+ }, [controlChecked]);
91
+
92
+ // Layout
93
+ return <FormControlLabel control={control} {...rest} />;
94
+ }
@@ -0,0 +1,95 @@
1
+ import { Stack, Typography } from '@mui/material';
2
+ import Switch, { SwitchProps } from '@mui/material/Switch';
3
+ import React from 'react';
4
+
5
+ /**
6
+ * Ant style switch props
7
+ */
8
+ export interface SwitchAntProps extends SwitchProps {
9
+ /**
10
+ *
11
+ */
12
+ activeColor?: string;
13
+
14
+ /**
15
+ * Start label
16
+ */
17
+ startLabel: string;
18
+
19
+ /**
20
+ * End label
21
+ */
22
+ endLabel: string;
23
+ }
24
+
25
+ /**
26
+ * Ant style switch
27
+ * @param props Props
28
+ * @returns Component
29
+ */
30
+ export function SwitchAnt(props: SwitchAntProps) {
31
+ // Destruct
32
+ const {
33
+ activeColor,
34
+ checked,
35
+ defaultChecked,
36
+ defaultValue,
37
+ endLabel,
38
+ startLabel,
39
+ onChange,
40
+ value = 'true',
41
+ ...rest
42
+ } = props;
43
+
44
+ // Checked state
45
+ const [controlChecked, setControlChecked] = React.useState(
46
+ checked ?? defaultChecked ?? defaultValue == value
47
+ );
48
+
49
+ React.useEffect(() => {
50
+ if (checked) setControlChecked(checked);
51
+ }, [checked]);
52
+
53
+ // On change
54
+ const onChangeLocal = (
55
+ event: React.ChangeEvent<HTMLInputElement>,
56
+ checked: boolean
57
+ ) => {
58
+ if (onChange) onChange(event, checked);
59
+ setControlChecked(checked);
60
+ };
61
+
62
+ // Layout
63
+ return (
64
+ <Stack direction="row" spacing={1} alignItems="center">
65
+ <Typography
66
+ onClick={() => setControlChecked(false)}
67
+ sx={{
68
+ cursor: 'pointer',
69
+ color: controlChecked
70
+ ? undefined
71
+ : (theme) => activeColor ?? theme.palette.warning.main
72
+ }}
73
+ >
74
+ {startLabel}
75
+ </Typography>
76
+ <Switch
77
+ checked={controlChecked}
78
+ value={value}
79
+ onChange={onChangeLocal}
80
+ {...rest}
81
+ />
82
+ <Typography
83
+ onClick={() => setControlChecked(true)}
84
+ sx={{
85
+ cursor: 'pointer',
86
+ color: controlChecked
87
+ ? (theme) => activeColor ?? theme.palette.warning.main
88
+ : undefined
89
+ }}
90
+ >
91
+ {endLabel}
92
+ </Typography>
93
+ </Stack>
94
+ );
95
+ }
package/src/TabBox.tsx ADDED
@@ -0,0 +1,118 @@
1
+ import { Utils } from '@etsoo/shared';
2
+ import { Box, BoxProps, Tab, TabProps, Tabs, TabsProps } from '@mui/material';
3
+ import React from 'react';
4
+ import { Link } from 'react-router-dom';
5
+
6
+ /**
7
+ * Tab with box panel props
8
+ */
9
+ export interface TabBoxPanel extends Omit<TabProps, 'value' | 'children'> {
10
+ /**
11
+ * Children
12
+ */
13
+ children?: ((visible: boolean) => React.ReactNode) | React.ReactNode;
14
+
15
+ /**
16
+ * Panel box props
17
+ */
18
+ panel?: Omit<BoxProps, 'hidden'>;
19
+
20
+ /**
21
+ * To URL
22
+ */
23
+ to?: string;
24
+ }
25
+
26
+ /**
27
+ * Tabs with box props
28
+ */
29
+ export interface TabBoxPros extends BoxProps {
30
+ /**
31
+ * Container props
32
+ */
33
+ container?: Omit<TabsProps, 'value'>;
34
+
35
+ /**
36
+ * Default selected index
37
+ */
38
+ defaultIndex?: number;
39
+
40
+ /**
41
+ * Current index
42
+ */
43
+ index?: number;
44
+
45
+ /**
46
+ * Add a hidden input and its name
47
+ */
48
+ inputName?: string;
49
+
50
+ /**
51
+ * Root props
52
+ */
53
+ root?: BoxProps;
54
+
55
+ /**
56
+ * Tabs
57
+ */
58
+ tabs: TabBoxPanel[];
59
+ }
60
+
61
+ /**
62
+ * Tabs with box
63
+ * @param props Props
64
+ * @returns Component
65
+ */
66
+ export function TabBox(props: TabBoxPros) {
67
+ // Destruct
68
+ const {
69
+ index,
70
+ inputName,
71
+ root,
72
+ container = {},
73
+ defaultIndex = 0,
74
+ tabs
75
+ } = props;
76
+ const { onChange, ...rest } = container;
77
+
78
+ // State
79
+ const [value, setValue] = React.useState(defaultIndex);
80
+
81
+ React.useEffect(() => {
82
+ if (index == null) return;
83
+ setValue(index);
84
+ }, [index]);
85
+
86
+ // Layout
87
+ return (
88
+ <React.Fragment>
89
+ {inputName && (
90
+ <input type="hidden" name={inputName} value={value} />
91
+ )}
92
+ <Box {...root}>
93
+ <Tabs
94
+ value={value}
95
+ onChange={(event, newValue) => {
96
+ setValue(newValue);
97
+ if (onChange) onChange(event, newValue);
98
+ }}
99
+ {...rest}
100
+ >
101
+ {tabs.map(({ children, panel, ...tabRest }, index) => (
102
+ <Tab
103
+ key={index}
104
+ value={index}
105
+ LinkComponent={tabRest.to ? Link : undefined}
106
+ {...tabRest}
107
+ />
108
+ ))}
109
+ </Tabs>
110
+ </Box>
111
+ {tabs.map(({ children, panel }, index) => (
112
+ <Box key={index} hidden={value !== index} {...panel}>
113
+ {Utils.getResult(children, value === index)}
114
+ </Box>
115
+ ))}
116
+ </React.Fragment>
117
+ );
118
+ }