@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
@@ -47,7 +47,8 @@ export declare enum DurationEnum {
47
47
  OneHour = "1_hour",
48
48
  FourHours = "4_hours",
49
49
  Today = "today",
50
- ThisWeek = "this_week"
50
+ ThisWeek = "this_week",
51
+ Custom = "custom"
51
52
  }
52
53
  export declare enum StatusEnum {
53
54
  Meeting = "meeting",
@@ -4,6 +4,7 @@ export var DurationEnum = /* @__PURE__ */ ((DurationEnum2) => {
4
4
  DurationEnum2["FourHours"] = "4_hours";
5
5
  DurationEnum2["Today"] = "today";
6
6
  DurationEnum2["ThisWeek"] = "this_week";
7
+ DurationEnum2["Custom"] = "custom";
7
8
  return DurationEnum2;
8
9
  })(DurationEnum || {});
9
10
  export var StatusEnum = /* @__PURE__ */ ((StatusEnum2) => {
@@ -17,6 +17,7 @@ interface EditableFieldProps {
17
17
  style?: React.CSSProperties;
18
18
  verified?: boolean;
19
19
  errorMsg?: string;
20
+ canEdit?: boolean;
20
21
  }
21
- declare function EditableField({ value, onChange, onValueValidate, errorMsg, editable, component, placeholder, rows, maxLength, icon, label, children, tooltip, inline, style, verified, }: EditableFieldProps): JSX.Element | null;
22
+ declare function EditableField({ value, onChange, onValueValidate, errorMsg, editable, component, placeholder, rows, maxLength, icon, label, children, tooltip, inline, style, verified, canEdit, }: EditableFieldProps): JSX.Element | null;
22
23
  export default EditableField;
@@ -42,7 +42,8 @@ function EditableField({
42
42
  tooltip,
43
43
  inline = true,
44
44
  style = {},
45
- verified = false
45
+ verified = false,
46
+ canEdit = true
46
47
  }) {
47
48
  const { locale } = useLocaleContext();
48
49
  const t = useMemoizedFn((key, data = {}) => {
@@ -116,6 +117,9 @@ function EditableField({
116
117
  )
117
118
  ] });
118
119
  }, [value, handleChange, component, placeholder, rows, children]);
120
+ if (!canEdit && editable) {
121
+ return null;
122
+ }
119
123
  if (!editable) {
120
124
  return value ? /* @__PURE__ */ jsx(Tooltip, { title: tooltip, placement: "top", children: /* @__PURE__ */ jsxs(
121
125
  Typography,
@@ -0,0 +1,10 @@
1
+ interface DateTimeInputProps {
2
+ value: Date;
3
+ onChange: (value: Date) => void;
4
+ error?: boolean;
5
+ helperText?: string;
6
+ label?: string;
7
+ timezone?: string;
8
+ }
9
+ export default function DateTimeInput({ value, onChange, error, helperText, label, timezone }: DateTimeInputProps): JSX.Element;
10
+ export {};
@@ -0,0 +1,52 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useRef } from "react";
3
+ import { TextField, FormControl, FormHelperText } from "@mui/material";
4
+ import dayjs from "dayjs";
5
+ import { useCreation } from "ahooks";
6
+ export default function DateTimeInput({ value, onChange, error, helperText, label, timezone }) {
7
+ const inputRef = useRef(null);
8
+ const minDateTime = dayjs().format("YYYY-MM-DDTHH:mm");
9
+ const formatValue = useCreation(() => {
10
+ let date = dayjs();
11
+ if (value) {
12
+ date = timezone ? dayjs(value).tz(timezone) : dayjs(value);
13
+ }
14
+ return date.format("YYYY-MM-DD HH:mm");
15
+ }, [value, timezone]);
16
+ const handleChange = (e) => {
17
+ onChange(new Date(e.target.value));
18
+ };
19
+ const handleClick = () => {
20
+ inputRef.current?.showPicker();
21
+ };
22
+ return /* @__PURE__ */ jsxs(FormControl, { fullWidth: true, error, children: [
23
+ /* @__PURE__ */ jsx(
24
+ TextField,
25
+ {
26
+ inputRef,
27
+ type: "datetime-local",
28
+ value: formatValue,
29
+ onChange: handleChange,
30
+ inputProps: {
31
+ min: minDateTime,
32
+ style: { cursor: "pointer" }
33
+ },
34
+ label,
35
+ error,
36
+ onClick: handleClick,
37
+ InputLabelProps: {
38
+ shrink: true
39
+ },
40
+ sx: {
41
+ "& .MuiInputBase-root": {
42
+ cursor: "pointer"
43
+ },
44
+ "& .MuiOutlinedInput-notchedOutline": {
45
+ cursor: "pointer"
46
+ }
47
+ }
48
+ }
49
+ ),
50
+ helperText && /* @__PURE__ */ jsx(FormHelperText, { children: helperText })
51
+ ] });
52
+ }
@@ -0,0 +1,12 @@
1
+ import { UserMetadata } from '../../../@types';
2
+ import { StatusItem } from '../status-selector/menu-item';
3
+ interface StatusDialogProps {
4
+ open: boolean;
5
+ onClose: () => void;
6
+ data: Array<StatusItem>;
7
+ selected: UserMetadata['status'];
8
+ onSelect: (v: UserMetadata['status']) => void;
9
+ timezone?: string;
10
+ }
11
+ export default function StatusDialog({ open, onClose, data, selected, onSelect, timezone }: StatusDialogProps): import("react").JSX.Element;
12
+ export {};
@@ -0,0 +1,238 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState, createElement, Suspense, useMemo } from "react";
3
+ import {
4
+ Dialog,
5
+ DialogTitle,
6
+ DialogContent,
7
+ DialogActions,
8
+ Button,
9
+ FormControl,
10
+ InputLabel,
11
+ Select,
12
+ MenuItem,
13
+ ListItemIcon,
14
+ Box,
15
+ Typography,
16
+ IconButton
17
+ } from "@mui/material";
18
+ import dayjs from "dayjs";
19
+ import CloseIcon from "@mui/icons-material/Close";
20
+ import { translate } from "@arcblock/ux/lib/Locale/util";
21
+ import { useMemoizedFn } from "ahooks";
22
+ import { temp as colors } from "@arcblock/ux/lib/Colors";
23
+ import ArrowDownwardIcon from "@arcblock/icons/lib/ArrowDown";
24
+ import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
25
+ import { DurationEnum } from "../../../@types/index.js";
26
+ import { translations } from "../../libs/locales.js";
27
+ import DateTimeInput from "./date-picker.js";
28
+ import { defaultButtonStyle, primaryButtonStyle } from "../user-info/utils.js";
29
+ const selectStyle = {
30
+ padding: "8px 16px",
31
+ borderRadius: "8px",
32
+ "&:hover": {
33
+ "fieldset.MuiOutlinedInput-notchedOutline": {
34
+ borderColor: colors.dividerColor
35
+ }
36
+ },
37
+ "fieldset.MuiOutlinedInput-notchedOutline": {
38
+ borderColor: colors.dividerColor,
39
+ borderRadius: "8px"
40
+ },
41
+ ".MuiSelect-select": {
42
+ padding: "0 !important",
43
+ display: "flex",
44
+ alignItems: "center"
45
+ }
46
+ };
47
+ export default function StatusDialog({ open, onClose, data, selected, onSelect, timezone }) {
48
+ const { locale } = useLocaleContext();
49
+ const t = useMemoizedFn((key, _data = {}) => {
50
+ return translate(translations, key, locale, "en", _data);
51
+ });
52
+ const [status, setStatus] = useState(selected?.value || "");
53
+ const [duration, setDuration] = useState(selected?.duration || "");
54
+ const [customDate, setCustomDate] = useState(selected?.dateRange?.[1] || "");
55
+ const [changed, setChanged] = useState(false);
56
+ useEffect(() => {
57
+ setStatus(selected?.value || "");
58
+ setDuration(selected?.duration || "");
59
+ setCustomDate(selected?.dateRange?.[1] || "");
60
+ }, [selected]);
61
+ const isValid = useMemo(() => {
62
+ return status && duration;
63
+ }, [status, duration]);
64
+ const selectedStatus = data.find((item) => item.id === status);
65
+ const handleQuickSettingClick = (item) => {
66
+ onSelect({
67
+ value: item.id,
68
+ ...item.duration ? { duration: item.duration } : {}
69
+ });
70
+ onClose();
71
+ };
72
+ const handleSubmit = (clean = false) => {
73
+ if (clean) {
74
+ onSelect(void 0);
75
+ } else {
76
+ const current = dayjs();
77
+ onSelect({
78
+ value: status,
79
+ ...duration ? { duration } : {},
80
+ ...duration === DurationEnum.Custom && customDate ? { dateRange: [current.toDate(), dayjs(customDate).toDate()] } : {}
81
+ });
82
+ }
83
+ onClose();
84
+ };
85
+ return /* @__PURE__ */ jsxs(
86
+ Dialog,
87
+ {
88
+ open,
89
+ onClose,
90
+ maxWidth: "xs",
91
+ fullWidth: true,
92
+ PaperProps: {
93
+ sx: {
94
+ borderRadius: "8px"
95
+ }
96
+ },
97
+ children: [
98
+ /* @__PURE__ */ jsxs(DialogTitle, { sx: { borderBottom: `1px solid ${colors.dividerColor}` }, children: [
99
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", sx: { fontSize: "16px !important", mb: 0 }, children: t("profile.setStatus") }),
100
+ /* @__PURE__ */ jsx(
101
+ IconButton,
102
+ {
103
+ "aria-label": "close",
104
+ onClick: onClose,
105
+ sx: {
106
+ position: "absolute",
107
+ right: 8,
108
+ top: 8,
109
+ color: (theme) => theme.palette.grey[500]
110
+ },
111
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
112
+ }
113
+ )
114
+ ] }),
115
+ /* @__PURE__ */ jsxs(DialogContent, { children: [
116
+ /* @__PURE__ */ jsxs(FormControl, { fullWidth: true, sx: { mt: 2 }, children: [
117
+ /* @__PURE__ */ jsx(InputLabel, { size: "small", sx: { fontSize: "14px" }, children: t("profile.setStatus") }),
118
+ /* @__PURE__ */ jsx(
119
+ Select,
120
+ {
121
+ value: status,
122
+ label: t("profile.setStatus"),
123
+ onChange: (e) => {
124
+ setStatus(e.target.value);
125
+ setChanged(true);
126
+ },
127
+ variant: "outlined",
128
+ IconComponent: (props) => /* @__PURE__ */ jsx(ArrowDownwardIcon, { ...props, width: 20, height: 20 }),
129
+ sx: selectStyle,
130
+ children: data.map((item) => /* @__PURE__ */ jsx(MenuItem, { value: item.id, children: /* @__PURE__ */ jsxs(Suspense, { fallback: null, children: [
131
+ item.icon && /* @__PURE__ */ jsx(ListItemIcon, { style: { minWidth: "24px" }, children: createElement(item.icon, {
132
+ style: { fontSize: "16px", width: "16px", height: "16px" }
133
+ }) }),
134
+ item.name
135
+ ] }) }, item.id))
136
+ }
137
+ )
138
+ ] }),
139
+ !status ? /* @__PURE__ */ jsxs(Box, { sx: { mt: "24px" }, children: [
140
+ /* @__PURE__ */ jsx(Typography, { variant: "body2", color: "text.secondary", children: t("profile.quickSettings") }),
141
+ data.map((item) => /* @__PURE__ */ jsx(
142
+ Box,
143
+ {
144
+ onClick: () => handleQuickSettingClick(item),
145
+ sx: {
146
+ display: "flex",
147
+ alignItems: "center",
148
+ gap: "4px",
149
+ p: "4px",
150
+ cursor: "pointer",
151
+ borderRadius: "8px",
152
+ "&:hover": {
153
+ bgcolor: "action.hover"
154
+ }
155
+ },
156
+ children: /* @__PURE__ */ jsxs(Suspense, { fallback: null, children: [
157
+ item.icon && /* @__PURE__ */ jsx(ListItemIcon, { style: { minWidth: "24px" }, children: createElement(item.icon, {
158
+ style: { fontSize: "16px", width: "16px", height: "16px" }
159
+ }) }),
160
+ item.name,
161
+ item.durationName && /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "text.secondary", children: [
162
+ "- ",
163
+ item.durationName
164
+ ] })
165
+ ] })
166
+ },
167
+ item.id
168
+ ))
169
+ ] }) : /* @__PURE__ */ jsxs(FormControl, { fullWidth: true, sx: { mt: "24px" }, children: [
170
+ /* @__PURE__ */ jsx(InputLabel, { size: "small", sx: { fontSize: "14px" }, children: t("profile.removeStatusAfter") }),
171
+ /* @__PURE__ */ jsx(
172
+ Select,
173
+ {
174
+ value: duration,
175
+ label: t("profile.removeStatusAfter"),
176
+ onChange: (e) => {
177
+ const { value } = e.target;
178
+ setDuration(value);
179
+ if (value === DurationEnum.Custom) {
180
+ setCustomDate(dayjs().toDate());
181
+ }
182
+ setChanged(true);
183
+ },
184
+ variant: "outlined",
185
+ IconComponent: (props) => /* @__PURE__ */ jsx(ArrowDownwardIcon, { ...props, width: 20, height: 20 }),
186
+ sx: selectStyle,
187
+ children: selectedStatus?.children?.map((item) => /* @__PURE__ */ jsx(MenuItem, { value: item.id, children: item.name }, item.id))
188
+ }
189
+ )
190
+ ] }),
191
+ duration === DurationEnum.Custom && /* @__PURE__ */ jsx(Box, { sx: { mt: "24px" }, children: /* @__PURE__ */ jsx(
192
+ DateTimeInput,
193
+ {
194
+ value: customDate,
195
+ onChange: (e) => {
196
+ setCustomDate(e);
197
+ setChanged(true);
198
+ },
199
+ label: t("profile.selectEndTime"),
200
+ timezone,
201
+ error: duration === DurationEnum.Custom && !customDate,
202
+ helperText: duration === DurationEnum.Custom && !customDate ? t("profile.pleaseSelectTime") : void 0
203
+ }
204
+ ) })
205
+ ] }),
206
+ /* @__PURE__ */ jsx(DialogActions, { sx: { p: "16px 24px", borderTop: `1px solid ${colors.dividerColor}` }, children: selected?.value && !changed ? /* @__PURE__ */ jsx(
207
+ Button,
208
+ {
209
+ sx: { ...defaultButtonStyle, minWidth: "54px" },
210
+ size: "small",
211
+ variant: "outlined",
212
+ onClick: () => handleSubmit(true),
213
+ children: t("profile.cleanStatus")
214
+ }
215
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
216
+ /* @__PURE__ */ jsx(Button, { size: "small", variant: "outlined", sx: { ...defaultButtonStyle, minWidth: "54px" }, onClick: onClose, children: t("common.cancel") }),
217
+ /* @__PURE__ */ jsx(
218
+ Button,
219
+ {
220
+ sx: {
221
+ ...primaryButtonStyle,
222
+ minWidth: "54px",
223
+ "&.Mui-disabled": {
224
+ backgroundColor: "rgba(0, 0, 0, 0.12)"
225
+ }
226
+ },
227
+ disabled: !isValid,
228
+ onClick: () => handleSubmit(false),
229
+ size: "small",
230
+ variant: "outlined",
231
+ children: t("common.confirm")
232
+ }
233
+ )
234
+ ] }) })
235
+ ]
236
+ }
237
+ );
238
+ }
@@ -15,6 +15,8 @@ export interface StatusItem {
15
15
  id: string;
16
16
  name: string;
17
17
  icon?: React.FC<SvgIconProps>;
18
+ duration?: string;
19
+ durationName?: string;
18
20
  children?: Array<StatusItem>;
19
21
  }
