@etsoo/materialui 1.1.39 → 1.1.42

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 (42) hide show
  1. package/lib/AuditDisplay.d.ts +5 -5
  2. package/lib/AuditDisplay.js +17 -19
  3. package/lib/BridgeCloseButton.d.ts +2 -2
  4. package/lib/BridgeCloseButton.js +7 -8
  5. package/lib/DataSteps.d.ts +4 -4
  6. package/lib/DataSteps.js +17 -14
  7. package/lib/DataTable.d.ts +33 -0
  8. package/lib/DataTable.js +56 -0
  9. package/lib/ListMoreDisplay.d.ts +5 -5
  10. package/lib/ListMoreDisplay.js +8 -10
  11. package/lib/OptionBool.js +2 -1
  12. package/lib/SelectBool.js +2 -1
  13. package/lib/ShowDataComparison.d.ts +1 -1
  14. package/lib/ShowDataComparison.js +27 -20
  15. package/lib/SwitchAnt.d.ts +3 -3
  16. package/lib/SwitchAnt.js +11 -8
  17. package/lib/SwitchField.d.ts +45 -0
  18. package/lib/SwitchField.js +33 -0
  19. package/lib/Tiplist.js +4 -4
  20. package/lib/UserAvatar.js +7 -8
  21. package/lib/app/ReactApp.d.ts +7 -7
  22. package/lib/app/ReactApp.js +26 -26
  23. package/lib/index.d.ts +2 -0
  24. package/lib/index.js +2 -0
  25. package/lib/pages/ViewPage.d.ts +7 -7
  26. package/lib/pages/ViewPage.js +30 -29
  27. package/package.json +3 -2
  28. package/src/AuditDisplay.tsx +92 -99
  29. package/src/BridgeCloseButton.tsx +48 -50
  30. package/src/DataSteps.tsx +188 -185
  31. package/src/DataTable.tsx +124 -0
  32. package/src/ListMoreDisplay.tsx +184 -188
  33. package/src/OptionBool.tsx +1 -1
  34. package/src/SelectBool.tsx +1 -1
  35. package/src/ShowDataComparison.tsx +88 -76
  36. package/src/SwitchAnt.tsx +82 -78
  37. package/src/SwitchField.tsx +133 -0
  38. package/src/Tiplist.tsx +5 -4
  39. package/src/UserAvatar.tsx +43 -45
  40. package/src/app/ReactApp.ts +485 -489
  41. package/src/index.ts +2 -0
  42. package/src/pages/ViewPage.tsx +272 -277
@@ -1,23 +1,23 @@
1
- import { BridgeUtils, IBridgeHost } from '@etsoo/appscript';
2
- import CloseIcon from '@mui/icons-material/Close';
3
- import { Box, BoxProps, IconButton, IconButtonProps } from '@mui/material';
4
- import React from 'react';
5
- import { globalApp } from './app/ReactApp';
1
+ import { BridgeUtils, IBridgeHost } from "@etsoo/appscript";
2
+ import CloseIcon from "@mui/icons-material/Close";
3
+ import { Box, BoxProps, IconButton, IconButtonProps } from "@mui/material";
4
+ import React from "react";
5
+ import { globalApp } from "./app/ReactApp";
6
6
 
7
7
  /**
8
8
  * Bridge close button props
9
9
  */
10
10
  export interface BridgeCloseButtonProps extends IconButtonProps {
11
- /**
12
- * Box props
13
- */
14
- boxProps?: BoxProps;
11
+ /**
12
+ * Box props
13
+ */
14
+ boxProps?: BoxProps;
15
15
 
16
- /**
17
- * Validate the host
18
- * @param host Host
19
- */
20
- validate?(host: IBridgeHost): boolean;
16
+ /**
17
+ * Validate the host
18
+ * @param host Host
19
+ */
20
+ validate?(host: IBridgeHost): boolean;
21
21
  }
22
22
 
23
23
  /**
@@ -26,44 +26,42 @@ export interface BridgeCloseButtonProps extends IconButtonProps {
26
26
  * @returns Component
27
27
  */
