@komensal/ui 0.16.1 → 0.17.1

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/maps.js CHANGED
@@ -1,7 +1,366 @@
1
1
  import {
2
- GoogleMapPicker,
3
- MapPickerField
4
- } from "./chunk-5VBG6IKL.js";
2
+ FormField,
3
+ FormFieldShell,
4
+ cn,
5
+ focusRing,
6
+ inputBase,
7
+ inputFocus,
8
+ zPopover
9
+ } from "./chunk-Y7PKMFTB.js";
10
+
11
+ // src/components/google-map-picker.tsx
12
+ import { AdvancedMarker, APIProvider, Map } from "@vis.gl/react-google-maps";
13
+ import { useId } from "react";
14
+
15
+ // src/components/use-google-map-picker.ts
16
+ import { useMap, useMapsLibrary } from "@vis.gl/react-google-maps";
17
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
18
+ var PICKER_MAP_ID = "komensal-restaurant-map";
19
+ var SELECTED_ZOOM = 15;
20
+ var CITY_ZOOM = 12;
21
+ var DEBOUNCE_MS = 300;
22
+ var BLUR_CLOSE_DELAY_MS = 150;
23
+ var parseComponents = (components) => {
24
+ const find = (type) => components.find((c) => c.types.includes(type));
25
+ const country = find("country");
26
+ const department = find("administrative_area_level_1");
27
+ const city = find("locality") ?? find("administrative_area_level_2");
28
+ return {
29
+ city: city?.long ?? null,
30
+ country: country?.short ?? null,
31
+ countryName: country?.long ?? null,
32
+ department: department?.long ?? null
33
+ };
34
+ };
35
+ function useGoogleMapPicker({
36
+ centerOn,
37
+ countryCode,
38
+ disabled,
39
+ initialPosition,
40
+ locationQuery,
41
+ locationZoom,
42
+ onAddressResolved,
43
+ onChange,
44
+ onError,
45
+ value
46
+ }) {
47
+ const placesLib = useMapsLibrary("places");
48
+ const geocodingLib = useMapsLibrary("geocoding");
49
+ const map = useMap(PICKER_MAP_ID);
50
+ const onChangeRef = useRef(onChange);
51
+ const onResolvedRef = useRef(onAddressResolved);
52
+ const onErrorRef = useRef(onError);
53
+ const geocoder = useMemo(
54
+ () => geocodingLib ? new geocodingLib.Geocoder() : null,
55
+ [geocodingLib]
56
+ );
57
+ const debounceRef = useRef(null);
58
+ const blurTimeoutRef = useRef(null);
59
+ const [inputValue, setInputValue] = useState(value);
60
+ const [suggestions, setSuggestions] = useState([]);
61
+ const [showDropdown, setShowDropdown] = useState(false);
62
+ const [sessionToken, setSessionToken] = useState();
63
+ const [position, setPosition] = useState(initialPosition ?? null);
64
+ useEffect(() => {
65
+ onChangeRef.current = onChange;
66
+ onResolvedRef.current = onAddressResolved;
67
+ onErrorRef.current = onError;
68
+ });
69
+ useEffect(
70
+ () => () => {
71
+ if (debounceRef.current) clearTimeout(debounceRef.current);
72
+ if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
73
+ },
74
+ []
75
+ );
76
+ const [prevValue, setPrevValue] = useState(value);
77
+ if (prevValue !== value) {
78
+ setPrevValue(value);
79
+ setInputValue(value);
80
+ }
81
+ useEffect(() => {
82
+ if (map && centerOn) {
83
+ map.panTo(centerOn);
84
+ map.setZoom(CITY_ZOOM);
85
+ }
86
+ }, [map, centerOn]);
87
+ useEffect(() => {
88
+ if (map && position) {
89
+ map.panTo(position);
90
+ map.setZoom(SELECTED_ZOOM);
91
+ }
92
+ }, [map, position]);
93
+ const [prevPlacesLib, setPrevPlacesLib] = useState(placesLib);
94
+ if (prevPlacesLib !== placesLib) {
95
+ setPrevPlacesLib(placesLib);
96
+ if (placesLib) setSessionToken(new placesLib.AutocompleteSessionToken());
97
+ }
98
+ useEffect(() => {
99
+ if (!geocoder || !locationQuery || !map || position) return;
100
+ const g = geocoder;
101
+ const ctrl = { cancelled: false };
102
+ void (async () => {
103
+ try {
104
+ const { results } = await g.geocode({ address: locationQuery });
105
+ if (ctrl.cancelled || results.length === 0) return;
106
+ const loc = results[0].geometry.location;
107
+ map.panTo({ lat: loc.lat(), lng: loc.lng() });
108
+ map.setZoom(locationZoom ?? CITY_ZOOM);
109
+ } catch (error) {
110
+ if (!ctrl.cancelled) {
111
+ console.error("[GoogleMapPicker] geocode by query failed", error);
112
+ onErrorRef.current?.(error);
113
+ }
114
+ }
115
+ })();
116
+ return () => {
117
+ ctrl.cancelled = true;
118
+ };
119
+ }, [geocoder, locationQuery, locationZoom, map, position]);
120
+ const fetchSuggestions = useCallback(
121
+ async (input) => {
122
+ if (!placesLib || input.length < 2) {
123
+ setSuggestions([]);
124
+ return;
125
+ }
126
+ try {
127
+ const result = await placesLib.AutocompleteSuggestion.fetchAutocompleteSuggestions({
128
+ includedRegionCodes: countryCode ? [countryCode.trim().toLowerCase()] : void 0,
129
+ input,
130
+ sessionToken
131
+ });
132
+ setSuggestions(result.suggestions);
133
+ setShowDropdown(true);
134
+ } catch (error) {
135
+ console.error("[GoogleMapPicker] fetchAutocompleteSuggestions failed", error);
136
+ onErrorRef.current?.(error);
137
+ setSuggestions([]);
138
+ }
139
+ },
140
+ [placesLib, sessionToken, countryCode]
141
+ );
142
+ const reverseGeocode = useCallback(
143
+ async (latLng) => {
144
+ if (!geocoder) return;
145
+ try {
146
+ const { results } = await geocoder.geocode({ location: latLng });
147
+ if (results.length > 0) {
148
+ const result = results[0];
149
+ const address = result.formatted_address;
150
+ setInputValue(address);
151
+ onChangeRef.current(address);
152
+ const parts = parseComponents(
153
+ result.address_components.map((c) => ({ long: c.long_name, short: c.short_name, types: c.types }))
154
+ );
155
+ onResolvedRef.current?.({ address, lat: latLng.lat, lng: latLng.lng, ...parts });
156
+ }
157
+ } catch (error) {
158
+ console.error("[GoogleMapPicker] reverse geocode failed", error);
159
+ onErrorRef.current?.(error);
160
+ }
161
+ },
162
+ [geocoder]
163
+ );
164
+ const handleInputChange = (e) => {
165
+ const val = e.target.value;
166
+ setInputValue(val);
167
+ onChangeRef.current(val);
168
+ if (debounceRef.current) clearTimeout(debounceRef.current);
169
+ debounceRef.current = setTimeout(() => void fetchSuggestions(val), DEBOUNCE_MS);
170
+ };
171
+ const handleSelect = async (suggestion) => {
172
+ const prediction = suggestion.placePrediction;
173
+ if (!prediction) return;
174
+ const place = prediction.toPlace();
175
+ await place.fetchFields({ fields: ["formattedAddress", "location", "addressComponents"] });
176
+ const address = place.formattedAddress ?? prediction.text.text;
177
+ setInputValue(address);
178
+ onChangeRef.current(address);
179
+ setSuggestions([]);
180
+ setShowDropdown(false);
181
+ const lat = place.location?.lat() ?? null;
182
+ const lng = place.location?.lng() ?? null;
183
+ if (place.location) {
184
+ setPosition({ lat: place.location.lat(), lng: place.location.lng() });
185
+ }
186
+ const parts = parseComponents(
187
+ (place.addressComponents ?? []).map((c) => ({ long: c.longText, short: c.shortText, types: c.types }))
188
+ );
189
+ onResolvedRef.current?.({ address, lat, lng, ...parts });
190
+ if (placesLib) setSessionToken(new placesLib.AutocompleteSessionToken());
191
+ };
192
+ const handleBlur = () => {
193
+ if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current);
194
+ blurTimeoutRef.current = setTimeout(() => setShowDropdown(false), BLUR_CLOSE_DELAY_MS);
195
+ };
196
+ const handleMapClick = useCallback(
197
+ (e) => {
198
+ if (disabled || !e.detail.latLng) return;
199
+ const latLng = e.detail.latLng;
200
+ setPosition(latLng);
201
+ void reverseGeocode(latLng);
202
+ },
203
+ [disabled, reverseGeocode]
204
+ );
205
+ const handleMarkerDragEnd = useCallback(
206
+ (e) => {
207
+ const latLng = e.latLng;
208
+ if (!latLng) return;
209
+ const pos = { lat: latLng.lat(), lng: latLng.lng() };
210
+ setPosition(pos);
211
+ void reverseGeocode(pos);
212
+ },
213
+ [reverseGeocode]
214
+ );
215
+ return {
216
+ handleBlur,
217
+ handleInputChange,
218
+ handleMapClick,
219
+ handleMarkerDragEnd,
220
+ handleSelect,
221
+ inputValue,
222
+ position,
223
+ setShowDropdown,
224
+ showDropdown,
225
+ suggestions
226
+ };
227
+ }
228
+
229
+ // src/components/google-map-picker.tsx
230
+ import { jsx, jsxs } from "react/jsx-runtime";
231
+ var DEFAULT_CENTER = { lat: 4.711, lng: -74.0721 };
232
+ var MapPickerContent = (props) => {
233
+ const {
234
+ "aria-describedby": ariaDescribedBy,
235
+ "aria-invalid": ariaInvalid,
236
+ "aria-label": ariaLabel,
237
+ "aria-labelledby": ariaLabelledBy,
238
+ disabled,
239
+ hasError,
240
+ id,
241
+ mapId,
242
+ placeholder
243
+ } = props;
244
+ const listboxId = useId();
245
+ const {
246
+ handleBlur,
247
+ handleInputChange,
248
+ handleMapClick,
249
+ handleMarkerDragEnd,
250
+ handleSelect,
251
+ inputValue,
252
+ position,
253
+ setShowDropdown,
254
+ showDropdown,
255
+ suggestions
256
+ } = useGoogleMapPicker(props);
257
+ const isExpanded = showDropdown && suggestions.length > 0;
258
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
259
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
260
+ /* @__PURE__ */ jsx(
261
+ "input",
262
+ {
263
+ "aria-autocomplete": "list",
264
+ "aria-controls": isExpanded ? listboxId : void 0,
265
+ "aria-describedby": ariaDescribedBy,
266
+ "aria-expanded": isExpanded,
267
+ "aria-invalid": ariaInvalid ?? (hasError ? true : void 0),
268
+ "aria-label": ariaLabel,
269
+ "aria-labelledby": ariaLabelledBy,
270
+ className: cn(inputBase, inputFocus, hasError && "border-destructive"),
271
+ disabled,
272
+ id,
273
+ onBlur: handleBlur,
274
+ onChange: handleInputChange,
275
+ onFocus: () => suggestions.length > 0 && setShowDropdown(true),
276
+ placeholder,
277
+ role: "combobox",
278
+ type: "text",
279
+ value: inputValue
280
+ }
281
+ ),
282
+ isExpanded && /* @__PURE__ */ jsx(
283
+ "ul",
284
+ {
285
+ className: cn(
286
+ "border-border bg-surface absolute mt-1 w-full overflow-hidden rounded-[10px] border shadow-md",
287
+ zPopover
288
+ ),
289
+ id: listboxId,
290
+ role: "listbox",
291
+ children: suggestions.map((suggestion) => /* @__PURE__ */ jsx("li", { role: "none", children: /* @__PURE__ */ jsx(
292
+ "button",
293
+ {
294
+ "aria-selected": false,
295
+ className: cn(
296
+ "text-foreground hover:bg-hover w-full px-4 py-3 text-left text-sm transition-colors focus-visible:ring-inset",
297
+ focusRing
298
+ ),
299
+ onClick: () => void handleSelect(suggestion),
300
+ role: "option",
301
+ type: "button",
302
+ children: suggestion.placePrediction?.text.text ?? ""
303
+ }
304
+ ) }, suggestion.placePrediction?.placeId ?? suggestion.placePrediction?.text.text))
305
+ }
306
+ )
307
+ ] }),
308
+ /* @__PURE__ */ jsx("div", { className: "border-border h-59 overflow-hidden rounded-[10px] border md:h-87.5", children: /* @__PURE__ */ jsx(
309
+ Map,
310
+ {
311
+ defaultCenter: DEFAULT_CENTER,
312
+ defaultZoom: 5,
313
+ disableDefaultUI: true,
314
+ gestureHandling: "cooperative",
315
+ id: PICKER_MAP_ID,
316
+ mapId: mapId ?? import.meta.env?.VITE_GOOGLE_MAPS_MAP_ID ?? "DEMO_MAP_ID",
317
+ onClick: handleMapClick,
318
+ style: { height: "100%", width: "100%" },
319
+ children: position && /* @__PURE__ */ jsx(AdvancedMarker, { draggable: !disabled, onDragEnd: handleMarkerDragEnd, position })
320
+ }
321
+ ) })
322
+ ] });
323
+ };
324
+ var GoogleMapPicker = (props) => /* @__PURE__ */ jsx(APIProvider, { apiKey: props.apiKey ?? import.meta.env?.VITE_GOOGLE_MAPS_API_KEY ?? "", language: "es", region: "CO", children: /* @__PURE__ */ jsx(MapPickerContent, { ...props }) });
325
+
326
+ // src/components/form-fields/map-picker-field.tsx
327
+ import { jsx as jsx2 } from "react/jsx-runtime";
328
+ var MapPickerField = ({
329
+ control,
330
+ label,
331
+ labelClassName,
332
+ mainClassName,
333
+ name,
334
+ visualRequired,
335
+ ...pickerProps
336
+ }) => /* @__PURE__ */ jsx2(
337
+ FormField,
338
+ {
339
+ control,
340
+ name,
341
+ render: ({ field, fieldState }) => /* @__PURE__ */ jsx2(
342
+ FormFieldShell,
343
+ {
344
+ hasError: !!fieldState.error,
345
+ label,
346
+ labelClassName,
347
+ mainClassName,
348
+ name,
349
+ visualRequired,
350
+ children: (controlProps) => /* @__PURE__ */ jsx2(
351
+ GoogleMapPicker,
352
+ {
353
+ ...pickerProps,
354
+ ...controlProps,
355
+ hasError: !!fieldState.error,
356
+ onChange: field.onChange,
357
+ value: field.value ?? ""
358
+ }
359
+ )
360
+ }
361
+ )
362
+ }
363
+ );
5
364
  export {
6
365
  GoogleMapPicker,
7
366
  MapPickerField
package/dist/maps.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/components/google-map-picker.tsx","../src/components/use-google-map-picker.ts","../src/components/form-fields/map-picker-field.tsx"],"sourcesContent":["import { AdvancedMarker, APIProvider, Map } from '@vis.gl/react-google-maps'\nimport { useId } from 'react'\n\nimport { cn, focusRing, inputBase, inputFocus, zPopover } from '../utils'\nimport { PICKER_MAP_ID, useGoogleMapPicker } from './use-google-map-picker'\n\nconst DEFAULT_CENTER = { lat: 4.711, lng: -74.0721 }\n\nexport interface GoogleMapPickerProps {\n apiKey?: string\n 'aria-describedby'?: string\n 'aria-invalid'?: boolean\n 'aria-label'?: string\n 'aria-labelledby'?: string\n centerOn?: google.maps.LatLngLiteral\n countryCode?: string\n disabled?: boolean\n hasError?: boolean\n id?: string\n initialPosition?: google.maps.LatLngLiteral\n locationQuery?: string\n locationZoom?: number\n mapId?: string\n onAddressResolved?: (resolved: ResolvedAddress) => void\n onChange: (address: string) => void\n onError?: (error: unknown) => void\n placeholder?: string\n value: string\n}\n\nexport interface ResolvedAddress {\n address: string\n city: null | string\n country: null | string\n countryName: null | string\n department: null | string\n lat: null | number\n lng: null | number\n}\n\nconst MapPickerContent = (props: GoogleMapPickerProps) => {\n const {\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': ariaInvalid,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n disabled,\n hasError,\n id,\n mapId,\n placeholder\n } = props\n const listboxId = useId()\n const {\n handleBlur,\n handleInputChange,\n handleMapClick,\n handleMarkerDragEnd,\n handleSelect,\n inputValue,\n position,\n setShowDropdown,\n showDropdown,\n suggestions\n } = useGoogleMapPicker(props)\n\n const isExpanded = showDropdown && suggestions.length > 0\n\n return (\n <div className='flex flex-col gap-3'>\n <div className='relative'>\n <input\n aria-autocomplete='list'\n aria-controls={isExpanded ? listboxId : undefined}\n aria-describedby={ariaDescribedBy}\n aria-expanded={isExpanded}\n aria-invalid={ariaInvalid ?? (hasError ? true : undefined)}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n className={cn(inputBase, inputFocus, hasError && 'border-destructive')}\n disabled={disabled}\n id={id}\n onBlur={handleBlur}\n onChange={handleInputChange}\n onFocus={() => suggestions.length > 0 && setShowDropdown(true)}\n placeholder={placeholder}\n role='combobox'\n type='text'\n value={inputValue}\n />\n\n {isExpanded && (\n <ul\n className={cn(\n 'border-border bg-surface absolute mt-1 w-full overflow-hidden rounded-[10px] border shadow-md',\n zPopover\n )}\n id={listboxId}\n role='listbox'\n >\n {suggestions.map((suggestion) => (\n <li key={suggestion.placePrediction?.placeId ?? suggestion.placePrediction?.text.text} role='none'>\n <button\n aria-selected={false}\n className={cn(\n 'text-foreground hover:bg-hover w-full px-4 py-3 text-left text-sm transition-colors focus-visible:ring-inset',\n focusRing\n )}\n onClick={() => void handleSelect(suggestion)}\n role='option'\n type='button'\n >\n {suggestion.placePrediction?.text.text ?? ''}\n </button>\n </li>\n ))}\n </ul>\n )}\n </div>\n\n <div className='border-border h-59 overflow-hidden rounded-[10px] border md:h-87.5'>\n <Map\n defaultCenter={DEFAULT_CENTER}\n defaultZoom={5}\n disableDefaultUI\n gestureHandling='cooperative'\n id={PICKER_MAP_ID}\n mapId={mapId ?? import.meta.env?.VITE_GOOGLE_MAPS_MAP_ID ?? 'DEMO_MAP_ID'}\n onClick={handleMapClick}\n style={{ height: '100%', width: '100%' }}\n >\n {position && <AdvancedMarker draggable={!disabled} onDragEnd={handleMarkerDragEnd} position={position} />}\n </Map>\n </div>\n </div>\n )\n}\n\nexport const GoogleMapPicker = (props: GoogleMapPickerProps) => (\n <APIProvider apiKey={props.apiKey ?? import.meta.env?.VITE_GOOGLE_MAPS_API_KEY ?? ''} language='es' region='CO'>\n <MapPickerContent {...props} />\n </APIProvider>\n)\n","import type { MapMouseEvent } from '@vis.gl/react-google-maps'\n\nimport { useMap, useMapsLibrary } from '@vis.gl/react-google-maps'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport type { GoogleMapPickerProps } from './google-map-picker'\n\nexport const PICKER_MAP_ID = 'komensal-restaurant-map'\nconst SELECTED_ZOOM = 15\nconst CITY_ZOOM = 12\nconst DEBOUNCE_MS = 300\nconst BLUR_CLOSE_DELAY_MS = 150\n\ninterface RawComponent {\n long: null | string\n short: null | string\n types: string[]\n}\n\nconst parseComponents = (components: RawComponent[]) => {\n const find = (type: string) => components.find((c) => c.types.includes(type))\n const country = find('country')\n const department = find('administrative_area_level_1')\n const city = find('locality') ?? find('administrative_area_level_2')\n return {\n city: city?.long ?? null,\n country: country?.short ?? null,\n countryName: country?.long ?? null,\n department: department?.long ?? null\n }\n}\n\nexport function useGoogleMapPicker({\n centerOn,\n countryCode,\n disabled,\n initialPosition,\n locationQuery,\n locationZoom,\n onAddressResolved,\n onChange,\n onError,\n value\n}: GoogleMapPickerProps) {\n const placesLib = useMapsLibrary('places')\n const geocodingLib = useMapsLibrary('geocoding')\n const map = useMap(PICKER_MAP_ID)\n const onChangeRef = useRef(onChange)\n const onResolvedRef = useRef(onAddressResolved)\n const onErrorRef = useRef(onError)\n const geocoder = useMemo<google.maps.Geocoder | null>(\n () => (geocodingLib ? new geocodingLib.Geocoder() : null),\n [geocodingLib]\n )\n const debounceRef = useRef<null | ReturnType<typeof setTimeout>>(null)\n const blurTimeoutRef = useRef<null | ReturnType<typeof setTimeout>>(null)\n\n const [inputValue, setInputValue] = useState(value)\n const [suggestions, setSuggestions] = useState<google.maps.places.AutocompleteSuggestion[]>([])\n const [showDropdown, setShowDropdown] = useState(false)\n const [sessionToken, setSessionToken] = useState<google.maps.places.AutocompleteSessionToken | undefined>()\n const [position, setPosition] = useState<google.maps.LatLngLiteral | null>(initialPosition ?? null)\n\n useEffect(() => {\n onChangeRef.current = onChange\n onResolvedRef.current = onAddressResolved\n onErrorRef.current = onError\n })\n\n useEffect(\n () => () => {\n if (debounceRef.current) clearTimeout(debounceRef.current)\n if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current)\n },\n []\n )\n\n const [prevValue, setPrevValue] = useState(value)\n if (prevValue !== value) {\n setPrevValue(value)\n setInputValue(value)\n }\n\n useEffect(() => {\n if (map && centerOn) {\n map.panTo(centerOn)\n map.setZoom(CITY_ZOOM)\n }\n }, [map, centerOn])\n\n useEffect(() => {\n if (map && position) {\n map.panTo(position)\n map.setZoom(SELECTED_ZOOM)\n }\n }, [map, position])\n\n const [prevPlacesLib, setPrevPlacesLib] = useState(placesLib)\n if (prevPlacesLib !== placesLib) {\n setPrevPlacesLib(placesLib)\n if (placesLib) setSessionToken(new placesLib.AutocompleteSessionToken())\n }\n\n useEffect(() => {\n if (!geocoder || !locationQuery || !map || position) return\n const g = geocoder\n const ctrl = { cancelled: false }\n void (async () => {\n try {\n const { results } = await g.geocode({ address: locationQuery })\n if (ctrl.cancelled || results.length === 0) return\n const loc = results[0].geometry.location\n map.panTo({ lat: loc.lat(), lng: loc.lng() })\n map.setZoom(locationZoom ?? CITY_ZOOM)\n } catch (error) {\n if (!ctrl.cancelled) {\n console.error('[GoogleMapPicker] geocode by query failed', error)\n onErrorRef.current?.(error)\n }\n }\n })()\n return () => {\n ctrl.cancelled = true\n }\n }, [geocoder, locationQuery, locationZoom, map, position])\n\n const fetchSuggestions = useCallback(\n async (input: string) => {\n if (!placesLib || input.length < 2) {\n setSuggestions([])\n return\n }\n try {\n const result = await placesLib.AutocompleteSuggestion.fetchAutocompleteSuggestions({\n includedRegionCodes: countryCode ? [countryCode.trim().toLowerCase()] : undefined,\n input,\n sessionToken\n })\n setSuggestions(result.suggestions)\n setShowDropdown(true)\n } catch (error) {\n console.error('[GoogleMapPicker] fetchAutocompleteSuggestions failed', error)\n onErrorRef.current?.(error)\n setSuggestions([])\n }\n },\n [placesLib, sessionToken, countryCode]\n )\n\n const reverseGeocode = useCallback(\n async (latLng: google.maps.LatLngLiteral) => {\n if (!geocoder) return\n try {\n const { results } = await geocoder.geocode({ location: latLng })\n if (results.length > 0) {\n const result = results[0]\n const address = result.formatted_address\n setInputValue(address)\n onChangeRef.current(address)\n const parts = parseComponents(\n result.address_components.map((c) => ({ long: c.long_name, short: c.short_name, types: c.types }))\n )\n onResolvedRef.current?.({ address, lat: latLng.lat, lng: latLng.lng, ...parts })\n }\n } catch (error) {\n console.error('[GoogleMapPicker] reverse geocode failed', error)\n onErrorRef.current?.(error)\n }\n },\n [geocoder]\n )\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const val = e.target.value\n setInputValue(val)\n onChangeRef.current(val)\n\n if (debounceRef.current) clearTimeout(debounceRef.current)\n debounceRef.current = setTimeout(() => void fetchSuggestions(val), DEBOUNCE_MS)\n }\n\n const handleSelect = async (suggestion: google.maps.places.AutocompleteSuggestion) => {\n const prediction = suggestion.placePrediction\n if (!prediction) return\n\n const place = prediction.toPlace()\n await place.fetchFields({ fields: ['formattedAddress', 'location', 'addressComponents'] })\n\n const address = place.formattedAddress ?? prediction.text.text\n setInputValue(address)\n onChangeRef.current(address)\n setSuggestions([])\n setShowDropdown(false)\n\n const lat = place.location?.lat() ?? null\n const lng = place.location?.lng() ?? null\n if (place.location) {\n setPosition({ lat: place.location.lat(), lng: place.location.lng() })\n }\n\n const parts = parseComponents(\n (place.addressComponents ?? []).map((c) => ({ long: c.longText, short: c.shortText, types: c.types }))\n )\n onResolvedRef.current?.({ address, lat, lng, ...parts })\n\n if (placesLib) setSessionToken(new placesLib.AutocompleteSessionToken())\n }\n\n const handleBlur = () => {\n if (blurTimeoutRef.current) clearTimeout(blurTimeoutRef.current)\n blurTimeoutRef.current = setTimeout(() => setShowDropdown(false), BLUR_CLOSE_DELAY_MS)\n }\n\n const handleMapClick = useCallback(\n (e: MapMouseEvent) => {\n if (disabled || !e.detail.latLng) return\n const latLng = e.detail.latLng\n setPosition(latLng)\n void reverseGeocode(latLng)\n },\n [disabled, reverseGeocode]\n )\n\n const handleMarkerDragEnd = useCallback(\n (e: google.maps.MapMouseEvent) => {\n const latLng = e.latLng\n if (!latLng) return\n const pos = { lat: latLng.lat(), lng: latLng.lng() }\n setPosition(pos)\n void reverseGeocode(pos)\n },\n [reverseGeocode]\n )\n\n return {\n handleBlur,\n handleInputChange,\n handleMapClick,\n handleMarkerDragEnd,\n handleSelect,\n inputValue,\n position,\n setShowDropdown,\n showDropdown,\n suggestions\n }\n}\n","import type { Control, FieldValues, Path } from 'react-hook-form'\n\nimport { FormField } from '../form'\nimport { GoogleMapPicker, type GoogleMapPickerProps } from '../google-map-picker'\nimport { FormFieldShell } from './form-field-shell'\n\ntype MapPickerFieldProps<T extends FieldValues> = Omit<GoogleMapPickerProps, 'hasError' | 'onChange' | 'value'> & {\n control: Control<T>\n label?: string\n labelClassName?: string\n mainClassName?: string\n name: Path<T>\n visualRequired?: boolean\n}\n\nexport const MapPickerField = <T extends FieldValues>({\n control,\n label,\n labelClassName,\n mainClassName,\n name,\n visualRequired,\n ...pickerProps\n}: MapPickerFieldProps<T>) => (\n <FormField\n control={control}\n name={name}\n render={({ field, fieldState }) => (\n <FormFieldShell\n hasError={!!fieldState.error}\n label={label}\n labelClassName={labelClassName}\n mainClassName={mainClassName}\n name={name}\n visualRequired={visualRequired}\n >\n {(controlProps) => (\n <GoogleMapPicker\n {...pickerProps}\n {...controlProps}\n hasError={!!fieldState.error}\n onChange={field.onChange}\n value={field.value ?? ''}\n />\n )}\n </FormFieldShell>\n )}\n />\n)\n"],"mappings":"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","names":["jsx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@komensal/ui",
3
- "version": "0.16.1",
3
+ "version": "0.17.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",