@abtnode/ux 1.16.20 → 1.16.21-beta-edf184e1
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.
- package/es/blocklet/configuration.js +9 -4
- package/es/blocklet/router/action/add-domain-alias.js +26 -6
- package/es/blocklet/router/add-domain.js +40 -12
- package/es/confirm.js +10 -4
- package/es/contexts/domain.js +75 -0
- package/es/form/form-autocomplete-input.js +282 -0
- package/es/hooks/use-snackbar.js +2 -0
- package/es/hooks/use-width.js +12 -0
- package/es/locales/en.js +8 -6
- package/es/locales/zh.js +8 -6
- package/es/notifications/addon.js +91 -0
- package/es/notifications/default-icon-variants.js +53 -0
- package/es/notifications/list.js +251 -0
- package/es/notifications/notification.js +148 -0
- package/es/notifications/page.js +316 -0
- package/es/notifications/tag.js +28 -0
- package/lib/blocklet/configuration.js +9 -4
- package/lib/blocklet/router/action/add-domain-alias.js +24 -6
- package/lib/blocklet/router/add-domain.js +39 -11
- package/lib/confirm.js +12 -6
- package/lib/contexts/domain.js +85 -0
- package/lib/form/form-autocomplete-input.js +295 -0
- package/lib/hooks/use-snackbar.js +8 -0
- package/lib/hooks/use-width.js +19 -0
- package/lib/locales/en.js +8 -6
- package/lib/locales/zh.js +8 -6
- package/lib/notifications/addon.js +99 -0
- package/lib/notifications/default-icon-variants.js +61 -0
- package/lib/notifications/list.js +216 -0
- package/lib/notifications/notification.js +139 -0
- package/lib/notifications/page.js +267 -0
- package/lib/notifications/tag.js +44 -0
- package/package.json +9 -7
|
@@ -14,7 +14,7 @@ import { BLOCKLET_CONFIGURABLE_KEY, SUPPORTED_LANGUAGES } from '@blocklet/consta
|
|
|
14
14
|
import { getWhoCanAccess } from '@blocklet/meta/lib/util';
|
|
15
15
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
16
16
|
import FormTextInput from '../form/form-text-input';
|
|
17
|
-
import
|
|
17
|
+
import FormAutocompleteInput from '../form/form-autocomplete-input';
|
|
18
18
|
import { useNodeContext } from '../contexts/node';
|
|
19
19
|
import { isInstalling, isValidClusterSize, BlockletAdminRoles } from '../util';
|
|
20
20
|
import { enableDebug } from '../util/mode';
|
|
@@ -28,8 +28,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
28
28
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
29
29
|
const languages = Object.keys(SUPPORTED_LANGUAGES).map(x => ({
|
|
30
30
|
code: x,
|
|
31
|
-
name: SUPPORTED_LANGUAGES[x].nativeName
|
|
31
|
+
name: SUPPORTED_LANGUAGES[x].nativeName,
|
|
32
|
+
enName: SUPPORTED_LANGUAGES[x].name
|
|
32
33
|
}));
|
|
34
|
+
const languagesFilterKeys = ['name', 'enName', 'code'];
|
|
33
35
|
function BlockletEnvironment({
|
|
34
36
|
blocklet,
|
|
35
37
|
onUpdate,
|
|
@@ -351,16 +353,19 @@ function BlockletEnvironment({
|
|
|
351
353
|
fontSize: 14,
|
|
352
354
|
mb: 0,
|
|
353
355
|
children: t('blocklet.config.languages')
|
|
354
|
-
}), /*#__PURE__*/_jsx(
|
|
356
|
+
}), /*#__PURE__*/_jsx(FormAutocompleteInput, {
|
|
355
357
|
style: {
|
|
356
358
|
marginTop: 0
|
|
357
359
|
},
|
|
358
360
|
disabled: loading || disabled,
|
|
359
361
|
loading: loading,
|
|
360
362
|
options: languages,
|
|
363
|
+
noEmpty: true,
|
|
361
364
|
initialValue: customLanguages ? customLanguages.value.split(',') : ['en', 'zh'],
|
|
362
365
|
onSubmit: value => onSubmitConfig('BLOCKLET_APP_LANGUAGES', value),
|
|
363
|
-
helperText: t('blocklet.config.selectLanguagesTip')
|
|
366
|
+
helperText: t('blocklet.config.selectLanguagesTip'),
|
|
367
|
+
placeholder: t('common.search'),
|
|
368
|
+
filterKeys: languagesFilterKeys
|
|
364
369
|
})]
|
|
365
370
|
}), /*#__PURE__*/_jsx(Box, {
|
|
366
371
|
className: "config-form",
|
|
@@ -6,14 +6,15 @@ import MenuItem from '@mui/material/MenuItem';
|
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
7
7
|
import useSetState from 'react-use/lib/useSetState';
|
|
8
8
|
import Confirm from '../../../confirm';
|
|
9
|
+
import { DomainProvider, useDomainContext } from '../../../contexts/domain';
|
|
9
10
|
import { useNodeContext } from '../../../contexts/node';
|
|
10
11
|
import { sleep } from '../../../util';
|
|
11
|
-
import { parseDomainFromURL } from '../util';
|
|
12
12
|
import AddDomain from '../add-domain';
|
|
13
|
+
import { parseDomainFromURL } from '../util';
|
|
13
14
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
15
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
15
16
|
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
16
|
-
|
|
17
|
+
function AddDomainAliasComponent({
|
|
17
18
|
id,
|
|
18
19
|
children,
|
|
19
20
|
title,
|
|
@@ -33,6 +34,7 @@ export default function AddDomainAlias({
|
|
|
33
34
|
confirmSetting: null
|
|
34
35
|
});
|
|
35
36
|
const cnameDomain = systemDomains.find(item => isDidDomain(item));
|
|
37
|
+
const domainContext = useDomainContext();
|
|
36
38
|
const onCancel = () => {
|
|
37
39
|
setState({
|
|
38
40
|
loading: false,
|
|
@@ -47,10 +49,16 @@ export default function AddDomainAlias({
|
|
|
47
49
|
loading: true
|
|
48
50
|
});
|
|
49
51
|
try {
|
|
52
|
+
const domain = parseDomainFromURL(params.domain);
|
|
53
|
+
const validateResult = await domainContext.validateDomain(domain);
|
|
54
|
+
if (!validateResult) {
|
|
55
|
+
// eslint-disable-next-line consistent-return
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
50
58
|
await api.addDomainAlias({
|
|
51
59
|
input: {
|
|
52
60
|
id,
|
|
53
|
-
domainAlias:
|
|
61
|
+
domainAlias: domain,
|
|
54
62
|
teamDid
|
|
55
63
|
}
|
|
56
64
|
});
|
|
@@ -58,6 +66,10 @@ export default function AddDomainAlias({
|
|
|
58
66
|
setState({
|
|
59
67
|
confirmSetting: null
|
|
60
68
|
});
|
|
69
|
+
} catch (error) {
|
|
70
|
+
domainContext.setError(error.message);
|
|
71
|
+
// eslint-disable-next-line consistent-return
|
|
72
|
+
return false; // 防止 Dialog 关闭
|
|
61
73
|
} finally {
|
|
62
74
|
setState({
|
|
63
75
|
loading: false
|
|
@@ -129,13 +141,14 @@ export default function AddDomainAlias({
|
|
|
129
141
|
params: state.confirmSetting.params,
|
|
130
142
|
onConfirm: state.confirmSetting.onConfirm,
|
|
131
143
|
onCancel: state.confirmSetting.onCancel,
|
|
144
|
+
displayError: false,
|
|
132
145
|
color: "primary",
|
|
133
146
|
loading: state.loading,
|
|
134
147
|
maxWidth: "lg"
|
|
135
148
|
})]
|
|
136
149
|
});
|
|
137
150
|
}
|
|
138
|
-
|
|
151
|
+
AddDomainAliasComponent.propTypes = {
|
|
139
152
|
id: PropTypes.string.isRequired,
|
|
140
153
|
children: PropTypes.any,
|
|
141
154
|
title: PropTypes.string,
|
|
@@ -144,9 +157,16 @@ AddDomainAlias.propTypes = {
|
|
|
144
157
|
appId: PropTypes.string,
|
|
145
158
|
appPk: PropTypes.string.isRequired
|
|
146
159
|
};
|
|
147
|
-
|
|
160
|
+
AddDomainAliasComponent.defaultProps = {
|
|
148
161
|
children: null,
|
|
149
162
|
title: '',
|
|
150
163
|
teamDid: '',
|
|
151
164
|
appId: null
|
|
152
|
-
};
|
|
165
|
+
};
|
|
166
|
+
export default function AddDomainAlias(props) {
|
|
167
|
+
return /*#__PURE__*/_jsx(DomainProvider, {
|
|
168
|
+
children: /*#__PURE__*/_jsx(AddDomainAliasComponent, {
|
|
169
|
+
...props
|
|
170
|
+
})
|
|
171
|
+
});
|
|
172
|
+
}
|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import Connect from '@arcblock/did-connect/lib/Connect';
|
|
2
2
|
import Button from '@arcblock/ux/lib/Button';
|
|
3
3
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
4
|
+
import IconInfo from '@mui/icons-material/InfoOutlined';
|
|
4
5
|
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
|
5
6
|
import Box from '@mui/material/Box';
|
|
6
7
|
import Link from '@mui/material/Link';
|
|
7
8
|
import TextField from '@mui/material/TextField';
|
|
9
|
+
import useTheme from '@mui/material/styles/useTheme';
|
|
8
10
|
import noop from 'lodash/noop';
|
|
9
11
|
import trim from 'lodash/trim';
|
|
10
12
|
import PropTypes from 'prop-types';
|
|
11
13
|
import useSetState from 'react-use/lib/useSetState';
|
|
12
14
|
import joinURL from 'url-join';
|
|
15
|
+
import { useDomainContext } from '../../contexts/domain';
|
|
13
16
|
import { useNodeContext } from '../../contexts/node';
|
|
14
17
|
import { sleep, validateDomain } from '../../util';
|
|
15
18
|
import { axios } from '../../util/api';
|
|
16
19
|
import DNSTip from './dns-tip';
|
|
17
20
|
import { parseDomainFromURL } from './util';
|
|
18
21
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
19
|
-
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
20
22
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
23
|
+
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
21
24
|
export default function AddDomain({
|
|
22
25
|
siteId,
|
|
23
26
|
teamDid,
|
|
@@ -40,11 +43,14 @@ export default function AddDomain({
|
|
|
40
43
|
api,
|
|
41
44
|
info
|
|
42
45
|
} = useNodeContext();
|
|
46
|
+
const domainContext = useDomainContext();
|
|
47
|
+
const theme = useTheme();
|
|
43
48
|
const [state, setState] = useSetState({
|
|
44
49
|
domain: defaultCustomDomain,
|
|
45
50
|
error: null,
|
|
46
51
|
type: info?.nftDomainUrl ? 'nftDomain' : 'inputDomain',
|
|
47
|
-
showNFTDomainAuth: false
|
|
52
|
+
showNFTDomainAuth: false,
|
|
53
|
+
showDnsTip: false
|
|
48
54
|
});
|
|
49
55
|
let nftDomainURL = '';
|
|
50
56
|
if (info.nftDomainUrl) {
|
|
@@ -85,24 +91,27 @@ export default function AddDomain({
|
|
|
85
91
|
});
|
|
86
92
|
}
|
|
87
93
|
};
|
|
94
|
+
const error = state.error || domainContext.error; // TODO: state error 可能不需要了
|
|
95
|
+
|
|
88
96
|
const domainTypes = {
|
|
89
97
|
inputDomain: {
|
|
90
98
|
name: t('router.domainAlias.inputDomain'),
|
|
91
99
|
component: /*#__PURE__*/_jsxs(_Fragment, {
|
|
92
100
|
children: [/*#__PURE__*/_jsx(TextField, {
|
|
93
101
|
label: t('router.domainAlias.add.domainDescription'),
|
|
94
|
-
helperText:
|
|
102
|
+
helperText: error || t('router.domainAlias.add.helperText'),
|
|
95
103
|
inputProps: {
|
|
96
104
|
'data-cy': 'domain-name-input'
|
|
97
105
|
},
|
|
98
106
|
fullWidth: true,
|
|
99
107
|
disabled: disabled,
|
|
100
108
|
value: state.domain,
|
|
109
|
+
size: "small",
|
|
110
|
+
variant: "outlined",
|
|
111
|
+
error: !!error,
|
|
101
112
|
onChange: e => {
|
|
102
113
|
const value = trim(e.target?.value);
|
|
103
|
-
|
|
104
|
-
domain: value
|
|
105
|
-
});
|
|
114
|
+
domainContext.setDomain(value);
|
|
106
115
|
const errMessage = validateDomain(value, locale);
|
|
107
116
|
setState({
|
|
108
117
|
domain: value,
|
|
@@ -115,20 +124,35 @@ export default function AddDomain({
|
|
|
115
124
|
},
|
|
116
125
|
autoFocus: autoFocus,
|
|
117
126
|
onBlur: () => {
|
|
118
|
-
if (!
|
|
127
|
+
if (!domainContext.domain) {
|
|
119
128
|
setState({
|
|
120
129
|
error: null
|
|
121
130
|
});
|
|
131
|
+
domainContext.setError(null);
|
|
122
132
|
}
|
|
133
|
+
}
|
|
134
|
+
}), /*#__PURE__*/_jsxs(Box, {
|
|
135
|
+
sx: {
|
|
136
|
+
cursor: 'pointer',
|
|
137
|
+
fontSize: '14px',
|
|
138
|
+
display: 'flex',
|
|
139
|
+
alignItems: 'center',
|
|
140
|
+
gap: '4px',
|
|
141
|
+
marginTop: '8px',
|
|
142
|
+
lineHeight: 1,
|
|
143
|
+
color: theme.palette.primary.main
|
|
123
144
|
},
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
onClick: () => setState({
|
|
146
|
+
showDnsTip: !state.showDnsTip
|
|
147
|
+
}),
|
|
148
|
+
children: [/*#__PURE__*/_jsx(IconInfo, {
|
|
149
|
+
fontSize: "1em"
|
|
150
|
+
}), t('router.domainAlias.showTipHint')]
|
|
151
|
+
}), state.showDnsTip && /*#__PURE__*/_jsx(DNSTip, {
|
|
128
152
|
domain: state.domain,
|
|
129
153
|
resolvedTarget: cnameDomain,
|
|
130
154
|
style: {
|
|
131
|
-
marginTop: '
|
|
155
|
+
marginTop: '4px'
|
|
132
156
|
}
|
|
133
157
|
})]
|
|
134
158
|
})
|
|
@@ -220,6 +244,10 @@ export default function AddDomain({
|
|
|
220
244
|
},
|
|
221
245
|
children: inputDomainTypes.map((type, index) => /*#__PURE__*/_jsxs(Box, {
|
|
222
246
|
children: [inputDomainTypes.length > 1 && /*#__PURE__*/_jsx(Box, {
|
|
247
|
+
sx: {
|
|
248
|
+
fontWeight: 700,
|
|
249
|
+
color: theme.palette.text.primary
|
|
250
|
+
},
|
|
223
251
|
children: `${t(`optionsOrder.${index + 1}`)}: ${domainTypes[type]?.name}`
|
|
224
252
|
}), /*#__PURE__*/_jsx(Box, {
|
|
225
253
|
sx: {
|
package/es/confirm.js
CHANGED
|
@@ -27,6 +27,7 @@ export default function ConfirmDialog({
|
|
|
27
27
|
onCancel,
|
|
28
28
|
onConfirm,
|
|
29
29
|
loading: inputLoading,
|
|
30
|
+
displayError,
|
|
30
31
|
...rest
|
|
31
32
|
}) {
|
|
32
33
|
const [params, setParams] = useState(initialParams);
|
|
@@ -41,7 +42,10 @@ export default function ConfirmDialog({
|
|
|
41
42
|
if (typeof cb === 'function') {
|
|
42
43
|
setLoading(true);
|
|
43
44
|
try {
|
|
44
|
-
await cb(params);
|
|
45
|
+
const res = await cb(params);
|
|
46
|
+
if (res === false) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
45
49
|
setOpen(false);
|
|
46
50
|
} catch (err) {
|
|
47
51
|
setError(formatError(err));
|
|
@@ -79,7 +83,7 @@ export default function ConfirmDialog({
|
|
|
79
83
|
children: [/*#__PURE__*/_jsx(DialogContentText, {
|
|
80
84
|
component: "div",
|
|
81
85
|
children: d
|
|
82
|
-
}), !!error && /*#__PURE__*/_jsx(Alert, {
|
|
86
|
+
}), !!error && displayError && /*#__PURE__*/_jsx(Alert, {
|
|
83
87
|
severity: "error",
|
|
84
88
|
style: {
|
|
85
89
|
width: '100%',
|
|
@@ -134,7 +138,8 @@ ConfirmDialog.propTypes = {
|
|
|
134
138
|
// This object holds states managed in the dialog
|
|
135
139
|
onCancel: PropTypes.func,
|
|
136
140
|
onConfirm: PropTypes.func.isRequired,
|
|
137
|
-
loading: PropTypes.bool
|
|
141
|
+
loading: PropTypes.bool,
|
|
142
|
+
displayError: PropTypes.bool
|
|
138
143
|
};
|
|
139
144
|
ConfirmDialog.defaultProps = {
|
|
140
145
|
onCancel: () => {},
|
|
@@ -143,7 +148,8 @@ ConfirmDialog.defaultProps = {
|
|
|
143
148
|
confirm: 'Confirm',
|
|
144
149
|
color: 'error',
|
|
145
150
|
params: {},
|
|
146
|
-
loading: false
|
|
151
|
+
loading: false,
|
|
152
|
+
displayError: true
|
|
147
153
|
};
|
|
148
154
|
const StyledDialog = styled(Dialog)`
|
|
149
155
|
.delete-actions .Mui-disabled {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
|
+
import trim from 'lodash/trim';
|
|
3
|
+
import { createContext, useContext } from 'react';
|
|
4
|
+
import useSetState from 'react-use/lib/useSetState';
|
|
5
|
+
import { validateDomain } from '../util';
|
|
6
|
+
import { useNodeContext } from './node';
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
const DomainContext = /*#__PURE__*/createContext({});
|
|
9
|
+
const {
|
|
10
|
+
Provider,
|
|
11
|
+
Consumer
|
|
12
|
+
} = DomainContext;
|
|
13
|
+
|
|
14
|
+
// eslint-disable-next-line react/prop-types
|
|
15
|
+
function DomainProvider({
|
|
16
|
+
children
|
|
17
|
+
}) {
|
|
18
|
+
const [state, setState] = useSetState({
|
|
19
|
+
domain: '',
|
|
20
|
+
error: null
|
|
21
|
+
});
|
|
22
|
+
const {
|
|
23
|
+
locale,
|
|
24
|
+
t
|
|
25
|
+
} = useLocaleContext();
|
|
26
|
+
const {
|
|
27
|
+
api
|
|
28
|
+
} = useNodeContext();
|
|
29
|
+
const value = {
|
|
30
|
+
error: state.error,
|
|
31
|
+
domain: state.domain,
|
|
32
|
+
setError: error => {
|
|
33
|
+
setState({
|
|
34
|
+
error
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
setDomain: domain => {
|
|
38
|
+
const tmpDomain = trim(domain);
|
|
39
|
+
const errMessage = validateDomain(tmpDomain, locale);
|
|
40
|
+
setState({
|
|
41
|
+
domain: tmpDomain,
|
|
42
|
+
error: errMessage
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
validateDomain: async domain => {
|
|
46
|
+
if (!state.error) {
|
|
47
|
+
const result = await api.isDidDomain({
|
|
48
|
+
input: {
|
|
49
|
+
domain
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
if (result.value === true) {
|
|
53
|
+
setState({
|
|
54
|
+
error: t('router.domainAlias.add.isDidDomainError')
|
|
55
|
+
});
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return /*#__PURE__*/_jsx(Provider, {
|
|
63
|
+
value: {
|
|
64
|
+
value
|
|
65
|
+
},
|
|
66
|
+
children: children
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function useDomainContext() {
|
|
70
|
+
const {
|
|
71
|
+
value
|
|
72
|
+
} = useContext(DomainContext);
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
export { Consumer as DomainConsumer, DomainContext, DomainProvider, useDomainContext };
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import Box from '@mui/material/Box';
|
|
4
|
+
import CircularProgress from '@mui/material/CircularProgress';
|
|
5
|
+
import CloseIcon from '@mui/icons-material/Close';
|
|
6
|
+
import DoneIcon from '@mui/icons-material/Done';
|
|
7
|
+
import IconButton from '@mui/material/IconButton';
|
|
8
|
+
import Chip from '@mui/material/Chip';
|
|
9
|
+
import FormHelperText from '@mui/material/FormHelperText';
|
|
10
|
+
import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete';
|
|
11
|
+
import TextField from '@mui/material/TextField';
|
|
12
|
+
import styled from '@emotion/styled';
|
|
13
|
+
import Popper from '@mui/material/Popper';
|
|
14
|
+
import Required from './required';
|
|
15
|
+
import FormWrapper from './form-wrapper';
|
|
16
|
+
import InputTriggers from './input-triggers';
|
|
17
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
18
|
+
import { createElement as _createElement } from "react";
|
|
19
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
20
|
+
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
21
|
+
export default function FormAutocompleteInput({
|
|
22
|
+
label,
|
|
23
|
+
disabled,
|
|
24
|
+
required,
|
|
25
|
+
initialValue,
|
|
26
|
+
onChange,
|
|
27
|
+
style,
|
|
28
|
+
onSubmit,
|
|
29
|
+
triggers,
|
|
30
|
+
options,
|
|
31
|
+
formatterBeforeSubmit,
|
|
32
|
+
helperText,
|
|
33
|
+
placeholder,
|
|
34
|
+
renderImage,
|
|
35
|
+
filterKeys,
|
|
36
|
+
noEmpty,
|
|
37
|
+
...rest
|
|
38
|
+
}) {
|
|
39
|
+
const [editing, setEditing] = useState(false);
|
|
40
|
+
const [loading, setLoading] = useState(false);
|
|
41
|
+
const [inputValue, setInputValue] = React.useState('');
|
|
42
|
+
const [defaultValue, setDefaultValue] = useState(initialValue);
|
|
43
|
+
const [value, setValue] = useState(defaultValue);
|
|
44
|
+
const optionsMap = useMemo(() => {
|
|
45
|
+
if (!options) {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
const cache = {};
|
|
49
|
+
options.forEach(v => {
|
|
50
|
+
cache[v.code] = v;
|
|
51
|
+
});
|
|
52
|
+
return cache;
|
|
53
|
+
}, [options]);
|
|
54
|
+
const filterOptions = () => {
|
|
55
|
+
if (!options) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
if (!inputValue) {
|
|
59
|
+
return options.map(v => v.code);
|
|
60
|
+
}
|
|
61
|
+
const reg = new RegExp(inputValue.toLowerCase());
|
|
62
|
+
|
|
63
|
+
// name 或者其他字段被检索到, 都可以被搜出
|
|
64
|
+
return options.filter(v => {
|
|
65
|
+
for (let i = 0; i < filterKeys.length; i++) {
|
|
66
|
+
const key = filterKeys[i];
|
|
67
|
+
if (reg.test(v[key]?.toLowerCase())) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}).map(v => v.code);
|
|
73
|
+
};
|
|
74
|
+
const handleSubmit = async () => {
|
|
75
|
+
setLoading(true);
|
|
76
|
+
try {
|
|
77
|
+
const shouldUpdate = await onSubmit(typeof formatterBeforeSubmit === 'function' ? formatterBeforeSubmit(value) : value);
|
|
78
|
+
if (shouldUpdate === false) {
|
|
79
|
+
setValue(defaultValue);
|
|
80
|
+
} else if (noEmpty && !value.length) {
|
|
81
|
+
setValue(initialValue);
|
|
82
|
+
setDefaultValue(initialValue);
|
|
83
|
+
} else {
|
|
84
|
+
setValue(value);
|
|
85
|
+
setDefaultValue(value);
|
|
86
|
+
}
|
|
87
|
+
} catch (err) {
|
|
88
|
+
setValue(defaultValue);
|
|
89
|
+
} finally {
|
|
90
|
+
setLoading(false);
|
|
91
|
+
setEditing(false);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const onSelectChange = (_, newValue) => {
|
|
95
|
+
// 确保 newValue 是数组, 并且每个元素都在 options 中
|
|
96
|
+
if (!Array.isArray(newValue)) {
|
|
97
|
+
setValue([]);
|
|
98
|
+
onChange([]);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const nextValue = newValue.filter(x => optionsMap[x]);
|
|
102
|
+
if (!nextValue.length) {
|
|
103
|
+
setValue([]);
|
|
104
|
+
onChange([]);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
setValue(nextValue);
|
|
108
|
+
onChange(nextValue);
|
|
109
|
+
};
|
|
110
|
+
const input = editing ? /*#__PURE__*/_jsx(Autocomplete, {
|
|
111
|
+
options: options,
|
|
112
|
+
value: value,
|
|
113
|
+
onChange: onSelectChange,
|
|
114
|
+
inputValue: inputValue,
|
|
115
|
+
onInputChange: (_, newInputValue) => {
|
|
116
|
+
setInputValue(newInputValue);
|
|
117
|
+
}
|
|
118
|
+
// eslint-disable-next-line no-use-before-define
|
|
119
|
+
,
|
|
120
|
+
PopperComponent: StyledPopper,
|
|
121
|
+
fullWidth: true,
|
|
122
|
+
multiple: true,
|
|
123
|
+
variant: "outlined",
|
|
124
|
+
margin: "dense"
|
|
125
|
+
// size="small"
|
|
126
|
+
,
|
|
127
|
+
filterOptions: filterOptions,
|
|
128
|
+
getOptionLabel: option => optionsMap[option]?.name,
|
|
129
|
+
renderInput: params => /*#__PURE__*/_jsx(TextField, {
|
|
130
|
+
...params,
|
|
131
|
+
label: "",
|
|
132
|
+
placeholder: placeholder
|
|
133
|
+
}),
|
|
134
|
+
renderOption: (props, code) => {
|
|
135
|
+
const selected = value.indexOf(code) !== -1;
|
|
136
|
+
return /*#__PURE__*/_createElement(Row, {
|
|
137
|
+
component: "li",
|
|
138
|
+
...props,
|
|
139
|
+
key: code
|
|
140
|
+
}, renderImage(optionsMap[code] || {}), /*#__PURE__*/_jsx(Box, {
|
|
141
|
+
sx: {
|
|
142
|
+
'& > img': {
|
|
143
|
+
mr: 2,
|
|
144
|
+
flexShrink: 0
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
style: {
|
|
148
|
+
flex: 1,
|
|
149
|
+
fontWeight: selected ? '700' : 'normal'
|
|
150
|
+
},
|
|
151
|
+
children: optionsMap[code]?.name
|
|
152
|
+
}), /*#__PURE__*/_jsx(Box, {
|
|
153
|
+
component: CloseIcon,
|
|
154
|
+
sx: {
|
|
155
|
+
opacity: 0.5,
|
|
156
|
+
width: 18,
|
|
157
|
+
height: 18
|
|
158
|
+
},
|
|
159
|
+
style: {
|
|
160
|
+
visibility: selected ? 'visible' : 'hidden'
|
|
161
|
+
}
|
|
162
|
+
}));
|
|
163
|
+
},
|
|
164
|
+
...rest
|
|
165
|
+
}) : /*#__PURE__*/_jsx(Box, {
|
|
166
|
+
px: 1,
|
|
167
|
+
className: "form-item-input slot",
|
|
168
|
+
style: {
|
|
169
|
+
whiteSpace: 'pre-wrap'
|
|
170
|
+
},
|
|
171
|
+
children: value.filter(x => options.some(o => o.code === x)).map(x => /*#__PURE__*/_jsx(Chip, {
|
|
172
|
+
label: options.find(o => o.code === x).name,
|
|
173
|
+
style: {
|
|
174
|
+
marginRight: 8
|
|
175
|
+
},
|
|
176
|
+
size: "small"
|
|
177
|
+
}, x))
|
|
178
|
+
});
|
|
179
|
+
return /*#__PURE__*/_jsx(FormWrapper, {
|
|
180
|
+
className: "form",
|
|
181
|
+
style: style,
|
|
182
|
+
children: /*#__PURE__*/_jsxs(Box, {
|
|
183
|
+
className: "form-item",
|
|
184
|
+
children: [/*#__PURE__*/_jsxs(Box, {
|
|
185
|
+
className: "form-item-label",
|
|
186
|
+
children: [label, required && /*#__PURE__*/_jsx(Required, {})]
|
|
187
|
+
}), /*#__PURE__*/_jsxs(Box, {
|
|
188
|
+
className: "form-item-body",
|
|
189
|
+
children: [/*#__PURE__*/_jsx(Box, {
|
|
190
|
+
className: "form-item-input",
|
|
191
|
+
children: input
|
|
192
|
+
}), /*#__PURE__*/_jsx(Box, {
|
|
193
|
+
className: "form-item-action",
|
|
194
|
+
children: editing ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
195
|
+
children: [/*#__PURE__*/_jsx(IconButton, {
|
|
196
|
+
"data-cy": "schema-form-item-confirm",
|
|
197
|
+
onClick: handleSubmit,
|
|
198
|
+
disabled: disabled || loading,
|
|
199
|
+
size: "large",
|
|
200
|
+
children: loading ? /*#__PURE__*/_jsx(CircularProgress, {
|
|
201
|
+
size: 24
|
|
202
|
+
}) : /*#__PURE__*/_jsx(DoneIcon, {})
|
|
203
|
+
}), /*#__PURE__*/_jsx(IconButton, {
|
|
204
|
+
"data-cy": "schema-form-item-cancel",
|
|
205
|
+
onClick: () => {
|
|
206
|
+
setValue(defaultValue);
|
|
207
|
+
setEditing(false);
|
|
208
|
+
},
|
|
209
|
+
disabled: disabled || loading,
|
|
210
|
+
size: "large",
|
|
211
|
+
children: /*#__PURE__*/_jsx(CloseIcon, {})
|
|
212
|
+
})]
|
|
213
|
+
}) : /*#__PURE__*/_jsx(InputTriggers, {
|
|
214
|
+
triggers: triggers,
|
|
215
|
+
onEnterEdit: () => setEditing(true),
|
|
216
|
+
disabled: disabled
|
|
217
|
+
})
|
|
218
|
+
})]
|
|
219
|
+
}), editing && helperText && /*#__PURE__*/_jsx(FormHelperText, {
|
|
220
|
+
children: helperText
|
|
221
|
+
})]
|
|
222
|
+
})
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
FormAutocompleteInput.propTypes = {
|
|
226
|
+
style: PropTypes.object,
|
|
227
|
+
onSubmit: PropTypes.func,
|
|
228
|
+
onChange: PropTypes.func,
|
|
229
|
+
label: PropTypes.string,
|
|
230
|
+
initialValue: PropTypes.string,
|
|
231
|
+
required: PropTypes.bool,
|
|
232
|
+
disabled: PropTypes.bool,
|
|
233
|
+
triggers: PropTypes.array,
|
|
234
|
+
options: PropTypes.array,
|
|
235
|
+
helperText: PropTypes.string,
|
|
236
|
+
formatterBeforeSubmit: PropTypes.func,
|
|
237
|
+
placeholder: PropTypes.string,
|
|
238
|
+
renderImage: PropTypes.func,
|
|
239
|
+
filterKeys: PropTypes.arrayOf(PropTypes.string),
|
|
240
|
+
noEmpty: PropTypes.bool
|
|
241
|
+
};
|
|
242
|
+
FormAutocompleteInput.defaultProps = {
|
|
243
|
+
style: {},
|
|
244
|
+
onSubmit: () => {},
|
|
245
|
+
onChange: () => {},
|
|
246
|
+
label: '',
|
|
247
|
+
initialValue: '',
|
|
248
|
+
required: false,
|
|
249
|
+
disabled: false,
|
|
250
|
+
triggers: [],
|
|
251
|
+
options: [],
|
|
252
|
+
helperText: '',
|
|
253
|
+
formatterBeforeSubmit: x => x.join(','),
|
|
254
|
+
placeholder: '',
|
|
255
|
+
filterKeys: ['name'],
|
|
256
|
+
renderImage: () => null,
|
|
257
|
+
noEmpty: false
|
|
258
|
+
};
|
|
259
|
+
const Row = styled.div`
|
|
260
|
+
display: flex;
|
|
261
|
+
justify-content: space-between;
|
|
262
|
+
align-items: center;
|
|
263
|
+
height: 48px;
|
|
264
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
265
|
+
font-size: 1.1em;
|
|
266
|
+
`;
|
|
267
|
+
const StyledPopper = styled(Popper)(() => ({
|
|
268
|
+
[`& .${autocompleteClasses.paper}`]: {
|
|
269
|
+
boxShadow: '0px 8px 12px rgba(92, 92, 92, 0.04), 0px -10px 12px rgba(92, 92, 92, 0.04)',
|
|
270
|
+
borderRadius: '6px',
|
|
271
|
+
width: '100%',
|
|
272
|
+
border: '1px solid rgba(0, 0, 0, 0.12)',
|
|
273
|
+
color: 'inherit',
|
|
274
|
+
fontSize: 13,
|
|
275
|
+
position: 'absolute',
|
|
276
|
+
left: 0,
|
|
277
|
+
top: 0
|
|
278
|
+
},
|
|
279
|
+
[`& .${autocompleteClasses.listbox}`]: {
|
|
280
|
+
padding: 0
|
|
281
|
+
}
|
|
282
|
+
}));
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useTheme } from '@mui/styles';
|
|
2
|
+
import useMediaQuery from '@mui/material/useMediaQuery';
|
|
3
|
+
function useWidth() {
|
|
4
|
+
const theme = useTheme();
|
|
5
|
+
const keys = [...theme.breakpoints.keys].reverse();
|
|
6
|
+
return keys.reduce((output, key) => {
|
|
7
|
+
// eslint-disable-next-line
|
|
8
|
+
const matches = useMediaQuery(theme.breakpoints.up(key));
|
|
9
|
+
return !output && matches ? key : output;
|
|
10
|
+
}, null) || 'xs';
|
|
11
|
+
}
|
|
12
|
+
export default useWidth;
|