@fairmint/ui 0.2.12 → 0.2.13

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.
@@ -1,8 +1,37 @@
1
1
  import { z } from 'zod';
2
2
  import { TextInputProps } from '../primitives/TextInput/TextInput';
3
- export declare const phoneSchema: z.ZodString;
3
+ /**
4
+ * Validates that a phone string contains enough digits to be a real number.
5
+ * - Characters allowed: digits, spaces, `(`, `)`, `.`, `-`, and optional leading `+`
6
+ * - Domestic (no +): exactly 10 digits, or 11 digits starting with `1`
7
+ * - US/CA (+1): exactly 11 digits total
8
+ * - Other international (+): 8–15 digits (E.164 range)
9
+ */
10
+ export declare const phoneSchema: z.ZodEffects<z.ZodString, string, string>;
11
+ /**
12
+ * Formats a phone string for display, progressively as digits are added.
13
+ *
14
+ * - Domestic (no +): `(XXX) XXX-XXXX`
15
+ * - US/CA with +1: `+1 (XXX) XXX-XXXX`
16
+ * - Other international: `+{digits}` (no structural formatting)
17
+ *
18
+ * Idempotent: passing an already-formatted string returns the same result.
19
+ */
20
+ export declare function formatPhone(raw: string): string;
21
+ /**
22
+ * Strips a formatted phone string to E.164-ish digits for API storage.
23
+ *
24
+ * - `(260) 255-5555` → `+12602555555`
25
+ * - `+1 (260) 255-5555` → `+12602555555`
26
+ * - `+44 20 7946 0958` → `+442079460958`
27
+ *
28
+ * Note: 10-digit numbers without `+` are assumed to be US/CA and get `+1`.
29
+ */
30
+ export declare function normalizePhone(formatted: string): string;
31
+ /** @deprecated Use `formatPhone` instead. */
32
+ export declare const formatPhoneDisplay: typeof formatPhone;
4
33
  export interface PhoneFieldProps extends Omit<TextInputProps, "type"> {
5
34
  error?: string;
6
35
  }
7
- export declare function PhoneField({ error, ...rest }: PhoneFieldProps): import("react/jsx-runtime").JSX.Element;
36
+ export declare function PhoneField({ error, onChange, value, ...rest }: PhoneFieldProps): import("react/jsx-runtime").JSX.Element;
8
37
  //# sourceMappingURL=PhoneField.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneField.d.ts","sourceRoot":"","sources":["../../../src/components/form/PhoneField.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAGxE,eAAO,MAAM,WAAW,aAE+C,CAAC;AAExE,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,eAAe,2CAW7D"}
