@blocklet/ui-react 2.12.20 → 2.12.22

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.
@@ -43,6 +43,7 @@ export type UserMetadataLink = {
43
43
  favicon?: string;
44
44
  };
45
45
  export declare enum DurationEnum {
46
+ NoClear = "no_clear",
46
47
  ThirtyMinutes = "30_minutes",
47
48
  OneHour = "1_hour",
48
49
  FourHours = "4_hours",
@@ -1,4 +1,5 @@
1
1
  export var DurationEnum = /* @__PURE__ */ ((DurationEnum2) => {
2
+ DurationEnum2["NoClear"] = "no_clear";
2
3
  DurationEnum2["ThirtyMinutes"] = "30_minutes";
3
4
  DurationEnum2["OneHour"] = "1_hour";
4
5
  DurationEnum2["FourHours"] = "4_hours";
@@ -20,5 +20,31 @@ interface EditableFieldProps {
20
20
  canEdit?: boolean;
21
21
  renderValue?: (value: string) => React.ReactNode;
22
22
  }
23
+ export declare const commonInputStyle: {
24
+ '.MuiOutlinedInput-root': {
25
+ '&:hover': {
26
+ fieldset: {
27
+ borderColor: string;
28
+ };
29
+ };
30
+ '&.Mui-focused': {
31
+ fieldset: {
32
+ borderColor: string;
33
+ };
34
+ };
35
+ };
36
+ };
37
+ export declare const inputFieldStyle: {
38
+ width: string;
39
+ '& .MuiFormHelperText-root': {
40
+ position: string;
41
+ bottom: number;
42
+ left: number;
43
+ margin: number;
44
+ };
45
+ fieldset: {
46
+ borderColor: string;
47
+ };
48
+ };
23
49
  declare function EditableField({ value, onChange, onValueValidate, errorMsg, editable, component, placeholder, rows, maxLength, icon, label, children, tooltip, inline, style, verified, canEdit, renderValue, }: EditableFieldProps): JSX.Element | null;
24
50
  export default EditableField;
@@ -5,9 +5,9 @@ import { temp as colors } from "@arcblock/ux/lib/Colors";
5
5
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
6
6
  import { translate } from "@arcblock/ux/lib/Locale/util";
7
7
  import VerifiedIcon from "@mui/icons-material/Verified";
8
+ import { mergeSx } from "@arcblock/ux/lib/Util/style";
8
9
  import { translations } from "../libs/locales.js";
9
- const inputFieldStyle = {
10
- width: "100%",
10
+ export const commonInputStyle = {
11
11
  ".MuiOutlinedInput-root": {
12
12
  "&:hover": {
13
13
  fieldset: {
@@ -19,6 +19,15 @@ const inputFieldStyle = {
19
19
  borderColor: colors.dividerColor
20
20
  }
21
21
  }
22
+ }
23
+ };
24
+ export const inputFieldStyle = {
25
+ width: "100%",
26
+ "& .MuiFormHelperText-root": {
27
+ position: "relative",
28
+ bottom: 0,
29
+ left: 0,
30
+ margin: 0
22
31
  },
23
32
  fieldset: {
24
33
  borderColor: colors.dividerColor
@@ -74,7 +83,7 @@ function EditableField({
74
83
  },
75
84
  value,
76
85
  onChange: (e) => handleChange(e.target.value),
77
- sx: inputFieldStyle,
86
+ sx: mergeSx(inputFieldStyle, !errorMsg ? commonInputStyle : {}),
78
87
  error: Boolean(errorMsg),
79
88
  helperText: errorMsg
80
89
  }
@@ -91,7 +100,7 @@ function EditableField({
91
100
  minRows: rows,
92
101
  placeholder
93
102
  },
94
- sx: inputFieldStyle,
103
+ sx: mergeSx(inputFieldStyle, !errorMsg ? commonInputStyle : {}),
95
104
  value,
96
105
  onChange: (e) => handleChange(e.target.value),
97
106
  error: Boolean(errorMsg),
@@ -9,10 +9,27 @@ import { translate } from "@arcblock/ux/lib/Locale/util";
9
9
  import { useMemoizedFn } from "ahooks";
10
10
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
11
11
  import LinkIcon from "@arcblock/icons/lib/Link";
12
- import { joinURL } from "ufo";
12
+ import { mergeSx } from "@arcblock/ux/lib/Util/style";
13
+ import { joinURL, parseURL } from "ufo";
13
14
  import { isValidUrl } from "./utils.js";
14
15
  import { translations } from "../../libs/locales.js";
15
- function LinkInput({ value, onChange, error }) {
16
+ import { commonInputStyle, inputFieldStyle } from "../editable-field.js";
17
+ function formatLinkDisplay(link) {
18
+ try {
19
+ const url = parseURL(link);
20
+ if (url.protocol === "http:" || url.protocol === "https:") {
21
+ return url.host || link;
22
+ }
23
+ return link;
24
+ } catch {
25
+ return link;
26
+ }
27
+ }
28
+ function LinkInput({
29
+ value,
30
+ onChange,
31
+ errorMsg
32
+ }) {
16
33
  const handleUrlChange = (event) => {
17
34
  const inputValue = event.target.value;
18
35
  onChange(inputValue);
@@ -24,13 +41,9 @@ function LinkInput({ value, onChange, error }) {
24
41
  value,
25
42
  onChange: handleUrlChange,
26
43
  fullWidth: true,
27
- error,
28
- helperText: error ? "Invalid URL" : "",
29
- sx: {
30
- fieldset: {
31
- borderColor: colors.dividerColor
32
- }
33
- }
44
+ error: !!errorMsg,
45
+ helperText: errorMsg,
46
+ sx: mergeSx(inputFieldStyle, !errorMsg ? commonInputStyle : {})
34
47
  }
35
48
  ) });
36
49
  }
@@ -68,7 +81,14 @@ function DynamicLinkForm({ links = [], onChange }) {
68
81
  links.map((link, index) => (
69
82
  // eslint-disable-next-line react/no-array-index-key
70
83
  /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "flex-start", mb: 1, children: [
71
- /* @__PURE__ */ jsx(LinkInput, { value: link, onChange: (value) => handleInputChange(index, value), error: errors[index] }),
84
+ /* @__PURE__ */ jsx(
85
+ LinkInput,
86
+ {
87
+ value: link,
88
+ onChange: (value) => handleInputChange(index, value),
89
+ errorMsg: errors[index] ? t("profile.invalidURL") : ""
90
+ }
91
+ ),
72
92
  /* @__PURE__ */ jsx(IconButton, { sx: { color: colors.foregroundsFgMuted }, onClick: () => handleRemoveLink(index), children: /* @__PURE__ */ jsx(RemoveIcon, {}) })
73
93
  ] }, index)
74
94
  )),
@@ -155,7 +175,7 @@ export function LinkPreviewInput({
155
175
  },
156
176
  children: [
157
177
  /* @__PURE__ */ jsx(FaviconPreview, { link }),
158
- /* @__PURE__ */ jsx(LinkDiv, { children: /* @__PURE__ */ jsx(Box, { component: "a", href: link, style: { textDecoration: "none" }, target: "_blank", rel: "noopener noreferrer", children: link }) })
178
+ /* @__PURE__ */ jsx(LinkDiv, { children: /* @__PURE__ */ jsx(Box, { component: "a", href: link, style: { textDecoration: "none" }, target: "_blank", rel: "noopener noreferrer", children: formatLinkDisplay(link) }) })
159
179
  ]
160
180
  },
161
181
  link
@@ -9,15 +9,15 @@ import { joinURL } from "ufo";
9
9
  import Button from "@arcblock/ux/lib/Button";
10
10
  import PhoneInput, { validatePhoneNumber } from "@arcblock/ux/lib/PhoneInput";
11
11
  import cloneDeep from "lodash/cloneDeep";
12
+ import { mergeSx } from "@arcblock/ux/lib/Util/style";
12
13
  import { useCreation, useMemoizedFn, useReactive } from "ahooks";
13
14
  import { useMemo, useRef, useState, memo, forwardRef, useEffect, lazy } from "react";
14
15
  import { translate } from "@arcblock/ux/lib/Locale/util";
15
16
  import isEmail from "validator/lib/isEmail";
16
- import { temp as colors } from "@arcblock/ux/lib/Colors";
17
17
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
18
18
  import { useBrowser } from "@arcblock/react-hooks";
19
19
  import { translations } from "../../libs/locales.js";
20
- import EditableField from "../editable-field.js";
20
+ import EditableField, { commonInputStyle, inputFieldStyle } from "../editable-field.js";
21
21
  import { LinkPreviewInput } from "./link-preview-input.js";
22
22
  import { currentTimezone, defaultButtonStyle, primaryButtonStyle } from "./utils.js";
23
23
  import { TimezoneSelect } from "./timezone-select.js";
@@ -316,24 +316,7 @@ export default function UserMetadataComponent({
316
316
  value: phoneValue,
317
317
  error: !!validateMsg.phone,
318
318
  helperText: validateMsg.phone,
319
- sx: {
320
- width: "100%",
321
- ".MuiOutlinedInput-root": {
322
- "&:hover": {
323
- fieldset: {
324
- borderColor: colors.dividerColor
325
- }
326
- },
327
- "&.Mui-focused": {
328
- fieldset: {
329
- borderColor: colors.dividerColor
330
- }
331
- }
332
- },
333
- fieldset: {
334
- borderColor: colors.dividerColor
335
- }
336
- },
319
+ sx: mergeSx(inputFieldStyle, !validateMsg.phone ? commonInputStyle : {}),
337
320
  onChange: (value) => {
338
321
  const isValid = validatePhoneNumber(value.phone);
339
322
  if (!isValid) {
@@ -11,7 +11,7 @@ import { formatToDatetime } from "@arcblock/ux/lib/Util";
11
11
  import { DurationEnum, StatusEnum } from "../../../@types/index.js";
12
12
  import StatusDialog from "../status-dialog/index.js";
13
13
  import { translations } from "../../libs/locales.js";
14
- import { getTimeRemaining, isWithinTimeRange } from "./utils.js";
14
+ import { getTimeRemaining, isNotClear, isWithinTimeRange } from "./utils.js";
15
15
  const MeetingIcon = lazy(() => import("@arcblock/icons/lib/Meeting"));
16
16
  const CommunityIcon = lazy(() => import("@arcblock/icons/lib/Community"));
17
17
  const HolidayIcon = lazy(() => import("@arcblock/icons/lib/Holiday"));
@@ -67,6 +67,10 @@ export default function UserStatus({
67
67
  }, [status]);
68
68
  const clear = useInterval(() => {
69
69
  if (status?.value && status?.dateRange?.length === 2) {
70
+ const notClear = isNotClear(status);
71
+ if (notClear) {
72
+ return;
73
+ }
70
74
  const isWithin = isWithinTimeRange(status.dateRange);
71
75
  if (!isWithin) {
72
76
  pauseInterval();
@@ -107,7 +111,7 @@ export default function UserStatus({
107
111
  children: durationData
108
112
  };
109
113
  });
110
- }, [t, getDurationData]);
114
+ }, [t, getDurationData, locale]);
111
115
  const onOpenStatusSelector = (event) => {
112
116
  if (!isMyself) {
113
117
  return;
@@ -129,10 +133,17 @@ export default function UserStatus({
129
133
  const currentStatus = statusData.find((item) => item.id === status?.value);
130
134
  if (currentStatus) {
131
135
  const localeOption = locale === "zh" ? "zh-cn" : "en-us";
132
- const range = status?.dateRange?.map((item) => {
133
- return formatToDatetime(item, { locale: localeOption });
134
- });
135
- return `${currentStatus?.name}: ${range?.join("~")}`;
136
+ let range;
137
+ const notClear = isNotClear(status);
138
+ if (!notClear) {
139
+ range = status?.dateRange?.map((item) => {
140
+ return formatToDatetime(item, { locale: localeOption });
141
+ });
142
+ }
143
+ if (range && range.length > 0) {
144
+ return `${currentStatus.name}: ${range.join("~")}`;
145
+ }
146
+ return currentStatus.name;
136
147
  }
137
148
  return null;
138
149
  }, [status, statusData, locale]);
@@ -14,6 +14,12 @@ export declare const getStatusDuration: (status: UserMetadata["status"]) => any[
14
14
  * @returns
15
15
  */
16
16
  export declare const isWithinTimeRange: (dateRange: [Date, Date]) => any;
17
+ /**
18
+ * 判断状态持续时间是否为不可清除
19
+ * @param status
20
+ * @returns
21
+ */
22
+ export declare const isNotClear: (status: UserMetadata["status"]) => any;
17
23
  /**
18
24
  * 获取当前时间距离结束时间还有多久
19
25
  */
@@ -52,6 +52,9 @@ export const getStatusDuration = (status) => {
52
52
  case DurationEnum.ThisWeek:
53
53
  dateRange = [current, current.endOf("week")];
54
54
  break;
55
+ case DurationEnum.NoClear:
56
+ dateRange = [current, current];
57
+ break;
55
58
  default:
56
59
  break;
57
60
  }
@@ -61,6 +64,13 @@ export const isWithinTimeRange = (dateRange) => {
61
64
  const current = dayjs();
62
65
  return current.isAfter(dayjs(dateRange[0])) && current.isBefore(dayjs(dateRange[1]));
63
66
  };
67
+ export const isNotClear = (status) => {
68
+ const { duration, dateRange } = status ?? {};
69
+ if (!duration || !dateRange) {
70
+ return false;
71
+ }
72
+ return duration === DurationEnum.NoClear || dayjs(dateRange?.[0]).isSame(dayjs(dateRange?.[1]));
73
+ };
64
74
  export const getTimeRemaining = (date) => {
65
75
  const now = dayjs();
66
76
  const end = dayjs(date);
@@ -105,6 +105,7 @@ export declare const translations: {
105
105
  OffSick: string;
106
106
  WorkingRemotely: string;
107
107
  duration: {
108
+ NoClear: string;
108
109
  ThirtyMinutes: string;
109
110
  OneHour: string;
110
111
  FourHours: string;
@@ -131,6 +132,7 @@ export declare const translations: {
131
132
  quickSettings: string;
132
133
  selectEndTime: string;
133
134
  pleaseSelectTime: string;
135
+ invalidURL: string;
134
136
  timezonePhase: {
135
137
  dawn: string;
136
138
  morning: string;
@@ -245,6 +247,7 @@ export declare const translations: {
245
247
  OffSick: string;
246
248
  WorkingRemotely: string;
247
249
  duration: {
250
+ NoClear: string;
248
251
  ThirtyMinutes: string;
249
252
  OneHour: string;
250
253
  FourHours: string;
@@ -272,6 +275,7 @@ export declare const translations: {
272
275
  pleaseSelectTime: string;
273
276
  cleanStatus: string;
274
277
  quickSettings: string;
278
+ invalidURL: string;
275
279
  timezonePhase: {
276
280
  dawn: string;
277
281
  morning: string;
@@ -105,6 +105,7 @@ export const translations = {
105
105
  OffSick: "\u75C5\u5047",
106
106
  WorkingRemotely: "\u8FDC\u7A0B\u5DE5\u4F5C",
107
107
  duration: {
108
+ NoClear: "\u4E0D\u8981\u6E05\u9664",
108
109
  ThirtyMinutes: "30\u5206\u949F",
109
110
  OneHour: "1\u5C0F\u65F6",
110
111
  FourHours: "4\u5C0F\u65F6",
@@ -131,6 +132,7 @@ export const translations = {
131
132
  quickSettings: "\u5FEB\u6377\u8BBE\u7F6E",
132
133
  selectEndTime: "\u9009\u62E9\u7ED3\u675F\u65F6\u95F4",
133
134
  pleaseSelectTime: "\u8BF7\u9009\u62E9\u65F6\u95F4",
135
+ invalidURL: "\u65E0\u6548\u7684 URL",
134
136
  timezonePhase: {
135
137
  dawn: "\u51CC\u6668",
136
138
  morning: "\u4E0A\u5348",
@@ -245,6 +247,7 @@ export const translations = {
245
247
  OffSick: "Off Sick",
246
248
  WorkingRemotely: "Working Remotely",
247
249
  duration: {
250
+ NoClear: "Don't clear",
248
251
  ThirtyMinutes: "30 minutes",
249
252
  OneHour: "1 hour",
250
253
  FourHours: "4 hours",
@@ -272,6 +275,7 @@ export const translations = {
272
275
  pleaseSelectTime: "Please select time",
273
276
  cleanStatus: "Clean status",
274
277
  quickSettings: "Quick Settings",
278
+ invalidURL: "Invalid URL",
275
279
  timezonePhase: {
276
280
  dawn: "AM",
277
281
  morning: "AM",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/ui-react",
3
- "version": "2.12.20",
3
+ "version": "2.12.22",
4
4
  "description": "Some useful front-end web components that can be used in Blocklets.",
5
5
  "keywords": [
6
6
  "react",
@@ -33,8 +33,8 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@abtnode/constant": "^1.16.39",
36
- "@arcblock/bridge": "^2.12.20",
37
- "@arcblock/react-hooks": "^2.12.20",
36
+ "@arcblock/bridge": "^2.12.22",
37
+ "@arcblock/react-hooks": "^2.12.22",
38
38
  "@arcblock/ws": "^1.19.15",
39
39
  "@blocklet/did-space-react": "^1.0.33",
40
40
  "@iconify-icons/logos": "^1.2.36",
@@ -87,5 +87,5 @@
87
87
  "jest": "^29.7.0",
88
88
  "unbuild": "^2.0.0"
89
89
  },
90
- "gitHead": "c2b00ec84f106657107bb415f255855e22e1de26"
90
+ "gitHead": "f6f7c7949553c8c3e66516f13f768264f480b5d6"
91
91
  }
@@ -50,6 +50,7 @@ export type UserMetadataLink = {
50
50
  };
51
51
 
52
52
  export enum DurationEnum {
53
+ NoClear = 'no_clear',
53
54
  ThirtyMinutes = '30_minutes',
54
55
  OneHour = '1_hour',
55
56
  FourHours = '4_hours',
@@ -5,6 +5,7 @@ import { temp as colors } from '@arcblock/ux/lib/Colors';
5
5
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
6
6
  import { translate } from '@arcblock/ux/lib/Locale/util';
7
7
  import VerifiedIcon from '@mui/icons-material/Verified';
8
+ import { mergeSx } from '@arcblock/ux/lib/Util/style';
8
9
  import { translations } from '../libs/locales';
9
10
 
10
11
  interface EditableFieldProps {
@@ -28,8 +29,7 @@ interface EditableFieldProps {
28
29
  renderValue?: (value: string) => React.ReactNode;
29
30
  }
30
31
 
31
- const inputFieldStyle = {
32
- width: '100%',
32
+ export const commonInputStyle = {
33
33
  '.MuiOutlinedInput-root': {
34
34
  '&:hover': {
35
35
  fieldset: {
@@ -42,7 +42,16 @@ const inputFieldStyle = {
42
42
  },
43
43
  },
44
44
  },
45
+ };
45
46
 
47
+ export const inputFieldStyle = {
48
+ width: '100%',
49
+ '& .MuiFormHelperText-root': {
50
+ position: 'relative',
51
+ bottom: 0,
52
+ left: 0,
53
+ margin: 0,
54
+ },
46
55
  fieldset: {
47
56
  borderColor: colors.dividerColor,
48
57
  },
@@ -97,7 +106,7 @@ function EditableField({
97
106
  }}
98
107
  value={value}
99
108
  onChange={(e) => handleChange(e.target.value)}
100
- sx={inputFieldStyle}
109
+ sx={mergeSx(inputFieldStyle, !errorMsg ? commonInputStyle : {})}
101
110
  error={Boolean(errorMsg)}
102
111
  helperText={errorMsg}
103
112
  />
@@ -114,7 +123,7 @@ function EditableField({
114
123
  minRows: rows,
115
124
  placeholder,
116
125
  }}
117
- sx={inputFieldStyle}
126
+ sx={mergeSx(inputFieldStyle, !errorMsg ? commonInputStyle : {})}
118
127
  value={value}
119
128
  onChange={(e) => handleChange(e.target.value)}
120
129
  error={Boolean(errorMsg)}
@@ -10,14 +10,42 @@ import { useMemoizedFn } from 'ahooks';
10
10
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
11
11
  // eslint-disable-next-line import/no-extraneous-dependencies
12
12
  import LinkIcon from '@arcblock/icons/lib/Link';
13
- import { joinURL } from 'ufo';
13
+ import { mergeSx } from '@arcblock/ux/lib/Util/style';
14
+ import { joinURL, parseURL } from 'ufo';
14
15
  import { isValidUrl } from './utils';
15
16
  import { translations } from '../../libs/locales';
17
+ import { commonInputStyle, inputFieldStyle } from '../editable-field';
18
+
19
+ /**
20
+ * 格式化链接显示
21
+ * 对于 http/https 协议只显示域名,其他协议显示完整链接
22
+ */
23
+ function formatLinkDisplay(link: string): string {
24
+ try {
25
+ const url = parseURL(link);
26
+ // 如果是 http 或 https 协议,只显示域名
27
+ if (url.protocol === 'http:' || url.protocol === 'https:') {
28
+ return url.host || link;
29
+ }
30
+ // 其他协议显示完整链接
31
+ return link;
32
+ } catch {
33
+ return link;
34
+ }
35
+ }
16
36
 
17
37
  /**
18
38
  * 链接预览输入框
19
39
  */
20
- function LinkInput({ value, onChange, error }: { value: string; onChange: (value: string) => void; error: boolean }) {
40
+ function LinkInput({
41
+ value,
42
+ onChange,
43
+ errorMsg,
44
+ }: {
45
+ value: string;
46
+ onChange: (value: string) => void;
47
+ errorMsg: string;
48
+ }) {
21
49
  const handleUrlChange = (event: ChangeEvent<HTMLInputElement>) => {
22
50
  const inputValue = event.target.value;
23
51
  onChange(inputValue);
@@ -30,13 +58,9 @@ function LinkInput({ value, onChange, error }: { value: string; onChange: (value
30
58
  value={value}
31
59
  onChange={handleUrlChange}
32
60
  fullWidth
33
- error={error}
34
- helperText={error ? 'Invalid URL' : ''}
35
- sx={{
36
- fieldset: {
37
- borderColor: colors.dividerColor,
38
- },
39
- }}
61
+ error={!!errorMsg}
62
+ helperText={errorMsg}
63
+ sx={mergeSx(inputFieldStyle, !errorMsg ? commonInputStyle : {})}
40
64
  />
41
65
  </FormControl>
42
66
  );
@@ -93,7 +117,11 @@ function DynamicLinkForm({ links = [], onChange }: { links: string[]; onChange:
93
117
  {links.map((link, index) => (
94
118
  // eslint-disable-next-line react/no-array-index-key
95
119
  <Box key={index} display="flex" alignItems="flex-start" mb={1}>
96
- <LinkInput value={link} onChange={(value) => handleInputChange(index, value)} error={errors[index]} />
120
+ <LinkInput
121
+ value={link}
122
+ onChange={(value) => handleInputChange(index, value)}
123
+ errorMsg={errors[index] ? t('profile.invalidURL') : ''}
124
+ />
97
125
  <IconButton sx={{ color: colors.foregroundsFgMuted }} onClick={() => handleRemoveLink(index)}>
98
126
  <RemoveIcon />
99
127
  </IconButton>
@@ -198,7 +226,7 @@ export function LinkPreviewInput({
198
226
  <FaviconPreview link={link} />
199
227
  <LinkDiv>
200
228
  <Box component="a" href={link} style={{ textDecoration: 'none' }} target="_blank" rel="noopener noreferrer">
201
- {link}
229
+ {formatLinkDisplay(link)}
202
230
  </Box>
203
231
  </LinkDiv>
204
232
  </Box>
@@ -11,17 +11,17 @@ import { joinURL } from 'ufo';
11
11
  import Button from '@arcblock/ux/lib/Button';
12
12
  import PhoneInput, { PhoneValue, validatePhoneNumber } from '@arcblock/ux/lib/PhoneInput';
13
13
  import cloneDeep from 'lodash/cloneDeep';
14
+ import { mergeSx } from '@arcblock/ux/lib/Util/style';
14
15
 
15
16
  import { useCreation, useMemoizedFn, useReactive } from 'ahooks';
16
17
  import { useMemo, useRef, useState, memo, forwardRef, useEffect, lazy } from 'react';
17
18
  import { translate } from '@arcblock/ux/lib/Locale/util';
18
19
  import isEmail from 'validator/lib/isEmail';
19
- import { temp as colors } from '@arcblock/ux/lib/Colors';
20
20
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
21
21
  import { useBrowser } from '@arcblock/react-hooks';
22
22
  import { translations } from '../../libs/locales';
23
23
  import type { User, UserMetadata, UserPhoneProps } from '../../../@types';
24
- import EditableField from '../editable-field';
24
+ import EditableField, { commonInputStyle, inputFieldStyle } from '../editable-field';
25
25
  import { LinkPreviewInput } from './link-preview-input';
26
26
  import { currentTimezone, defaultButtonStyle, primaryButtonStyle } from './utils';
27
27
  import { TimezoneSelect } from './timezone-select';
@@ -330,24 +330,7 @@ export default function UserMetadataComponent({
330
330
  value={phoneValue}
331
331
  error={!!validateMsg.phone}
332
332
  helperText={validateMsg.phone}
333
- sx={{
334
- width: '100%',
335
- '.MuiOutlinedInput-root': {
336
- '&:hover': {
337
- fieldset: {
338
- borderColor: colors.dividerColor,
339
- },
340
- },
341
- '&.Mui-focused': {
342
- fieldset: {
343
- borderColor: colors.dividerColor,
344
- },
345
- },
346
- },
347
- fieldset: {
348
- borderColor: colors.dividerColor,
349
- },
350
- }}
333
+ sx={mergeSx(inputFieldStyle, !validateMsg.phone ? commonInputStyle : {})}
351
334
  onChange={(value: any) => {
352
335
  const isValid = validatePhoneNumber(value.phone);
353
336
  if (!isValid) {
@@ -13,7 +13,7 @@ import type { UserMetadata } from '../../../@types';
13
13
  import { DurationEnum, StatusEnum } from '../../../@types';
14
14
  import StatusDialog from '../status-dialog';
15
15
  import { translations } from '../../libs/locales';
16
- import { getTimeRemaining, isWithinTimeRange } from './utils';
16
+ import { getTimeRemaining, isNotClear, isWithinTimeRange } from './utils';
17
17
  import { StatusItem } from '../status-selector/menu-item';
18
18
 
19
19
  const MeetingIcon = lazy(() => import('@arcblock/icons/lib/Meeting'));
@@ -85,6 +85,10 @@ export default function UserStatus({
85
85
 
86
86
  const clear = useInterval(() => {
87
87
  if (status?.value && status?.dateRange?.length === 2) {
88
+ const notClear = isNotClear(status);
89
+ if (notClear) {
90
+ return;
91
+ }
88
92
  const isWithin = isWithinTimeRange(status.dateRange as [Date, Date]);
89
93
  if (!isWithin) {
90
94
  pauseInterval();
@@ -131,7 +135,7 @@ export default function UserStatus({
131
135
  children: durationData,
132
136
  };
133
137
  });
134
- }, [t, getDurationData]);
138
+ }, [t, getDurationData, locale]);
135
139
 
136
140
  const onOpenStatusSelector = (event: React.MouseEvent<HTMLDivElement>) => {
137
141
  if (!isMyself) {
@@ -158,10 +162,17 @@ export default function UserStatus({
158
162
  const currentStatus = statusData.find((item) => item.id === status?.value);
159
163
  if (currentStatus) {
160
164
  const localeOption = locale === 'zh' ? 'zh-cn' : 'en-us';
161
- const range = status?.dateRange?.map((item) => {
162
- return formatToDatetime(item, { locale: localeOption });
163
- });
164
- return `${currentStatus?.name}: ${range?.join('~')}`;
165
+ let range;
166
+ const notClear = isNotClear(status);
167
+ if (!notClear) {
168
+ range = status?.dateRange?.map((item) => {
169
+ return formatToDatetime(item, { locale: localeOption });
170
+ });
171
+ }
172
+ if (range && range.length > 0) {
173
+ return `${currentStatus.name}: ${range.join('~')}`;
174
+ }
175
+ return currentStatus.name;
165
176
  }
166
177
  return null;
167
178
  }, [status, statusData, locale]);
@@ -68,6 +68,9 @@ export const getStatusDuration = (status: UserMetadata['status']) => {
68
68
  case DurationEnum.ThisWeek:
69
69
  dateRange = [current, current.endOf('week')];
70
70
  break;
71
+ case DurationEnum.NoClear:
72
+ dateRange = [current, current];
73
+ break;
71
74
  default:
72
75
  break;
73
76
  }
@@ -84,6 +87,19 @@ export const isWithinTimeRange = (dateRange: [Date, Date]) => {
84
87
  return current.isAfter(dayjs(dateRange[0])) && current.isBefore(dayjs(dateRange[1]));
85
88
  };
86
89
 
90
+ /**
91
+ * 判断状态持续时间是否为不可清除
92
+ * @param status
93
+ * @returns
94
+ */
95
+ export const isNotClear = (status: UserMetadata['status']) => {
96
+ const { duration, dateRange } = status ?? {};
97
+ if (!duration || !dateRange) {
98
+ return false;
99
+ }
100
+ return duration === DurationEnum.NoClear || dayjs(dateRange?.[0]).isSame(dayjs(dateRange?.[1]));
101
+ };
102
+
87
103
  /**
88
104
  * 获取当前时间距离结束时间还有多久
89
105
  */
@@ -107,6 +107,7 @@ export const translations = {
107
107
  OffSick: '病假',
108
108
  WorkingRemotely: '远程工作',
109
109
  duration: {
110
+ NoClear: '不要清除',
110
111
  ThirtyMinutes: '30分钟',
111
112
  OneHour: '1小时',
112
113
  FourHours: '4小时',
@@ -133,6 +134,7 @@ export const translations = {
133
134
  quickSettings: '快捷设置',
134
135
  selectEndTime: '选择结束时间',
135
136
  pleaseSelectTime: '请选择时间',
137
+ invalidURL: '无效的 URL',
136
138
  timezonePhase: {
137
139
  dawn: '凌晨',
138
140
  morning: '上午',
@@ -249,6 +251,7 @@ export const translations = {
249
251
  OffSick: 'Off Sick',
250
252
  WorkingRemotely: 'Working Remotely',
251
253
  duration: {
254
+ NoClear: "Don't clear",
252
255
  ThirtyMinutes: '30 minutes',
253
256
  OneHour: '1 hour',
254
257
  FourHours: '4 hours',
@@ -276,6 +279,7 @@ export const translations = {
276
279
  pleaseSelectTime: 'Please select time',
277
280
  cleanStatus: 'Clean status',
278
281
  quickSettings: 'Quick Settings',
282
+ invalidURL: 'Invalid URL',
279
283
  timezonePhase: {
280
284
  dawn: 'AM',
281
285
  morning: 'AM',