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

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
@@ -7362,6 +7362,15 @@ function useDebouncedValue(value, delayMs) {
7362
7362
  }, [value, delayMs]);
7363
7363
  return debounced;
7364
7364
  }
7365
+ function useDebouncedValueStrict(value, delayMs) {
7366
+ const [debounced, setDebounced] = React2.useState(void 0);
7367
+ React2.useEffect(() => {
7368
+ setDebounced(void 0);
7369
+ const id = window.setTimeout(() => setDebounced(value), delayMs);
7370
+ return () => window.clearTimeout(id);
7371
+ }, [value, delayMs]);
7372
+ return debounced;
7373
+ }
7365
7374
  function onlyDigits(s, max) {
7366
7375
  return s.replace(/\D/g, "").slice(0, max);
7367
7376
  }
@@ -7645,6 +7654,20 @@ function parseInitial(value) {
7645
7654
  country: "US"
7646
7655
  };
7647
7656
  }
7657
+ function addressSig(a) {
7658
+ return JSON.stringify({
7659
+ street: a.street.trim(),
7660
+ street2: (a.street2 ?? "").trim(),
7661
+ city: a.city.trim(),
7662
+ state: a.state.trim().toUpperCase(),
7663
+ zip: a.zip.replace(/\D/g, "").slice(0, 5)
7664
+ });
7665
+ }
7666
+ function isAddressComplete(a) {
7667
+ const zip5 = a.zip.replace(/\D/g, "").slice(0, 5);
7668
+ const st = a.state.trim();
7669
+ return a.street.trim().length > 0 && a.city.trim().length > 0 && /^[A-Za-z]{2}$/.test(st) && zip5.length === 5;
7670
+ }
7648
7671
  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
