UNPKG

@blocklet/ui-react

Version:

Some useful front-end web components that can be used in Blocklets.

500 lines (498 loc) 15 kB
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 };