@wherabouts/react 0.1.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 ADDED
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/use-autocomplete.ts
6
+ function useAutocomplete(client, options = {}) {
7
+ const { debounceMs = 300, limit, country, state } = options;
8
+ const [query, setQuery] = react.useState("");
9
+ const [results, setResults] = react.useState([]);
10
+ const [loading, setLoading] = react.useState(false);
11
+ const [error, setError] = react.useState(null);
12
+ const abortRef = react.useRef(null);
13
+ const timerRef = react.useRef(null);
14
+ const handleSetQuery = react.useCallback((q) => {
15
+ setQuery(q);
16
+ }, []);
17
+ react.useEffect(() => {
18
+ if (timerRef.current) {
19
+ clearTimeout(timerRef.current);
20
+ }
21
+ if (abortRef.current) {
22
+ abortRef.current.abort();
23
+ }
24
+ if (!query.trim()) {
25
+ setResults([]);
26
+ setLoading(false);
27
+ setError(null);
28
+ return;
29
+ }
30
+ setLoading(true);
31
+ timerRef.current = setTimeout(async () => {
32
+ const controller = new AbortController();
33
+ abortRef.current = controller;
34
+ setError(null);
35
+ try {
36
+ const res = await client.addresses.autocomplete(
37
+ { q: query, limit, country, state },
38
+ { signal: controller.signal }
39
+ );
40
+ if (!controller.signal.aborted) {
41
+ setResults(res.results);
42
+ }
43
+ } catch (e) {
44
+ if (!controller.signal.aborted) {
45
+ setError(e instanceof Error ? e : new Error(String(e)));
46
+ setResults([]);
47
+ }
48
+ } finally {
49
+ if (!controller.signal.aborted) {
50
+ setLoading(false);
51
+ }
52
+ }
53
+ }, debounceMs);
54
+ return () => {
55
+ if (timerRef.current) {
56
+ clearTimeout(timerRef.current);
57
+ }
58
+ if (abortRef.current) {
59
+ abortRef.current.abort();
60
+ }
61
+ };
62
+ }, [query, debounceMs, limit, country, state, client]);
63
+ return { query, setQuery: handleSetQuery, results, loading, error };
64
+ }
65
+ function useReverseGeocode(client, coords) {
66
+ const [address, setAddress] = react.useState(null);
67
+ const [distance, setDistance] = react.useState(null);
68
+ const [loading, setLoading] = react.useState(false);
69
+ const [error, setError] = react.useState(null);
70
+ const abortRef = react.useRef(null);
71
+ react.useEffect(() => {
72
+ if (abortRef.current) {
73
+ abortRef.current.abort();
74
+ }
75
+ if (!coords) {
76
+ setAddress(null);
77
+ setDistance(null);
78
+ setLoading(false);
79
+ setError(null);
80
+ return;
81
+ }
82
+ const controller = new AbortController();
83
+ abortRef.current = controller;
84
+ setLoading(true);
85
+ setError(null);
86
+ client.addresses.reverse(coords, { signal: controller.signal }).then((res) => {
87
+ if (!controller.signal.aborted) {
88
+ setAddress(res.address);
89
+ setDistance(res.distance);
90
+ }
91
+ }).catch((e) => {
92
+ if (!controller.signal.aborted) {
93
+ setError(e instanceof Error ? e : new Error(String(e)));
94
+ }
95
+ }).finally(() => {
96
+ if (!controller.signal.aborted) {
97
+ setLoading(false);
98
+ }
99
+ });
100
+ return () => {
101
+ controller.abort();
102
+ };
103
+ }, [coords?.lat, coords?.lng, client]);
104
+ return { address, distance, loading, error };
105
+ }
106
+ function useZoneContains(client, coords) {
107
+ const [zones, setZones] = react.useState([]);
108
+ const [loading, setLoading] = react.useState(false);
109
+ const [error, setError] = react.useState(null);
110
+ const abortRef = react.useRef(null);
111
+ react.useEffect(() => {
112
+ if (abortRef.current) {
113
+ abortRef.current.abort();
114
+ }
115
+ if (!coords) {
116
+ setZones([]);
117
+ setLoading(false);
118
+ setError(null);
119
+ return;
120
+ }
121
+ const controller = new AbortController();
122
+ abortRef.current = controller;
123
+ setLoading(true);
124
+ setError(null);
125
+ client.zones.contains(coords, { signal: controller.signal }).then((res) => {
126
+ if (!controller.signal.aborted) {
127
+ setZones(res.zones);
128
+ }
129
+ }).catch((e) => {
130
+ if (!controller.signal.aborted) {
131
+ setError(e instanceof Error ? e : new Error(String(e)));
132
+ }
133
+ }).finally(() => {
134
+ if (!controller.signal.aborted) {
135
+ setLoading(false);
136
+ }
137
+ });
138
+ return () => {
139
+ controller.abort();
140
+ };
141
+ }, [coords?.lat, coords?.lng, client]);
142
+ return { zones, loading, error };
143
+ }
144
+
145
+ exports.useAutocomplete = useAutocomplete;
146
+ exports.useReverseGeocode = useReverseGeocode;
147
+ exports.useZoneContains = useZoneContains;
148
+ //# sourceMappingURL=index.cjs.map
149
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/use-autocomplete.ts","../src/use-reverse-geocode.ts","../src/use-zone-contains.ts"],"names":["useState","useRef","useCallback","useEffect"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { AddressSuggestion, WheraboutsClient } from \"@wherabouts/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface UseAutocompleteOptions {\n\tcountry?: string;\n\tdebounceMs?: number;\n\tlimit?: number;\n\tstate?: string;\n}\n\nexport interface UseAutocompleteResult {\n\terror: Error | null;\n\tloading: boolean;\n\tquery: string;\n\tresults: AddressSuggestion[];\n\tsetQuery: (q: string) => void;\n}\n\nexport function useAutocomplete(\n\tclient: WheraboutsClient,\n\toptions: UseAutocompleteOptions = {}\n): UseAutocompleteResult {\n\tconst { debounceMs = 300, limit, country, state } = options;\n\n\tconst [query, setQuery] = useState(\"\");\n\tconst [results, setResults] = useState<AddressSuggestion[]>([]);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\n\tconst abortRef = useRef<AbortController | null>(null);\n\tconst timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\t// Stable setter — consumers can safely pass this to input onChange\n\tconst handleSetQuery = useCallback((q: string) => {\n\t\tsetQuery(q);\n\t}, []);\n\n\tuseEffect(() => {\n\t\tif (timerRef.current) {\n\t\t\tclearTimeout(timerRef.current);\n\t\t}\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!query.trim()) {\n\t\t\tsetResults([]);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tsetLoading(true);\n\n\t\ttimerRef.current = setTimeout(async () => {\n\t\t\tconst controller = new AbortController();\n\t\t\tabortRef.current = controller;\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\tconst res = await client.addresses.autocomplete(\n\t\t\t\t\t{ q: query, limit, country, state },\n\t\t\t\t\t{ signal: controller.signal }\n\t\t\t\t);\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetResults(res.results);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t\t\t\tsetResults([]);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}, debounceMs);\n\n\t\treturn () => {\n\t\t\tif (timerRef.current) {\n\t\t\t\tclearTimeout(timerRef.current);\n\t\t\t}\n\t\t\tif (abortRef.current) {\n\t\t\t\tabortRef.current.abort();\n\t\t\t}\n\t\t};\n\t}, [query, debounceMs, limit, country, state, client]);\n\n\treturn { query, setQuery: handleSetQuery, results, loading, error };\n}\n","import type { ReverseGeocodeAddress, WheraboutsClient } from \"@wherabouts/sdk\";\nimport { useEffect, useRef, useState } from \"react\";\n\nexport interface LatLng {\n\tlat: number;\n\tlng: number;\n}\n\nexport interface UseReverseGeocodeResult {\n\taddress: ReverseGeocodeAddress | null;\n\tdistance: number | null;\n\terror: Error | null;\n\tloading: boolean;\n}\n\nexport function useReverseGeocode(\n\tclient: WheraboutsClient,\n\tcoords: LatLng | null\n): UseReverseGeocodeResult {\n\tconst [address, setAddress] = useState<ReverseGeocodeAddress | null>(null);\n\tconst [distance, setDistance] = useState<number | null>(null);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst abortRef = useRef<AbortController | null>(null);\n\n\tuseEffect(() => {\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!coords) {\n\t\t\tsetAddress(null);\n\t\t\tsetDistance(null);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst controller = new AbortController();\n\t\tabortRef.current = controller;\n\t\tsetLoading(true);\n\t\tsetError(null);\n\n\t\tclient.addresses\n\t\t\t.reverse(coords, { signal: controller.signal })\n\t\t\t.then((res) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetAddress(res.address);\n\t\t\t\t\tsetDistance(res.distance);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((e: unknown) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t\t\t}\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t};\n\t}, [coords?.lat, coords?.lng, client]);\n\n\treturn { address, distance, loading, error };\n}\n","import type { WheraboutsClient, ZoneRecord } from \"@wherabouts/sdk\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport type { LatLng } from \"./use-reverse-geocode.ts\";\n\nexport interface UseZoneContainsResult {\n\terror: Error | null;\n\tloading: boolean;\n\tzones: ZoneRecord[];\n}\n\nexport function useZoneContains(\n\tclient: WheraboutsClient,\n\tcoords: LatLng | null\n): UseZoneContainsResult {\n\tconst [zones, setZones] = useState<ZoneRecord[]>([]);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst abortRef = useRef<AbortController | null>(null);\n\n\tuseEffect(() => {\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!coords) {\n\t\t\tsetZones([]);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst controller = new AbortController();\n\t\tabortRef.current = controller;\n\t\tsetLoading(true);\n\t\tsetError(null);\n\n\t\tclient.zones\n\t\t\t.contains(coords, { signal: controller.signal })\n\t\t\t.then((res) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetZones(res.zones);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((e: unknown) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t\t\t}\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t};\n\t}, [coords?.lat, coords?.lng, client]);\n\n\treturn { zones, loading, error };\n}\n"]}
@@ -0,0 +1,37 @@
1
+ import { AddressSuggestion, WheraboutsClient, ReverseGeocodeAddress, ZoneRecord } from '@wherabouts/sdk';
2
+
3
+ interface UseAutocompleteOptions {
4
+ country?: string;
5
+ debounceMs?: number;
6
+ limit?: number;
7
+ state?: string;
8
+ }
9
+ interface UseAutocompleteResult {
10
+ error: Error | null;
11
+ loading: boolean;
12
+ query: string;
13
+ results: AddressSuggestion[];
14
+ setQuery: (q: string) => void;
15
+ }
16
+ declare function useAutocomplete(client: WheraboutsClient, options?: UseAutocompleteOptions): UseAutocompleteResult;
17
+
18
+ interface LatLng {
19
+ lat: number;
20
+ lng: number;
21
+ }
22
+ interface UseReverseGeocodeResult {
23
+ address: ReverseGeocodeAddress | null;
24
+ distance: number | null;
25
+ error: Error | null;
26
+ loading: boolean;
27
+ }
28
+ declare function useReverseGeocode(client: WheraboutsClient, coords: LatLng | null): UseReverseGeocodeResult;
29
+
30
+ interface UseZoneContainsResult {
31
+ error: Error | null;
32
+ loading: boolean;
33
+ zones: ZoneRecord[];
34
+ }
35
+ declare function useZoneContains(client: WheraboutsClient, coords: LatLng | null): UseZoneContainsResult;
36
+
37
+ export { type LatLng, type UseAutocompleteOptions, type UseAutocompleteResult, type UseReverseGeocodeResult, type UseZoneContainsResult, useAutocomplete, useReverseGeocode, useZoneContains };
@@ -0,0 +1,37 @@
1
+ import { AddressSuggestion, WheraboutsClient, ReverseGeocodeAddress, ZoneRecord } from '@wherabouts/sdk';
2
+
3
+ interface UseAutocompleteOptions {
4
+ country?: string;
5
+ debounceMs?: number;
6
+ limit?: number;
7
+ state?: string;
8
+ }
9
+ interface UseAutocompleteResult {
10
+ error: Error | null;
11
+ loading: boolean;
12
+ query: string;
13
+ results: AddressSuggestion[];
14
+ setQuery: (q: string) => void;
15
+ }
16
+ declare function useAutocomplete(client: WheraboutsClient, options?: UseAutocompleteOptions): UseAutocompleteResult;
17
+
18
+ interface LatLng {
19
+ lat: number;
20
+ lng: number;
21
+ }
22
+ interface UseReverseGeocodeResult {
23
+ address: ReverseGeocodeAddress | null;
24
+ distance: number | null;
25
+ error: Error | null;
26
+ loading: boolean;
27
+ }
28
+ declare function useReverseGeocode(client: WheraboutsClient, coords: LatLng | null): UseReverseGeocodeResult;
29
+
30
+ interface UseZoneContainsResult {
31
+ error: Error | null;
32
+ loading: boolean;
33
+ zones: ZoneRecord[];
34
+ }
35
+ declare function useZoneContains(client: WheraboutsClient, coords: LatLng | null): UseZoneContainsResult;
36
+
37
+ export { type LatLng, type UseAutocompleteOptions, type UseAutocompleteResult, type UseReverseGeocodeResult, type UseZoneContainsResult, useAutocomplete, useReverseGeocode, useZoneContains };
package/dist/index.js ADDED
@@ -0,0 +1,145 @@
1
+ import { useState, useRef, useCallback, useEffect } from 'react';
2
+
3
+ // src/use-autocomplete.ts
4
+ function useAutocomplete(client, options = {}) {
5
+ const { debounceMs = 300, limit, country, state } = options;
6
+ const [query, setQuery] = useState("");
7
+ const [results, setResults] = useState([]);
8
+ const [loading, setLoading] = useState(false);
9
+ const [error, setError] = useState(null);
10
+ const abortRef = useRef(null);
11
+ const timerRef = useRef(null);
12
+ const handleSetQuery = useCallback((q) => {
13
+ setQuery(q);
14
+ }, []);
15
+ useEffect(() => {
16
+ if (timerRef.current) {
17
+ clearTimeout(timerRef.current);
18
+ }
19
+ if (abortRef.current) {
20
+ abortRef.current.abort();
21
+ }
22
+ if (!query.trim()) {
23
+ setResults([]);
24
+ setLoading(false);
25
+ setError(null);
26
+ return;
27
+ }
28
+ setLoading(true);
29
+ timerRef.current = setTimeout(async () => {
30
+ const controller = new AbortController();
31
+ abortRef.current = controller;
32
+ setError(null);
33
+ try {
34
+ const res = await client.addresses.autocomplete(
35
+ { q: query, limit, country, state },
36
+ { signal: controller.signal }
37
+ );
38
+ if (!controller.signal.aborted) {
39
+ setResults(res.results);
40
+ }
41
+ } catch (e) {
42
+ if (!controller.signal.aborted) {
43
+ setError(e instanceof Error ? e : new Error(String(e)));
44
+ setResults([]);
45
+ }
46
+ } finally {
47
+ if (!controller.signal.aborted) {
48
+ setLoading(false);
49
+ }
50
+ }
51
+ }, debounceMs);
52
+ return () => {
53
+ if (timerRef.current) {
54
+ clearTimeout(timerRef.current);
55
+ }
56
+ if (abortRef.current) {
57
+ abortRef.current.abort();
58
+ }
59
+ };
60
+ }, [query, debounceMs, limit, country, state, client]);
61
+ return { query, setQuery: handleSetQuery, results, loading, error };
62
+ }
63
+ function useReverseGeocode(client, coords) {
64
+ const [address, setAddress] = useState(null);
65
+ const [distance, setDistance] = useState(null);
66
+ const [loading, setLoading] = useState(false);
67
+ const [error, setError] = useState(null);
68
+ const abortRef = useRef(null);
69
+ useEffect(() => {
70
+ if (abortRef.current) {
71
+ abortRef.current.abort();
72
+ }
73
+ if (!coords) {
74
+ setAddress(null);
75
+ setDistance(null);
76
+ setLoading(false);
77
+ setError(null);
78
+ return;
79
+ }
80
+ const controller = new AbortController();
81
+ abortRef.current = controller;
82
+ setLoading(true);
83
+ setError(null);
84
+ client.addresses.reverse(coords, { signal: controller.signal }).then((res) => {
85
+ if (!controller.signal.aborted) {
86
+ setAddress(res.address);
87
+ setDistance(res.distance);
88
+ }
89
+ }).catch((e) => {
90
+ if (!controller.signal.aborted) {
91
+ setError(e instanceof Error ? e : new Error(String(e)));
92
+ }
93
+ }).finally(() => {
94
+ if (!controller.signal.aborted) {
95
+ setLoading(false);
96
+ }
97
+ });
98
+ return () => {
99
+ controller.abort();
100
+ };
101
+ }, [coords?.lat, coords?.lng, client]);
102
+ return { address, distance, loading, error };
103
+ }
104
+ function useZoneContains(client, coords) {
105
+ const [zones, setZones] = useState([]);
106
+ const [loading, setLoading] = useState(false);
107
+ const [error, setError] = useState(null);
108
+ const abortRef = useRef(null);
109
+ useEffect(() => {
110
+ if (abortRef.current) {
111
+ abortRef.current.abort();
112
+ }
113
+ if (!coords) {
114
+ setZones([]);
115
+ setLoading(false);
116
+ setError(null);
117
+ return;
118
+ }
119
+ const controller = new AbortController();
120
+ abortRef.current = controller;
121
+ setLoading(true);
122
+ setError(null);
123
+ client.zones.contains(coords, { signal: controller.signal }).then((res) => {
124
+ if (!controller.signal.aborted) {
125
+ setZones(res.zones);
126
+ }
127
+ }).catch((e) => {
128
+ if (!controller.signal.aborted) {
129
+ setError(e instanceof Error ? e : new Error(String(e)));
130
+ }
131
+ }).finally(() => {
132
+ if (!controller.signal.aborted) {
133
+ setLoading(false);
134
+ }
135
+ });
136
+ return () => {
137
+ controller.abort();
138
+ };
139
+ }, [coords?.lat, coords?.lng, client]);
140
+ return { zones, loading, error };
141
+ }
142
+
143
+ export { useAutocomplete, useReverseGeocode, useZoneContains };
144
+ //# sourceMappingURL=index.js.map
145
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/use-autocomplete.ts","../src/use-reverse-geocode.ts","../src/use-zone-contains.ts"],"names":["useState","useRef","useEffect"],"mappings":"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","file":"index.js","sourcesContent":["import type { AddressSuggestion, WheraboutsClient } from \"@wherabouts/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport interface UseAutocompleteOptions {\n\tcountry?: string;\n\tdebounceMs?: number;\n\tlimit?: number;\n\tstate?: string;\n}\n\nexport interface UseAutocompleteResult {\n\terror: Error | null;\n\tloading: boolean;\n\tquery: string;\n\tresults: AddressSuggestion[];\n\tsetQuery: (q: string) => void;\n}\n\nexport function useAutocomplete(\n\tclient: WheraboutsClient,\n\toptions: UseAutocompleteOptions = {}\n): UseAutocompleteResult {\n\tconst { debounceMs = 300, limit, country, state } = options;\n\n\tconst [query, setQuery] = useState(\"\");\n\tconst [results, setResults] = useState<AddressSuggestion[]>([]);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\n\tconst abortRef = useRef<AbortController | null>(null);\n\tconst timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n\t// Stable setter — consumers can safely pass this to input onChange\n\tconst handleSetQuery = useCallback((q: string) => {\n\t\tsetQuery(q);\n\t}, []);\n\n\tuseEffect(() => {\n\t\tif (timerRef.current) {\n\t\t\tclearTimeout(timerRef.current);\n\t\t}\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!query.trim()) {\n\t\t\tsetResults([]);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tsetLoading(true);\n\n\t\ttimerRef.current = setTimeout(async () => {\n\t\t\tconst controller = new AbortController();\n\t\t\tabortRef.current = controller;\n\t\t\tsetError(null);\n\n\t\t\ttry {\n\t\t\t\tconst res = await client.addresses.autocomplete(\n\t\t\t\t\t{ q: query, limit, country, state },\n\t\t\t\t\t{ signal: controller.signal }\n\t\t\t\t);\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetResults(res.results);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t\t\t\tsetResults([]);\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}, debounceMs);\n\n\t\treturn () => {\n\t\t\tif (timerRef.current) {\n\t\t\t\tclearTimeout(timerRef.current);\n\t\t\t}\n\t\t\tif (abortRef.current) {\n\t\t\t\tabortRef.current.abort();\n\t\t\t}\n\t\t};\n\t}, [query, debounceMs, limit, country, state, client]);\n\n\treturn { query, setQuery: handleSetQuery, results, loading, error };\n}\n","import type { ReverseGeocodeAddress, WheraboutsClient } from \"@wherabouts/sdk\";\nimport { useEffect, useRef, useState } from \"react\";\n\nexport interface LatLng {\n\tlat: number;\n\tlng: number;\n}\n\nexport interface UseReverseGeocodeResult {\n\taddress: ReverseGeocodeAddress | null;\n\tdistance: number | null;\n\terror: Error | null;\n\tloading: boolean;\n}\n\nexport function useReverseGeocode(\n\tclient: WheraboutsClient,\n\tcoords: LatLng | null\n): UseReverseGeocodeResult {\n\tconst [address, setAddress] = useState<ReverseGeocodeAddress | null>(null);\n\tconst [distance, setDistance] = useState<number | null>(null);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst abortRef = useRef<AbortController | null>(null);\n\n\tuseEffect(() => {\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!coords) {\n\t\t\tsetAddress(null);\n\t\t\tsetDistance(null);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst controller = new AbortController();\n\t\tabortRef.current = controller;\n\t\tsetLoading(true);\n\t\tsetError(null);\n\n\t\tclient.addresses\n\t\t\t.reverse(coords, { signal: controller.signal })\n\t\t\t.then((res) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetAddress(res.address);\n\t\t\t\t\tsetDistance(res.distance);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((e: unknown) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t\t\t}\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t};\n\t}, [coords?.lat, coords?.lng, client]);\n\n\treturn { address, distance, loading, error };\n}\n","import type { WheraboutsClient, ZoneRecord } from \"@wherabouts/sdk\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport type { LatLng } from \"./use-reverse-geocode.ts\";\n\nexport interface UseZoneContainsResult {\n\terror: Error | null;\n\tloading: boolean;\n\tzones: ZoneRecord[];\n}\n\nexport function useZoneContains(\n\tclient: WheraboutsClient,\n\tcoords: LatLng | null\n): UseZoneContainsResult {\n\tconst [zones, setZones] = useState<ZoneRecord[]>([]);\n\tconst [loading, setLoading] = useState(false);\n\tconst [error, setError] = useState<Error | null>(null);\n\tconst abortRef = useRef<AbortController | null>(null);\n\n\tuseEffect(() => {\n\t\tif (abortRef.current) {\n\t\t\tabortRef.current.abort();\n\t\t}\n\n\t\tif (!coords) {\n\t\t\tsetZones([]);\n\t\t\tsetLoading(false);\n\t\t\tsetError(null);\n\t\t\treturn;\n\t\t}\n\n\t\tconst controller = new AbortController();\n\t\tabortRef.current = controller;\n\t\tsetLoading(true);\n\t\tsetError(null);\n\n\t\tclient.zones\n\t\t\t.contains(coords, { signal: controller.signal })\n\t\t\t.then((res) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetZones(res.zones);\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((e: unknown) => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t\t\t}\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t});\n\n\t\treturn () => {\n\t\t\tcontroller.abort();\n\t\t};\n\t}, [coords?.lat, coords?.lng, client]);\n\n\treturn { zones, loading, error };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@wherabouts/react",
3
+ "version": "0.1.0",
4
+ "description": "React hooks for the Wherabouts location API",
5
+ "type": "module",
6
+ "license": "UNLICENSED",
7
+ "sideEffects": false,
8
+ "engines": {
9
+ "node": ">=18"
10
+ },
11
+ "main": "./dist/index.cjs",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "require": {
21
+ "types": "./dist/index.d.cts",
22
+ "default": "./dist/index.cjs"
23
+ }
24
+ }
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md"
32
+ ],
33
+ "peerDependencies": {
34
+ "react": ">=18",
35
+ "@wherabouts/sdk": ">=0.4.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/react": "^19",
39
+ "react": "^19.2.3",
40
+ "tsup": "^8.5.1",
41
+ "typescript": "^5",
42
+ "vitest": "^4.1.4",
43
+ "@wherabouts/sdk": "0.4.0",
44
+ "@wherabouts.com/config": "0.0.0"
45
+ },
46
+ "scripts": {
47
+ "build": "tsup",
48
+ "check-types": "tsc --noEmit",
49
+ "test": "vitest run"
50
+ }
51
+ }