7672
  function USAddressInput({
7650
7673
  value,
@@ -7656,10 +7679,8 @@ function USAddressInput({
7656
7679
  disabled = false,
7657
7680
  largeText = false,
7658
7681
  className,
7659
- onError,
7660
- showAutocompleteToggle = true
7682
+ onError
7661
7683
  }) {
7662
- const [autocompleteEnabled, setAutocompleteEnabled] = React2.useState(false);
7663
7684
  const [searchQuery, setSearchQuery] = React2.useState("");
7664
7685
  const [showSuggestions, setShowSuggestions] = React2.useState(false);
7665
7686
  const [suggestions, setSuggestions] = React2.useState([]);
@@ -7675,6 +7696,7 @@ function USAddressInput({
7675
7696
  const [uspsSuggestion, setUspsSuggestion] = React2.useState(
7676
7697
  null
7677
7698
  );
7699
+ const [uspsVerifiedNoChange, setUspsVerifiedNoChange] = React2.useState(false);
7678
7700
  const searchRef = React2.useRef(null);
7679
7701
  const suggestionCache = React2.useRef(/* @__PURE__ */ new Map());
7680
7702
  const warnedRef = React2.useRef({
@@ -7683,9 +7705,13 @@ function USAddressInput({
7683
7705
  validate: false
7684
7706
  });
7685
7707
  const lastZipLookup = React2.useRef("");
7686
- const debouncedSearch = useDebouncedValue(searchQuery, 300);
7708
+ const addressRef = React2.useRef(address);
7709
+ addressRef.current = address;
7710
+ const lastValidatedSigRef = React2.useRef("");
7711
+ const debouncedSearch = useDebouncedValue(searchQuery, 400);
7687
7712
  const zipDigits = address.zip.replace(/\D/g, "").slice(0, 5);
7688
7713
  const debouncedZip = useDebouncedValue(zipDigits, 400);
7714
+ const debouncedAddressForValidate = useDebouncedValueStrict(address, 1200);
7689
7715
  const inputClass = largeText ? "text-base py-3" : "";
7690
7716
  const labelClass = largeText ? "text-base" : "text-sm";
7691
7717
  React2.useEffect(() => {
@@ -7708,11 +7734,18 @@ function USAddressInput({
7708
7734
  return next;
7709
7735
  });
7710
7736
  };
7737
+ React2.useEffect(() => {
7738
+ const sig = addressSig(addressRef.current);
7739
+ if (sig !== lastValidatedSigRef.current) {
7740
+ setUspsVerifiedNoChange(false);
7741
+ setUspsSuggestion(null);
7742
+ }
7743
+ }, [address]);
7711
7744
  const allowAutocomplete = Boolean(autocomplete);
7712
7745
  const allowUspsValidation = Boolean(validateAddress);
7713
7746
  const allowZipLookup = Boolean(lookupZip);
7714
7747
  React2.useEffect(() => {
7715
- if (!allowAutocomplete || !autocompleteEnabled || !autocomplete) return;
7748
+ if (!allowAutocomplete || !autocomplete) return;
7716
7749
  const q = debouncedSearch.trim();
7717
7750
  if (q.length < 3) {
7718
7751
  setSuggestions([]);
@@ -7731,6 +7764,7 @@ function USAddressInput({
7731
7764
  const ctrl = new AbortController();
7732
7765
  setLoadingAutocomplete(true);
7733
7766
  autocomplete.search(q, sessionToken, ctrl.signal).then((list) => {
7767
+ if (ctrl.signal.aborted) return;
7734
7768
  suggestionCache.current.set(cacheKey, list);
7735
7769
  setSuggestions(list);
7736
7770
  setShowSuggestions(list.length > 0);
@@ -7747,7 +7781,6 @@ function USAddressInput({
7747
7781
  }, [
7748
7782
  debouncedSearch,
7749
7783
  allowAutocomplete,
7750
- autocompleteEnabled,
7751
7784
  autocomplete,
7752
7785
  sessionToken,
7753
7786
  labels.autocompleteUnavailable,
@@ -7760,6 +7793,7 @@ function USAddressInput({
7760
7793
  const ctrl = new AbortController();
7761
7794
  setLoadingZip(true);
7762
7795
  lookupZip(debouncedZip, ctrl.signal).then((data) => {
7796
+ if (ctrl.signal.aborted) return;
7763
7797
  lastZipLookup.current = debouncedZip;
7764
7798
  setAddress((prev) => {
7765
7799
  const cityEmpty = !prev.city.trim();
@@ -7786,6 +7820,52 @@ function USAddressInput({
7786
7820
  labels.zipLookupFailed,
7787
7821
  onError
7788
7822
  ]);
7823
+ React2.useEffect(() => {
7824
+ if (!allowUspsValidation || !validateAddress || disabled) return;
7825
+ if (debouncedAddressForValidate === void 0) return;
7826
+ const addr = debouncedAddressForValidate;
7827
+ if (!isAddressComplete(addr)) return;
7828
+ const requestSig = addressSig(addr);
7829
+ if (requestSig === lastValidatedSigRef.current) return;
7830
+ const ctrl = new AbortController();
7831
+ const timeoutId = window.setTimeout(() => ctrl.abort(), 8e3);
7832
+ setLoadingValidate(true);
7833
+ setUspsVerifiedNoChange(false);
7834
+ setUspsSuggestion(null);
7835
+ validateAddress(addr, ctrl.signal).then((result) => {
7836
+ if (ctrl.signal.aborted) return;
7837
+ if (addressSig(addressRef.current) !== requestSig) return;
7838
+ lastValidatedSigRef.current = requestSig;
7839
+ const std = result.standardized;
7840
+ if (result.changed) {
7841
+ setUspsSuggestion(std);
7842
+ setUspsVerifiedNoChange(false);
7843
+ } else {
7844
+ setUspsSuggestion(null);
7845
+ setUspsVerifiedNoChange(true);
7846
+ }
7847
+ }).catch(() => {
7848
+ if (ctrl.signal.aborted) return;
7849
+ if (!warnedRef.current.validate) {
7850
+ onError?.(labels.uspsUnavailable);
7851
+ warnedRef.current.validate = true;
7852
+ }
7853
+ }).finally(() => {
7854
+ window.clearTimeout(timeoutId);
7855
+ setLoadingValidate(false);
7856
+ });
7857
+ return () => {
7858
+ window.clearTimeout(timeoutId);
7859
+ ctrl.abort();
7860
+ };
7861
+ }, [
7862
+ debouncedAddressForValidate,
7863
+ allowUspsValidation,
7864
+ validateAddress,
7865
+ disabled,
7866
+ labels.uspsUnavailable,
7867
+ onError
7868
+ ]);
7789
7869
  const handleSelectSuggestion = async (row) => {
7790
7870
  if (!autocomplete) return;
7791
7871
  try {
@@ -7802,72 +7882,27 @@ function USAddressInput({
7802
7882
  setShowSuggestions(false);
7803
7883
  setSearchQuery("");
7804
7884
  setUspsSuggestion(null);
7885
+ lastValidatedSigRef.current = "";
7886
+ setUspsVerifiedNoChange(false);
7805
7887
  } catch {
7806
7888
  onError?.(labels.placeLookupFailed);
7807
7889
  }
7808
7890
  };
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
7891
  const applyUspsSuggestion = () => {
7829
7892
  if (!uspsSuggestion) return;
7830
7893
  setAddress(uspsSuggestion);
7831
7894
  onChange(uspsSuggestion);
7832
7895
  setUspsSuggestion(null);
7896
+ lastValidatedSigRef.current = addressSig(uspsSuggestion);
7897
+ setUspsVerifiedNoChange(true);
7898
+ };
7899
+ const dismissUspsSuggestion = () => {
7900
+ setUspsSuggestion(null);
7901
+ lastValidatedSigRef.current = addressSig(addressRef.current);
7902
+ setUspsVerifiedNoChange(true);
7833
7903
  };
7834
- const showAutocompleteUi = allowAutocomplete && showAutocompleteToggle && autocompleteEnabled;
7835
- const toggleTitle = labels.autocompleteTitle ?? labels.searchPlaceholder;
7836
7904
  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: [
7905
+ allowAutocomplete && autocomplete && /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: searchRef, className: "relative", children: [
7871
7906
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
7872
7907
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }),
7873
7908
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -7877,7 +7912,8 @@ function USAddressInput({
7877
7912
  onChange: (e) => setSearchQuery(e.target.value),
7878
7913
  placeholder: labels.searchPlaceholder,
7879
7914
  disabled,
7880
- className: cn(inputClassName2, "pl-9 pr-9", inputClass)
7915
+ className: cn(inputClassName2, "pl-9 pr-9", inputClass),
7916
+ "aria-label": labels.searchPlaceholder
7881
7917
  }
7882
7918
  ),
7883
7919
  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" }),
@@ -7905,29 +7941,6 @@ function USAddressInput({
7905
7941
  suggestion.placeId
7906
7942
  )) })
7907
7943
  ] }),
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
7944
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4", children: [
7932
7945
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
7933
7946
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -8075,22 +8088,46 @@ function USAddressInput({
8075
8088
  }
8076
8089
  )
8077
8090
  ] })
8078
- ] }),
8079
- allowUspsValidation && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
8080
- Button,
8091
+ ] })
8092
+ ] }),
8093
+ allowUspsValidation && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8094
+ /* @__PURE__ */ jsxRuntime.jsx(
8095
+ "div",
8081
8096
  {
8082
- type: "button",
8083
- variant: "secondary",
8084
- size: "sm",
8085
- disabled: disabled || loadingValidate,
8086
- onClick: () => void runUspsValidate(),
8087
- className: "gap-2",
8097
+ className: "flex min-h-7 items-center gap-2 text-xs text-muted-foreground",
8098
+ "aria-live": "polite",
8088
8099
  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
8100
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 shrink-0 animate-spin" }),
8101
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.uspsValidating })
8102
+ ] }) : uspsVerifiedNoChange && !uspsSuggestion ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8103
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "h-3.5 w-3.5 shrink-0 text-emerald-600 dark:text-emerald-400" }),
8104
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.uspsVerified })
8105
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "select-none opacity-0", "aria-hidden": true, children: "\xA0" })
8092
8106
  }
