@blocklet/ui-react 2.12.13 → 2.12.14

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 (35) hide show
  1. package/lib/@types/index.d.ts +2 -1
  2. package/lib/@types/index.js +1 -0
  3. package/lib/UserCenter/components/editable-field.d.ts +2 -1
  4. package/lib/UserCenter/components/editable-field.js +5 -1
  5. package/lib/UserCenter/components/status-dialog/date-picker.d.ts +10 -0
  6. package/lib/UserCenter/components/status-dialog/date-picker.js +52 -0
  7. package/lib/UserCenter/components/status-dialog/index.d.ts +12 -0
  8. package/lib/UserCenter/components/status-dialog/index.js +238 -0
  9. package/lib/UserCenter/components/status-selector/menu-item.d.ts +2 -0
  10. package/lib/UserCenter/components/user-center.js +8 -3
  11. package/lib/UserCenter/components/user-info/clock.js +3 -2
  12. package/lib/UserCenter/components/user-info/metadata.js +15 -62
  13. package/lib/UserCenter/components/user-info/timezone-select.d.ts +8 -0
  14. package/lib/UserCenter/components/user-info/timezone-select.js +99 -0
  15. package/lib/UserCenter/components/user-info/user-basic-info.js +4 -14
  16. package/lib/UserCenter/components/user-info/user-status.d.ts +2 -1
  17. package/lib/UserCenter/components/user-info/user-status.js +49 -14
  18. package/lib/UserCenter/components/user-info/utils.d.ts +23 -0
  19. package/lib/UserCenter/components/user-info/utils.js +25 -1
  20. package/lib/UserCenter/libs/locales.d.ts +12 -0
  21. package/lib/UserCenter/libs/locales.js +18 -6
  22. package/package.json +4 -4
  23. package/src/@types/index.ts +1 -0
  24. package/src/UserCenter/components/editable-field.tsx +6 -0
  25. package/src/UserCenter/components/status-dialog/date-picker.tsx +67 -0
  26. package/src/UserCenter/components/status-dialog/index.tsx +280 -0
  27. package/src/UserCenter/components/status-selector/menu-item.tsx +2 -0
  28. package/src/UserCenter/components/user-center.tsx +7 -2
  29. package/src/UserCenter/components/user-info/clock.tsx +3 -2
  30. package/src/UserCenter/components/user-info/metadata.tsx +17 -70
  31. package/src/UserCenter/components/user-info/timezone-select.tsx +114 -0
  32. package/src/UserCenter/components/user-info/user-basic-info.tsx +4 -11
  33. package/src/UserCenter/components/user-info/user-status.tsx +57 -13
  34. package/src/UserCenter/components/user-info/utils.ts +29 -1
  35. package/src/UserCenter/libs/locales.ts +14 -2
@@ -12,7 +12,7 @@ import { temp as colors } from "@arcblock/ux/lib/Colors";
12
12
  import { parseURL, joinURL } from "ufo";
13
13
  import { translations } from "../../libs/locales.js";
14
14
  import { formatAxiosError } from "../../libs/utils.js";
15
- import { getStatusDuration, isValidUrl } from "./utils.js";
15
+ import { currentTimezone, getStatusDuration, isValidUrl } from "./utils.js";
16
16
  import SwitchRole from "./switch-role.js";
17
17
  import UserMetadataComponent from "./metadata.js";
18
18
  import UserStatus from "./user-status.js";
@@ -49,7 +49,7 @@ export default function UserBasicInfo({
49
49
  // @ts-ignore
50
50
  metadata: {
51
51
  ...user?.metadata ?? { joinedAt: user?.createdAt, email: user?.email, phone: user?.phone },
52
- status: v
52
+ status: v || {}
53
53
  }
54
54
  });