20
22
  export interface BaseStatusProps {
@@ -52,6 +52,11 @@ const ContentWrapper = styled(Box)(({ theme }) => ({
52
52
  flex: "revert",
53
53
  [theme.breakpoints.up("md")]: {
54
54
  flex: 1
55
+ },
56
+ "@media (min-width: 850px) and (max-width: 1050px)": {
57
+ "& .user-center-tabs": {
58
+ maxWidth: "500px"
59
+ }
55
60
  }
56
61
  }));
57
62
  export default function UserCenter({
@@ -193,8 +198,8 @@ export default function UserCenter({
193
198
  label: x.title || x.label,
194
199
  url: x.link || x.url,
195
200
  protected: privacyState?.data?.[value] ?? false,
196
- isPrivate: x.isPrivate || x._rawLink === "/payment-kit/customer"
197
- // FIXME: HACK: 隐藏 payment-kit/customer 菜单, 需要一个通用的解决方案,在嵌入的时候就决定是否是私有的
201
+ isPrivate: x.isPrivate || x.private || x._rawLink.includes("/customer")
202
+ // FIXME: HACK: 隐藏 /customer 菜单, 需要一个通用的解决方案,在嵌入的时候就决定是否是私有的
198
203
  // icon: x.icon,
199
204
  };
200
205
  }).filter((x) => isMyself || !x.isPrivate);
@@ -382,7 +387,7 @@ export default function UserCenter({
382
387
  ] });
383
388
  }
