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