55
55
  } catch (err) {
@@ -146,6 +146,7 @@ export default function UserBasicInfo({
146
146
  isMobile,
147
147
  size: rest.size || (isMobile ? 64 : 100),
148
148
  isMyself,
149
+ timezone: user?.metadata?.timezone || currentTimezone,
149
150
  status: userStatus,
150
151
  onChange: onUpdateUserStatus
151
152
  }
@@ -178,18 +179,7 @@ export default function UserBasicInfo({
178
179
  ]
179
180
  }
180
181
  ),
181
- /* @__PURE__ */ jsx(
182
- DID,
183
- {
184
- did: user.did,
185
- showQrcode: true,
186
- copyable: true,
187
- compact: !showFullDid,
188
- responsive: !showFullDid,
189
- locale,
190
- style: { maxWidth: 260 }
191
- }
192
- )
182
+ /* @__PURE__ */ jsx(DID, { did: user.did, showQrcode: true, copyable: true, compact: !showFullDid, responsive: !showFullDid, locale })
193
183
  ]
194
184
  }
195
185
  )
@@ -1,8 +1,9 @@
1
1
  import type { UserMetadata } from '../../../@types';
2
- export default function UserStatus({ isMobile, size, isMyself, status, onChange, }: {
2
+ export default function UserStatus({ isMobile, size, isMyself, status, onChange, timezone, }: {
3
3
  isMobile?: boolean;
4
4
  size: number;
5
5
  isMyself: boolean;
6
6
  status: UserMetadata['status'];
7
7
  onChange: (v: UserMetadata['status']) => void;
8
+ timezone?: string;
8
9
  }): import("react").JSX.Element;
@@ -2,14 +2,14 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import Badge from "@mui/material/Badge";
3
3
  import Box from "@mui/material/Box";
4
4
  import styled from "@emotion/styled";
5
- import { lazy, useCallback, useEffect, useMemo, useState } from "react";
5
+ import { lazy, Suspense, useCallback, useEffect, useMemo, useState } from "react";
6
6
  import { useCreation, useMemoizedFn, useInterval, useUnmount } from "ahooks";
7
7
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
8
8
  import Tooltip from "@mui/material/Tooltip";
9
9
  import { translate } from "@arcblock/ux/lib/Locale/util";
10
10
  import { formatToDatetime } from "@arcblock/ux/lib/Util";
11
11
  import { DurationEnum, StatusEnum } from "../../../@types/index.js";
12
- import StatusSelector from "../status-selector/index.js";
12
+ import StatusDialog from "../status-dialog/index.js";
13
13
  import { translations } from "../../libs/locales.js";
14
14
  import { getTimeRemaining, isWithinTimeRange } from "./utils.js";
15
15
  const MeetingIcon = lazy(() => import("@arcblock/icons/lib/Meeting"));
@@ -24,12 +24,35 @@ const StatusIconMap = {
24
24
  [StatusEnum.OffSick]: OffSickIcon,
25
25
  [StatusEnum.WorkingRemotely]: WorkingRemotelyIcon
26
26
  };
27
+ const QuickSettings = {
28
+ [StatusEnum.Meeting]: {
29
+ duration: DurationEnum.OneHour,
30
+ durationName: "userStatus.duration.OneHour"
31
+ },
32
+ [StatusEnum.Community]: {
33
+ duration: DurationEnum.ThirtyMinutes,
34
+ durationName: "userStatus.duration.ThirtyMinutes"
35
+ },
36
+ [StatusEnum.Holiday]: {
37
+ duration: DurationEnum.ThisWeek,
38
+ durationName: "userStatus.duration.ThisWeek"
39
+ },
40
+ [StatusEnum.OffSick]: {
41
+ duration: DurationEnum.Today,
42
+ durationName: "userStatus.duration.Today"
43
+ },
44
+ [StatusEnum.WorkingRemotely]: {
45
+ duration: DurationEnum.ThisWeek,
46
+ durationName: "userStatus.duration.ThisWeek"
47
+ }
48
+ };
27
49
  export default function UserStatus({
28
50
  isMobile,
29
51
  size,
30
52
  isMyself,
31
53
  status,
32
- onChange
54
+ onChange,
55
+ timezone
33
56
  }) {
34
57
  const { locale } = useLocaleContext();
35
58
  const t = useMemoizedFn((key, data = {}) => {
@@ -74,12 +97,16 @@ export default function UserStatus({
74
97
  }, [t]);
75
98
  const statusData = useCreation(() => {
76
99
  const durationData = getDurationData();
77
- return Object.keys(StatusEnum).map((key) => ({
78
- id: StatusEnum[key],
79
- name: t(`userStatus.${key}`),
80
- icon: StatusIconMap[StatusEnum[key]],
81
- children: durationData
82
- }));
100
+ return Object.keys(StatusEnum).map((key) => {
101
+ const quickSetting = QuickSettings[StatusEnum[key]];
102
+ return {
103
+ id: StatusEnum[key],
104
+ name: t(`userStatus.${key}`),
105
+ icon: StatusIconMap[StatusEnum[key]],
106
+ ...quickSetting ? { duration: quickSetting.duration, durationName: t(quickSetting.durationName) } : {},
107
+ children: durationData
108
+ };
109
+ });
83
110
  }, [t, getDurationData]);
84
111
  const onOpenStatusSelector = (event) => {
85
112
  if (!isMyself) {
@@ -94,9 +121,7 @@ export default function UserStatus({
94
121
  if (!isMyself) {
95
122
  return;
96
123
  }
97
- if (v) {
98
- onChange(v);
99
- }
124
+ onChange(v);
100
125
  onCloseStatusSelector();
101
126
  };
102
127
  const StatusIcon = StatusIconMap[status?.value];
@@ -121,10 +146,20 @@ export default function UserStatus({
121
146
  alignItems: "center",
122
147
  justifyContent: "center",
123
148
  onClick: onOpenStatusSelector,
124
- children: StatusIcon ? /* @__PURE__ */ jsx(StatusIcon, { style: { width: 16, height: 16 } }) : /* @__PURE__ */ jsx(Badge, { color: "success", variant: "dot" })
149
+ children: StatusIcon ? /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(StatusIcon, { style: { width: 16, height: 16 } }) }) : /* @__PURE__ */ jsx(Badge, { color: "success", variant: "dot" })
125
150
  }
126
151
  ) }),
127
- /* @__PURE__ */ jsx(StatusSelector, { selected: status, data: statusData, open, onSelect: onStatusChange, anchorEl })
152
+ open && /* @__PURE__ */ jsx(
153
+ StatusDialog,
154
+ {
155
+ selected: status,
156
+ data: statusData,
157
+ open,
158
+ onSelect: onStatusChange,
159
+ onClose: onCloseStatusSelector,
160
+ timezone
161
+ }
162
+ )
128
163
  ] });
129
164
  }
