@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,75 @@
1
+ import {
2
+ Button,
3
+ ButtonProps,
4
+ CircularProgress,
5
+ CircularProgressProps
6
+ } from '@mui/material';
7
+ import React from 'react';
8
+
9
+ /**
10
+ * Loading button props
11
+ */
12
+ export type LoadingButtonProps = ButtonProps & {
13
+ /**
14
+ * Loading icon props
15
+ */
16
+ loadingIconProps?: CircularProgressProps;
17
+ };
18
+
19
+ /**
20
+ * Loading button
21
+ * @param props Props
22
+ */
23
+ export function LoadingButton(props: LoadingButtonProps) {
24
+ // Destruct
25
+ const { endIcon, loadingIconProps = {}, onClick, ...rest } = props;
26
+
27
+ // Default size
28
+ loadingIconProps.size ??= 12;
29
+
30
+ // State
31
+ // https://stackoverflow.com/questions/55265255/react-usestate-hook-event-handler-using-initial-state
32
+ const [loading, setLoading] = React.useState(false);
33
+
34
+ // Icon
35
+ const localEndIcon = loading ? (
36
+ <CircularProgress {...loadingIconProps} />
37
+ ) : (
38
+ endIcon
39
+ );
40
+
41
+ // Check if the component is mounted
42
+ const isMounted = React.useRef(true);
43
+
44
+ React.useEffect(() => {
45
+ return () => {
46
+ isMounted.current = false;
47
+ };
48
+ }, []);
49
+
50
+ // Layout
51
+ return (
52
+ <Button
53
+ disabled={loading}
54
+ endIcon={localEndIcon}
55
+ onClick={async (event) => {
56
+ if (onClick) {
57
+ // Update state
58
+ setLoading(true);
59
+
60
+ // https://stackoverflow.com/questions/38508420/how-to-know-if-a-function-is-async
61
+ // const AsyncFunction = (async () => {}).constructor;
62
+ // onClick instanceof AsyncFunction
63
+ await onClick(event);
64
+
65
+ // Warning: Can't perform a React state update on an unmounted component
66
+ // It's necessary to check the component is mounted now
67
+ if (isMounted.current) {
68
+ setLoading(false);
69
+ }
70
+ }
71
+ }}
72
+ {...rest}
73
+ />
74
+ );
75
+ }
@@ -0,0 +1,220 @@
1
+ import { NumberUtils } from '@etsoo/shared';
2
+ import { Breakpoint, ListItemButtonProps, Theme } from '@mui/material';
3
+ import { RLink } from './RLink';
4
+
5
+ /**
6
+ * Mouse event handler with data
7
+ */
8
+ export type MouseEventWithDataHandler<T> = (
9
+ event: React.MouseEvent<HTMLDivElement>,
10
+ data: T
11
+ ) => void;
12
+
13
+ /**
14
+ * MUGlobal for global configurations
15
+ */
16
+ export class MUGlobal {
17
+ /**
18
+ * Search field shrink
19
+ */
20
+ static searchFieldShrink: boolean = true;
21
+
22
+ /**
23
+ * Search field size
24
+ */
25
+ static searchFieldSize: 'small' | 'medium' = 'small';
26
+
27
+ /**
28
+ * Search field variant
29
+ */
30
+ static searchFieldVariant: 'standard' | 'filled' | 'outlined' = 'outlined';
31
+
32
+ /**
33
+ * Input field shrink
34
+ */
35
+ static inputFieldShrink: boolean = true;
36
+
37
+ /**
38
+ * Input field size
39
+ */
40
+ static inputFieldSize: 'small' | 'medium' = 'medium';
41
+
42
+ /**
43
+ * Input field variant
44
+ */
45
+ static inputFieldVariant: 'standard' | 'filled' | 'outlined' = 'outlined';
46
+
47
+ /**
48
+ * TextField variant
49
+ */
50
+ static textFieldVariant: 'standard' | 'filled' | 'outlined' = 'filled';
51
+
52
+ /**
53
+ * Page default paddings
54
+ */
55
+ static pagePaddings = { xs: 2, sm: 3 };
56
+
57
+ /**
58
+ * Get menu item props
59
+ * @param path Current path
60
+ * @param href Item's href
61
+ * @returns Props
62
+ */
63
+ static getMenuItem(path: string, href: string) {
64
+ let selected = false;
65
+
66
+ if (path === href) {
67
+ // Exact match, most common case
68
+ selected = true;
69
+ } else if (href.endsWith('*')) {
70
+ href = href.slice(0, -1);
71
+ selected = path.startsWith(href);
72
+ } else if (href.endsWith('/all')) {
73
+ selected = path.startsWith(href.slice(0, -3));
74
+ }
75
+
76
+ return {
77
+ component: RLink,
78
+ selected,
79
+ href,
80
+ sx: {
81
+ ...(selected && {
82
+ '.MuiListItemIcon-root': {
83
+ color: (theme) => theme.palette.primary.main
84
+ }
85
+ })
86
+ }
87
+ } as ListItemButtonProps;
88
+ }
89
+
90
+ /**
91
+ * Update object number properties with half of it
92
+ * @param input Input object
93
+ * @returns Updated object
94
+ */
95
+ static half(input: object) {
96
+ const newObj = { ...input };
97
+ Object.entries(newObj).forEach(([key, value]) => {
98
+ if (typeof value === 'number') {
99
+ Reflect.set(newObj, key, value / 2.0);
100
+ }
101
+ });
102
+ return newObj;
103
+ }
104
+
105
+ /**
106
+ * Reverse object number properties, like 5 to -5
107
+ * @param input Input object
108
+ * @returns Updated object
109
+ */
110
+ static reverse(input: object) {
111
+ const newObj = { ...input };
112
+ Object.entries(newObj).forEach(([key, value]) => {
113
+ if (typeof value === 'number') {
114
+ Reflect.set(newObj, key, -value);
115
+ }
116
+ });
117
+ return newObj;
118
+ }
119
+
120
+ /**
121
+ * Update object number properties with adjustment
122
+ * @param input Input object
123
+ * @param adjust Adjust value or new size object
124
+ * @param field Specific field
125
+ * @returns Updated object
126
+ */
127
+ static increase(input: object, adjust: number | object, field?: string) {
128
+ const newObj = { ...input };
129
+ Object.entries(newObj).forEach(([key, value]) => {
130
+ if (typeof value === 'number') {
131
+ if (field == null || field === key) {
132
+ const adjustValue =
133
+ typeof adjust === 'number'
134
+ ? adjust
135
+ : Reflect.get(adjust, key);
136
+ if (adjustValue == null || typeof adjustValue !== 'number')
137
+ return;
138
+
139
+ Reflect.set(newObj, key, value + adjustValue);
140
+ }
141
+ }
142
+ });
143
+ return newObj;
144
+ }
145
+
146
+ /**
147
+ * Adjust size with theme update
148
+ * @param size Base size
149
+ * @param adjust Adjustment
150
+ * @param updateFunc Theme update function
151
+ * @returns Updated object
152
+ */
153
+ static adjustWithTheme(
154
+ size: number,
155
+ adjust: object,
156
+ updateFunc: (value: number) => string
157
+ ) {
158
+ const newObj = { ...adjust };
159
+ Object.entries(newObj).forEach(([key, value]) => {
160
+ if (typeof value === 'number') {
161
+ const newValue = NumberUtils.parseWithUnit(updateFunc(value));
162
+ if (newValue != null) {
163
+ Reflect.set(
164
+ newObj,
165
+ key,
166
+ `${size - newValue[0]}${newValue[1]}`
167
+ );
168
+ }
169
+ }
170
+ });
171
+ return newObj;
172
+ }
173
+
174
+ /**
175
+ * Break points defined
176
+ */
177
+ static breakpoints = ['xs', 'sm', 'md', 'lg', 'xl'] as const;
178
+
179
+ /**
180
+ * Get multple medias theme space
181
+ * Responsive values and Breakpoints as an object
182
+ * xs = theme.breakpoints.up('xs')
183
+ * https://mui.com/system/basics/
184
+ * @param spaces Spaces
185
+ * @param theme Theme
186
+ * @returns Result
187
+ */
188
+ static getSpace(spaces: object, theme: Theme) {
189
+ const start = this.breakpoints.length - 1;
190
+ for (let i = start; i >= 0; i--) {
191
+ const key = this.breakpoints[i];
192
+ const value = Reflect.get(spaces, key);
193
+ if (typeof value === 'number') {
194
+ const mediaRaw = theme.breakpoints.up(key as Breakpoint);
195
+ const mediaQuery = mediaRaw.substring(mediaRaw.indexOf('('));
196
+ if (window.matchMedia(mediaQuery).matches) {
197
+ return parseInt(theme.spacing(value), 10);
198
+ }
199
+ }
200
+ }
201
+
202
+ return 0;
203
+ }
204
+
205
+ /**
206
+ * Update object number properties with theme
207
+ * @param input Input object
208
+ * @param updateFunc Theme update function
209
+ * @returns Updated object
210
+ */
211
+ static updateWithTheme(input: {}, updateFunc: (value: number) => string) {
212
+ const newObj = { ...input };
213
+ Object.entries(newObj).forEach(([key, value]) => {
214
+ if (typeof value === 'number') {
215
+ Reflect.set(newObj, key, updateFunc(value));
216
+ }
217
+ });
218
+ return newObj;
219
+ }
220
+ }
@@ -0,0 +1,107 @@
1
+ import { TextField, TextFieldProps } from '@mui/material';
2
+ import React from 'react';
3
+ import { MUGlobal } from './MUGlobal';
4
+ import { useIMask } from 'react-imask';
5
+
6
+ /**
7
+ * Mask input props
8
+ */
9
+ export type MaskInputProps<T extends IMask.AnyMaskedOptions> =
10
+ TextFieldProps & {
11
+ /**
12
+ * Mask props
13
+ */
14
+ mask: T;
15
+
16
+ /**
17
+ * Accept hanlder
18
+ */
19
+ onAccept?: (
20
+ value: unknown,
21
+ maskRef: IMask.InputMask<T>,
22
+ e?: InputEvent
23
+ ) => void;
24
+
25
+ /**
26
+ * Complete handler
27
+ */
28
+ onComplete?: (
29
+ value: unknown,
30
+ maskRef: IMask.InputMask<T>,
31
+ e?: InputEvent
32
+ ) => void;
33
+
34
+ /**
35
+ * Is the field read only?
36
+ */
37
+ readOnly?: boolean;
38
+
39
+ /**
40
+ * Search case
41
+ */
42
+ search?: boolean;
43
+ };
44
+
45
+ /**
46
+ * Mask input
47
+ * https://imask.js.org/
48
+ * @param props Props
49
+ * @returns Component
50
+ */
51
+ export function MaskInput<
52
+ T extends IMask.AnyMaskedOptions = IMask.AnyMaskedOptions
53
+ >(props: MaskInputProps<T>) {
54
+ // Destruct
55
+ const {
56
+ defaultValue,
57
+ mask,
58
+ InputLabelProps = {},
59
+ InputProps = {},
60
+ onAccept,
61
+ onComplete,
62
+ readOnly,
63
+ search = false,
64
+ size = search ? MUGlobal.searchFieldSize : MUGlobal.inputFieldSize,
65
+ value,
66
+ variant = search
67
+ ? MUGlobal.searchFieldVariant
68
+ : MUGlobal.inputFieldVariant,
69
+ ...rest
70
+ } = props;
71
+
72
+ const { ref, maskRef } = useIMask(mask, {
73
+ onAccept: (value, maskRef, event) => {
74
+ if (onAccept) onAccept(value, maskRef, event);
75
+ },
76
+ onComplete: (value, maskRef, event) => {
77
+ if (onComplete) onComplete(value, maskRef, event);
78
+ }
79
+ });
80
+ const localValue = defaultValue ?? value;
81
+
82
+ // Shrink
83
+ InputLabelProps.shrink = search
84
+ ? MUGlobal.searchFieldShrink
85
+ : MUGlobal.inputFieldShrink;
86
+
87
+ // Read only
88
+ if (readOnly != null) InputProps.readOnly = readOnly;
89
+ InputProps.inputRef = ref;
90
+
91
+ React.useEffect(() => {
92
+ if (maskRef.current == null || localValue == null) return;
93
+ maskRef.current.value = String(localValue);
94
+ maskRef.current.updateValue();
95
+ }, [maskRef.current, localValue]);
96
+
97
+ // Layout
98
+ return (
99
+ <TextField
100
+ InputLabelProps={InputLabelProps}
101
+ InputProps={InputProps}
102
+ size={size}
103
+ variant={variant}
104
+ {...rest}
105
+ />
106
+ );
107
+ }
@@ -0,0 +1,79 @@
1
+ import { ListItemReact } from '@etsoo/react';
2
+ import { Card, CardContent, CardHeader, LinearProgress } from '@mui/material';
3
+ import React from 'react';
4
+ import { MoreFab } from './MoreFab';
5
+ import { ScrollerListExInnerItemRendererProps } from './ScrollerListEx';
6
+
7
+ /**
8
+ * Default mobile list item renderer
9
+ * @param param0 List renderer props
10
+ * @param margin Margin
11
+ * @param renderer Renderer for card content
12
+ * @returns Component
13
+ */
14
+ export function MobileListItemRenderer<T>(
15
+ { data, itemHeight, margins }: ScrollerListExInnerItemRendererProps<T>,
16
+ renderer: (
17
+ data: T
18
+ ) => [
19
+ string,
20
+ string | undefined,
21
+ React.ReactNode | (ListItemReact | boolean)[],
22
+ React.ReactNode,
23
+ React.ReactNode?
24
+ ]
25
+ ) {
26
+ // Loading
27
+ if (data == null) return <LinearProgress />;
28
+
29
+ // Elements
30
+ const [title, subheader, actions, children, cardActions] = renderer(data);
31
+
32
+ return (
33
+ <Card
34
+ sx={{
35
+ height: itemHeight,
36
+ ...margins
37
+ }}
38
+ >
39
+ <CardHeader
40
+ sx={{ paddingBottom: 0.5 }}
41
+ action={
42
+ Array.isArray(actions) ? (
43
+ <MoreFab
44
+ iconButton
45
+ size="small"
46
+ anchorOrigin={{
47
+ vertical: 'bottom',
48
+ horizontal: 'right'
49
+ }}
50
+ transformOrigin={{
51
+ vertical: 'top',
52
+ horizontal: 'right'
53
+ }}
54
+ actions={actions}
55
+ />
56
+ ) : (
57
+ actions
58
+ )
59
+ }
60
+ title={title}
61
+ titleTypographyProps={{ variant: 'body2' }}
62
+ subheader={subheader}
63
+ subheaderTypographyProps={{ variant: 'caption' }}
64
+ />
65
+ <CardContent
66
+ sx={{
67
+ paddingTop: 0,
68
+ paddingBottom:
69
+ cardActions == null
70
+ ? Reflect.get(margins, 'marginBottom')
71
+ : 0
72
+ }}
73
+ >
74
+ {children}
75
+ </CardContent>
76
+ {cardActions}
77
+ </Card>
78
+ );
79
+ }
@@ -0,0 +1,211 @@
1
+ import { CustomFabProps } from './CustomFabProps';
2
+ import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
3
+ import React from 'react';
4
+ import {
5
+ Divider,
6
+ Fab,
7
+ IconButton,
8
+ ListItemIcon,
9
+ ListItemText,
10
+ Menu,
11
+ MenuItem,
12
+ PaperProps,
13
+ PopoverOrigin
14
+ } from '@mui/material';
15
+ import { Link } from 'react-router-dom';
16
+ import { ListItemReact } from '@etsoo/react';
17
+
18
+ /**
19
+ * More fab props
20
+ */
21
+ export interface MoreFabProps extends CustomFabProps {
22
+ /**
23
+ * Actions
24
+ */
25
+ actions?: (ListItemReact | boolean)[];
26
+
27
+ /**
28
+ * Dray arrow
29
+ */
30
+ drawArrow?: boolean;
31
+
32
+ /**
33
+ * Main icon
34
+ */
35
+ icon?: React.ReactNode;
36
+
37
+ /**
38
+ * Show as icon button
39
+ */
40
+ iconButton?: boolean;
41
+
42
+ /**
43
+ * This is the point on the anchor where the popover's
44
+ * `anchorEl` will attach to
45
+ */
46
+ anchorOrigin?: PopoverOrigin;
47
+
48
+ /**
49
+ * Props applied to the [`Paper`](/api/paper/) element.
50
+ * @default {}
51
+ */
52
+ PaperProps?: Partial<PaperProps>;
53
+
54
+ /**
55
+ * This is the point on the popover which
56
+ * will attach to the anchor's origin
57
+ */
58
+ transformOrigin?: PopoverOrigin;
59
+ }
60
+
61
+ function getActions(input: (ListItemReact | boolean)[]): ListItemReact[] {
62
+ // Actions
63
+ const actions: ListItemReact[] = [];
64
+ input.forEach((action) => {
65
+ if (typeof action === 'boolean') return;
66
+ actions.push(action);
67
+ });
68
+ return actions;
69
+ }
70
+
71
+ /**
72
+ * More fab
73
+ * @returns Component
74
+ */
75
+ export function MoreFab(props: MoreFabProps) {
76
+ // Destruct
77
+ const {
78
+ actions,
79
+ drawArrow = true,
80
+ anchorOrigin = {
81
+ vertical: 'top',
82
+ horizontal: 'right'
83
+ },
84
+ color = 'primary',
85
+ icon = <MoreHorizIcon />,
86
+ iconButton = false,
87
+ PaperProps = drawArrow
88
+ ? {
89
+ elevation: 0,
90
+ sx: {
91
+ overflow: 'visible',
92
+ filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))',
93
+ mt: -0.4,
94
+ '& .MuiAvatar-root': {
95
+ width: 32,
96
+ height: 32,
97
+ ml: -0.5,
98
+ mr: 1
99
+ },
100
+ '&:before': {
101
+ content: '""',
102
+ display: 'block',
103
+ position: 'absolute',
104
+ top: 0,
105
+ right: 14,
106
+ width: 10,
107
+ height: 10,
108
+ bgcolor: 'background.paper',
109
+ transform: 'translateY(-50%) rotate(45deg)',
110
+ zIndex: 0
111
+ }
112
+ }
113
+ }
114
+ : undefined,
115
+ size,
116
+ title,
117
+ transformOrigin = {
118
+ vertical: 'bottom',
119
+ horizontal: 'right'
120
+ }
121
+ } = props;
122
+
123
+ // State
124
+ const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement>();
125
+
126
+ // Open state
127
+ const open = Boolean(anchorEl);
128
+
129
+ // Handle click
130
+ const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
131
+ setAnchorEl(event.currentTarget);
132
+ };
133
+
134
+ // Handle close
135
+ const handleClose = () => {
136
+ setAnchorEl(undefined);
137
+ };
138
+
139
+ // No actions
140
+ if (actions == null || actions.length == 0) return <React.Fragment />;
141
+
142
+ // Actions
143
+ const actionsLocal = getActions(actions);
144
+
145
+ // Has any icon
146
+ const hasIcon = actionsLocal.some((action) => action.icon != null);
147
+
148
+ // Main
149
+ const main = iconButton ? (
150
+ <IconButton
151
+ color={color}
152
+ size={size}
153
+ title={title}
154
+ onClick={handleClick}
155
+ >
156
+ {icon}
157
+ </IconButton>
158
+ ) : (
159
+ <Fab color={color} size={size} title={title} onClick={handleClick}>
160
+ {icon}
161
+ </Fab>
162
+ );
163
+
164
+ return (
165
+ <React.Fragment>
166
+ {main}
167
+ <Menu
168
+ disableScrollLock={true}
169
+ anchorEl={anchorEl}
170
+ anchorOrigin={anchorOrigin}
171
+ keepMounted
172
+ transformOrigin={transformOrigin}
173
+ open={open}
174
+ onClose={handleClose}
175
+ PaperProps={PaperProps}
176
+ >
177
+ {actionsLocal.map(({ label, icon, action }, index) =>
178
+ label === '-' ? (
179
+ <Divider key={index} />
180
+ ) : (
181
+ <MenuItem
182
+ key={label}
183
+ {...(typeof action === 'string'
184
+ ? action.includes('://')
185
+ ? {
186
+ component: 'a',
187
+ href: action,
188
+ target: '_blank'
189
+ }
190
+ : { component: Link, to: action }
191
+ : {
192
+ onClick: () => {
193
+ handleClose();
194
+ if (typeof action === 'function')
195
+ action();
196
+ }
197
+ })}
198
+ >
199
+ {icon != null && (
200
+ <ListItemIcon>{icon}</ListItemIcon>
201
+ )}
202
+ <ListItemText inset={icon == null && hasIcon}>
203
+ {label}
204
+ </ListItemText>
205
+ </MenuItem>
206
+ )
207
+ )}
208
+ </Menu>
209
+ </React.Fragment>
210
+ );
211
+ }