@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,396 @@
1
+ import { Button, Drawer, IconButton, Stack, useTheme } from '@mui/material';
2
+ import React from 'react';
3
+ import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
4
+ import { DomUtils } from '@etsoo/shared';
5
+ import { ReactUtils, useDelayedExecutor, useDimensions } from '@etsoo/react';
6
+ import { Labels } from './app/Labels';
7
+
8
+ /**
9
+ * Search bar props
10
+ */
11
+ export interface SearchBarProps {
12
+ /**
13
+ * Style class name
14
+ */
15
+ className?: string;
16
+
17
+ /**
18
+ * Fields
19
+ */
20
+ fields: React.ReactElement[];
21
+
22
+ /**
23
+ * Inner height
24
+ * @default 40
25
+ */
26
+ innerHeight?: number;
27
+
28
+ /**
29
+ * On submit callback
30
+ */
31
+ onSubmit: (data: FormData, reset: boolean) => void | PromiseLike<void>;
32
+ }
33
+
34
+ // Cached width attribute name
35
+ const cachedWidthName: string = 'data-cached-width';
36
+
37
+ // Reset form
38
+ const resetForm = (form: HTMLFormElement) => {
39
+ for (const input of form.elements) {
40
+ // Ignore disabled inputs
41
+ if ('disabled' in input && (input as any).disabled) continue;
42
+
43
+ // All non hidden inputs
44
+ if (input instanceof HTMLInputElement) {
45
+ // Ignore hidden input
46
+ if (input.type === 'hidden') continue;
47
+
48
+ // Ignore readOnly without data-reset=true inputs
49
+ if (!input.readOnly || input.dataset.reset === 'true') {
50
+ ReactUtils.triggerChange(input, '', true);
51
+ }
52
+ continue;
53
+ }
54
+
55
+ // All selects
56
+ if (input instanceof HTMLSelectElement) {
57
+ if (input.options.length > 0 && input.options[0].value === '') {
58
+ input.selectedIndex = 0;
59
+ } else {
60
+ input.selectedIndex = -1;
61
+ }
62
+ continue;
63
+ }
64
+ }
65
+
66
+ // Trigger reset event
67
+ const resetEvent = new Event('reset');
68
+ form.dispatchEvent(resetEvent);
69
+ };
70
+
71
+ // Disable inputs avoid auto trigger change events for them
72
+ const setChildState = (child: Element, enabled: boolean) => {
73
+ const inputs = child.getElementsByTagName('input');
74
+ for (const input of inputs) {
75
+ input.disabled = !enabled;
76
+ }
77
+ };
78
+
79
+ /**
80
+ * Search bar
81
+ * @param props Props
82
+ * @returns Component
83
+ */
84
+ export function SearchBar(props: SearchBarProps) {
85
+ // Destruct
86
+ const { className, fields, innerHeight = 40, onSubmit } = props;
87
+
88
+ // Labels
89
+ const labels = Labels.CommonPage;
90
+
91
+ // Spacing
92
+ const theme = useTheme();
93
+ const gap = parseFloat(theme.spacing(1));
94
+
95
+ // Menu index
96
+ const [index, updateIndex] = React.useState<number>();
97
+
98
+ // Drawer open / close
99
+ const [open, updateOpen] = React.useState(false);
100
+
101
+ // State
102
+ const state = React.useRef<{
103
+ form?: HTMLFormElement;
104
+ moreForm?: HTMLFormElement;
105
+ lastMaxWidth: number;
106
+ hasMore: boolean;
107
+ }>({ hasMore: true, lastMaxWidth: 9999 }).current;
108
+
109
+ // Watch container
110
+ const { dimensions } = useDimensions(
111
+ 1,
112
+ (target, rect) => {
113
+ // Same logic from resetButtonRef
114
+ if (
115
+ rect.width === state.lastMaxWidth ||
116
+ (!state.hasMore && rect.width > state.lastMaxWidth)
117
+ )
118
+ return false;
119
+
120
+ // Len
121
+ const len = target.children.length;
122
+ for (let i = 0; i < len; i++) {
123
+ var classList = target.children[i].classList;
124
+ classList.remove('showChild');
125
+ }
126
+ },
127
+ 0
128
+ );
129
+
130
+ // Show or hide element
131
+ const setElementVisible = (element: Element, visible: boolean) => {
132
+ element.classList.remove(visible ? 'hiddenChild' : 'showChild');
133
+ element.classList.add(visible ? 'showChild' : 'hiddenChild');
134
+ };
135
+
136
+ // Reset button ref
137
+ const resetButtonRef = (instance: HTMLButtonElement) => {
138
+ // Reset button
139
+ const resetButton = instance;
140
+ if (resetButton == null) return;
141
+
142
+ // First
143
+ const [_, container, containerRect] = dimensions[0];
144
+ if (
145
+ container == null ||
146
+ containerRect == null ||
147
+ containerRect.width < 10
148
+ )
149
+ return;
150
+
151
+ // Container width
152
+ let maxWidth = containerRect.width;
153
+ if (
154
+ maxWidth === state.lastMaxWidth ||
155
+ (!state.hasMore && maxWidth > state.lastMaxWidth)
156
+ ) {
157
+ return;
158
+ }
159
+ state.lastMaxWidth = maxWidth;
160
+
161
+ // More button
162
+ const buttonMore = resetButton.previousElementSibling!;
163
+
164
+ // Cached button width
165
+ const cachedButtonWidth = container.getAttribute(cachedWidthName);
166
+ if (cachedButtonWidth) {
167
+ maxWidth -= Number.parseFloat(cachedButtonWidth);
168
+ } else {
169
+ // Reset button rect
170
+ const resetButtonRect = resetButton.getBoundingClientRect();
171
+
172
+ // More button rect
173
+ const buttonMoreRect = buttonMore.getBoundingClientRect();
174
+
175
+ // Total
176
+ const totalButtonWidth =
177
+ resetButtonRect.width + buttonMoreRect.width + 3 * gap;
178
+
179
+ // Cache
180
+ container.setAttribute(
181
+ cachedWidthName,
182
+ totalButtonWidth.toString()
183
+ );
184
+
185
+ maxWidth -= totalButtonWidth;
186
+ }
187
+
188
+ // Children
189
+ const children = container.children;
190
+
191
+ // Len
192
+ const len = children.length;
193
+
194
+ // Other elements
195
+ const others = len - 2;
196
+ let hasMore = false;
197
+ let newIndex: number = others;
198
+ for (let c: number = 0; c < others; c++) {
199
+ const child = children[c];
200
+ const cachedWidth = child.getAttribute(cachedWidthName);
201
+ let childWidth: number;
202
+ if (cachedWidth) {
203
+ childWidth = Number.parseFloat(cachedWidth);
204
+ } else {
205
+ const childD = child.getBoundingClientRect();
206
+ childWidth = childD.width + gap;
207
+ child.setAttribute(cachedWidthName, childWidth.toString());
208
+ }
209
+
210
+ // No gap here, child width includes the gap
211
+ if (childWidth <= maxWidth) {
212
+ maxWidth -= childWidth;
213
+ setChildState(child, true);
214
+ setElementVisible(child, true);
215
+ } else {
216
+ setChildState(child, false);
217
+ setElementVisible(child, false);
218
+
219
+ if (!hasMore) {
220
+ // Make sure coming logic to the block
221
+ maxWidth = 0;
222
+
223
+ // Keep the current index
224
+ newIndex = c;
225
+
226
+ // Indicates more
227
+ hasMore = true;
228
+ }
229
+ }
230
+ }
231
+
232
+ // Show or hide more button
233
+ state.hasMore = hasMore;
234
+ setElementVisible(buttonMore, hasMore);
235
+ setElementVisible(resetButton, true);
236
+
237
+ // Update menu start index
238
+ updateIndex(newIndex);
239
+ };
240
+
241
+ // More items creator
242
+ const moreItems: React.ReactElement[] = [];
243
+ if (index != null) {
244
+ for (let i: number = index; i < fields.length; i++) {
245
+ moreItems.push(
246
+ <React.Fragment key={i}>{fields[i]}</React.Fragment>
247
+ );
248
+ }
249
+ }
250
+
251
+ // Handle main form
252
+ const handleForm = (event: React.FormEvent<HTMLFormElement>) => {
253
+ if (event.nativeEvent.cancelable && !event.nativeEvent.composed) return;
254
+
255
+ if (state.form == null) state.form = event.currentTarget;
256
+
257
+ delayed.call();
258
+ };
259
+
260
+ // Handle more button click
261
+ const handleMore = () => {
262
+ updateOpen(!open);
263
+ };
264
+
265
+ // More form change
266
+ const moreFormChange = (event: React.FormEvent<HTMLFormElement>) => {
267
+ if (event.nativeEvent.cancelable && !event.nativeEvent.composed) return;
268
+
269
+ if (state.moreForm == null) state.moreForm = event.currentTarget;
270
+
271
+ delayed.call();
272
+ };
273
+
274
+ // Submit at once
275
+ const handleSubmitInstant = (reset: boolean = false) => {
276
+ // Prepare data
277
+ const data = new FormData(state.form);
278
+ if (state.moreForm != null) {
279
+ DomUtils.mergeFormData(data, new FormData(state.moreForm));
280
+ }
281
+
282
+ onSubmit(data, reset);
283
+ };
284
+
285
+ const delayed = useDelayedExecutor(handleSubmitInstant, 480);
286
+
287
+ // Reset
288
+ const handleReset = () => {
289
+ // Clear form values
290
+ if (state.form != null) resetForm(state.form);
291
+ if (state.moreForm != null) resetForm(state.moreForm);
292
+
293
+ // Resubmit
294
+ handleSubmitInstant(true);
295
+ };
296
+
297
+ React.useEffect(() => {
298
+ // Delayed way
299
+ delayed.call(100);
300
+
301
+ return () => {
302
+ delayed.clear();
303
+ };
304
+ }, [className]);
305
+
306
+ // Layout
307
+ return (
308
+ <React.Fragment>
309
+ <form
310
+ id="SearchBarForm"
311
+ className={className}
312
+ onChange={handleForm}
313
+ ref={(form) => {
314
+ if (form) state.form = form;
315
+ }}
316
+ >
317
+ <Stack
318
+ ref={dimensions[0][0]}
319
+ justifyContent="center"
320
+ alignItems="center"
321
+ direction="row"
322
+ spacing={1}
323
+ height={innerHeight}
324
+ sx={{
325
+ '& > :not(style)': {
326
+ flexBasis: 'auto',
327
+ flexGrow: 0,
328
+ flexShrink: 0,
329
+ maxWidth: '180px',
330
+ visibility: 'hidden'
331
+ },
332
+ '& > .hiddenChild': {
333
+ display: 'none'
334
+ },
335
+ '& > .showChild': {
336
+ display: 'block',
337
+ visibility: 'visible'
338
+ }
339
+ }}
340
+ >
341
+ {fields.map((item, index) => (
342
+ <React.Fragment key={index}>{item}</React.Fragment>
343
+ ))}
344
+
345
+ <IconButton
346
+ aria-label="delete"
347
+ size="medium"
348
+ onClick={handleMore}
349
+ >
350
+ <MoreHorizIcon />
351
+ </IconButton>
352
+ <Button
353
+ variant="contained"
354
+ size="medium"
355
+ ref={resetButtonRef}
356
+ onClick={handleReset}
357
+ >
358
+ {labels.reset}
359
+ </Button>
360
+ </Stack>
361
+ </form>
362
+ {index != null && index < fields.length && (
363
+ <Drawer
364
+ anchor="right"
365
+ sx={{ minWidth: '250px' }}
366
+ ModalProps={{
367
+ keepMounted: true // Better open performance on mobile.
368
+ }}
369
+ open={open}
370
+ onClose={() => updateOpen(false)}
371
+ >
372
+ <form
373
+ onChange={moreFormChange}
374
+ ref={(form) => {
375
+ if (form) state.moreForm = form;
376
+ }}
377
+ >
378
+ <Stack
379
+ direction="column"
380
+ alignItems="stretch"
381
+ spacing={2}
382
+ padding={2}
383
+ sx={{
384
+ '& > :not(style)': {
385
+ minWidth: '100px'
386
+ }
387
+ }}
388
+ >
389
+ {moreItems}
390
+ </Stack>
391
+ </form>
392
+ </Drawer>
393
+ )}
394
+ </React.Fragment>
395
+ );
396
+ }
@@ -0,0 +1,82 @@
1
+ import { useDelayedExecutor } from '@etsoo/react';
2
+ import { TextField, TextFieldProps } from '@mui/material';
3
+ import React from 'react';
4
+ import { MUGlobal } from './MUGlobal';
5
+
6
+ /**
7
+ * Search field props
8
+ */
9
+ export type SearchFieldProps = TextFieldProps & {
10
+ /**
11
+ * Change delay (ms) to avoid repeatly dispatch onChange
12
+ */
13
+ changeDelay?: number;
14
+
15
+ /**
16
+ * Is the field read only?
17
+ */
18
+ readOnly?: boolean;
19
+ };
20
+
21
+ /**
22
+ * Search field
23
+ * @param props Props
24
+ * @returns Component
25
+ */
26
+ export function SearchField(props: SearchFieldProps) {
27
+ // Destruct
28
+ const {
29
+ changeDelay,
30
+ InputLabelProps = {},
31
+ InputProps = {},
32
+ onChange,
33
+ readOnly,
34
+ size = MUGlobal.searchFieldSize,
35
+ variant = MUGlobal.searchFieldVariant,
36
+ ...rest
37
+ } = props;
38
+
39
+ // Shrink
40
+ InputLabelProps.shrink = MUGlobal.searchFieldShrink;
41
+
42
+ // Read only
43
+ if (readOnly != null) InputProps.readOnly = readOnly;
44
+
45
+ const isMounted = React.useRef(true);
46
+ const delayed =
47
+ onChange != null && changeDelay != null && changeDelay >= 1
48
+ ? useDelayedExecutor(onChange, changeDelay)
49
+ : undefined;
50
+
51
+ const onChangeEx = (
52
+ event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
53
+ ) => {
54
+ if (onChange == null) return;
55
+
56
+ if (changeDelay == null || changeDelay < 1) {
57
+ onChange(event);
58
+ return;
59
+ }
60
+
61
+ delayed?.call(undefined, event);
62
+ };
63
+
64
+ React.useEffect(() => {
65
+ return () => {
66
+ isMounted.current = false;
67
+ delayed?.clear();
68
+ };
69
+ }, []);
70
+
71
+ // Layout
72
+ return (
73
+ <TextField
74
+ InputLabelProps={InputLabelProps}
75
+ InputProps={InputProps}
76
+ onChange={onChangeEx}
77
+ size={size}
78
+ variant={variant}
79
+ {...rest}
80
+ />
81
+ );
82
+ }
@@ -0,0 +1,31 @@
1
+ import {
2
+ DataTypes,
3
+ IdDefaultType,
4
+ LabelDefaultType,
5
+ ListType
6
+ } from '@etsoo/shared';
7
+ import React from 'react';
8
+ import { MUGlobal } from './MUGlobal';
9
+ import { OptionGroup, OptionGroupProps } from './OptionGroup';
10
+
11
+ /**
12
+ * Search OptionGroup
13
+ * @param props Props
14
+ * @returns Component
15
+ */
16
+ export function SearchOptionGroup<
17
+ T extends object = ListType,
18
+ D extends DataTypes.Keys<T> = IdDefaultType<T>,
19
+ L extends DataTypes.Keys<T, string> = LabelDefaultType<T>
20
+ >(props: OptionGroupProps<T, D, L>) {
21
+ // Destruct
22
+ const {
23
+ row = true,
24
+ size = MUGlobal.searchFieldSize,
25
+ sx = { '& .MuiFormLabel-root': { fontSize: '0.75em' } },
26
+ ...rest
27
+ } = props;
28
+
29
+ // Layout
30
+ return <OptionGroup<T, D, L> row={row} size={size} sx={sx} {...rest} />;
31
+ }
@@ -0,0 +1,33 @@
1
+ import { ListType1, Utils } from '@etsoo/shared';
2
+ import React from 'react';
3
+ import { globalApp } from './app/ReactApp';
4
+ import { SelectEx, SelectExProps } from './SelectEx';
5
+
6
+ /**
7
+ * SelectBool props
8
+ */
9
+ export type SelectBoolProps = Omit<
10
+ SelectExProps<ListType1>,
11
+ 'options' | 'loadData'
12
+ >;
13
+
14
+ /**
15
+ * SelectBool (yes/no)
16
+ * @param props Props
17
+ * @returns Component
18
+ */
19
+ export function SelectBool(props: SelectBoolProps) {
20
+ // Destruct
21
+ const { search = true, autoAddBlankItem = search, ...rest } = props;
22
+
23
+ // Options
24
+ const options: ListType1[] = [
25
+ { id: 'false', label: globalApp.get('no')! },
26
+ { id: 'true', label: globalApp.get('yes')! }
27
+ ];
28
+
29
+ if (autoAddBlankItem) Utils.addBlankItem(options);
30
+
31
+ // Layout
32
+ return <SelectEx<ListType1> options={options} search={search} {...rest} />;
33
+ }