28
28
  export function BridgeCloseButton(props: BridgeCloseButtonProps) {
29
- // Destruct
30
- const {
31
- boxProps,
32
- onClick,
33
- title = typeof globalApp === 'undefined'
34
- ? 'Close'
35
- : globalApp.get('close'),
36
- validate,
37
- ...rest
38
- } = props;
29
+ // Destruct
30
+ const {
31
+ boxProps,
32
+ onClick,
33
+ title = globalApp?.get("close") ?? "Close",
34
+ validate,
35
+ ...rest
36
+ } = props;
39
37
 
40
- // Host
41
- const host = BridgeUtils.host;
38
+ // Host
39
+ const host = BridgeUtils.host;
42
40
 
43
- if (
44
- host == null ||
45
- !host.closable() ||
46
- (validate && validate(host) === false)
47
- ) {
48
- return <React.Fragment />;
49
- }
41
+ if (
42
+ host == null ||
43
+ !host.closable() ||
44
+ (validate && validate(host) === false)
45
+ ) {
46
+ return <React.Fragment />;
47
+ }
50
48
 
51
- // Click handler
52
- const onClickLocal = (event: React.MouseEvent<HTMLButtonElement>) => {
53
- if (onClick) onClick(event);
54
- host.exit();
55
- };
49
+ // Click handler
50
+ const onClickLocal = (event: React.MouseEvent<HTMLButtonElement>) => {
51
+ if (onClick) onClick(event);
52
+ host.exit();
53
+ };
56
54
 
57
- return (
58
- <Box {...boxProps}>
59
- <IconButton
60
- aria-label="close"
61
- onClick={onClickLocal}
62
- title={title}
63
- {...rest}
64
- >
65
- <CloseIcon />
66
- </IconButton>
67
- </Box>
68
- );
55
+ return (
56
+ <Box {...boxProps}>
57
+ <IconButton
58
+ aria-label="close"
59
+ onClick={onClickLocal}
60
+ title={title}
61
+ {...rest}
62
+ >
63
+ <CloseIcon />
64
+ </IconButton>
65
+ </Box>
66
+ );
69
67
  }
package/src/DataSteps.tsx CHANGED
@@ -1,57 +1,57 @@
1
1
  import {
2
- Button,
3
- IconButton,
4
- InputAdornment,
5
- TextField,
6
- TextFieldProps
7
- } from '@mui/material';
8
- import CloseIcon from '@mui/icons-material/Close';
9
- import NavigateNextIcon from '@mui/icons-material/NavigateNext';
10
- import NavigateBeforeIcon from '@mui/icons-material/NavigateBefore';
11
- import CheckIcon from '@mui/icons-material/Check';
12
- import StartIcon from '@mui/icons-material/Start';
13
- import { InputDialogProps } from '@etsoo/react';
14
- import React from 'react';
15
- import { HBox } from './FlexBox';
16
- import { globalApp } from './app/ReactApp';
17
- import { MUGlobal } from './MUGlobal';
2
+ Button,
3
+ IconButton,
4
+ InputAdornment,
5
+ TextField,
6
+ TextFieldProps
7
+ } from "@mui/material";
8
+ import CloseIcon from "@mui/icons-material/Close";
9
+ import NavigateNextIcon from "@mui/icons-material/NavigateNext";
10
+ import NavigateBeforeIcon from "@mui/icons-material/NavigateBefore";
11
+ import CheckIcon from "@mui/icons-material/Check";
12
+ import StartIcon from "@mui/icons-material/Start";
13
+ import { InputDialogProps } from "@etsoo/react";
14
+ import React from "react";
15
+ import { HBox } from "./FlexBox";
16
+ import { globalApp } from "./app/ReactApp";
17
+ import { MUGlobal } from "./MUGlobal";
18
18
 
19
19
  /**
20
20
  * Data step
21
21
  */