130
165
  const StatusDiv = styled("div")`
@@ -1,4 +1,5 @@
1
1
  import { UserMetadata } from '../../../@types';
2
+ export declare const currentTimezone: any;
2
3
  export declare const getTimezones: () => any;
3
4
  export declare const isValidUrl: (url: string) => boolean;
4
5
  /**
@@ -17,3 +18,25 @@ export declare const isWithinTimeRange: (dateRange: [Date, Date]) => any;
17
18
  * 获取当前时间距离结束时间还有多久
18
19
  */
19
20
  export declare const getTimeRemaining: (date: Date) => number;
21
+ export declare const defaultButtonStyle: {
22
+ color: string;
23
+ borderColor: string;
24
+ backgroundColor: string;
25
+ '&:hover': {
26
+ borderColor: string;
27
+ backgroundColor: string;
28
+ };
29
+ py: number;
30
+ borderRadius: string;
31
+ };
32
+ export declare const primaryButtonStyle: {
33
+ color: string;
34
+ borderColor: string;
35
+ backgroundColor: string;
36
+ '&:hover': {
37
+ borderColor: string;
38
+ backgroundColor: string;
39
+ };
40
+ py: number;
41
+ borderRadius: string;
42
+ };
@@ -1,5 +1,6 @@
1
1
  import moment from "moment-timezone";
2
2
  import dayjs from "dayjs";
3
+ import { temp as colors } from "@arcblock/ux/lib/Colors";
3
4
  import { DurationEnum } from "../../../@types/index.js";
4
5
  const HOUR = 3600;
5
6
  const MINUTES_30 = 1800;
@@ -7,6 +8,7 @@ const MINUTES_10 = 600;
7
8
  const MINUTES_5 = 300;
8
9
  const MINUTES_1 = 60;
9
10
  const SECOND = 1;
11
+ export const currentTimezone = moment.tz.guess();
10
12
  export const getTimezones = () => {
11
13
  const timezones = moment.tz.names();
12
14
  const formattedTimezones = timezones.map((tz) => {
@@ -32,7 +34,7 @@ export const isValidUrl = (url) => {
32
34
  return urlPattern.test(url);
33
35
  };
34
36
  export const getStatusDuration = (status) => {
35
- let dateRange = [];
37
+ let dateRange = status?.dateRange?.map((d) => dayjs(d)) ?? [];
36
38
  const current = dayjs();
37
39
  switch (status?.duration) {
38
40
  case DurationEnum.ThirtyMinutes:
@@ -84,3 +86,25 @@ export const getTimeRemaining = (date) => {
84
86
  }
85
87
  return 0;
86
88
  };
89
+ export const defaultButtonStyle = {
90
+ color: colors.foregroundsFgBase,
91
+ borderColor: colors.strokeBorderBase,
92
+ backgroundColor: colors.buttonsButtonNeutral,
93
+ "&:hover": {
94
+ borderColor: colors.strokeBorderBase,
95
+ backgroundColor: colors.buttonsButtonNeutralHover
96
+ },
97
+ py: 0.5,
98
+ borderRadius: "8px"
99
+ };
100
+ export const primaryButtonStyle = {
101
+ color: colors.buttonsButtonNeutral,
102
+ borderColor: colors.foregroundsFgInteractive,
103
+ backgroundColor: colors.foregroundsFgInteractive,
104
+ "&:hover": {
105
+ borderColor: colors.foregroundsFgInteractive,
106
+ backgroundColor: colors.foregroundsFgInteractive
107
+ },
108
+ py: 0.5,
109
+ borderRadius: "8px"
110
+ };
@@ -110,12 +110,14 @@ export declare const translations: {
110
110
  FourHours: string;
111
111
  Today: string;
112
112
  ThisWeek: string;
113
+ Custom: string;
113
114
  };
114
115
  };
115
116
  profile: {
116
117
  addLink: string;
117
118
  location: string;
118
119
  timezone: string;
120
+ bio: string;
119
121
  setStatus: string;
120
122
  localTime: string;
121
123
  email: string;
@@ -125,6 +127,10 @@ export declare const translations: {
125
127
  editProfile: string;
126
128
  removeStatusAfter: string;
127
129
  justForYou: string;
130
+ cleanStatus: string;
131
+ quickSettings: string;
132
+ selectEndTime: string;
133
+ pleaseSelectTime: string;
128
134
  };
129
135
  };
130
136
  en: {
@@ -238,12 +244,14 @@ export declare const translations: {
238
244
  FourHours: string;
239
245
  Today: string;
240
246
  ThisWeek: string;
247
+ Custom: string;
241
248
  };
242
249
  };
243
250
  profile: {
244
251
  addLink: string;
245
252
  location: string;
246
253
  timezone: string;
254
+ bio: string;
247
255
  setStatus: string;
248
256
  localTime: string;
249
257
  email: string;
@@ -254,6 +262,10 @@ export declare const translations: {
254
262
  removeStatusAfter: string;
255
263
  justForYou: string;
256
264
  maxInputLength: string;
265
+ selectEndTime: string;
266
+ pleaseSelectTime: string;
267
+ cleanStatus: string;
268
+ quickSettings: string;
257
269
  };
258
270
  };
259
271
  };
@@ -109,13 +109,15 @@ export const translations = {
109
109
  OneHour: "1\u5C0F\u65F6",
110
110
  FourHours: "4\u5C0F\u65F6",
111
111
  Today: "\u4ECA\u5929",
112
- ThisWeek: "\u672C\u5468"
112
+ ThisWeek: "\u672C\u5468",
113
+ Custom: "\u81EA\u5B9A\u4E49\u65F6\u95F4"
113
114
  }
114
115
  },
115
116
  profile: {
116
117
  addLink: "\u6DFB\u52A0\u8FDE\u63A5",
117
118
  location: "\u4F4D\u7F6E",
118
119
  timezone: "\u65F6\u533A",
120
+ bio: "\u4E2A\u4EBA\u7B80\u4ECB",
119
121
  setStatus: "\u8BBE\u7F6E\u72B6\u6001",
120
122
  localTime: "\u5F53\u5730\u65F6\u95F4:",
121
123
  email: "\u90AE\u7BB1\u5730\u5740",
@@ -123,8 +125,12 @@ export const translations = {
123
125
  phone: "\u7535\u8BDD\u53F7\u7801",
124
126
  phoneInvalid: "\u7535\u8BDD\u53F7\u7801\u683C\u5F0F\u4E0D\u6B63\u786E",
125
127
  editProfile: "\u7F16\u8F91\u8D44\u6599",
126
- removeStatusAfter: "\u79FB\u9664\u72B6\u6001",
127
- justForYou: "\u4EC5\u5BF9\u60A8\u53EF\u89C1"
128
+ removeStatusAfter: "\u5728...\u4E4B\u540E\u79FB\u9664\u72B6\u6001",
129
+ justForYou: "\u4EC5\u5BF9\u60A8\u53EF\u89C1",
130
+ cleanStatus: "\u6E05\u9664\u72B6\u6001",
131
+ quickSettings: "\u5FEB\u6377\u8BBE\u7F6E",
132
+ selectEndTime: "\u9009\u62E9\u7ED3\u675F\u65F6\u95F4",
133
+ pleaseSelectTime: "\u8BF7\u9009\u62E9\u65F6\u95F4"
128
134
  }
129
135
  },
130
136
  en: {
@@ -237,13 +243,15 @@ export const translations = {
237
243
  OneHour: "1 hour",
238
244
  FourHours: "4 hours",
239
245
  Today: "Today",
240
- ThisWeek: "This Week"
246
+ ThisWeek: "This Week",
247
+ Custom: "Custom"
241
248
  }
242
249
  },
243
250
  profile: {
244
251
  addLink: "Add link",
245
252
  location: "Location",
246
253
  timezone: "Timezone",
254
+ bio: "Bio",
247
255
  setStatus: "Set a Status",
248
256
  localTime: "Local Time: ",
249
257
  email: "Email",
@@ -251,9 +259,13 @@ export const translations = {
251
259
  phone: "Phone",
252
260
  phoneInvalid: "Phone number is invalid",
253
261
  editProfile: "Edit Profile",
254
- removeStatusAfter: "Remove status after",
262
+ removeStatusAfter: "Remove status after...",
255
263
  justForYou: "Visible to you only",
256
- maxInputLength: "Max input length is"
264
+ maxInputLength: "Max input length is",
265
+ selectEndTime: "Select end time",
266
+ pleaseSelectTime: "Please select time",
267
+ cleanStatus: "Clean status",
268
+ quickSettings: "Quick Settings"
257
269
  }
258
270
  }
259
271
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/ui-react",
3
- "version": "2.12.13",
3
+ "version": "2.12.14",
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.13",
37
- "@arcblock/react-hooks": "^2.12.13",
36
+ "@arcblock/bridge": "^2.12.14",
37
+ "@arcblock/react-hooks": "^2.12.14",
38
38
  "@arcblock/ws": "^1.19.15",
39
39
  "@blocklet/did-space-react": "^1.0.26",
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": "82ab8092972356c7746cb2b1c77bd133ef00ebca"
90
+ "gitHead": "ac2b40cdc5ca3cf446e5a1ced41d5eada7860cbd"
91
91
  }
@@ -55,6 +55,7 @@ export enum DurationEnum {
55
55
  FourHours = '4_hours',
56
56
  Today = 'today',
57
57
  ThisWeek = 'this_week',
58
+ Custom = 'custom',
58
59
  }
59
60
 
60
61
  export enum StatusEnum {
@@ -24,6 +24,7 @@ interface EditableFieldProps {
24
24
  style?: React.CSSProperties;
25
25
  verified?: boolean;
26
26
  errorMsg?: string;
27
+ canEdit?: boolean;
27
28
  }
28
29
 
29
30
  const inputFieldStyle = {
@@ -63,6 +64,7 @@ function EditableField({
63
64
  inline = true,
64
65
  style = {},
65
66
  verified = false,
67
+ canEdit = true,
66
68
  }: EditableFieldProps) {
67
69
  const { locale } = useLocaleContext();
68
70
  const t = useMemoizedFn((key, data = {}) => {
@@ -131,6 +133,10 @@ function EditableField({
131
133
  );
132
134
  }, [value, handleChange, component, placeholder, rows, children]);
133
135
 
136
+ if (!canEdit && editable) {
137
+ return null;
138
+ }
139
+
134
140
  if (!editable) {
135
141
  return value ? (
136
142
  <Tooltip title={tooltip} placement="top">
@@ -0,0 +1,67 @@
1
+ import React, { useRef } from 'react';
2
+ import { TextField, FormControl, FormHelperText } from '@mui/material';
3
+ import dayjs from 'dayjs';
4
+ import { useCreation } from 'ahooks';
5
+
6
+ interface DateTimeInputProps {
7
+ value: Date;
8
+ onChange: (value: Date) => void;
9
+ error?: boolean;
10
+ helperText?: string;
11
+ label?: string;
12
+ timezone?: string;
13
+ }
14
+
15
+ export default function DateTimeInput({ value, onChange, error, helperText, label, timezone }: DateTimeInputProps) {
16
+ const inputRef = useRef<HTMLInputElement>(null);
17
+ const minDateTime = dayjs().format('YYYY-MM-DDTHH:mm');
18
+
19
+ const formatValue = useCreation(() => {
20
+ let date = dayjs();
21
+ if (value) {
22
+ date = timezone ? dayjs(value).tz(timezone) : dayjs(value);
23
+ }
24
+
25
+ return date.format('YYYY-MM-DD HH:mm');
26
+ }, [value, timezone]);
27
+
28
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
29
+ onChange(new Date(e.target.value));
30
+ };
31
+
32
+ // 处理点击事件
33
+ const handleClick = () => {
34
+ // 通过编程方式触发原生日期选择器
35
+ inputRef.current?.showPicker();
36
+ };
37
+
38
+ return (
39
+ <FormControl fullWidth error={error}>
40
+ <TextField
41
+ inputRef={inputRef}
42
+ type="datetime-local"
43
+ value={formatValue}
44
+ onChange={handleChange}
45
+ inputProps={{
46
+ min: minDateTime,
47
+ style: { cursor: 'pointer' },
48
+ }}
49
+ label={label}
50
+ error={error}
51
+ onClick={handleClick}
52
+ InputLabelProps={{
53
+ shrink: true,
54
+ }}
55
+ sx={{
56
+ '& .MuiInputBase-root': {
57
+ cursor: 'pointer',
58
+ },
59
+ '& .MuiOutlinedInput-notchedOutline': {
60
+ cursor: 'pointer',
61
+ },
62
+ }}
63
+ />
64
+ {helperText && <FormHelperText>{helperText}</FormHelperText>}
65
+ </FormControl>
66
+ );
67
+ }