@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,140 @@
1
+ import React from 'react';
2
+ import { CircularProgress } from '@mui/material';
3
+ import { DateUtils, NumberUtils } from '@etsoo/shared';
4
+ import CheckIcon from '@mui/icons-material/Check';
5
+ import ClearIcon from '@mui/icons-material/Clear';
6
+ import { DataGridExFooterItemRendererProps } from './DataGridEx';
7
+ import { DateText } from './texts/DateText';
8
+ import { GridCellRendererProps, GridDataType } from '@etsoo/react';
9
+
10
+ /**
11
+ * Data grid renderers
12
+ */
13
+ export namespace DataGridRenderers {
14
+ /**
15
+ * Default cell renderer
16
+ * @param param Props
17
+ * @returns Component
18
+ */
19
+ export function defaultCellRenderer<T extends Record<string, any>>({
20
+ cellProps,
21
+ data,
22
+ field,
23
+ formattedValue,
24
+ columnIndex,
25
+ type,
26
+ renderProps
27
+ }: GridCellRendererProps<T>): React.ReactNode {
28
+ // Is loading
29
+ if (data == null) {
30
+ // First column, show loading indicator
31
+ if (columnIndex === 0) return <CircularProgress size={15} />;
32
+
33
+ // Others return undefined
34
+ return undefined;
35
+ }
36
+
37
+ // No formatted value and data field
38
+ if (formattedValue == null && field == null) return undefined;
39
+
40
+ // Cell value
41
+ const value = formattedValue ?? data[field!];
42
+ if (value == null) return undefined;
43
+
44
+ // For date time
45
+ // Conversion if necessary
46
+ if (type === GridDataType.Date || type === GridDataType.DateTime) {
47
+ const dateValue = value instanceof Date ? value : new Date(value);
48
+
49
+ const option = type === GridDataType.DateTime ? 'ds' : 'd';
50
+
51
+ const nearDays = renderProps?.nearDays;
52
+ if (nearDays != null) {
53
+ return (
54
+ <DateText
55
+ value={dateValue}
56
+ locale={renderProps?.culture}
57
+ timeZone={renderProps?.timeZone}
58
+ options={option}
59
+ nearDays={nearDays}
60
+ />
61
+ );
62
+ }
63
+
64
+ return DateUtils.format(
65
+ dateValue,
66
+ renderProps?.culture,
67
+ option,
68
+ renderProps?.timeZone
69
+ );
70
+ }
71
+
72
+ // For numbers
73
+ if (typeof value === 'number') {
74
+ if (type === GridDataType.Money || type === GridDataType.IntMoney)
75
+ return NumberUtils.formatMoney(
76
+ value,
77
+ renderProps?.currency,
78
+ renderProps?.culture,
79
+ type === GridDataType.IntMoney,
80
+ renderProps?.numberFormatOptions
81
+ );
82
+ else
83
+ return NumberUtils.format(
84
+ value,
85
+ renderProps?.culture,
86
+ renderProps?.numberFormatOptions
87
+ );
88
+ }
89
+
90
+ // For boolean
91
+ if (typeof value === 'boolean') {
92
+ // Add style
93
+ if ('align' in cellProps) {
94
+ cellProps.sx = {
95
+ paddingTop: '12px!important',
96
+ paddingBottom: '6px!important'
97
+ };
98
+ } else {
99
+ cellProps.sx = {
100
+ paddingTop: '16px!important',
101
+ paddingBottom: '8px!important'
102
+ };
103
+ }
104
+
105
+ if (value) return <CheckIcon fontSize="small" />;
106
+ else return <ClearIcon fontSize="small" color="warning" />;
107
+ }
108
+
109
+ // To string
110
+ return new String(value);
111
+ }
112
+
113
+ /**
114
+ * Default footer item renderer
115
+ * @param rows Rows
116
+ * @param props Renderer props
117
+ * @param location Renderer location (column index)
118
+ * @returns Component
119
+ */
120
+ export function defaultFooterItemRenderer<T extends object>(
121
+ _rows: T[],
122
+ { index, states, checkable }: DataGridExFooterItemRendererProps<T>,
123
+ location: number = 0
124
+ ) {
125
+ const { selectedItems, loadedItems, hasNextPage } = states;
126
+
127
+ if (index === location) {
128
+ if (checkable) {
129
+ return [
130
+ selectedItems.length,
131
+ loadedItems.toLocaleString() + (hasNextPage ? '+' : '')
132
+ ].join(' / ');
133
+ } else {
134
+ return loadedItems.toLocaleString() + (hasNextPage ? '+' : '');
135
+ }
136
+ }
137
+
138
+ return undefined;
139
+ }
140
+ }
@@ -0,0 +1,163 @@
1
+ import {
2
+ Breakpoint,
3
+ Button,
4
+ ButtonProps,
5
+ Dialog,
6
+ DialogActions,
7
+ DialogContent,
8
+ DialogContentText,
9
+ DialogTitle,
10
+ IconButton
11
+ } from '@mui/material';
12
+ import React from 'react';
13
+ import { Labels } from './app/Labels';
14
+
15
+ export interface DialogButtonProps extends ButtonProps {
16
+ /**
17
+ * Button label
18
+ */
19
+ buttonLabel?: string;
20
+
21
+ /**
22
+ * Dialog content
23
+ */
24
+ content: string;
25
+
26
+ /**
27
+ * Show content in pre component
28
+ */
29
+ contentPre?: boolean;
30
+
31
+ /**
32
+ * Default is label
33
+ */
34
+ dialogTitle?: string;
35
+
36
+ /**
37
+ * Disable the scroll lock behavior.
38
+ * @default false
39
+ */
40
+ disableScrollLock?: boolean;
41
+
42
+ /**
43
+ * Show fullscreen dialog
44
+ */
45
+ fullScreen?: boolean;
46
+
47
+ /**
48
+ * If `true`, the dialog stretches to `maxWidth`.
49
+ *
50
+ * Notice that the dialog width grow is limited by the default margin.
51
+ * @default false
52
+ */
53
+ fullWidth?: boolean;
54
+
55
+ /**
56
+ * Other layouts
57
+ */
58
+ inputs?: React.ReactNode;
59
+
60
+ /**
61
+ * Max width of the dialog
62
+ */
63
+ maxWidth?: Breakpoint | false;
64
+
65
+ /**
66
+ * Icon button
67
+ */
68
+ icon?: React.ReactNode;
69
+ }
70
+
71
+ /**
72
+ * Dialog button
73
+ * @param props Props
74
+ * @returns Component
75
+ */
76
+ export function DialogButton(props: DialogButtonProps) {
77
+ // Labels shared with NotificationMU
78
+ const labels = Labels.NotificationMU;
79
+
80
+ // Destruct
81
+ const {
82
+ buttonLabel = labels.alertOK,
83
+ children,
84
+ content,
85
+ contentPre,
86
+ dialogTitle,
87
+ disableScrollLock,
88
+ fullScreen,
89
+ fullWidth,
90
+ icon,
91
+ inputs,
92
+ maxWidth,
93
+ onClick,
94
+ title,
95
+ ...rest
96
+ } = props;
97
+
98
+ // Open state
99
+ const [open, setOpen] = React.useState(false);
100
+
101
+ const handleClickOpen = () => {
102
+ setOpen(true);
103
+ };
104
+
105
+ // Onclick handler
106
+ const onClickLocal = (event: React.MouseEvent<HTMLButtonElement>) => {
107
+ // Stop propagation
108
+ event.stopPropagation();
109
+ event.preventDefault();
110
+
111
+ // Show dialog
112
+ handleClickOpen();
113
+
114
+ // Additional callback
115
+ if (onClick) onClick(event);
116
+ };
117
+
118
+ // Layout
119
+ return (
120
+ <React.Fragment>
121
+ {icon == null ? (
122
+ <Button {...rest} title={title} onClick={onClickLocal}>
123
+ {children}
124
+ </Button>
125
+ ) : (
126
+ <IconButton
127
+ {...rest}
128
+ onClick={onClickLocal}
129
+ title={title ?? children?.toString()}
130
+ >
131
+ {icon}
132
+ </IconButton>
133
+ )}
134
+
135
+ <Dialog
136
+ disableScrollLock={disableScrollLock}
137
+ fullScreen={fullScreen}
138
+ fullWidth={fullWidth}
139
+ maxWidth={maxWidth}
140
+ open={open}
141
+ onClose={() => setOpen(false)}
142
+ onClick={(event) => {
143
+ // The dialog will be embeded and the click event will bubble up
144
+ // Stop propatation but will also cancel onClose and onBackdropClick event
145
+ event.stopPropagation();
146
+ }}
147
+ >
148
+ <DialogTitle>{dialogTitle ?? title ?? children}</DialogTitle>
149
+ <DialogContent>
150
+ <DialogContentText component={contentPre ? 'pre' : 'span'}>
151
+ {content}
152
+ </DialogContentText>
153
+ {inputs}
154
+ </DialogContent>
155
+ <DialogActions>
156
+ <Button onClick={() => setOpen(false)}>
157
+ {buttonLabel}
158
+ </Button>
159
+ </DialogActions>
160
+ </Dialog>
161
+ </React.Fragment>
162
+ );
163
+ }
@@ -0,0 +1,344 @@
1
+ import {
2
+ DndContext,
3
+ DragEndEvent,
4
+ DragStartEvent,
5
+ UniqueIdentifier
6
+ } from '@dnd-kit/core';
7
+ import {
8
+ SortableContext,
9
+ useSortable,
10
+ verticalListSortingStrategy
11
+ } from '@dnd-kit/sortable';
12
+ import { CSS } from '@dnd-kit/utilities';
13
+ import { DataTypes } from '@etsoo/shared';
14
+ import { Theme, useTheme } from '@mui/material';
15
+ import React, { CSSProperties } from 'react';
16
+
17
+ function SortableItem(props: {
18
+ id: UniqueIdentifier;
19
+ itemRenderer: (
20
+ nodeRef: React.ComponentProps<any>,
21
+ actionNodeRef: React.ComponentProps<any>
22
+ ) => React.ReactElement;
23
+ style?: React.CSSProperties;
24
+ }) {
25
+ // Destruct
26
+ const { id, itemRenderer, style = {} } = props;
27
+
28
+ // Use sortable
29
+ const {
30
+ attributes,
31
+ listeners,
32
+ setNodeRef,
33
+ transform,
34
+ transition,
35
+ setActivatorNodeRef
36
+ } = useSortable({ id });
37
+
38
+ const allStyle = {
39
+ ...style,
40
+ transform: CSS.Transform.toString(transform),
41
+ transition
42
+ };
43
+
44
+ const nodeRef = {
45
+ style: allStyle,
46
+ ref: setNodeRef,
47
+ ...attributes
48
+ };
49
+
50
+ const actionNodeRef = {
51
+ ...listeners,
52
+ ref: setActivatorNodeRef
53
+ };
54
+
55
+ return itemRenderer(nodeRef, actionNodeRef);
56
+ }
57
+
58
+ /**
59
+ * DnD item default style
60
+ * @param index Item index
61
+ * @param isDragging Is dragging
62
+ * @param theme Theme
63
+ * @returns Style
64
+ */
65
+ export const DnDItemStyle = (
66
+ index: number,
67
+ isDragging: boolean,
68
+ theme: Theme
69
+ ) => ({
70
+ padding: theme.spacing(1),
71
+ zIndex: isDragging ? 1 : 'auto',
72
+ background: isDragging
73
+ ? theme.palette.primary.light
74
+ : index % 2 === 0
75
+ ? theme.palette.grey[100]
76
+ : theme.palette.grey[50]
77
+ });
78
+
79
+ /**
80
+ * DnD list forward ref
81
+ */
82
+ export interface DnDListRef<D extends object> {
83
+ /**
84
+ * Add item
85
+ * @param item New item
86
+ */
87
+ addItem(item: D): void;
88
+
89
+ /**
90
+ * Add items
91
+ * @param items items
92
+ */
93
+ addItems(items: D[]): void;
94
+
95
+ /**
96
+ * Delete item
97
+ * @param index Item index
98
+ */
99
+ deleteItem(index: number): void;
100
+
101
+ /**
102
+ * Edit item
103
+ * @param newItem New item
104
+ * @param index Index
105
+ */
106
+ editItem(newItem: D, index: number): boolean;
107
+ }
108
+
109
+ /**
110
+ * DnD sortable list properties
111
+ */
112
+ export interface DnDListPros<D extends object, K extends DataTypes.Keys<D>> {
113
+ /**
114
+ * Get list item style callback
115
+ */
116
+ getItemStyle?: (index: number, isDragging: boolean) => CSSProperties;
117
+
118
+ /**
119
+ * Item renderer
120
+ */
121
+ itemRenderer: (
122
+ item: D,
123
+ index: number,
124
+ nodeRef: React.ComponentProps<any>,
125
+ actionNodeRef: React.ComponentProps<any>
126
+ ) => React.ReactElement;
127
+
128
+ /**
129
+ * List items
130
+ */
131
+ items: D[];
132
+
133
+ /**
134
+ * Unique key field
135
+ */
136
+ keyField: K;
137
+
138
+ /**
139
+ * Label field
140
+ */
141
+ labelField: K;
142
+
143
+ /**
144
+ * Methods ref
145
+ */
146
+ mRef?: React.Ref<DnDListRef<D>>;
147
+
148
+ /**
149
+ * Data change handler
150
+ */
151
+ onChange?: (items: D[]) => void;
152
+
153
+ /**
154
+ * Drag end handler
155
+ */
156
+ onDragEnd?: (items: D[]) => void;
157
+ }
158
+
159
+ /**
160
+ * DnD (Drag and Drop) sortable list
161
+ * @param props Props
162
+ * @returns Component
163
+ */
164
+ export function DnDList<
165
+ D extends { id: UniqueIdentifier },
166
+ K extends DataTypes.Keys<D, UniqueIdentifier> = DataTypes.Keys<
167
+ D,
168
+ UniqueIdentifier
169
+ >
170
+ >(props: DnDListPros<D, K>) {
171
+ // Destruct
172
+ const { keyField, itemRenderer, labelField, mRef, onChange, onDragEnd } =
173
+ props;
174
+
175
+ let getItemStyle = props.getItemStyle;
176
+ if (getItemStyle == null) {
177
+ // Theme
178
+ const theme = useTheme();
179
+ getItemStyle = (index, isDragging) =>
180
+ DnDItemStyle(index, isDragging, theme);
181
+ }
182
+
183
+ // States
184
+ const [items, setItems] = React.useState<D[]>([]);
185
+ const [activeId, setActiveId] = React.useState<UniqueIdentifier>();
186
+
187
+ const changeItems = (newItems: D[]) => {
188
+ // Possible to alter items with the handler
189
+ if (onChange) onChange(newItems);
190
+
191
+ // Update state
192
+ setItems(newItems);
193
+ };
194
+
195
+ // Drag event handlers
196
+ function handleDragStart(event: DragStartEvent) {
197
+ const { active } = event;
198
+ setActiveId(active.id);
199
+ }
200
+
201
+ function handleDragEnd(event: DragEndEvent) {
202
+ const { active, over } = event;
203
+
204
+ if (over && active.id !== over.id) {
205
+ // Indices
206
+ const oldIndex = items.findIndex((item) => item.id === active.id);
207
+ const newIndex = items.findIndex((item) => item.id === over.id);
208
+
209
+ // Clone
210
+ const newItems = [...items];
211
+
212
+ // Removed item
213
+ const [removed] = newItems.splice(oldIndex, 1);
214
+
215
+ // Insert to the destination index
216
+ newItems.splice(newIndex, 0, removed);
217
+
218
+ changeItems(newItems);
219
+
220
+ // Drag end handler
221
+ if (onDragEnd) onDragEnd(newItems);
222
+ }
223
+
224
+ setActiveId(undefined);
225
+ }
226
+
227
+ // Methods
228
+ React.useImperativeHandle(
229
+ mRef,
230
+ () => {
231
+ return {
232
+ addItem(newItem: D) {
233
+ // Existence check
234
+ if (
235
+ items.some(
236
+ (item) => item[labelField] === newItem[labelField]
237
+ )
238
+ ) {
239
+ return false;
240
+ }
241
+
242
+ // Clone
243
+ const newItems = [newItem, ...items];
244
+
245
+ // Update the state
246
+ changeItems(newItems);
247
+
248
+ return true;
249
+ },
250
+
251
+ addItems(inputItems: D[]) {
252
+ // Clone
253
+ const newItems = [...items];
254
+
255
+ // Insert items
256
+ inputItems.forEach((newItem) => {
257
+ // Existence check
258
+ if (
259
+ newItems.some(
260
+ (item) =>
261
+ item[labelField] === newItem[labelField]
262
+ )
263
+ ) {
264
+ return;
265
+ }
266
+
267
+ newItems.push(newItem);
268
+ });
269
+
270
+ // Update the state
271
+ changeItems(newItems);
272
+
273
+ return newItems.length - items.length;
274
+ },
275
+
276
+ editItem(newItem: D, index: number) {
277
+ // Existence check
278
+ const newIndex = items.findIndex(
279
+ (item) => item[labelField] === newItem[labelField]
280
+ );
281
+ if (newIndex >= 0 && newIndex !== index) {
282
+ // Label field is the same with a different item
283
+ return false;
284
+ }
285
+
286
+ // Clone
287
+ const newItems = [...items];
288
+
289
+ // Remove the item
290
+ newItems.splice(index, 1, newItem);
291
+
292
+ // Update the state
293
+ changeItems(newItems);
294
+
295
+ return true;
296
+ },
297
+
298
+ deleteItem(index: number) {
299
+ // Clone
300
+ const newItems = [...items];
301
+
302
+ // Remove the item
303
+ newItems.splice(index, 1);
304
+
305
+ // Update the state
306
+ changeItems(newItems);
307
+ }
308
+ };
309
+ },
310
+ [items]
311
+ );
312
+
313
+ React.useEffect(() => {
314
+ setItems(props.items);
315
+ }, [props.items]);
316
+
317
+ return (
318
+ <DndContext onDragStart={handleDragStart} onDragEnd={handleDragEnd}>
319
+ <SortableContext
320
+ items={items}
321
+ strategy={verticalListSortingStrategy}
322
+ >
323
+ {items.map((item, index) => {
324
+ const id = item[keyField] as unknown as UniqueIdentifier;
325
+ return (
326
+ <SortableItem
327
+ id={id}
328
+ key={id}
329
+ style={getItemStyle!(index, id === activeId)}
330
+ itemRenderer={(nodeRef, actionNodeRef) =>
331
+ itemRenderer(
332
+ item,
333
+ index,
334
+ nodeRef,
335
+ actionNodeRef
336
+ )
337
+ }
338
+ />
339
+ );
340
+ })}
341
+ </SortableContext>
342
+ </DndContext>
343
+ );
344
+ }
@@ -0,0 +1,19 @@
1
+ import { Paper, PaperProps } from '@mui/material';
2
+ import React from 'react';
3
+ import Draggable from 'react-draggable';
4
+
5
+ /**
6
+ * Draggable paper component
7
+ * @param props Props
8
+ * @returns Component
9
+ */
10
+ export function DraggablePaperComponent(props: PaperProps) {
11
+ return (
12
+ <Draggable
13
+ handle=".draggable-dialog-title"
14
+ cancel={'[class*="MuiDialogContent-root"]'}
15
+ >
16
+ <Paper {...props} />
17
+ </Draggable>
18
+ );
19
+ }
@@ -0,0 +1,24 @@
1
+ import { TextField, TextFieldProps } from '@mui/material';
2
+ import React from 'react';
3
+
4
+ /**
5
+ * Email input props
6
+ */
7
+ export type EmailInputProps = Omit<TextFieldProps, 'type'> & {};
8
+
9
+ /**
10
+ * Email input
11
+ * @param props Props
12
+ */
13
+ export function EmailInput(props: EmailInputProps) {
14
+ // Destruct
15
+ const { inputProps = {}, ...rest } = props;
16
+
17
+ // Default max length
18
+ inputProps.maxLength ??= 128;
19
+
20
+ // Layout
21
+ return (
22
+ <TextField type="email" fullWidth inputProps={inputProps} {...rest} />
23
+ );
24
+ }