1
+ {"version":3,"file":"PhoneField.d.ts","sourceRoot":"","sources":["../../../src/components/form/PhoneField.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAGxE;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,2CAUe,CAAC;AAExC;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAwB/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAOxD;AAED,6CAA6C;AAC7C,eAAO,MAAM,kBAAkB,oBAAc,CAAC;AAE9C,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,EAAE,eAAe,2CAqD9E"}
@@ -16,6 +16,10 @@ export interface InvestorProfileCardProps {
16
16
  children?: ReactNode;
17
17
  /** If undefined, no pencil icon is shown. */
18
18
  onEditName?: () => void;
19
+ /** When true, the pencil icon is shown but disabled. Requires `onEditName` to be provided. */
20
+ editNameDisabled?: boolean;
21
+ /** Tooltip label shown on the disabled pencil icon. Only used when `editNameDisabled` is true. */
22
+ editNameTooltip?: string;
19
23
  /** If undefined, no camera overlay is shown. */
20
24
  onUploadPhoto?: () => void;
21
25
  /** Shows a centered Loader over the avatar during upload. */
@@ -31,5 +35,5 @@ export interface InvestorProfileCardProps {
31
35
  */
32
36
  uploadPhotoIcon?: ReactNode;
33
37
  }
34
- export declare function InvestorProfileCard({ name, email, avatarUrl, verificationStatus, memberSince, subtitle, children, onEditName, onUploadPhoto, photoUploading, editNameIcon, uploadPhotoIcon, }: InvestorProfileCardProps): import("react/jsx-runtime").JSX.Element;
38
+ export declare function InvestorProfileCard({ name, email, avatarUrl, verificationStatus, memberSince, subtitle, children, onEditName, editNameDisabled, editNameTooltip, onUploadPhoto, photoUploading, editNameIcon, uploadPhotoIcon, }: InvestorProfileCardProps): import("react/jsx-runtime").JSX.Element;
35
39
  //# sourceMappingURL=InvestorProfileCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"InvestorProfileCard.d.ts","sourceRoot":"","sources":["../../../../src/components/specific/InvestorProfileCard/InvestorProfileCard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AASjD,MAAM,WAAW,wBAAwB;IACvC,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wJAAwJ;IACxJ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,6DAA6D;IAC7D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;CAC7B;AA6CD,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,KAAK,EACL,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,aAAa,EACb,cAAc,EACd,YAAY,EACZ,eAAe,GAChB,EAAE,wBAAwB,2CAsH1B"}
1
+ {"version":3,"file":"InvestorProfileCard.d.ts","sourceRoot":"","sources":["../../../../src/components/specific/InvestorProfileCard/InvestorProfileCard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAUjD,MAAM,WAAW,wBAAwB;IACvC,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,wJAAwJ;IACxJ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,SAAS,CAAC;IAErB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,8FAA8F;IAC9F,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kGAAkG;IAClG,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,6DAA6D;IAC7D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;CAC7B;AA6CD,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,KAAK,EACL,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,cAAc,EACd,YAAY,EACZ,eAAe,GAChB,EAAE,wBAAwB,2CAuI1B"}
@@ -26886,7 +26886,7 @@ const Tabs = Object.assign(TabsBase, {
26886
26886
  const TextInput = React.forwardRef((props, ref) => /* @__PURE__ */ jsxRuntime.jsx(TextInput$1, { size: "md", ...props, ref }));
26887
26887
  TextInput.displayName = "TextInput";
26888
26888
  function Tooltip(props) {
26889
- return /* @__PURE__ */ jsxRuntime.jsx(Tooltip$1, { ...props });
26889
+ return /* @__PURE__ */ jsxRuntime.jsx(Tooltip$1, { fz: "xs", ...props });
26890
26890
  }
26891
26891
  function ErrorFallback({
26892
26892
  title = "Something went wrong",
@@ -28245,17 +28245,77 @@ function PasswordField({ error: error2, ...rest }) {
28245
28245
  }
28246
28246
  );
28247
28247
  }
28248
- const phoneSchema = stringType().regex(/^\+?[\d\s\-().]{7,20}$/, "Please enter a valid phone number");
28249
- function PhoneField({ error: error2, ...rest }) {
28248
+ const phoneSchema = stringType().refine((value) => {
28249
+ if (!/^\+?[\d\s\-().]*$/.test(value)) return false;
28250
+ const digits = value.replace(/\D/g, "");
28251
+ if (value.startsWith("+")) {
28252
+ if (digits.startsWith("1")) return digits.length === 11;
28253
+ return digits.length >= 8 && digits.length <= 15;
28254
+ }
28255
+ return digits.length === 10 || digits.length === 11 && digits.startsWith("1");
28256
+ }, "Please enter a valid phone number");
28257
+ function formatPhone(raw) {
28258
+ const hasPlus = raw.startsWith("+");
28259
+ const digits = raw.replace(/\D/g, "");
28260
+ if (!digits) return hasPlus ? "+" : "";
28261
+ if (!hasPlus && digits.length === 11 && digits.startsWith("1")) {
28262
+ return formatPhone(`+${digits}`);
28263
+ }
28264
+ if (hasPlus && digits.startsWith("1")) {
28265
+ const local = digits.slice(1);
28266
+ if (!local.length) return "+1";
28267
+ if (local.length <= 3) return `+1 (${local}`;
28268
+ if (local.length <= 6) return `+1 (${local.slice(0, 3)}) ${local.slice(3)}`;
28269
+ return `+1 (${local.slice(0, 3)}) ${local.slice(3, 6)}-${local.slice(6, 10)}`;
28270
+ }
28271
+ if (hasPlus) return `+${digits}`;
28272
+ if (digits.length <= 3) return digits;
28273
+ if (digits.length <= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;
28274
+ return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6, 10)}`;
28275
+ }
28276
+ function normalizePhone(formatted) {
28277
+ const hasPlus = formatted.startsWith("+");
28278
+ const digits = formatted.replace(/\D/g, "");
28279
+ if (!digits) return "";
28280
+ if (hasPlus) return `+${digits}`;
28281
+ if (digits.length === 10) return `+1${digits}`;
28282
+ return `+${digits}`;
28283
+ }
28284
+ const formatPhoneDisplay = formatPhone;
28285
+ function PhoneField({ error: error2, onChange, value, ...rest }) {
28286
+ const inputRef = React.useRef(null);
28287
+ const handleChange = (e) => {
28288
+ const raw = e.target.value;
28289
+ const hasPlus = raw.startsWith("+");
28290
+ let digits = raw.replace(/\D/g, "");
28291
+ const shouldFormatAsUsCountryCode = digits.startsWith("1") && (hasPlus || !hasPlus && digits.length > 10);
28292
+ if (shouldFormatAsUsCountryCode) {
28293
+ digits = digits.slice(0, 11);
28294
+ } else {
28295
+ digits = digits.slice(0, hasPlus ? 15 : 10);
28296
+ }
28297
+ const formatted = formatPhone(hasPlus || shouldFormatAsUsCountryCode ? `+${digits}` : digits);
28298
+ e.target.value = formatted;
28299
+ onChange?.(e);
28300
+ };
28301
+ const displayValue = typeof value === "string" ? formatPhone(value) : value;
28302
+ React.useEffect(() => {
28303
+ if (inputRef.current && displayValue != null && inputRef.current.value !== String(displayValue)) {
28304
+ inputRef.current.value = String(displayValue);
28305
+ }
28306
+ }, [displayValue]);
28250
28307
  return /* @__PURE__ */ jsxRuntime.jsx(
28251
28308
  TextInput,
28252
28309
  {
28310
+ ref: inputRef,
28253
28311
  type: "tel",
28254
28312
  label: rest.label ?? "Phone",
28255
- placeholder: rest.placeholder ?? "+1 (555) 000-0000",
28313
+ placeholder: rest.placeholder ?? "(555) 000-0000",
28256
28314
  autoComplete: "tel",
28315
+ ...rest,
28257
28316
  error: error2,
28258
- ...rest
28317
+ value: displayValue,
28318
+ onChange: handleChange
28259
28319
  }
28260
28320
  );
28261
28321
  }
@@ -30373,6 +30433,8 @@ function InvestorProfileCard({
30373
30433
  subtitle,
30374
30434
  children,
30375
30435
  onEditName,
30436
+ editNameDisabled,
30437
+ editNameTooltip,
30376
30438
  onUploadPhoto,
30377
30439
  photoUploading,
30378
30440
  editNameIcon,
@@ -30461,7 +30523,21 @@ function InvestorProfileCard({
30461
30523
  /* @__PURE__ */ jsxRuntime.jsxs(Group, { gap: "xs", wrap: "nowrap", align: "center", children: [
30462
30524
  /* @__PURE__ */ jsxRuntime.jsx(Text, { fw: 600, size: "md", children: displayName }),
30463
30525
  verificationStatus && /* @__PURE__ */ jsxRuntime.jsx(StatusBadge, { status: verificationStatus, size: "xs" }),
30464
- onEditName && /* @__PURE__ */ jsxRuntime.jsx(ActionIcon, { variant: "subtle", size: "sm", onClick: onEditName, "aria-label": "Edit name", children: editNameIcon ?? /* @__PURE__ */ jsxRuntime.jsx(DefaultPencilIcon, {}) })
30526
+ onEditName && (editNameDisabled ? /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { label: editNameTooltip ?? "Coming soon", children: /* @__PURE__ */ jsxRuntime.jsx(
30527
+ ActionIcon,
30528
+ {
30529
+ variant: "subtle",
30530
+ size: "sm",
30531
+ "aria-label": "Edit name",
30532
+ "aria-disabled": "true",
30533
+ onClick: (event) => {
30534
+ event.preventDefault();
30535
+ event.stopPropagation();
30536
+ },
30537
+ style: { opacity: 0.5, cursor: "not-allowed" },
30538
+ children: editNameIcon ?? /* @__PURE__ */ jsxRuntime.jsx(DefaultPencilIcon, {})
30539
+ }
30540
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(ActionIcon, { variant: "subtle", size: "sm", onClick: onEditName, "aria-label": "Edit name", children: editNameIcon ?? /* @__PURE__ */ jsxRuntime.jsx(DefaultPencilIcon, {}) }))
30465
30541
  ] }),
30466
30542
  email && /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "sm", c: `${colors.neutral}.5`, children: email }),
30467
30543
  memberSinceText && /* @__PURE__ */ jsxRuntime.jsx(Text, { size: "xs", c: `${colors.neutral}.4`, children: memberSinceText }),
@@ -30787,10 +30863,13 @@ exports.formatCurrencyPrecise = formatCurrencyPrecise;
30787
30863
  exports.formatDate = formatDate;
30788
30864
  exports.formatNumber = formatNumber;
30789
30865
  exports.formatPercent = formatPercent;
30866
+ exports.formatPhone = formatPhone;
30867
+ exports.formatPhoneDisplay = formatPhoneDisplay;
30790
30868
  exports.formatPreciseWithCurrency = formatPreciseWithCurrency;
30791
30869
  exports.getAvatarColor = getAvatarColor;
30792
30870
  exports.isEthereumAddress = isEthereumAddress;
30793
30871
  exports.neutralColors = neutralColors;
30872
+ exports.normalizePhone = normalizePhone;
30794
30873
  exports.notify = notify;
30795
30874
  exports.passwordSchema = passwordSchema;
30796
30875
  exports.phoneSchema = phoneSchema;