8093
- ) })
8107
+ ),
8108
+ 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: [
8109
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-amber-900 dark:text-amber-100", children: labels.uspsSuggested }),
8110
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: [
8111
+ uspsSuggestion.street,
8112
+ uspsSuggestion.street2,
8113
+ uspsSuggestion.city,
8114
+ uspsSuggestion.state,
8115
+ uspsSuggestion.zip
8116
+ ].filter(Boolean).join(", ") }),
8117
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
8118
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", size: "sm", onClick: applyUspsSuggestion, children: labels.uspsUseSuggestion }),
8119
+ /* @__PURE__ */ jsxRuntime.jsx(
8120
+ Button,
8121
+ {
8122
+ type: "button",
8123
+ size: "sm",
8124
+ variant: "outline",
8125
+ onClick: dismissUspsSuggestion,
8126
+ children: labels.uspsKeepMine
8127
+ }
8128
+ )
8129
+ ] })
8130
+ ] }) : null
8094
8131
  ] })
8095
8132
  ] });
8096
8133
  }
@@ -8181,6 +8218,7 @@ exports.useCalendarContext = useCalendarContext;
8181
8218
  exports.useChatRoom = useChatRoom;
8182
8219
  exports.useChatSidebar = useChatSidebar;
8183
8220
  exports.useDebouncedValue = useDebouncedValue;
8221
+ exports.useDebouncedValueStrict = useDebouncedValueStrict;
8184
8222
  exports.useEnvironmentContext = useEnvironmentContext;
8185
8223
  exports.useLanguageContext = useLanguageContext;
8186
8224
  exports.useNotificationsContext = useNotificationsContext;