22
- export type DataStep = Omit<InputDialogProps, 'callback'> & {
23
- /**
24
- * Callback
25
- */
26
- callback: (form: HTMLFormElement) => boolean | void;
22
+ export type DataStep = Omit<InputDialogProps, "callback"> & {
23
+ /**
24
+ * Callback
25
+ */
26
+ callback: (form: HTMLFormElement) => boolean | void;
27
27
  };
28
28
 
29
29
  /**
30
30
  * Data collecting steps component props
31
31
  */
32
32
  export type DataStepsProps<T extends object> = Omit<
33
- TextFieldProps,
34
- 'InputProps' | 'onClick'
33
+ TextFieldProps,
34
+ "InputProps" | "onClick"
35
35
  > & {
36
- /**
37
- * JSON value
38
- */
39
- jsonValue: T;
40
-
41
- /**
42
- * Value formatter
43
- */
44
- valueFormatter?: (data: T) => string;
45
-
46
- /**
47
- * Steps
48
- */
49
- steps: (index: number, data: T) => [DataStep, boolean];
50
-
51
- /**
52
- * On value change handler
53
- */
54
- onValueChange?: (value: T) => void;
36
+ /**
37
+ * JSON value
38
+ */
39
+ jsonValue: T;
40
+
41
+ /**
42
+ * Value formatter
43
+ */
44
+ valueFormatter?: (data: T) => string;
45
+
46
+ /**
47
+ * Steps
48
+ */
49
+ steps: (index: number, data: T) => [DataStep, boolean];
50
+
51
+ /**
52
+ * On value change handler
53
+ */
54
+ onValueChange?: (value: T) => void;
55
55
  };
56
56
 
57
57
  /**
@@ -60,147 +60,150 @@ export type DataStepsProps<T extends object> = Omit<
60
60
  * @returns Component
61
61
  */
62
62
  export function DataSteps<T extends object>(props: DataStepsProps<T>) {
63
- // App
64
- const app = globalApp;
65
-
66
- // Labels
67
- const labels = app.getLabels('close', 'nextStep', 'previousStep', 'submit');
68
-
69
- // Destruct
70
- const {
71
- InputLabelProps = {},
72
- jsonValue,
73
- valueFormatter = (_data) => '...',
74
- onValueChange,
75
- steps,
76
- value = '',
77
- ...rest
78
- } = props;
79
-
80
- // Shrink
81
- InputLabelProps.shrink ??= MUGlobal.searchFieldShrink;
82
-
83
- // Current index
84
- const indexRef = React.useRef<number>(-1);
85
-
86
- // Current Json data
87
- const jsonRef = React.useRef<T>(jsonValue);
88
-
89
- // Ignore empty value case
90
- if (jsonValue !== jsonRef.current && valueFormatter(jsonValue))
91
- jsonRef.current = jsonValue;
92
-
93
- // Current value
94
- const [localValue, setLocalValue] = React.useState(value);
95
-
96
- // Get step
97
- const showStep = (index: number) => {
98
- indexRef.current = index;
99
- const [{ callback, ...rest }, more] = steps(index, jsonRef.current);
100
-
101
- app.showInputDialog({
102
- ...rest,
103
- buttons: (n, callback) => (
104
- <HBox
105
- paddingLeft={2}
106
- paddingRight={2}
107
- paddingBottom={2}
108
- gap={2}
109
- justifyContent="space-between"
110
- >
111
- {index === 0 ? (
112
- <Button
113
- variant="outlined"
114
- startIcon={<CloseIcon />}
115
- onClick={() => n.dismiss()}
116
- >
117
- {labels.close}
118
- </Button>
119
- ) : (
120
- <Button
121
- variant="outlined"
122
- startIcon={<NavigateBeforeIcon />}
123
- onClick={() => {
124
- n.dismiss();
125
- showStep(indexRef.current - 1);
126
- }}
127
- >
128
- {labels.previousStep}
129
- </Button>
130
- )}
131
-
132
- {more ? (
133
- <Button
134
- variant="contained"
135
- startIcon={<NavigateNextIcon />}
136
- onClick={async (event) => {
137
- const result = await callback(event);
138
- if (!result) return;
139
- showStep(indexRef.current + 1);
140
- }}
141
- >
142
- {labels.nextStep}
143
- </Button>
144
- ) : (
145
- <Button
146
- variant="contained"
147
- startIcon={<CheckIcon />}
148
- onClick={async (event) => {
149
- const result = await callback(event);
150
- if (!result) return;
151
-
152
- const value = jsonRef.current;
153
- setLocalValue(valueFormatter(value));
154
-
155
- if (onValueChange) onValueChange(value);
156
- }}
157
- >
158
- {labels.submit}
159
- </Button>
160
- )}
161
- </HBox>
162
- ),
163
- callback: (form) => {
164
- if (form == null) return;
165
- const result = callback(form);
166
- if (result !== true) {
167
- return false;
168
- }
169
- }
170
- });
171
- };
172
-
173
- const cancelInput = (event: React.SyntheticEvent) => {
174
- event.stopPropagation();
175
- event.preventDefault();
176
- };
177
-
178
- React.useEffect(() => {
179
- setLocalValue(valueFormatter(jsonRef.current));
180
- }, [valueFormatter]);
181
-
182
- return (
183
- <TextField
184
- autoComplete="new-password"
185
- InputLabelProps={InputLabelProps}
186
- inputProps={{ style: { cursor: 'pointer' } }}
187
- InputProps={{
188
- onKeyDown: (event) => {
189
- if (event.key === 'Tab') return;
190
- cancelInput(event);
191
- },
192
- onPaste: cancelInput,
193
- endAdornment: (
194
- <InputAdornment position="end">
195
- <IconButton edge="end" size="small">
196
- <StartIcon />
197
- </IconButton>
198
- </InputAdornment>
199
- )
200
- }}
201
- onClick={() => showStep(0)}
202
- value={localValue}
203
- {...rest}
204
- />
205
- );
63
+ // App
64
+ const app = globalApp;
65
+ if (app == null) {
66
+ throw new Error("No globalApp");
67
+ }
68
+
69
+ // Labels
70
+ const labels = app.getLabels("close", "nextStep", "previousStep", "submit");
71
+
72
+ // Destruct
73
+ const {
74
+ InputLabelProps = {},
75
+ jsonValue,
76
+ valueFormatter = (_data) => "...",
77
+ onValueChange,
78
+ steps,
79
+ value = "",
80
+ ...rest
81
+ } = props;
82
+
83
+ // Shrink
84
+ InputLabelProps.shrink ??= MUGlobal.searchFieldShrink;
85
+
86
+ // Current index
87
+ const indexRef = React.useRef<number>(-1);
88
+
89
+ // Current Json data
90
+ const jsonRef = React.useRef<T>(jsonValue);
91
+
92
+ // Ignore empty value case
93
+ if (jsonValue !== jsonRef.current && valueFormatter(jsonValue))
94
+ jsonRef.current = jsonValue;
95
+
96
+ // Current value
97
+ const [localValue, setLocalValue] = React.useState(value);
98
+
99
+ // Get step
100
+ const showStep = (index: number) => {
101
+ indexRef.current = index;
102
+ const [{ callback, ...rest }, more] = steps(index, jsonRef.current);
103
+
104
+ app.showInputDialog({
105
+ ...rest,
106
+ buttons: (n, callback) => (
107
+ <HBox
108
+ paddingLeft={2}
109
+ paddingRight={2}
110
+ paddingBottom={2}
111
+ gap={2}
112
+ justifyContent="space-between"
113
+ >
114
+ {index === 0 ? (
115
+ <Button
116
+ variant="outlined"
117
+ startIcon={<CloseIcon />}
118
+ onClick={() => n.dismiss()}
119
+ >
120
+ {labels.close}
121
+ </Button>
122
+ ) : (
123
+ <Button
124
+ variant="outlined"
125
+ startIcon={<NavigateBeforeIcon />}
126
+ onClick={() => {
127
+ n.dismiss();
128
+ showStep(indexRef.current - 1);
129
+ }}
130
+ >
131
+ {labels.previousStep}
132
+ </Button>
133
+ )}
134
+
135
+ {more ? (
136
+ <Button
137
+ variant="contained"
138
+ startIcon={<NavigateNextIcon />}
139
+ onClick={async (event) => {
140
+ const result = await callback(event);
141
+ if (!result) return;
142
+ showStep(indexRef.current + 1);
143
+ }}
144
+ >
145
+ {labels.nextStep}
146
+ </Button>
147
+ ) : (
148
+ <Button
149
+ variant="contained"
150
+ startIcon={<CheckIcon />}
151
+ onClick={async (event) => {
152
+ const result = await callback(event);
153
+ if (!result) return;
154
+
155
+ const value = jsonRef.current;
156
+ setLocalValue(valueFormatter(value));
157
+
158
+ if (onValueChange) onValueChange(value);
159
+ }}
160
+ >
161
+ {labels.submit}
162
+ </Button>
163
+ )}
164
+ </HBox>
165
+ ),
166
+ callback: (form) => {
167
+ if (form == null) return;
168
+ const result = callback(form);
169
+ if (result !== true) {
170
+ return false;
171
+ }
172
+ }
173
+ });
174
+ };
175
+
176
+ const cancelInput = (event: React.SyntheticEvent) => {
177
+ event.stopPropagation();
178
+ event.preventDefault();
179
+ };
180
+
181
+ React.useEffect(() => {
182
+ setLocalValue(valueFormatter(jsonRef.current));
183
+ }, [valueFormatter]);
184
+
185
+ return (
186
+ <TextField
187
+ autoComplete="new-password"
188
+ InputLabelProps={InputLabelProps}
189
+ inputProps={{ style: { cursor: "pointer" } }}
190
+ InputProps={{
191
+ onKeyDown: (event) => {
192
+ if (event.key === "Tab") return;
193
+ cancelInput(event);
194
+ },
195
+ onPaste: cancelInput,
196
+ endAdornment: (
197
+ <InputAdornment position="end">
198
+ <IconButton edge="end" size="small">
199
+ <StartIcon />
200
+ </IconButton>
201
+ </InputAdornment>
202
+ )
203
+ }}
204
+ onClick={() => showStep(0)}
205
+ value={localValue}
206
+ {...rest}
207
+ />
208
+ );
206
209
  }
@@ -0,0 +1,124 @@
1
+ import {
2
+ DataGrid,
3
+ GridCellModesModel,
4
+ GridRowId,
5
+ GridValidRowModel
6
+ } from "@mui/x-data-grid";
7
+ import { DataGridProps } from "@mui/x-data-grid/models/props/DataGridProps";
8
+ import React from "react";
9
+ import { globalApp } from "./app/ReactApp";
10
+
11
+ /**
12
+ * Data table selected cell params
13
+ */
14
+ export interface DataTableSelectedCellParams {
15
+ id: GridRowId;
16
+ field: string;
17
+ index: number;
18
+ }
19
+
20
+ /**
21
+ * Data table props
22
+ */
23
+ export type DataTableProps<R extends GridValidRowModel = any> = Omit<
24
+ DataGridProps<R>,
25
+ "disableColumnMenu"
26
+ > & {
27
+ /**
28
+ * Cell selection handler
29
+ * @param params Params
30
+ * @returns Result
31
+ */
32
+ onCellSelection?: (params: DataTableSelectedCellParams) => void | false;
33
+
34
+ /**
35
+ * Toolbar creator
36
+ * @returns Toolbar
37
+ */
38
+ toolbarCreator?: (
39
+ selectedCellParams: DataTableSelectedCellParams | null,
40
+ setCellModesModel: React.Dispatch<React.SetStateAction<GridCellModesModel>>,
41
+ cellModesModel: GridCellModesModel
42
+ ) => React.ReactElement;
43
+ };
44
+
45
+ /**
46
+ * Data table
47
+ * @param props Props
48
+ * @returns Component
49
+ */
50
+ export function DataTable<R extends GridValidRowModel = any>(
51
+ props: DataTableProps<R>
52
+ ) {
53
+ // Destructor
54
+ const { localeText = {}, onCellSelection, toolbarCreator, ...rest } = props;
55
+
56
+ // Labels
57
+ const { noRows } = globalApp?.getLabels("noRows") ?? {};
58
+ if (noRows && !localeText.noRowsLabel) localeText.noRowsLabel = noRows;
59
+
60
+ const [selectedCellParams, setSelectedCellParams] =
61
+ React.useState<DataTableSelectedCellParams | null>(null);
62
+
63
+ const [cellModesModel, setCellModesModel] =
64
+ React.useState<GridCellModesModel>({});
65
+
66
+ const handleCellFocus = React.useCallback(
67
+ (event: React.FocusEvent<HTMLDivElement>) => {
68
+ // event.target is the element that triggered the event
69
+ // event.currentTarget is the element that the event listener is attached to
70
+ const cell = event.currentTarget;
71
+ const row = cell.parentElement;
72
+ if (row == null) return;
73
+
74
+ const id = row.dataset.id;
75
+ const index = row.dataset.rowindex;
76
+ const field = cell.dataset.field;
77
+ if (id == null || index == null || field == null) return;
78
+
79
+ const params: DataTableSelectedCellParams = {
80
+ id,
81
+ field,
82
+ index: parseInt(index)
83
+ };
84
+
85
+ if (onCellSelection) {
86
+ if (onCellSelection(params) === false) return;
87
+ }
88
+
89
+ setSelectedCellParams(params);
90
+ },
91
+ []
92
+ );
93
+
94
+ return (
95
+ <DataGrid
96
+ disableColumnMenu
97
+ hideFooter
98
+ localeText={localeText}
99
+ cellModesModel={cellModesModel}
100
+ onCellModesModelChange={(model) => setCellModesModel(model)}
101
+ components={{
102
+ Toolbar: toolbarCreator
103
+ ? ({ selectedCellParams, setCellModesModel, cellModesModel }) =>
104
+ toolbarCreator(
105
+ selectedCellParams,
106
+ setCellModesModel,
107
+ cellModesModel
108
+ )
109
+ : undefined
110
+ }}
111
+ componentsProps={{
112
+ toolbar: {
113
+ selectedCellParams,
114
+ setCellModesModel,
115
+ cellModesModel
116
+ },
117
+ cell: {
118
+ onFocus: handleCellFocus
119
+ }
120
+ }}
121
+ {...rest}
122
+ />
123
+ );
124
+ }