@wherabouts/react-ui 0.1.0 → 0.3.0

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.js CHANGED
@@ -1,24 +1,45 @@
1
1
  import { useAutocomplete, useCombobox, useReverseGeocode } from '@wherabouts/react';
2
2
  import { useMemo, useRef, useState, useEffect } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
+ import { countryName } from '@wherabouts/sdk';
4
5
  import { jsxs, jsx } from 'react/jsx-runtime';
5
6
 
6
- // src/utils/parse-address.ts
7
- var COUNTRY_NAMES = {
8
- AU: "Australia"
9
- };
10
- function toAddressWithParsed(suggestion) {
11
- return {
12
- id: suggestion.id,
13
- formattedAddress: suggestion.formattedAddress,
14
- latitude: suggestion.latitude,
15
- longitude: suggestion.longitude,
16
- streetAddress: suggestion.streetAddress,
17
- suburb: suggestion.locality,
18
- state: suggestion.state,
19
- postcode: suggestion.postcode,
20
- country: COUNTRY_NAMES[suggestion.country] ?? suggestion.country
21
- };
7
+ // src/components/address-autocomplete.tsx
8
+ function useAddressGeolocation(enabled) {
9
+ const [lat, setLat] = useState(void 0);
10
+ const [lng, setLng] = useState(void 0);
11
+ const [loading, setLoading] = useState(false);
12
+ const [error, setError] = useState(null);
13
+ useEffect(() => {
14
+ if (!enabled) {
15
+ return;
16
+ }
17
+ if (typeof navigator === "undefined" || !("geolocation" in navigator)) {
18
+ return;
19
+ }
20
+ setLoading(true);
21
+ const controller = new AbortController();
22
+ navigator.geolocation.getCurrentPosition(
23
+ (position) => {
24
+ if (!controller.signal.aborted) {
25
+ setLat(position.coords.latitude);
26
+ setLng(position.coords.longitude);
27
+ setError(null);
28
+ setLoading(false);
29
+ }
30
+ },
31
+ (err) => {
32
+ if (!controller.signal.aborted) {
33
+ setError(err);
34
+ setLoading(false);
35
+ }
36
+ }
37
+ );
38
+ return () => {
39
+ controller.abort();
40
+ };
41
+ }, [enabled]);
42
+ return { lat, lng, loading, error };
22
43
  }
23
44
 
24
45
  // ../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
@@ -2500,41 +2521,18 @@ var twMerge = /* @__PURE__ */ createTailwindMerge(getDefaultConfig);
2500
2521
  function cn(...inputs) {
2501
2522
  return twMerge(clsx(inputs));
2502
2523
  }
2503
- function useAddressGeolocation(enabled) {
2504
- const [lat, setLat] = useState(void 0);
2505
- const [lng, setLng] = useState(void 0);
2506
- const [loading, setLoading] = useState(false);
2507
- const [error, setError] = useState(null);
2508
- useEffect(() => {
2509
- if (!enabled) {
2510
- return;
2511
- }
2512
- if (typeof navigator === "undefined" || !("geolocation" in navigator)) {
2513
- return;
2514
- }
2515
- setLoading(true);
2516
- const controller = new AbortController();
2517
- navigator.geolocation.getCurrentPosition(
2518
- (position) => {
2519
- if (!controller.signal.aborted) {
2520
- setLat(position.coords.latitude);
2521
- setLng(position.coords.longitude);
2522
- setError(null);
2523
- setLoading(false);
2524
- }
2525
- },
2526
- (err) => {
2527
- if (!controller.signal.aborted) {
2528
- setError(err);
2529
- setLoading(false);
2530
- }
2531
- }
2532
- );
2533
- return () => {
2534
- controller.abort();
2535
- };
2536
- }, [enabled]);
2537
- return { lat, lng, loading, error };
2524
+ function toAddressWithParsed(suggestion) {
2525
+ return {
2526
+ id: suggestion.id,
2527
+ formattedAddress: suggestion.formattedAddress,
2528
+ latitude: suggestion.latitude,
2529
+ longitude: suggestion.longitude,
2530
+ streetAddress: suggestion.streetAddress,
2531
+ suburb: suggestion.locality,
2532
+ state: suggestion.state,
2533
+ postcode: suggestion.postcode,
2534
+ country: countryName(suggestion.country)
2535
+ };
2538
2536
  }
2539
2537
  var DEFAULT_I18N = {
2540
2538
  noResults: "No addresses found",
@@ -2778,186 +2776,6 @@ function AddressAutocomplete({
2778
2776
  }
2779
2777
  );
2780
2778
  }
2781
- function AddressFormField({
2782
- label,
2783
- labelClassName,
2784
- errorClassName,
2785
- error,
2786
- required,
2787
- disabled,
2788
- id: customId,
2789
- className,
2790
- ...autocompleteProps
2791
- }) {
2792
- const id = customId ?? "wherabouts-field";
2793
- return /* @__PURE__ */ jsxs(
2794
- "div",
2795
- {
2796
- "data-slot": "address-form-field",
2797
- className: "flex flex-col gap-2",
2798
- children: [
2799
- /* @__PURE__ */ jsxs(
2800
- "label",
2801
- {
2802
- htmlFor: id,
2803
- className: cn(
2804
- "block text-sm font-medium text-gray-900",
2805
- labelClassName
2806
- ),
2807
- children: [
2808
- label,
2809
- required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-1 text-red-600", children: "*" })
2810
- ]
2811
- }
2812
- ),
2813
- /* @__PURE__ */ jsx(
2814
- AddressAutocomplete,
2815
- {
2816
- ...autocompleteProps,
2817
- id,
2818
- required,
2819
- disabled,
2820
- error,
2821
- className
2822
- }
2823
- ),
2824
- error && /* @__PURE__ */ jsx(
2825
- "p",
2826
- {
2827
- role: "alert",
2828
- "aria-live": "polite",
2829
- className: cn(
2830
- "text-sm text-red-600",
2831
- errorClassName
2832
- ),
2833
- children: error
2834
- }
2835
- )
2836
- ]
2837
- }
2838
- );
2839
- }
2840
- function ReverseGeocodeInput({
2841
- client,
2842
- latitude,
2843
- longitude,
2844
- onResult,
2845
- className,
2846
- disabled,
2847
- placeholder = "Address will appear here",
2848
- id
2849
- }) {
2850
- const { address, distance } = useReverseGeocode(
2851
- client,
2852
- latitude != null && longitude != null ? { lat: latitude, lng: longitude } : null
2853
- );
2854
- useEffect(() => {
2855
- onResult?.({
2856
- address: address?.formattedAddress ?? null,
2857
- distance: distance ?? null
2858
- });
2859
- }, [address, distance, onResult]);
2860
- const displayText = address?.formattedAddress ?? "";
2861
- return /* @__PURE__ */ jsx(
2862
- "input",
2863
- {
2864
- id,
2865
- "data-slot": "geocode-input",
2866
- type: "text",
2867
- readOnly: true,
2868
- disabled,
2869
- value: displayText,
2870
- placeholder,
2871
- className: cn(
2872
- "block h-8 w-full cursor-default rounded-none border border-input bg-muted/40 px-2.5 py-1 text-foreground text-xs",
2873
- className
2874
- )
2875
- }
2876
- );
2877
- }
2878
- var toGeocodeAddress = (address) => address ? {
2879
- id: address.id,
2880
- formattedAddress: address.formattedAddress,
2881
- latitude: address.latitude,
2882
- longitude: address.longitude
2883
- } : null;
2884
- function useForwardGeocode(client, query) {
2885
- const [data, setData] = useState(null);
2886
- const [loading, setLoading] = useState(false);
2887
- const [error, setError] = useState(null);
2888
- useEffect(() => {
2889
- if (!query) {
2890
- setData(null);
2891
- setError(null);
2892
- return;
2893
- }
2894
- const controller = new AbortController();
2895
- async function fetch() {
2896
- setLoading(true);
2897
- setError(null);
2898
- try {
2899
- const response = await client.geocode.forward(
2900
- { q: query },
2901
- { signal: controller.signal }
2902
- );
2903
- if (controller.signal.aborted) {
2904
- return;
2905
- }
2906
- setData(toGeocodeAddress(response.address));
2907
- } catch (err) {
2908
- if (controller.signal.aborted) {
2909
- return;
2910
- }
2911
- setError(err instanceof Error ? err : new Error(String(err)));
2912
- setData(null);
2913
- } finally {
2914
- if (!controller.signal.aborted) {
2915
- setLoading(false);
2916
- }
2917
- }
2918
- }
2919
- fetch();
2920
- return () => {
2921
- controller.abort();
2922
- };
2923
- }, [client, query]);
2924
- return { data, loading, error };
2925
- }
2926
- function ForwardGeocodeInput({
2927
- client,
2928
- query,
2929
- onResult,
2930
- className,
2931
- disabled,
2932
- placeholder = "Coordinates will appear here",
2933
- id
2934
- }) {
2935
- const { data } = useForwardGeocode(client, query);
2936
- useEffect(() => {
2937
- onResult?.({
2938
- latitude: data?.latitude ?? null,
2939
- longitude: data?.longitude ?? null,
2940
- formattedAddress: data?.formattedAddress ?? null
2941
- });
2942
- }, [data, onResult]);
2943
- const displayText = data ? `${data.latitude.toFixed(4)}, ${data.longitude.toFixed(4)}` : "";
2944
- return /* @__PURE__ */ jsx(
2945
- "input",
2946
- {
2947
- id,
2948
- "data-slot": "geocode-input",
2949
- type: "text",
2950
- readOnly: true,
2951
- disabled,
2952
- value: displayText,
2953
- placeholder,
2954
- className: cn(
2955
- "block h-8 w-full cursor-default rounded-none border border-input bg-muted/40 px-2.5 py-1 text-foreground text-xs",
2956
- className
2957
- )
2958
- }
2959
- );
2960
- }
2961
2779
  function AddressFieldGroup({
2962
2780
  client,
2963
2781
  value,
@@ -2986,8 +2804,8 @@ function AddressFieldGroup({
2986
2804
  return /* @__PURE__ */ jsxs(
2987
2805
  "div",
2988
2806
  {
2989
- "data-slot": "address-field-group",
2990
2807
  className: cn("flex flex-col gap-4", className),
2808
+ "data-slot": "address-field-group",
2991
2809
  children: [
2992
2810
  /* @__PURE__ */ jsx(
2993
2811
  AddressAutocomplete,
@@ -3001,28 +2819,28 @@ function AddressFieldGroup({
3001
2819
  /* @__PURE__ */ jsxs(
3002
2820
  "div",
3003
2821
  {
3004
- "data-slot": "address-field-group-inputs",
3005
2822
  className: "grid grid-cols-2 gap-4",
2823
+ "data-slot": "address-field-group-inputs",
3006
2824
  children: [
3007
2825
  /* @__PURE__ */ jsxs("div", { className: "col-span-2", children: [
3008
2826
  /* @__PURE__ */ jsx(
3009
2827
  "label",
3010
2828
  {
3011
- htmlFor: "field-street",
3012
2829
  className: "mb-1 block font-medium text-foreground text-sm",
2830
+ htmlFor: "field-street",
3013
2831
  children: streetLabel
3014
2832
  }
3015
2833
  ),
3016
2834
  /* @__PURE__ */ jsx(
3017
2835
  "input",
3018
2836
  {
3019
- id: "field-street",
3020
- type: "text",
2837
+ className: "block h-8 w-full rounded-none border border-input bg-transparent px-2.5 py-1 text-foreground text-xs outline-none transition-colors placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30",
3021
2838
  disabled,
3022
- value: value.street,
2839
+ id: "field-street",
3023
2840
  onChange: (e) => handleFieldChange("street", e.target.value),
3024
- className: "block h-8 w-full rounded-none border border-input bg-transparent px-2.5 py-1 text-foreground text-xs outline-none transition-colors placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30",
3025
- placeholder: "Street address"
2841
+ placeholder: "Street address",
2842
+ type: "text",
2843
+ value: value.street
3026
2844
  }
3027
2845
  )
3028
2846
  ] }),
@@ -3030,21 +2848,21 @@ function AddressFieldGroup({
3030
2848
  /* @__PURE__ */ jsx(
3031
2849
  "label",
3032
2850
  {
3033
- htmlFor: "field-suburb",
3034
2851
  className: "mb-1 block font-medium text-foreground text-sm",
2852
+ htmlFor: "field-suburb",
3035
2853
  children: suburbLabel
3036
2854
  }
3037
2855
  ),
3038
2856
  /* @__PURE__ */ jsx(
3039
2857
  "input",
3040
2858
  {
3041
- id: "field-suburb",
3042
- type: "text",
2859
+ className: "block h-8 w-full rounded-none border border-input bg-transparent px-2.5 py-1 text-foreground text-xs outline-none transition-colors placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30",
3043
2860
  disabled,
3044
- value: value.suburb,
2861
+ id: "field-suburb",
3045
2862
  onChange: (e) => handleFieldChange("suburb", e.target.value),
3046
- className: "block h-8 w-full rounded-none border border-input bg-transparent px-2.5 py-1 text-foreground text-xs outline-none transition-colors placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30",
3047
- placeholder: "Suburb"
2863
+ placeholder: "Suburb",
2864
+ type: "text",
2865
+ value: value.suburb
3048
2866
  }
3049
2867
  )
3050
2868
  ] }),
@@ -3052,21 +2870,21 @@ function AddressFieldGroup({
3052
2870
  /* @__PURE__ */ jsx(
3053
2871
  "label",
3054
2872
  {
3055
- htmlFor: "field-state",
3056
2873
  className: "mb-1 block font-medium text-foreground text-sm",
2874
+ htmlFor: "field-state",
3057
2875
  children: stateLabel
3058
2876
  }
3059
2877
  ),
3060
2878
  /* @__PURE__ */ jsx(
3061
2879
  "input",
3062
2880
  {
3063
- id: "field-state",
3064
- type: "text",
2881
+ className: "block h-8 w-full rounded-none border border-input bg-transparent px-2.5 py-1 text-foreground text-xs outline-none transition-colors placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30",
3065
2882
  disabled,
3066
- value: value.state,
2883
+ id: "field-state",
3067
2884
  onChange: (e) => handleFieldChange("state", e.target.value),
3068
- className: "block h-8 w-full rounded-none border border-input bg-transparent px-2.5 py-1 text-foreground text-xs outline-none transition-colors placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30",
3069
- placeholder: "State"
2885
+ placeholder: "State",
2886
+ type: "text",
2887
+ value: value.state
3070
2888
  }
3071
2889
  )
3072
2890
  ] }),
@@ -3074,21 +2892,21 @@ function AddressFieldGroup({
3074
2892
  /* @__PURE__ */ jsx(
3075
2893
  "label",
3076
2894
  {
3077
- htmlFor: "field-postcode",
3078
2895
  className: "mb-1 block font-medium text-foreground text-sm",
2896
+ htmlFor: "field-postcode",
3079
2897
  children: postcodeLabel
3080
2898
  }
3081
2899
  ),
3082
2900
  /* @__PURE__ */ jsx(
3083
2901
  "input",
3084
2902
  {
3085
- id: "field-postcode",
3086
- type: "text",
2903
+ className: "block h-8 w-full rounded-none border border-input bg-transparent px-2.5 py-1 text-foreground text-xs outline-none transition-colors placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30",
3087
2904
  disabled,
3088
- value: value.postcode,
2905
+ id: "field-postcode",
3089
2906
  onChange: (e) => handleFieldChange("postcode", e.target.value),
3090
- className: "block h-8 w-full rounded-none border border-input bg-transparent px-2.5 py-1 text-foreground text-xs outline-none transition-colors placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-input/30",
3091
- placeholder: "Postcode"
2907
+ placeholder: "Postcode",
2908
+ type: "text",
2909
+ value: value.postcode
3092
2910
  }
3093
2911
  )
3094
2912
  ] })
@@ -3099,6 +2917,177 @@ function AddressFieldGroup({
3099
2917
  }
3100
2918
  );
3101
2919
  }
2920
+ function AddressFormField({
2921
+ label,
2922
+ labelClassName,
2923
+ errorClassName,
2924
+ error,
2925
+ required,
2926
+ disabled,
2927
+ id: customId,
2928
+ className,
2929
+ ...autocompleteProps
2930
+ }) {
2931
+ const id = customId ?? "wherabouts-field";
2932
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", "data-slot": "address-form-field", children: [
2933
+ /* @__PURE__ */ jsxs(
2934
+ "label",
2935
+ {
2936
+ className: cn(
2937
+ "block font-medium text-gray-900 text-sm",
2938
+ labelClassName
2939
+ ),
2940
+ htmlFor: id,
2941
+ children: [
2942
+ label,
2943
+ required && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "ml-1 text-red-600", children: "*" })
2944
+ ]
2945
+ }
2946
+ ),
2947
+ /* @__PURE__ */ jsx(
2948
+ AddressAutocomplete,
2949
+ {
2950
+ ...autocompleteProps,
2951
+ className,
2952
+ disabled,
2953
+ error,
2954
+ id,
2955
+ required
2956
+ }
2957
+ ),
2958
+ error && /* @__PURE__ */ jsx(
2959
+ "p",
2960
+ {
2961
+ "aria-live": "polite",
2962
+ className: cn("text-red-600 text-sm", errorClassName),
2963
+ role: "alert",
2964
+ children: error
2965
+ }
2966
+ )
2967
+ ] });
2968
+ }
2969
+ var toGeocodeAddress = (address) => address ? {
2970
+ id: address.id,
2971
+ formattedAddress: address.formattedAddress,
2972
+ latitude: address.latitude,
2973
+ longitude: address.longitude
2974
+ } : null;
2975
+ function useForwardGeocode(client, query) {
2976
+ const [data, setData] = useState(null);
2977
+ const [loading, setLoading] = useState(false);
2978
+ const [error, setError] = useState(null);
2979
+ useEffect(() => {
2980
+ if (!query) {
2981
+ setData(null);
2982
+ setError(null);
2983
+ return;
2984
+ }
2985
+ const activeQuery = query;
2986
+ const controller = new AbortController();
2987
+ async function fetch() {
2988
+ setLoading(true);
2989
+ setError(null);
2990
+ try {
2991
+ const response = await client.geocode.forward(
2992
+ { q: activeQuery },
2993
+ { signal: controller.signal }
2994
+ );
2995
+ if (controller.signal.aborted) {
2996
+ return;
2997
+ }
2998
+ setData(toGeocodeAddress(response.address));
2999
+ } catch (err) {
3000
+ if (controller.signal.aborted) {
3001
+ return;
3002
+ }
3003
+ setError(err instanceof Error ? err : new Error(String(err)));
3004
+ setData(null);
3005
+ } finally {
3006
+ if (!controller.signal.aborted) {
3007
+ setLoading(false);
3008
+ }
3009
+ }
3010
+ }
3011
+ fetch();
3012
+ return () => {
3013
+ controller.abort();
3014
+ };
3015
+ }, [client, query]);
3016
+ return { data, loading, error };
3017
+ }
3018
+ function ForwardGeocodeInput({
3019
+ client,
3020
+ query,
3021
+ onResult,
3022
+ className,
3023
+ disabled,
3024
+ placeholder = "Coordinates will appear here",
3025
+ id
3026
+ }) {
3027
+ const { data } = useForwardGeocode(client, query);
3028
+ useEffect(() => {
3029
+ onResult?.({
3030
+ latitude: data?.latitude ?? null,
3031
+ longitude: data?.longitude ?? null,
3032
+ formattedAddress: data?.formattedAddress ?? null
3033
+ });
3034
+ }, [data, onResult]);
3035
+ const displayText = data ? `${data.latitude.toFixed(4)}, ${data.longitude.toFixed(4)}` : "";
3036
+ return /* @__PURE__ */ jsx(
3037
+ "input",
3038
+ {
3039
+ className: cn(
3040
+ "block h-8 w-full cursor-default rounded-none border border-input bg-muted/40 px-2.5 py-1 text-foreground text-xs",
3041
+ className
3042
+ ),
3043
+ "data-slot": "geocode-input",
3044
+ disabled,
3045
+ id,
3046
+ placeholder,
3047
+ readOnly: true,
3048
+ type: "text",
3049
+ value: displayText
3050
+ }
3051
+ );
3052
+ }
3053
+ function ReverseGeocodeInput({
3054
+ client,
3055
+ latitude,
3056
+ longitude,
3057
+ onResult,
3058
+ className,
3059
+ disabled,
3060
+ placeholder = "Address will appear here",
3061
+ id
3062
+ }) {
3063
+ const { address, distance } = useReverseGeocode(
3064
+ client,
3065
+ latitude != null && longitude != null ? { lat: latitude, lng: longitude } : null
3066
+ );
3067
+ useEffect(() => {
3068
+ onResult?.({
3069
+ address: address?.formattedAddress ?? null,
3070
+ distance: distance ?? null
3071
+ });
3072
+ }, [address, distance, onResult]);
3073
+ const displayText = address?.formattedAddress ?? "";
3074
+ return /* @__PURE__ */ jsx(
3075
+ "input",
3076
+ {
3077
+ className: cn(
3078
+ "block h-8 w-full cursor-default rounded-none border border-input bg-muted/40 px-2.5 py-1 text-foreground text-xs",
3079
+ className
3080
+ ),
3081
+ "data-slot": "geocode-input",
3082
+ disabled,
3083
+ id,
3084
+ placeholder,
3085
+ readOnly: true,
3086
+ type: "text",
3087
+ value: displayText
3088
+ }
3089
+ );
3090
+ }
3102
3091
 
3103
3092
  export { AddressAutocomplete, AddressFieldGroup, AddressFormField, ForwardGeocodeInput, ReverseGeocodeInput, cn, toAddressWithParsed };
3104
3093
  //# sourceMappingURL=index.js.map