@algenium/blocks 1.5.0-rc.2 → 1.5.0-rc.3

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.
package/dist/index.cjs CHANGED
@@ -16,6 +16,9 @@ var PopoverPrimitive = require('@radix-ui/react-popover');
16
16
  var ScrollAreaPrimitive = require('@radix-ui/react-scroll-area');
17
17
  var dateFns = require('date-fns');
18
18
  var reactDayPicker = require('react-day-picker');
19
+ var valid = require('card-validator');
20
+
21
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
19
22
 
20
23
  function _interopNamespace(e) {
21
24
  if (e && e.__esModule) return e;
@@ -43,6 +46,7 @@ var TogglePrimitive__namespace = /*#__PURE__*/_interopNamespace(TogglePrimitive)
43
46
  var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive);
44
47
  var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespace(PopoverPrimitive);
45
48
  var ScrollAreaPrimitive__namespace = /*#__PURE__*/_interopNamespace(ScrollAreaPrimitive);
49
+ var valid__default = /*#__PURE__*/_interopDefault(valid);
46
50
 
47
51
  var CalendarContext = React2.createContext(null);
48
52
  function useCalendarContext() {
@@ -7350,6 +7354,746 @@ function formatRelativeTime(dateStr) {
7350
7354
  if (diffD < 7) return `${diffD}d`;
7351
7355
  return date.toLocaleDateString([], { month: "short", day: "numeric" });
7352
7356
  }
7357
+ function useDebouncedValue(value, delayMs) {
7358
+ const [debounced, setDebounced] = React2.useState(value);
7359
+ React2.useEffect(() => {
7360
+ const id = window.setTimeout(() => setDebounced(value), delayMs);
7361
+ return () => window.clearTimeout(id);
7362
+ }, [value, delayMs]);
7363
+ return debounced;
7364
+ }
7365
+ function onlyDigits(s, max) {
7366
+ return s.replace(/\D/g, "").slice(0, max);
7367
+ }
7368
+ function formatPan(digits) {
7369
+ const cardInfo = valid__default.default.number(digits).card;
7370
+ const gaps = cardInfo?.gaps ?? [4, 8, 12];
7371
+ const parts = [];
7372
+ let idx = 0;
7373
+ for (const g of gaps) {
7374
+ parts.push(digits.slice(idx, g));
7375
+ idx = g;
7376
+ }
7377
+ parts.push(digits.slice(idx));
7378
+ return parts.filter((p) => p.length > 0).join(" ");
7379
+ }
7380
+ function parseExpiry(raw) {
7381
+ const d = onlyDigits(raw, 4);
7382
+ if (d.length < 4) return null;
7383
+ const mm = Number.parseInt(d.slice(0, 2), 10);
7384
+ const yy = Number.parseInt(d.slice(2, 4), 10);
7385
+ if (mm < 1 || mm > 12) return null;
7386
+ const year = yy < 100 ? 2e3 + yy : yy;
7387
+ const exp = valid__default.default.expirationDate({ month: String(mm), year: String(year) });
7388
+ if (!exp.isValid) return null;
7389
+ return { month: mm, year };
7390
+ }
7391
+ function normalizeBrand(detected) {
7392
+ if (!detected) return "";
7393
+ if (detected === "american-express") return "amex";
7394
+ return detected;
7395
+ }
7396
+ function brandAllowed(detected, accepted) {
7397
+ if (!accepted?.length) return true;
7398
+ if (!detected) return true;
7399
+ const n = normalizeBrand(detected);
7400
+ return accepted.map((a) => a.toLowerCase()).includes(n.toLowerCase());
7401
+ }
7402
+ var inputClassName = "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm";
7403
+ function CardInput({
7404
+ tokenize,
7405
+ value,
7406
+ onChange,
7407
+ acceptedBrands,
7408
+ requireHolderName = false,
7409
+ labels,
7410
+ disabled = false,
7411
+ largeText = false,
7412
+ className,
7413
+ onError
7414
+ }) {
7415
+ const baseId = React2.useId();
7416
+ const [panDigits, setPanDigits] = React2.useState("");
7417
+ const [expiryRaw, setExpiryRaw] = React2.useState("");
7418
+ const [cvc, setCvc] = React2.useState("");
7419
+ const [holderName, setHolderName] = React2.useState("");
7420
+ const [panFocused, setPanFocused] = React2.useState(false);
7421
+ const [submitting, setSubmitting] = React2.useState(false);
7422
+ const inputClass = largeText ? "text-base py-3" : "";
7423
+ const labelClass = largeText ? "text-base" : "text-sm";
7424
+ const numberValidation = valid__default.default.number(panDigits);
7425
+ const rawBrand = numberValidation.card?.type;
7426
+ const brand = normalizeBrand(rawBrand) || "unknown";
7427
+ const maxPanLen = numberValidation.card?.lengths?.slice(-1)[0] ?? 19;
7428
+ const minPanLenForType = numberValidation.card?.lengths?.[0];
7429
+ const panPastMinimumLength = minPanLenForType !== void 0 && panDigits.length >= minPanLenForType;
7430
+ const cvcSize = rawBrand === "american-express" || brand === "amex" ? 4 : 3;
7431
+ const expiryParsed = parseExpiry(expiryRaw);
7432
+ const expiryValid = expiryRaw.replace(/\D/g, "").length >= 4 && expiryParsed !== null ? valid__default.default.expirationDate({
7433
+ month: String(expiryParsed.month),
7434
+ year: String(expiryParsed.year)
7435
+ }).isValid : false;
7436
+ const cvvValid = cvc.length >= cvcSize && valid__default.default.cvv(cvc, cvcSize).isValid;
7437
+ const panComplete = numberValidation.isValid && brandAllowed(rawBrand, acceptedBrands);
7438
+ const holderOk = !requireHolderName || holderName.trim().length > 1;
7439
+ const canSubmit = panComplete && expiryValid && cvvValid && holderOk && !disabled && !value;
7440
+ const handlePanChange = (e) => {
7441
+ const d = onlyDigits(e.target.value, maxPanLen);
7442
+ setPanDigits(d);
7443
+ };
7444
+ const handleExpiryChange = (e) => {
7445
+ let d = onlyDigits(e.target.value, 4);
7446
+ if (d.length >= 2) d = `${d.slice(0, 2)}/${d.slice(2)}`;
7447
+ setExpiryRaw(d);
7448
+ };
7449
+ const runTokenize = React2.useCallback(async () => {
7450
+ if (!expiryParsed) return;
7451
+ setSubmitting(true);
7452
+ try {
7453
+ const result = await tokenize({
7454
+ pan: panDigits,
7455
+ expMonth: expiryParsed.month,
7456
+ expYear: expiryParsed.year,
7457
+ cvc,
7458
+ holderName: holderName.trim() || void 0
7459
+ });
7460
+ onChange(result);
7461
+ setPanDigits("");
7462
+ setExpiryRaw("");
7463
+ setCvc("");
7464
+ setHolderName("");
7465
+ } catch (err) {
7466
+ const msg = err instanceof Error ? err.message : labels.tokenizeFailed;
7467
+ onError?.(msg);
7468
+ } finally {
7469
+ setSubmitting(false);
7470
+ }
7471
+ }, [
7472
+ cvc,
7473
+ expiryParsed,
7474
+ holderName,
7475
+ labels.tokenizeFailed,
7476
+ onChange,
7477
+ onError,
7478
+ panDigits,
7479
+ tokenize
7480
+ ]);
7481
+ const displayPan = panFocused || panDigits.length <= 4 ? formatPan(panDigits) : `\u2022\u2022\u2022\u2022 \u2022\u2022\u2022\u2022 \u2022\u2022\u2022\u2022 ${panDigits.slice(-4)}`;
7482
+ if (value?.tokenId) {
7483
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7484
+ "div",
7485
+ {
7486
+ className: cn("space-y-3 rounded-lg border bg-muted/30 p-4", className),
7487
+ children: [
7488
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm", children: [
7489
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CreditCard, { className: "h-4 w-4 text-muted-foreground" }),
7490
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: value.masked }),
7491
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
7492
+ String(value.expMonth).padStart(2, "0"),
7493
+ "/",
7494
+ String(value.expYear).slice(-2)
7495
+ ] })
7496
+ ] }),
7497
+ value.holderName ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: value.holderName }) : null,
7498
+ /* @__PURE__ */ jsxRuntime.jsx(
7499
+ Button,
7500
+ {
7501
+ type: "button",
7502
+ variant: "outline",
7503
+ size: "sm",
7504
+ disabled,
7505
+ onClick: () => onChange(null),
7506
+ children: labels.replace
7507
+ }
7508
+ )
7509
+ ]
7510
+ }
7511
+ );
7512
+ }
7513
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-4 rounded-lg border bg-card p-4", className), children: [
7514
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
7515
+ /* @__PURE__ */ jsxRuntime.jsx(
7516
+ "label",
7517
+ {
7518
+ htmlFor: `${baseId}-pan`,
7519
+ className: cn("font-medium leading-none", labelClass),
7520
+ children: labels.number
7521
+ }
7522
+ ),
7523
+ /* @__PURE__ */ jsxRuntime.jsx(
7524
+ "input",
7525
+ {
7526
+ id: `${baseId}-pan`,
7527
+ inputMode: "numeric",
7528
+ autoComplete: "cc-number",
7529
+ value: displayPan,
7530
+ onChange: handlePanChange,
7531
+ onFocus: () => setPanFocused(true),
7532
+ onBlur: () => setPanFocused(false),
7533
+ placeholder: "4242 4242 4242 4242",
7534
+ disabled,
7535
+ className: cn(inputClassName, inputClass)
7536
+ }
7537
+ ),
7538
+ panDigits.length > 0 && (!numberValidation.isPotentiallyValid || panPastMinimumLength && !numberValidation.isValid) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: labels.invalidNumber }),
7539
+ panDigits.length > 0 && numberValidation.isPotentiallyValid && !brandAllowed(rawBrand, acceptedBrands) && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: labels.invalidNumber })
7540
+ ] }),
7541
+ requireHolderName && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
7542
+ /* @__PURE__ */ jsxRuntime.jsx(
7543
+ "label",
7544
+ {
7545
+ htmlFor: `${baseId}-holder`,
7546
+ className: cn("font-medium leading-none", labelClass),
7547
+ children: labels.holderName
7548
+ }
7549
+ ),
7550
+ /* @__PURE__ */ jsxRuntime.jsx(
7551
+ "input",
7552
+ {
7553
+ id: `${baseId}-holder`,
7554
+ autoComplete: "cc-name",
7555
+ value: holderName,
7556
+ onChange: (e) => setHolderName(e.target.value),
7557
+ disabled,
7558
+ className: cn(inputClassName, inputClass)
7559
+ }
7560
+ )
7561
+ ] }),
7562
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
7563
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
7564
+ /* @__PURE__ */ jsxRuntime.jsx(
7565
+ "label",
7566
+ {
7567
+ htmlFor: `${baseId}-exp`,
7568
+ className: cn("font-medium leading-none", labelClass),
7569
+ children: labels.expiry
7570
+ }
7571
+ ),
7572
+ /* @__PURE__ */ jsxRuntime.jsx(
7573
+ "input",
7574
+ {
7575
+ id: `${baseId}-exp`,
7576
+ inputMode: "numeric",
7577
+ autoComplete: "cc-exp",
7578
+ value: expiryRaw,
7579
+ onChange: handleExpiryChange,
7580
+ placeholder: "MM/YY",
7581
+ disabled,
7582
+ className: cn(inputClassName, inputClass)
7583
+ }
7584
+ ),
7585
+ expiryRaw.replace(/\D/g, "").length >= 4 && !expiryValid && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: labels.expiredCard })
7586
+ ] }),
7587
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
7588
+ /* @__PURE__ */ jsxRuntime.jsx(
7589
+ "label",
7590
+ {
7591
+ htmlFor: `${baseId}-cvc`,
7592
+ className: cn("font-medium leading-none", labelClass),
7593
+ children: labels.cvc
7594
+ }
7595
+ ),
7596
+ /* @__PURE__ */ jsxRuntime.jsx(
7597
+ "input",
7598
+ {
7599
+ id: `${baseId}-cvc`,
7600
+ inputMode: "numeric",
7601
+ autoComplete: "cc-csc",
7602
+ value: cvc,
7603
+ onChange: (e) => setCvc(onlyDigits(e.target.value, cvcSize)),
7604
+ placeholder: cvcSize === 4 ? "0000" : "000",
7605
+ disabled,
7606
+ className: cn(inputClassName, inputClass),
7607
+ maxLength: cvcSize
7608
+ }
7609
+ ),
7610
+ cvc.length > 0 && !cvvValid && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: labels.invalidCvc })
7611
+ ] })
7612
+ ] }),
7613
+ /* @__PURE__ */ jsxRuntime.jsxs(
7614
+ Button,
7615
+ {
7616
+ type: "button",
7617
+ disabled: !canSubmit || submitting,
7618
+ onClick: () => void runTokenize(),
7619
+ className: "gap-2",
7620
+ children: [
7621
+ submitting ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CreditCard, { className: "h-4 w-4" }),
7622
+ labels.tokenize
7623
+ ]
7624
+ }
7625
+ )
7626
+ ] });
7627
+ }
7628
+ function parseInitial(value) {
7629
+ if (!value) {
7630
+ return {
7631
+ street: "",
7632
+ street2: "",
7633
+ city: "",
7634
+ state: "",
7635
+ zip: "",
7636
+ country: "US"
7637
+ };
7638
+ }
7639
+ return {
7640
+ street: value.street ?? "",
7641
+ street2: value.street2 ?? "",
7642
+ city: value.city ?? "",
7643
+ state: value.state ?? "",
7644
+ zip: value.zip ?? "",
7645
+ country: "US"
7646
+ };
7647
+ }
7648
+ var inputClassName2 = "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm";
7649
+ function USAddressInput({
7650
+ value,
7651
+ onChange,
7652
+ lookupZip,
7653
+ validateAddress,
7654
+ autocomplete,
7655
+ labels,
7656
+ disabled = false,
7657
+ largeText = false,
7658
+ className,
7659
+ onError,
7660
+ showAutocompleteToggle = true
7661
+ }) {
7662
+ const [autocompleteEnabled, setAutocompleteEnabled] = React2.useState(false);
7663
+ const [searchQuery, setSearchQuery] = React2.useState("");
7664
+ const [showSuggestions, setShowSuggestions] = React2.useState(false);
7665
+ const [suggestions, setSuggestions] = React2.useState([]);
7666
+ const [address, setAddress] = React2.useState(
7667
+ () => parseInitial(value)
7668
+ );
7669
+ const [sessionToken] = React2.useState(
7670
+ () => typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`
7671
+ );
7672
+ const [loadingAutocomplete, setLoadingAutocomplete] = React2.useState(false);
7673
+ const [loadingZip, setLoadingZip] = React2.useState(false);
7674
+ const [loadingValidate, setLoadingValidate] = React2.useState(false);
7675
+ const [uspsSuggestion, setUspsSuggestion] = React2.useState(
7676
+ null
7677
+ );
7678
+ const searchRef = React2.useRef(null);
7679
+ const suggestionCache = React2.useRef(/* @__PURE__ */ new Map());
7680
+ const warnedRef = React2.useRef({
7681
+ autocomplete: false,
7682
+ zip: false,
7683
+ validate: false
7684
+ });
7685
+ const lastZipLookup = React2.useRef("");
7686
+ const debouncedSearch = useDebouncedValue(searchQuery, 300);
7687
+ const zipDigits = address.zip.replace(/\D/g, "").slice(0, 5);
7688
+ const debouncedZip = useDebouncedValue(zipDigits, 400);
7689
+ const inputClass = largeText ? "text-base py-3" : "";
7690
+ const labelClass = largeText ? "text-base" : "text-sm";
7691
+ React2.useEffect(() => {
7692
+ const handleClickOutside = (event) => {
7693
+ if (searchRef.current && !searchRef.current.contains(event.target)) {
7694
+ setShowSuggestions(false);
7695
+ }
7696
+ };
7697
+ document.addEventListener("mousedown", handleClickOutside);
7698
+ return () => document.removeEventListener("mousedown", handleClickOutside);
7699
+ }, []);
7700
+ const valueSyncKey = value == null ? "" : `o:${JSON.stringify(value)}`;
7701
+ React2.useEffect(() => {
7702
+ setAddress(parseInitial(value));
7703
+ }, [valueSyncKey]);
7704
+ const handleFieldChange = (field, fieldValue) => {
7705
+ setAddress((prev) => {
7706
+ const next = field === "country" ? { ...prev, country: "US" } : { ...prev, [field]: fieldValue };
7707
+ onChange(next);
7708
+ return next;
7709
+ });
7710
+ };
7711
+ const allowAutocomplete = Boolean(autocomplete);
7712
+ const allowUspsValidation = Boolean(validateAddress);
7713
+ const allowZipLookup = Boolean(lookupZip);
7714
+ React2.useEffect(() => {
7715
+ if (!allowAutocomplete || !autocompleteEnabled || !autocomplete) return;
7716
+ const q = debouncedSearch.trim();
7717
+ if (q.length < 3) {
7718
+ setSuggestions([]);
7719
+ setShowSuggestions(false);
7720
+ setLoadingAutocomplete(false);
7721
+ return;
7722
+ }
7723
+ const cacheKey = q.toLowerCase();
7724
+ const cached = suggestionCache.current.get(cacheKey);
7725
+ if (cached) {
7726
+ setSuggestions(cached);
7727
+ setShowSuggestions(true);
7728
+ setLoadingAutocomplete(false);
7729
+ return;
7730
+ }
7731
+ const ctrl = new AbortController();
7732
+ setLoadingAutocomplete(true);
7733
+ autocomplete.search(q, sessionToken, ctrl.signal).then((list) => {
7734
+ suggestionCache.current.set(cacheKey, list);
7735
+ setSuggestions(list);
7736
+ setShowSuggestions(list.length > 0);
7737
+ }).catch((e) => {
7738
+ if (e.name === "AbortError") return;
7739
+ if (!warnedRef.current.autocomplete) {
7740
+ onError?.(labels.autocompleteUnavailable);
7741
+ warnedRef.current.autocomplete = true;
7742
+ }
7743
+ setSuggestions([]);
7744
+ setShowSuggestions(false);
7745
+ }).finally(() => setLoadingAutocomplete(false));
7746
+ return () => ctrl.abort();
7747
+ }, [
7748
+ debouncedSearch,
7749
+ allowAutocomplete,
7750
+ autocompleteEnabled,
7751
+ autocomplete,
7752
+ sessionToken,
7753
+ labels.autocompleteUnavailable,
7754
+ onError
7755
+ ]);
7756
+ React2.useEffect(() => {
7757
+ if (!allowZipLookup || !lookupZip) return;
7758
+ if (debouncedZip.length !== 5) return;
7759
+ if (debouncedZip === lastZipLookup.current) return;
7760
+ const ctrl = new AbortController();
7761
+ setLoadingZip(true);
7762
+ lookupZip(debouncedZip, ctrl.signal).then((data) => {
7763
+ lastZipLookup.current = debouncedZip;
7764
+ setAddress((prev) => {
7765
+ const cityEmpty = !prev.city.trim();
7766
+ const stateEmpty = !prev.state.trim();
7767
+ const next = { ...prev };
7768
+ if (cityEmpty && data.city) next.city = data.city;
7769
+ if (stateEmpty && data.state) next.state = data.state;
7770
+ next.country = "US";
7771
+ onChange(next);
7772
+ return next;
7773
+ });
7774
+ }).catch(() => {
7775
+ if (!warnedRef.current.zip) {
7776
+ onError?.(labels.zipLookupFailed);
7777
+ warnedRef.current.zip = true;
7778
+ }
7779
+ }).finally(() => setLoadingZip(false));
7780
+ return () => ctrl.abort();
7781
+ }, [
7782
+ debouncedZip,
7783
+ allowZipLookup,
7784
+ lookupZip,
7785
+ onChange,
7786
+ labels.zipLookupFailed,
7787
+ onError
7788
+ ]);
7789
+ const handleSelectSuggestion = async (row) => {
7790
+ if (!autocomplete) return;
7791
+ try {
7792
+ const next = await autocomplete.details(
7793
+ row.placeId,
7794
+ AbortSignal.timeout(8e3)
7795
+ );
7796
+ const normalized = {
7797
+ ...next,
7798
+ country: "US"
7799
+ };
7800
+ setAddress(normalized);
7801
+ onChange(normalized);
7802
+ setShowSuggestions(false);
7803
+ setSearchQuery("");
7804
+ setUspsSuggestion(null);
7805
+ } catch {
7806
+ onError?.(labels.placeLookupFailed);
7807
+ }
7808
+ };
7809
+ const runUspsValidate = async () => {
7810
+ if (!allowUspsValidation || !validateAddress || disabled) return;
7811
+ setLoadingValidate(true);
7812
+ setUspsSuggestion(null);
7813
+ try {
7814
+ const result = await validateAddress(address, AbortSignal.timeout(8e3));
7815
+ const std = result.standardized;
7816
+ if (result.changed) {
7817
+ setUspsSuggestion(std);
7818
+ }
7819
+ } catch {
7820
+ if (!warnedRef.current.validate) {
7821
+ onError?.(labels.uspsUnavailable);
7822
+ warnedRef.current.validate = true;
7823
+ }
7824
+ } finally {
7825
+ setLoadingValidate(false);
7826
+ }
7827
+ };
7828
+ const applyUspsSuggestion = () => {
7829
+ if (!uspsSuggestion) return;
7830
+ setAddress(uspsSuggestion);
7831
+ onChange(uspsSuggestion);
7832
+ setUspsSuggestion(null);
7833
+ };
7834
+ const showAutocompleteUi = allowAutocomplete && showAutocompleteToggle && autocompleteEnabled;
7835
+ const toggleTitle = labels.autocompleteTitle ?? labels.searchPlaceholder;
7836
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-4", className), children: [
7837
+ allowAutocomplete && showAutocompleteToggle && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between rounded-lg border bg-muted/50 p-3", children: [
7838
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
7839
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MapPin, { className: "h-4 w-4 text-muted-foreground" }),
7840
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
7841
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("font-medium", labelClass), children: toggleTitle }),
7842
+ labels.autocompleteDescription ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: labels.autocompleteDescription }) : null
7843
+ ] })
7844
+ ] }),
7845
+ /* @__PURE__ */ jsxRuntime.jsx(
7846
+ "button",
7847
+ {
7848
+ type: "button",
7849
+ role: "switch",
7850
+ "aria-checked": autocompleteEnabled,
7851
+ disabled,
7852
+ onClick: () => setAutocompleteEnabled((v) => !v),
7853
+ className: cn(
7854
+ "relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors",
7855
+ autocompleteEnabled ? "bg-primary" : "bg-input",
7856
+ disabled && "cursor-not-allowed opacity-50"
7857
+ ),
7858
+ children: /* @__PURE__ */ jsxRuntime.jsx(
7859
+ "span",
7860
+ {
7861
+ className: cn(
7862
+ "pointer-events-none block size-5 translate-x-0 rounded-full bg-background shadow-lg ring-0 transition-transform",
7863
+ autocompleteEnabled && "translate-x-5"
7864
+ )
7865
+ }
7866
+ )
7867
+ }
7868
+ )
7869
+ ] }),
7870
+ showAutocompleteUi && autocomplete && /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: searchRef, className: "relative", children: [
7871
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
7872
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
7873
+ /* @__PURE__ */ jsxRuntime.jsx(
7874
+ "input",
7875
+ {
7876
+ value: searchQuery,
7877
+ onChange: (e) => setSearchQuery(e.target.value),
7878
+ placeholder: labels.searchPlaceholder,
7879
+ disabled,
7880
+ className: cn(inputClassName2, "pl-9 pr-9", inputClass)
7881
+ }
7882
+ ),
7883
+ loadingAutocomplete && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "absolute right-10 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-muted-foreground" }),
7884
+ searchQuery ? /* @__PURE__ */ jsxRuntime.jsx(
7885
+ "button",
7886
+ {
7887
+ type: "button",
7888
+ onClick: () => setSearchQuery(""),
7889
+ className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground",
7890
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" })
7891
+ }
7892
+ ) : null
7893
+ ] }),
7894
+ showSuggestions && suggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute z-10 mt-1 max-h-48 w-full overflow-y-auto rounded-lg border bg-background shadow-lg", children: suggestions.map((suggestion) => /* @__PURE__ */ jsxRuntime.jsxs(
7895
+ "button",
7896
+ {
7897
+ type: "button",
7898
+ onClick: () => void handleSelectSuggestion(suggestion),
7899
+ className: "w-full border-b px-4 py-3 text-left transition-colors last:border-b-0 hover:bg-muted",
7900
+ children: [
7901
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: suggestion.mainText }),
7902
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: suggestion.secondaryText })
7903
+ ]
7904
+ },
7905
+ suggestion.placeId
7906
+ )) })
7907
+ ] }),
7908
+ allowUspsValidation && uspsSuggestion && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-900 dark:bg-amber-950/30", children: [
7909
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-amber-900 dark:text-amber-100", children: labels.uspsSuggested }),
7910
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: [
7911
+ uspsSuggestion.street,
7912
+ uspsSuggestion.street2,
7913
+ uspsSuggestion.city,
7914
+ uspsSuggestion.state,
7915
+ uspsSuggestion.zip
7916
+ ].filter(Boolean).join(", ") }),
7917
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
7918
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", size: "sm", onClick: applyUspsSuggestion, children: labels.uspsUseSuggestion }),
7919
+ /* @__PURE__ */ jsxRuntime.jsx(
7920
+ Button,
7921
+ {
7922
+ type: "button",
7923
+ size: "sm",
7924
+ variant: "outline",
7925
+ onClick: () => setUspsSuggestion(null),
7926
+ children: labels.uspsKeepMine
7927
+ }
7928
+ )
7929
+ ] })
7930
+ ] }),
7931
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4", children: [
7932
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
7933
+ /* @__PURE__ */ jsxRuntime.jsx(
7934
+ "label",
7935
+ {
7936
+ htmlFor: "blocks-usaddress-street",
7937
+ className: cn("font-medium leading-none", labelClass),
7938
+ children: labels.street
7939
+ }
7940
+ ),
7941
+ /* @__PURE__ */ jsxRuntime.jsx(
7942
+ "input",
7943
+ {
7944
+ id: "blocks-usaddress-street",
7945
+ value: address.street,
7946
+ onChange: (e) => handleFieldChange("street", e.target.value),
7947
+ placeholder: labels.streetPlaceholder ?? "",
7948
+ disabled,
7949
+ className: cn(inputClassName2, inputClass)
7950
+ }
7951
+ )
7952
+ ] }),
7953
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
7954
+ /* @__PURE__ */ jsxRuntime.jsxs(
7955
+ "label",
7956
+ {
7957
+ htmlFor: "blocks-usaddress-street2",
7958
+ className: cn(
7959
+ `${labelClass} text-muted-foreground`,
7960
+ "leading-none"
7961
+ ),
7962
+ children: [
7963
+ labels.street2 ?? "Address line 2",
7964
+ " ",
7965
+ labels.street2OptionalHint ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs", children: [
7966
+ "(",
7967
+ labels.street2OptionalHint,
7968
+ ")"
7969
+ ] }) : null
7970
+ ]
7971
+ }
7972
+ ),
7973
+ /* @__PURE__ */ jsxRuntime.jsx(
7974
+ "input",
7975
+ {
7976
+ id: "blocks-usaddress-street2",
7977
+ value: address.street2 || "",
7978
+ onChange: (e) => handleFieldChange("street2", e.target.value),
7979
+ placeholder: labels.street2Placeholder ?? "",
7980
+ disabled,
7981
+ className: cn(inputClassName2, inputClass)
7982
+ }
7983
+ )
7984
+ ] }),
7985
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
7986
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
7987
+ /* @__PURE__ */ jsxRuntime.jsx(
7988
+ "label",
7989
+ {
7990
+ htmlFor: "blocks-usaddress-city",
7991
+ className: cn("font-medium leading-none", labelClass),
7992
+ children: labels.city
7993
+ }
7994
+ ),
7995
+ /* @__PURE__ */ jsxRuntime.jsx(
7996
+ "input",
7997
+ {
7998
+ id: "blocks-usaddress-city",
7999
+ value: address.city,
8000
+ onChange: (e) => handleFieldChange("city", e.target.value),
8001
+ placeholder: labels.cityPlaceholder ?? "",
8002
+ disabled,
8003
+ className: cn(inputClassName2, inputClass)
8004
+ }
8005
+ )
8006
+ ] }),
8007
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
8008
+ /* @__PURE__ */ jsxRuntime.jsx(
8009
+ "label",
8010
+ {
8011
+ htmlFor: "blocks-usaddress-state",
8012
+ className: cn("font-medium leading-none", labelClass),
8013
+ children: labels.state
8014
+ }
8015
+ ),
8016
+ /* @__PURE__ */ jsxRuntime.jsx(
8017
+ "input",
8018
+ {
8019
+ id: "blocks-usaddress-state",
8020
+ value: address.state,
8021
+ onChange: (e) => handleFieldChange("state", e.target.value),
8022
+ placeholder: labels.statePlaceholder ?? "",
8023
+ disabled,
8024
+ className: cn(inputClassName2, inputClass)
8025
+ }
8026
+ )
8027
+ ] })
8028
+ ] }),
8029
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
8030
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
8031
+ /* @__PURE__ */ jsxRuntime.jsx(
8032
+ "label",
8033
+ {
8034
+ htmlFor: "blocks-usaddress-zip",
8035
+ className: cn("font-medium leading-none", labelClass),
8036
+ children: labels.zip
8037
+ }
8038
+ ),
8039
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
8040
+ /* @__PURE__ */ jsxRuntime.jsx(
8041
+ "input",
8042
+ {
8043
+ id: "blocks-usaddress-zip",
8044
+ value: address.zip,
8045
+ onChange: (e) => handleFieldChange(
8046
+ "zip",
8047
+ e.target.value.replace(/\D/g, "").slice(0, 10)
8048
+ ),
8049
+ placeholder: labels.zipPlaceholder ?? "",
8050
+ disabled,
8051
+ className: cn(inputClassName2, inputClass)
8052
+ }
8053
+ ),
8054
+ loadingZip && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-muted-foreground" })
8055
+ ] })
8056
+ ] }),
8057
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
8058
+ /* @__PURE__ */ jsxRuntime.jsx(
8059
+ "label",
8060
+ {
8061
+ htmlFor: "blocks-usaddress-country",
8062
+ className: cn("font-medium leading-none", labelClass),
8063
+ children: labels.countryLabel ?? "Country"
8064
+ }
8065
+ ),
8066
+ /* @__PURE__ */ jsxRuntime.jsx(
8067
+ "input",
8068
+ {
8069
+ id: "blocks-usaddress-country",
8070
+ value: "United States",
8071
+ readOnly: true,
8072
+ disabled,
8073
+ className: cn(inputClassName2, inputClass, "bg-muted/50"),
8074
+ "aria-readonly": "true"
8075
+ }
8076
+ )
8077
+ ] })
8078
+ ] }),
8079
+ allowUspsValidation && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
8080
+ Button,
8081
+ {
8082
+ type: "button",
8083
+ variant: "secondary",
8084
+ size: "sm",
8085
+ disabled: disabled || loadingValidate,
8086
+ onClick: () => void runUspsValidate(),
8087
+ className: "gap-2",
8088
+ children: loadingValidate ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8089
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
8090
+ labels.uspsValidating
8091
+ ] }) : labels.uspsValidate
8092
+ }
8093
+ ) })
8094
+ ] })
8095
+ ] });
8096
+ }
7353
8097
 
7354
8098
  exports.AvatarEditor = AvatarEditor;
7355
8099
  exports.AvatarEditorDialog = AvatarEditorDialog;
@@ -7359,6 +8103,7 @@ exports.CalendarContext = CalendarContext;
7359
8103
  exports.CalendarSubscribeButton = CalendarSubscribeButton;
7360
8104
  exports.CalendarView = CalendarView;
7361
8105
  exports.CalendarWidget = CalendarWidget;
8106
+ exports.CardInput = CardInput;
7362
8107
  exports.ChatRoomView = ChatRoomView;
7363
8108
  exports.ChatSidebar = ChatSidebar;
7364
8109
  exports.ChatSidebarContext = ChatSidebarContext;
@@ -7423,6 +8168,7 @@ exports.Tooltip = Tooltip;
7423
8168
  exports.TooltipContent = TooltipContent;
7424
8169
  exports.TooltipProvider = TooltipProvider;
7425
8170
  exports.TooltipTrigger = TooltipTrigger;
8171
+ exports.USAddressInput = USAddressInput;
7426
8172
  exports.UpcomingEvents = UpcomingEvents;
7427
8173
  exports.buttonVariants = buttonVariants;
7428
8174
  exports.cn = cn;
@@ -7434,6 +8180,7 @@ exports.toggleVariants = toggleVariants;
7434
8180
  exports.useCalendarContext = useCalendarContext;
7435
8181
  exports.useChatRoom = useChatRoom;
7436
8182
  exports.useChatSidebar = useChatSidebar;
8183
+ exports.useDebouncedValue = useDebouncedValue;
7437
8184
  exports.useEnvironmentContext = useEnvironmentContext;
7438
8185
  exports.useLanguageContext = useLanguageContext;
7439
8186
  exports.useNotificationsContext = useNotificationsContext;