@blocklet/ui-react
Version:
Some useful front-end web components that can be used in Blocklets.
500 lines (498 loc) • 15 kB
JavaScript
import { jsxs as y, Fragment as L, jsx as t } from "react/jsx-runtime";
import { lazy as I, memo as ie, createElement as ae, useState as T, useRef as le, useEffect as de, useMemo as F } from "react";
import { Icon as ce } from "@iconify/react";
import { Backdrop as se, Box as m, useMediaQuery as pe, SwipeableDrawer as me, Typography as N, Tooltip as ue } from "@mui/material";
import he from "@emotion/styled";
import j from "@arcblock/ux/lib/Button";
import M, { detectCountryFromPhone as $, getCountryNameByCountry as fe, validatePhoneNumber as ye, getDialCodeByCountry as xe } from "@arcblock/ux/lib/PhoneInput";
import v from "lodash/cloneDeep";
import be from "lodash/omit";
import { mergeSx as ge } from "@arcblock/ux/lib/Util/style";
import { LOGIN_PROVIDER as G } from "@arcblock/ux/lib/Util/constant";
import { useReactive as w, useCreation as k, useMemoizedFn as Ce } from "ahooks";
import { translate as ve } from "@arcblock/ux/lib/Locale/util";
import we from "validator/lib/isEmail";
import H from "validator/lib/isPostalCode";
import { useLocaleContext as ke } from "@arcblock/ux/lib/Locale/context";
import { useBrowser as Ee } from "@arcblock/react-hooks";
import Ie from "@arcblock/ux/lib/UserCard/Content/clock";
import { translations as ze } from "../../libs/locales.js";
import g, { inputFieldStyle as Pe, commonInputStyle as Ve } from "../editable-field.js";
import { LinkPreviewInput as Be } from "./link-preview-input.js";
import { defaultButtonStyle as Q, currentTimezone as A, primaryButtonStyle as Le } from "./utils.js";
import { TimezoneSelect as Ne } from "./timezone-select.js";
import je from "./address.js";
const Ae = I(() => import("@arcblock/icons/lib/Location")), Se = I(() => import("@arcblock/icons/lib/Timezone")), Oe = I(() => import("@arcblock/icons/lib/Email")), We = I(() => import("@arcblock/icons/lib/Phone")), E = {
width: 20,
height: 20
}, U = 200, Y = ie(function({
ref: r,
...z
}) {
return /* @__PURE__ */ ae(
se,
{
ref: r,
component: "div",
style: {
backgroundColor: "rgba(0, 0, 0, 0.6)",
backdropFilter: "blur(3px)",
touchAction: "none"
},
...z,
key: "background"
}
);
});
Y.displayName = "BackdropWrap";
function so({
isMyself: s,
user: r,
onSave: z,
isMobile: x
}) {
const [_, P] = T(!1), [q, C] = T(!1), J = le(null), K = Ee(), b = pe("(max-width:640px)") || K.mobile.any, c = w({
email: "",
phone: ""
}), u = w({
country: "",
province: "",
city: "",
line1: "",
line2: "",
postalCode: ""
});
de(() => {
b || C(!1);
}, [b]);
const S = k(() => r?.phoneVerified ?? !1, [r?.phoneVerified]), O = k(() => r?.emailVerified ?? !1, [r?.emailVerified]), { locale: V } = ke(), d = Ce((o, n = {}) => ve(ze, o, V, "en", n)), a = w(
r?.metadata ? v(r.metadata) : {
joinedAt: r?.createdAt,
email: r?.email,
phone: {
country: "cn",
phoneNumber: r?.phone ?? ""
}
}
), l = w(
r?.address ? v(r.address) : {
country: "",
province: "",
city: "",
line1: "",
line2: "",
postalCode: ""
}
), X = k(() => !!r?.address, [r?.address?.country]), h = F(() => r?.address?.country ? r.address.country : V === "zh" ? "cn" : "us", [r?.address?.country, V]), B = k(() => {
const o = a.phone ?? r?.phone ?? {
country: h,
phone: ""
};
if (o && typeof o == "string")
return {
country: $(o) || h,
phone: o
};
if (o && typeof o == "object") {
const { country: n = "", phoneNumber: e = "" } = o;
return {
country: n || $(e) || "",
phone: e || ""
};
}
return {
country: h,
phone: ""
};
}, [a.phone, r?.phone, h]), p = (o, n) => {
a[n] = o;
}, Z = (o, n) => {
l[o] = n, o === "city" && p(n, "location"), o === "postalCode" ? u.postalCode = n && !H(n, "any") ? d("profile.address.invalidPostalCode") : "" : u[o] = "";
}, W = () => {
b ? C(!0) : P(!0);
}, R = () => {
const o = v(r?.metadata) ?? {};
o && Object.keys(a).forEach((e) => {
const i = e;
a[i] = o[i];
});
const n = v(r?.address) ?? {};
n && Object.keys(l).forEach((e) => {
const i = e;
l[i] = n[i];
}), [c, u].forEach((e) => {
Object.keys(e).forEach((i) => {
e[i] = "";
});
}), b ? C(!1) : P(!1);
}, ee = F(() => {
const o = a?.links?.map((n) => n.url) || [""];
return o.length > 0 ? o : [""];
}, [a.links]), oe = (o) => {
const n = o.map((e) => {
if (!e)
return {
url: e
};
const i = a.links?.find((f) => f.url === e);
return i ? {
...i,
url: e
} : {
url: e,
favicon: e
};
});
p(n, "links");
}, ne = () => {
if (Object.keys(a).forEach((n) => {
const e = n, i = a[e];
if (i && typeof i == "string" && (a[e] = i.trim()), e === "bio" && (a[e] = a[e]?.slice(0, U)), e === "timezone" && (a[e] = i || A), e === "phone" && i && typeof i == "object") {
const f = i, te = xe(f.country), re = (f.phoneNumber?.replace(new RegExp(`^\\+${te}`), "") || "")?.trim().length > 0;
a[e] = {
country: f.country,
...re ? { phoneNumber: f.phoneNumber } : {}
};
}
}), l.postalCode && !H(l.postalCode, "any") && (u.postalCode = d("profile.address.invalidPostalCode")), [c, u].some((n) => Object.values(n).some((e) => e)))
return;
const o = be(l, "detailedAddress");
o.country || (o.country = h), z({ metadata: a, address: o }), P(!1), C(!1);
}, D = (o, n = "self") => /* @__PURE__ */ y(
Re,
{
pt: 2,
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
alignItems: "flex-start",
gap: x ? "4px" : "16px",
children: [
/* @__PURE__ */ t(
g,
{
value: a.bio ?? "",
onChange: (e) => p(e, "bio"),
editable: o,
placeholder: "Bio",
component: "textarea",
inline: !1,
rows: 3,
label: d("profile.bio"),
maxLength: U,
style: {
...o ? { marginBottom: 8 } : {}
}
}
),
!o && s ? /* @__PURE__ */ t(
j,
{
size: x ? "small" : "large",
variant: "outlined",
sx: {
...Q,
mb: x ? "4px" : 2,
mt: x ? "4px" : 2,
height: x ? "32px" : "40px"
},
onClick: W,
fullWidth: !0,
children: d("profile.editProfile")
}
) : null,
o && s && X ? /* @__PURE__ */ t(
je,
{
address: l,
errors: u,
handleChange: Z,
defaultCountry: h
}
) : /* @__PURE__ */ t(
g,
{
value: a.location ?? r?.address?.city ?? "",
onChange: (e) => p(e, "location"),
editable: o,
placeholder: "Location",
label: d("profile.location"),
tooltip: s && (l.line1 || l.line2) ? /* @__PURE__ */ t(L, { children: ["line1", "line2"].map((e) => l[e] ? /* @__PURE__ */ y(m, { children: [
/* @__PURE__ */ t(
N,
{
variant: "caption",
component: "p",
sx: {
fontWeight: 600,
mb: 0
},
children: d(`profile.address.${e}`)
}
),
/* @__PURE__ */ t(N, { variant: "caption", component: "span", children: l[e] })
] }, e) : null) }) : null,
renderValue: () => {
const i = [l?.country ? fe(l?.country) : "", l.province, l.city || a.location || ""].filter(Boolean).join(" ");
return /* @__PURE__ */ t(N, { component: "span", children: i });
},
icon: /* @__PURE__ */ t(Ae, { ...E })
}
),
/* @__PURE__ */ t(
g,
{
value: a.timezone || A,
onChange: (e) => p(e, "timezone"),
editable: o,
placeholder: "timezone",
icon: /* @__PURE__ */ t(Se, { ...E }),
label: d("profile.timezone"),
renderValue: (e) => /* @__PURE__ */ t(Ie, { value: e }),
children: /* @__PURE__ */ t(
Ne,
{
value: a.timezone || A,
onChange: (e) => p(e, "timezone"),
disabled: !o,
mode: n
}
)
}
),
/* @__PURE__ */ t(
g,
{
value: a.email ?? r?.email ?? "",
editable: o,
hidePreview: !s,
disabled: r?.sourceProvider === G.EMAIL,
canEdit: !O,
verified: O,
placeholder: "Email",
icon: /* @__PURE__ */ t(Oe, { ...E }),
label: /* @__PURE__ */ y(L, { children: [
d("profile.email"),
r?.sourceProvider === G.EMAIL ? /* @__PURE__ */ t(ue, { title: d("profile.emailSourceProviderNotAllowEdit"), children: /* @__PURE__ */ t(
m,
{
component: ce,
icon: "mdi:info-outline",
sx: {
verticalAlign: "middle",
ml: 0.25
}
}
) }) : null
] }),
onChange: (e) => p(e, "email"),
errorMsg: c.email,
renderValue: (e) => s ? /* @__PURE__ */ t(
"a",
{
href: `mailto:${e}`,
style: {
color: "inherit",
textDecoration: "none"
},
children: e
}
) : null,
onValueValidate: (e) => {
let i = "";
e && !we(e) && (i = d("profile.emailInvalid")), c.email = i;
}
}
),
/* @__PURE__ */ t(
g,
{
value: B.phone,
editable: o,
hidePreview: !s,
canEdit: !S,
verified: S,
placeholder: "Phone",
icon: /* @__PURE__ */ t(We, { ...E }),
onChange: (e) => p(e, "phone"),
label: d("profile.phone"),
renderValue: () => s ? /* @__PURE__ */ t(M, { value: B, preview: !0 }) : null,
children: /* @__PURE__ */ t(
M,
{
variant: "outlined",
className: "editable-field",
InputProps: {
sx: { backgroundColor: "transparent" },
placeholder: "Phone"
},
value: B,
error: !!c.phone,
helperText: c.phone,
sx: ge(Pe, c.phone ? {} : Ve),
onChange: (e) => {
ye(e.phone, e.country) ? c.phone = "" : c.phone = d("profile.phoneInvalid"), p(
{
country: e.country,
phoneNumber: e.phone
},
"phone"
);
}
}
)
}
),
/* @__PURE__ */ t(Be, { editable: o, links: ee, onChange: oe }),
o && s ? /* @__PURE__ */ y(
m,
{
style: { width: "100%" },
sx: {
display: "flex",
gap: 1,
justifyContent: "flex-end",
flexDirection: n === "drawer" ? "column" : "row"
},
children: [
/* @__PURE__ */ t(
j,
{
fullWidth: n === "drawer",
size: "small",
variant: "outlined",
sx: { ...Q, minWidth: "54px" },
onClick: R,
children: d("common.cancel")
}
),
/* @__PURE__ */ t(
j,
{
fullWidth: n === "drawer",
size: "small",
disabled: !!c.email || !!c.phone,
variant: "outlined",
sx: {
...Le,
minWidth: "54px",
"&.Mui-disabled": {
backgroundColor: "rgba(0, 0, 0, 0.12)"
}
},
onClick: ne,
children: d("common.save")
}
)
]
}
) : null
]
}
);
return /* @__PURE__ */ y(L, { children: [
D(_),
b && /* @__PURE__ */ y(
me,
{
sx: {
zIndex: 9999
},
disableSwipeToOpen: !0,
onOpen: W,
open: q,
anchor: "bottom",
onClose: R,
slots: {
backdrop: Y
},
slotProps: {
paper: {
sx: {
boxShadow: "0px -2px 16px 0px rgba(0, 0, 0, 0.08)",
borderRadius: 1.5,
// 保持跟 DID Wallet 一致
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0
}
}
},
children: [
/* @__PURE__ */ t(
m,
{
ref: J,
sx: {
padding: "16px 32px",
margin: "-8px auto -16px",
zIndex: 1
},
children: /* @__PURE__ */ t(
m,
{
sx: {
width: "48px",
height: "4px",
borderRadius: "100vw",
backgroundColor: "rgba(0, 0, 0, 0.2)"
}
}
)
}
),
/* @__PURE__ */ t(
m,
{
sx: {
p: 2,
maxHeight: "500px",
overflowY: "auto"
},
children: D(!0, "drawer")
}
)
]
}
)
] });
}
const Re = he(m)`
width: 100%;
.MuiOutlinedInput-root {
padding: 8px;
.MuiOutlinedInput-input {
padding: 0;
}
}
.timezone-select {
min-width: 150px;
&.disabled {
padding: 4px 8px;
fieldset {
border: unset;
}
svg {
display: none;
}
}
}
.info-row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 4px;
margin: 0;
p {
display: flex;
align-items: center;
margin: 0;
font-size: 16px;
font-weight: 400;
color: #666;
}
}
`;
export {
so as default
};