384
389
  return /* @__PURE__ */ jsxs(ContentWrapper, { display: "flex", flexDirection: isMobile ? "column" : "row", children: [
385
- /* @__PURE__ */ jsxs(Box, { flex: "1", order: isMobile ? 2 : "unset", children: [
390
+ /* @__PURE__ */ jsxs(Box, { flex: "1", className: "user-center-tabs", order: isMobile ? 2 : "unset", children: [
386
391
  userCenterTabs.length > 0 && currentTab ? /* @__PURE__ */ jsxs(
387
392
  Box,
388
393
  {
@@ -5,10 +5,11 @@ import utc from "dayjs/plugin/utc";
5
5
  import timezonePlugin from "dayjs/plugin/timezone";
6
6
  import { formatToDatetime } from "@arcblock/ux/lib/Util";
7
7
  import { useCreation } from "ahooks";
8
+ import { currentTimezone } from "./utils.js";
8
9
  dayjs.extend(utc);
9
10
  dayjs.extend(timezonePlugin);
10
- export default function Clock({ timezone = "utc", locale = "zh" }) {
11
- const [time, setTime] = useState(dayjs().tz(timezone));
11
+ export default function Clock({ timezone = currentTimezone, locale = "zh" }) {
12
+ const [time, setTime] = useState(dayjs().tz(timezone || currentTimezone));
12
13
  useEffect(() => {
13
14
  const timerId = setInterval(() => {
14
15
  setTime(dayjs().tz(timezone));
@@ -1,8 +1,6 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { createElement } from "react";
3
3
  import Box from "@mui/material/Box";
4
- import MenuItem from "@mui/material/MenuItem";
5
- import Select from "@mui/material/Select";
6
4
  import useMediaQuery from "@mui/material/useMediaQuery";
7
5
  import SwipeableDrawer from "@mui/material/SwipeableDrawer";
8
6
  import Backdrop from "@mui/material/Backdrop";
@@ -10,47 +8,23 @@ import styled from "@emotion/styled";
10
8
  import { joinURL } from "ufo";
11
9
  import Button from "@arcblock/ux/lib/Button";
12
10
  import cloneDeep from "lodash/cloneDeep";
13
- import { temp as colors } from "@arcblock/ux/lib/Colors";
14
11
  import { useCreation, useMemoizedFn, useReactive } from "ahooks";
15
12
  import { useMemo, useRef, useState, memo, forwardRef, useEffect, lazy } from "react";
16
13
  import { translate } from "@arcblock/ux/lib/Locale/util";
17
14
  import isEmail from "validator/lib/isEmail";
18
15
  import isMobilePhone from "validator/lib/isMobilePhone";
19
16
  import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
20
- import ArrowDownwardIcon from "@arcblock/icons/lib/ArrowDown";
21
17
  import { useBrowser } from "@arcblock/react-hooks";
22
18
  import { translations } from "../../libs/locales.js";
23
19
  import EditableField from "../editable-field.js";
24
20
  import { LinkPreviewInput } from "./link-preview-input.js";
25
- import { getTimezones } from "./utils.js";
21
+ import { currentTimezone, defaultButtonStyle, primaryButtonStyle } from "./utils.js";
26
22
  import Clock from "./clock.js";
27
- const timezones = getTimezones();
23
+ import { TimezoneSelect } from "./timezone-select.js";
28
24
  const LocationIcon = lazy(() => import("@arcblock/icons/lib/Location"));
29
25
  const TimezoneIcon = lazy(() => import("@arcblock/icons/lib/Timezone"));
30
26
  const EmailIcon = lazy(() => import("@arcblock/icons/lib/Email"));
31
27
  const PhoneIcon = lazy(() => import("@arcblock/icons/lib/Phone"));
32
- const defaultButtonStyle = {
33
- color: colors.foregroundsFgBase,
34
- borderColor: colors.strokeBorderBase,
35
- backgroundColor: colors.buttonsButtonNeutral,
36
- "&:hover": {
37
- borderColor: colors.strokeBorderBase,
38
- backgroundColor: colors.buttonsButtonNeutralHover
39
- },
40
- py: 0.5,
41
- borderRadius: 2
42
- };
43
- const primaryButtonStyle = {
44
- color: colors.buttonsButtonNeutral,
45
- borderColor: colors.foregroundsFgInteractive,
46
- backgroundColor: colors.foregroundsFgInteractive,
47
- "&:hover": {
48
- borderColor: colors.foregroundsFgInteractive,
49
- backgroundColor: colors.foregroundsFgInteractive
50
- },
51
- py: 0.5,
52
- borderRadius: 2
53
- };
54
28
  const iconSize = {
55
29
  width: 20,
56
30
  height: 20
@@ -172,6 +146,9 @@ export default function UserMetadataComponent({
172
146
  if (k === "bio") {
173
147
  metadata[k] = metadata[k]?.slice(0, bioMaxLength);
174
148
  }
149
+ if (k === "timezone") {
150
+ metadata[k] = value || currentTimezone;
151
+ }
175
152
  });
176
153
  onSave(metadata);
177
154
  setEditable(false);
@@ -189,6 +166,7 @@ export default function UserMetadataComponent({
189
166
  component: "textarea",
190
167
  inline: false,
191
168
  rows: 3,
169
+ label: t("profile.bio"),
192
170
  maxLength: bioMaxLength,
193
171
  style: {
194
172
  ...editing ? { marginBottom: 8 } : {}
@@ -225,7 +203,7 @@ export default function UserMetadataComponent({
225
203
  /* @__PURE__ */ jsx(
226
204
  EditableField,
227
205
  {
228
- value: metadata.timezone ?? "",
206
+ value: metadata.timezone || currentTimezone,
229
207
  onChange: (value) => onChange(value, "timezone"),
230
208
  editable: editing,
231
209
  placeholder: "timezone",
@@ -236,38 +214,11 @@ export default function UserMetadataComponent({
236
214
  /* @__PURE__ */ jsx(Clock, { timezone: metadata.timezone, locale })
237
215
  ] }),
238
216
  children: /* @__PURE__ */ jsx(
239
- Select,
217
+ TimezoneSelect,
240
218
  {
241
- className: `timezone-select ${editing ? "" : "disabled"}`,
242
- value: metadata.timezone,
243
- onChange: (e) => onChange(e.target.value, "timezone"),
244
- disabled: !editing,
245
- displayEmpty: true,
246
- variant: "outlined",
247
- placeholder: "Timezone",
248
- IconComponent: (props) => /* @__PURE__ */ jsx(ArrowDownwardIcon, { ...props, width: 20, height: 20 }),
249
- MenuProps: {
250
- PaperProps: {
251
- style: {
252
- maxHeight: "400px"
253
- }
254
- },
255
- style: {
256
- zIndex: mode === "drawer" ? 9999 : 1300
257
- }
258
- },
259
- sx: {
260
- width: "100%",
261
- "&:hover": {
262
- "fieldset.MuiOutlinedInput-notchedOutline": {
263
- borderColor: colors.dividerColor
264
- }
265
- },
266
- fieldset: {
267
- borderColor: colors.dividerColor
268
- }
269
- },
270
- children: timezones.map((tz) => /* @__PURE__ */ jsx(MenuItem, { value: tz.value, children: tz.label }, tz.value))
219
+ value: metadata.timezone || currentTimezone,
220
+ onChange: (value) => onChange(value, "timezone"),
221
+ disabled: !editing
271
222
  }
272
223
  )
273
224
  }
@@ -276,7 +227,8 @@ export default function UserMetadataComponent({
276
227
  EditableField,
277
228
  {
278
229
  value: metadata.email ?? user?.email ?? "",
279
- editable: editing && !emailVerified,
230
+ editable: editing,
231
+ canEdit: !emailVerified,
280
232
  verified: emailVerified,
281
233
  placeholder: "Email",
282
234
  icon: /* @__PURE__ */ jsx(EmailIcon, { ...iconSize }),
@@ -296,7 +248,8 @@ export default function UserMetadataComponent({
296
248
  EditableField,
297
249
  {
298
250
  value: metadata.phone ?? user?.phone ?? "",
299
- editable: editing && !phoneVerified,
251
+ editable: editing,
252
+ canEdit: !phoneVerified,
300
253
  verified: phoneVerified,
301
254
  placeholder: "Phone",
302
255
  icon: /* @__PURE__ */ jsx(PhoneIcon, { ...iconSize }),
@@ -0,0 +1,8 @@
1
+ interface TimezoneSelectProps {
2
+ value: string;
3
+ onChange: (value: string) => void;
4
+ disabled?: boolean;
5
+ mode?: 'drawer' | 'self';
6
+ }
7
+ export declare function TimezoneSelect({ value, onChange, disabled, mode }: TimezoneSelectProps): import("react").JSX.Element;
8
+ export {};
@@ -0,0 +1,99 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useMemo } from "react";
3
+ import MenuItem from "@mui/material/MenuItem";
4
+ import Select from "@mui/material/Select";
5
+ import ListSubheader from "@mui/material/ListSubheader";
6
+ import TextField from "@mui/material/TextField";
7
+ import debounce from "lodash/debounce";
8
+ import { temp as colors } from "@arcblock/ux/lib/Colors";
9
+ import ArrowDownwardIcon from "@arcblock/icons/lib/ArrowDown";
10
+ import { getTimezones } from "./utils.js";
11
+ const timezones = getTimezones();
12
+ export function TimezoneSelect({ value, onChange, disabled = false, mode = "self" }) {
13
+ const [timezoneData, setTimezoneData] = useState(timezones);
14
+ const [searchText, setSearchText] = useState("");
15
+ const timezoneDebounce = useMemo(
16
+ () => debounce((v) => {
17
+ return v ? timezones.filter((tz) => tz.value.toLowerCase().includes(v.toLowerCase())) : timezones;
18
+ }, 300),
19
+ []
20
+ );
21
+ useEffect(() => {
22
+ const data = timezoneDebounce(searchText);
23
+ setTimezoneData(data ?? timezones);
24
+ }, [searchText, timezoneDebounce]);
25
+ useEffect(() => {
26
+ return () => {
27
+ timezoneDebounce.cancel();
28
+ };
29
+ }, [timezoneDebounce]);
30
+ return /* @__PURE__ */ jsxs(
31
+ Select,
32
+ {
33
+ className: `timezone-select ${disabled ? "disabled" : ""}`,
34
+ value,
35
+ onChange: (e) => onChange(e.target.value),
36
+ disabled,
37
+ displayEmpty: true,
38
+ variant: "outlined",
39
+ placeholder: "Timezone",
40
+ IconComponent: (props) => /* @__PURE__ */ jsx(ArrowDownwardIcon, { ...props, width: 20, height: 20 }),
41
+ MenuProps: {
42
+ PaperProps: {
43
+ style: {
44
+ maxHeight: "400px"
45
+ }
46
+ },
47
+ style: {
48
+ zIndex: mode === "drawer" ? 9999 : 1300
49
+ }
50
+ },
51
+ sx: {
52
+ width: "100%",
53
+ "&:hover": {
54
+ "fieldset.MuiOutlinedInput-notchedOutline": {
55
+ borderColor: colors.dividerColor
56
+ }
57
+ },
58
+ "fieldset.MuiOutlinedInput-notchedOutline": {
59
+ borderColor: colors.dividerColor
60
+ }
61
+ },
62
+ children: [
63
+ /* @__PURE__ */ jsx(ListSubheader, { children: /* @__PURE__ */ jsx(
64
+ TextField,
65
+ {
66
+ autoFocus: true,
67
+ value: searchText,
68
+ placeholder: "Timezone",
69
+ variant: "outlined",
70
+ size: "small",
71
+ fullWidth: true,
72
+ onChange: (event) => setSearchText(event.target.value),
73
+ onKeyDown: (e) => {
74
+ if (e.key !== "Escape") {
75
+ e.stopPropagation();
76
+ }
77
+ },
78
+ sx: {
79
+ marginTop: "8px",
80
+ "& .MuiOutlinedInput-root": {
81
+ "& fieldset": {
82
+ borderColor: colors.dividerColor,
83
+ borderWidth: "1px"
84
+ },
85
+ "&:hover fieldset": {
86
+ borderColor: colors.dividerColor
87
+ },
88
+ "&.Mui-focused fieldset": {
89
+ borderColor: colors.dividerColor
90
+ }
91
+ }
92
+ }
93
+ }
94
+ ) }),
95
+ timezoneData.map((tz) => /* @__PURE__ */ jsx(MenuItem, { value: tz.value, children: tz.label }, tz.value))
96
+ ]
97
+ }
98
+ );
99
+ }