@arquimedes.co/eureka-forms 3.0.47-test → 3.0.49-new-steps

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.
Files changed (86) hide show
  1. package/dist/@Types/AvailabilityFormStep.d.ts +48 -0
  2. package/dist/@Types/AvailabilityFormStep.js +24 -0
  3. package/dist/@Types/BankAccountFormStep.d.ts +67 -0
  4. package/dist/@Types/BankAccountFormStep.js +14 -0
  5. package/dist/@Types/BookingFormStep.d.ts +156 -0
  6. package/dist/@Types/BookingFormStep.js +44 -0
  7. package/dist/@Types/CalendarFormStep.d.ts +192 -0
  8. package/dist/@Types/CalendarFormStep.js +48 -0
  9. package/dist/@Types/CommunicationChannelFormStep.d.ts +51 -0
  10. package/dist/@Types/CommunicationChannelFormStep.js +15 -0
  11. package/dist/@Types/ConsentFormStep.d.ts +36 -0
  12. package/dist/@Types/ConsentFormStep.js +8 -0
  13. package/dist/@Types/EntityFormFormStep.d.ts +66 -0
  14. package/dist/@Types/EntityFormFormStep.js +20 -0
  15. package/dist/@Types/EventFormStep.d.ts +52 -0
  16. package/dist/@Types/EventFormStep.js +13 -0
  17. package/dist/@Types/ExternalReferenceFormStep.d.ts +74 -0
  18. package/dist/@Types/ExternalReferenceFormStep.js +20 -0
  19. package/dist/@Types/FormStep.d.ts +7 -2
  20. package/dist/@Types/FormStep.js +2 -1
  21. package/dist/@Types/LocationFormStep.d.ts +159 -0
  22. package/dist/@Types/LocationFormStep.js +46 -0
  23. package/dist/@Types/NumericFormSteps.d.ts +108 -0
  24. package/dist/@Types/NumericFormSteps.js +20 -0
  25. package/dist/@Types/PredefinedSelectorFormStep.d.ts +51 -0
  26. package/dist/@Types/PredefinedSelectorFormStep.js +39 -0
  27. package/dist/@Types/StepRef.d.ts +135 -0
  28. package/dist/@Types/StepRef.js +49 -0
  29. package/dist/FormSteps/BookingStep/BookingStep.d.ts +17 -0
  30. package/dist/FormSteps/BookingStep/BookingStep.js +14 -0
  31. package/dist/FormSteps/BookingStep/MaterialBookingStep/MaterialBookingStep.d.ts +3 -0
  32. package/dist/FormSteps/BookingStep/MaterialBookingStep/MaterialBookingStep.js +362 -0
  33. package/dist/FormSteps/BookingStep/MaterialBookingStep/MaterialBookingStep.module.css +174 -0
  34. package/dist/FormSteps/CalendarStep/CalendarStep.d.ts +7 -0
  35. package/dist/FormSteps/CalendarStep/CalendarStep.js +14 -0
  36. package/dist/FormSteps/CalendarStep/MaterialCalendarStep/MaterialCalendarStep.d.ts +3 -0
  37. package/dist/FormSteps/CalendarStep/MaterialCalendarStep/MaterialCalendarStep.js +269 -0
  38. package/dist/FormSteps/CalendarStep/MaterialCalendarStep/MaterialCalendarStep.module.css +531 -0
  39. package/dist/FormSteps/LocationStep/LocationStep.d.ts +10 -0
  40. package/dist/FormSteps/LocationStep/LocationStep.js +14 -0
  41. package/dist/FormSteps/LocationStep/MaterialLocationStep/MaterialLocationStep.d.ts +3 -0
  42. package/dist/FormSteps/LocationStep/MaterialLocationStep/MaterialLocationStep.js +47 -0
  43. package/dist/FormSteps/LocationStep/MaterialLocationStep/MaterialLocationStep.module.css +18 -0
  44. package/dist/FormSteps/NumberStep/MaterialNumberStep/MaterialNumberStep.d.ts +3 -0
  45. package/dist/FormSteps/NumberStep/MaterialNumberStep/MaterialNumberStep.js +168 -0
  46. package/dist/FormSteps/NumberStep/MaterialNumberStep/MaterialNumberStep.module.css +94 -0
  47. package/dist/FormSteps/NumberStep/NumberStep.d.ts +10 -0
  48. package/dist/FormSteps/NumberStep/NumberStep.js +14 -0
  49. package/dist/FormSteps/Step.js +16 -0
  50. package/dist/FormSteps/StepFunctions.js +14 -0
  51. package/dist/FormSteps/StepFunctions.test.js +57 -3
  52. package/dist/Icons/@ErkIcon.d.ts +2 -2
  53. package/dist/Icons/@ErkIcon.js +2 -2
  54. package/dist/Icons/AddIcon.d.ts +3 -0
  55. package/dist/Icons/AddIcon.js +7 -0
  56. package/dist/Icons/EditIcon.d.ts +3 -0
  57. package/dist/Icons/EditIcon.js +7 -0
  58. package/dist/Icons/RemoveIcon.d.ts +3 -0
  59. package/dist/Icons/RemoveIcon.js +7 -0
  60. package/dist/Icons/ViewDayIcon.d.ts +3 -0
  61. package/dist/Icons/ViewDayIcon.js +7 -0
  62. package/dist/Icons/ViewWeekIcon.d.ts +3 -0
  63. package/dist/Icons/ViewWeekIcon.js +7 -0
  64. package/dist/Shared/ErkAddressAutocomplete/ErkAddressAutocomplete.d.ts +19 -0
  65. package/dist/Shared/ErkAddressAutocomplete/ErkAddressAutocomplete.js +80 -0
  66. package/dist/Shared/ErkAddressAutocomplete/ErkAddressAutocomplete.module.css +82 -0
  67. package/dist/Shared/ErkAddressAutocomplete/googlePlaces.d.ts +29 -0
  68. package/dist/Shared/ErkAddressAutocomplete/googlePlaces.js +139 -0
  69. package/dist/Shared/ErkDatePicker/ErkDatePicker.js +1 -3
  70. package/dist/Shared/ErkDateRangePicker/ErkDateRangePicker.d.ts +2 -3
  71. package/dist/Shared/ErkDateRangePicker/ErkDateRangePicker.js +1 -2
  72. package/dist/Shared/ErkLocationField/ErkLocationField.d.ts +23 -0
  73. package/dist/Shared/ErkLocationField/ErkLocationField.js +73 -0
  74. package/dist/Shared/ErkLocationField/ErkLocationField.module.css +29 -0
  75. package/dist/Shared/ErkLocationField/addressFields.d.ts +12 -0
  76. package/dist/Shared/ErkLocationField/addressFields.js +34 -0
  77. package/dist/Shared/ErkLocationField/locationRestrictions.d.ts +6 -0
  78. package/dist/Shared/ErkLocationField/locationRestrictions.js +51 -0
  79. package/dist/Shared/ErkLocationInput/ErkLocationInput.d.ts +15 -0
  80. package/dist/Shared/ErkLocationInput/ErkLocationInput.js +499 -0
  81. package/dist/Shared/ErkLocationInput/ErkLocationInput.module.css +115 -0
  82. package/dist/Shared/ErkNumberField/ErkNumberField.d.ts +42 -0
  83. package/dist/Shared/ErkNumberField/ErkNumberField.js +170 -0
  84. package/dist/constants/FormStepTypes.d.ts +5 -1
  85. package/dist/constants/FormStepTypes.js +4 -0
  86. package/package.json +2 -1
@@ -0,0 +1,499 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState, useEffect, useRef, useMemo } from 'react';
3
+ import { Autocomplete, CircularProgress, Typography, IconButton, Tooltip, Dialog, DialogTitle, DialogContent, DialogActions, } from '@mui/material';
4
+ import { useAppSelector } from '../../hooks';
5
+ import ErkButton from '../ErkButton/ErkButton';
6
+ import { LocationInputMode } from '../../@Types/LocationFormStep';
7
+ import { StyledTextField } from '../ErkTextField/ErkTextField';
8
+ import ErkTextField from '../ErkTextField/ErkTextField';
9
+ import LocationIcon from '../../Icons/LocationIcon';
10
+ import EditIcon from '../../Icons/EditIcon';
11
+ import CloseIcon from '../../Icons/CloseIcon';
12
+ import styles from './ErkLocationInput.module.css';
13
+ // ─── Script Loader ────────────────────────────────────────────────────────────
14
+ let loadPromise = null;
15
+ function loadGoogleMaps(apiKey) {
16
+ if (typeof window === 'undefined') {
17
+ return Promise.resolve();
18
+ }
19
+ const win = window;
20
+ if (win.google?.maps?.importLibrary) {
21
+ return Promise.resolve();
22
+ }
23
+ if (loadPromise) {
24
+ return loadPromise;
25
+ }
26
+ loadPromise = new Promise((resolve, reject) => {
27
+ const script = document.createElement('script');
28
+ script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&loading=async`;
29
+ script.async = true;
30
+ script.defer = true;
31
+ script.onload = () => {
32
+ resolve();
33
+ };
34
+ script.onerror = (err) => {
35
+ loadPromise = null;
36
+ reject(new Error(typeof err === 'string' ? err : 'Google Maps script failed to load.'));
37
+ };
38
+ document.head.appendChild(script);
39
+ });
40
+ return loadPromise;
41
+ }
42
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
43
+ function parseAddressComponents(components) {
44
+ const detail = {};
45
+ for (const component of components) {
46
+ const types = component.types;
47
+ const longName = component.long_name ?? component.longText;
48
+ const shortName = component.short_name ?? component.shortText;
49
+ if (types.includes('route')) {
50
+ detail.street = longName;
51
+ }
52
+ else if (types.includes('street_number')) {
53
+ detail.streetNumber = longName;
54
+ }
55
+ else if (types.includes('subpremise')) {
56
+ detail.unit = longName;
57
+ }
58
+ else if (types.includes('neighborhood') || types.includes('sublocality')) {
59
+ detail.neighborhood = longName;
60
+ }
61
+ else if (types.includes('locality')) {
62
+ detail.city = longName;
63
+ }
64
+ else if (types.includes('administrative_area_level_1')) {
65
+ detail.state = longName;
66
+ }
67
+ else if (types.includes('country')) {
68
+ detail.country = longName;
69
+ detail.countryCode = shortName;
70
+ }
71
+ else if (types.includes('postal_code')) {
72
+ detail.postalCode = longName;
73
+ }
74
+ }
75
+ return detail;
76
+ }
77
+ // ─── Main Component ───────────────────────────────────────────────────────────
78
+ export default function ErkLocationInput({ value, onChange, allowedModes, label, helperText, required = false, error = false, readOnly = false, inputRef, }) {
79
+ // Brand design theme
80
+ const { formStyle } = useAppSelector((state) => state.global);
81
+ // Google Maps API Key
82
+ const apiKey = import.meta.env.VITE_GOOGLE_MAPS_API_KEY ?? '';
83
+ // Autocomplete / Search States
84
+ const [loadingScript, setLoadingScript] = useState(false);
85
+ const [scriptError, setScriptError] = useState(false);
86
+ const [inputValue, setInputValue] = useState('');
87
+ const [options, setOptions] = useState([]);
88
+ const [searching, setSearching] = useState(false);
89
+ const [selectedPlace, setSelectedPlace] = useState(null);
90
+ // Modern Google Places Class References
91
+ const [autocompleteSuggestionClass, setAutocompleteSuggestionClass] = useState(null);
92
+ const [autocompleteSessionTokenClass, setAutocompleteSessionTokenClass] = useState(null);
93
+ const [geocoderClass, setGeocoderClass] = useState(null);
94
+ // Geolocation / GPS States
95
+ const [gpsLoading, setGpsLoading] = useState(false);
96
+ const [gpsError, setGpsError] = useState(null);
97
+ // Manual Modal States
98
+ const [manualModalOpen, setManualModalOpen] = useState(false);
99
+ const [localStreet, setLocalStreet] = useState('');
100
+ const [localStreetNumber, setLocalStreetNumber] = useState('');
101
+ const [localUnit, setLocalUnit] = useState('');
102
+ const [localNeighborhood, setLocalNeighborhood] = useState('');
103
+ const [localCity, setLocalCity] = useState('');
104
+ const [localState, setLocalState] = useState('');
105
+ const [localCountry, setLocalCountry] = useState('');
106
+ const [localPostalCode, setLocalPostalCode] = useState('');
107
+ // References for Google Services
108
+ const geocoderRef = useRef(null);
109
+ const sessionTokenRef = useRef(null);
110
+ // Load Google Maps Script if GOOGLE_PLACES is allowed
111
+ const isGooglePlacesAllowed = allowedModes.includes(LocationInputMode.GOOGLE_PLACES);
112
+ useEffect(() => {
113
+ const win = window;
114
+ if (isGooglePlacesAllowed && !readOnly) {
115
+ void Promise.resolve().then(() => {
116
+ setLoadingScript(true);
117
+ });
118
+ loadGoogleMaps(apiKey)
119
+ .then(async () => {
120
+ if (win.google?.maps?.importLibrary) {
121
+ try {
122
+ const { AutocompleteSuggestion, AutocompleteSessionToken } = await win.google.maps.importLibrary('places');
123
+ const { Geocoder } = await win.google.maps.importLibrary('geocoding');
124
+ setAutocompleteSuggestionClass(() => AutocompleteSuggestion);
125
+ setAutocompleteSessionTokenClass(() => AutocompleteSessionToken);
126
+ setGeocoderClass(() => Geocoder);
127
+ geocoderRef.current = new Geocoder();
128
+ sessionTokenRef.current = new AutocompleteSessionToken();
129
+ setLoadingScript(false);
130
+ setScriptError(false);
131
+ }
132
+ catch (err) {
133
+ console.error('Error importing Google Maps libraries:', err);
134
+ setLoadingScript(false);
135
+ setScriptError(true);
136
+ }
137
+ }
138
+ else if (win.google?.maps) {
139
+ // Fallback if importLibrary doesn't exist
140
+ geocoderRef.current = new win.google.maps.Geocoder();
141
+ setLoadingScript(false);
142
+ setScriptError(false);
143
+ }
144
+ })
145
+ .catch((err) => {
146
+ console.error('Error loading Google Maps:', err);
147
+ setLoadingScript(false);
148
+ setScriptError(true);
149
+ });
150
+ }
151
+ }, [isGooglePlacesAllowed, apiKey, readOnly]);
152
+ // Google Places Search Hook/Debounce (Supports new AutocompleteSuggestion & fallback)
153
+ useEffect(() => {
154
+ const win = window;
155
+ if (!win.google?.maps || !inputValue || inputValue.length < 3 || readOnly) {
156
+ void Promise.resolve().then(() => {
157
+ setOptions([]);
158
+ });
159
+ return;
160
+ }
161
+ void Promise.resolve().then(() => {
162
+ setSearching(true);
163
+ });
164
+ const timeout = setTimeout(() => {
165
+ // Check if modern AutocompleteSuggestion API is available
166
+ if (autocompleteSuggestionClass) {
167
+ const request = {
168
+ input: inputValue,
169
+ };
170
+ if (sessionTokenRef.current) {
171
+ request.sessionToken = sessionTokenRef.current;
172
+ }
173
+ autocompleteSuggestionClass
174
+ .fetchAutocompleteSuggestions(request)
175
+ .then((response) => {
176
+ setSearching(false);
177
+ if (response.suggestions) {
178
+ const mapped = response.suggestions
179
+ .filter((s) => s.placePrediction)
180
+ .map((s) => ({
181
+ description: s.placePrediction.text.text,
182
+ place_id: s.placePrediction.placeId,
183
+ mainText: s.placePrediction.mainText?.text ?? s.placePrediction.text.text,
184
+ toPlace: () => s.placePrediction.toPlace(),
185
+ }));
186
+ setOptions(mapped);
187
+ }
188
+ else {
189
+ setOptions([]);
190
+ }
191
+ })
192
+ .catch((err) => {
193
+ console.error('Error fetching autocomplete suggestions:', err);
194
+ setSearching(false);
195
+ setOptions([]);
196
+ });
197
+ }
198
+ else if (win.google.maps.places?.AutocompleteService) {
199
+ // Fallback to legacy AutocompleteService
200
+ const service = new win.google.maps.places.AutocompleteService();
201
+ service.getPlacePredictions({ input: inputValue, types: ['geocode', 'establishment'] }, (predictions, status) => {
202
+ setSearching(false);
203
+ if (status === 'OK' && predictions) {
204
+ setOptions(predictions.map((p) => ({
205
+ description: p.description,
206
+ place_id: p.place_id,
207
+ mainText: p.structured_formatting.main_text,
208
+ })));
209
+ }
210
+ else {
211
+ setOptions([]);
212
+ }
213
+ });
214
+ }
215
+ else {
216
+ setSearching(false);
217
+ setOptions([]);
218
+ }
219
+ }, 300);
220
+ return () => {
221
+ clearTimeout(timeout);
222
+ };
223
+ }, [inputValue, readOnly, autocompleteSuggestionClass]);
224
+ // Map current value to Autocomplete object if available
225
+ const autocompleteValue = useMemo(() => {
226
+ if (selectedPlace)
227
+ return selectedPlace;
228
+ if (value?.address && value.source === LocationInputMode.GOOGLE_PLACES) {
229
+ return {
230
+ description: value.address,
231
+ place_id: value.placeId ?? '',
232
+ mainText: value.name ?? value.address,
233
+ };
234
+ }
235
+ return null;
236
+ }, [selectedPlace, value]);
237
+ // Initialize Manual Modal states when opened
238
+ useEffect(() => {
239
+ if (manualModalOpen) {
240
+ void Promise.resolve().then(() => {
241
+ setLocalStreet(value?.addressDetail?.street ?? '');
242
+ setLocalStreetNumber(value?.addressDetail?.streetNumber ?? '');
243
+ setLocalUnit(value?.addressDetail?.unit ?? '');
244
+ setLocalNeighborhood(value?.addressDetail?.neighborhood ?? '');
245
+ setLocalCity(value?.addressDetail?.city ?? '');
246
+ setLocalState(value?.addressDetail?.state ?? '');
247
+ setLocalCountry(value?.addressDetail?.country ?? '');
248
+ setLocalPostalCode(value?.addressDetail?.postalCode ?? '');
249
+ });
250
+ }
251
+ }, [manualModalOpen, value]);
252
+ // Geolocation triggering
253
+ const handleGPSFetch = () => {
254
+ if (!navigator.geolocation) {
255
+ setGpsError('La geolocalización no es compatible con este navegador.');
256
+ return;
257
+ }
258
+ setGpsLoading(true);
259
+ setGpsError(null);
260
+ // Low accuracy fallback options to prevent VM/Desktop timeouts
261
+ const geoOptions = {
262
+ enableHighAccuracy: false,
263
+ timeout: 15000,
264
+ maximumAge: 300000, // Allow cached positions up to 5 minutes old
265
+ };
266
+ navigator.geolocation.getCurrentPosition((position) => {
267
+ const lat = position.coords.latitude;
268
+ const lng = position.coords.longitude;
269
+ const win = window;
270
+ const resolveLocation = () => {
271
+ const geocoderInstance = geocoderRef.current ??
272
+ (geocoderClass
273
+ ? new geocoderClass()
274
+ : win.google?.maps?.Geocoder
275
+ ? new win.google.maps.Geocoder()
276
+ : null);
277
+ if (geocoderInstance) {
278
+ geocoderInstance.geocode({ location: { lat, lng } }, (results, status) => {
279
+ let address = undefined;
280
+ let addressDetail = undefined;
281
+ if (status === 'OK' && results?.[0]) {
282
+ address = results[0].formatted_address;
283
+ addressDetail = parseAddressComponents(results[0].address_components);
284
+ }
285
+ onChange?.({
286
+ source: LocationInputMode.GPS,
287
+ latitude: lat,
288
+ longitude: lng,
289
+ address: address ?? `${lat.toFixed(6)}, ${lng.toFixed(6)}`,
290
+ addressDetail,
291
+ });
292
+ setGpsLoading(false);
293
+ });
294
+ }
295
+ else {
296
+ onChange?.({
297
+ source: LocationInputMode.GPS,
298
+ latitude: lat,
299
+ longitude: lng,
300
+ address: `${lat.toFixed(6)}, ${lng.toFixed(6)}`,
301
+ });
302
+ setGpsLoading(false);
303
+ }
304
+ };
305
+ if (!win.google?.maps && apiKey) {
306
+ loadGoogleMaps(apiKey)
307
+ .then(() => {
308
+ resolveLocation();
309
+ })
310
+ .catch(() => {
311
+ onChange?.({
312
+ source: LocationInputMode.GPS,
313
+ latitude: lat,
314
+ longitude: lng,
315
+ address: `${lat.toFixed(6)}, ${lng.toFixed(6)}`,
316
+ });
317
+ setGpsLoading(false);
318
+ });
319
+ }
320
+ else {
321
+ resolveLocation();
322
+ }
323
+ }, (err) => {
324
+ console.error('GPS error:', err);
325
+ let message = 'No se pudo obtener la ubicación GPS.';
326
+ if (err.code === err.PERMISSION_DENIED) {
327
+ message =
328
+ 'Permiso denegado para acceder a la ubicación. Asegúrate de habilitar los permisos en tu navegador y en los Ajustes del Sistema (Privacidad y seguridad > Localización).';
329
+ }
330
+ else if (err.code === err.TIMEOUT) {
331
+ message =
332
+ 'Tiempo de espera agotado al obtener la ubicación. Si estás en Mac, asegúrate de activar el Wi-Fi para ayudar a la localización, o introduce la dirección manualmente.';
333
+ }
334
+ setGpsError(message);
335
+ setGpsLoading(false);
336
+ }, geoOptions);
337
+ };
338
+ // Save Manual fields
339
+ const handleSaveManual = () => {
340
+ if (!localStreet || !localCity || !localCountry) {
341
+ return;
342
+ }
343
+ const updatedDetail = {
344
+ street: localStreet,
345
+ streetNumber: localStreetNumber,
346
+ unit: localUnit,
347
+ neighborhood: localNeighborhood,
348
+ city: localCity,
349
+ state: localState,
350
+ country: localCountry,
351
+ postalCode: localPostalCode,
352
+ };
353
+ const parts = [
354
+ localStreet,
355
+ localStreetNumber,
356
+ localUnit ? `Apto/Int ${localUnit}` : '',
357
+ localNeighborhood,
358
+ localCity,
359
+ localState,
360
+ localCountry,
361
+ ].filter(Boolean);
362
+ const formattedAddress = parts.join(', ');
363
+ onChange?.({
364
+ source: LocationInputMode.MANUAL,
365
+ address: formattedAddress,
366
+ addressDetail: updatedDetail,
367
+ });
368
+ setManualModalOpen(false);
369
+ };
370
+ // Render Read-Only View
371
+ if (readOnly) {
372
+ return (_jsx("div", { className: styles.readOnlyContainer, style: {
373
+ '--eureka-outline': formStyle.outlineColor,
374
+ '--eureka-primary': formStyle.primaryColor,
375
+ '--eureka-text': formStyle.textColor,
376
+ '--eureka-bg': formStyle.standAloneBackgroundColor || '#fff',
377
+ }, children: _jsxs("div", { className: styles.readOnlyCard, children: [_jsx("div", { className: styles.readOnlyIcon, children: _jsx(LocationIcon, { fill: "var(--eureka-primary)" }) }), _jsxs("div", { className: styles.readOnlyDetails, children: [_jsx(Typography, { className: styles.readOnlyLabel, children: label }), value?.name && _jsx(Typography, { className: styles.readOnlyName, children: value.name }), _jsx(Typography, { className: styles.readOnlyAddress, children: value?.address ?? 'Ubicación no proporcionada' }), value?.latitude !== undefined && value?.longitude !== undefined && (_jsxs(Typography, { className: styles.readOnlyCoords, children: ["Coordenadas: ", value.latitude.toFixed(6), ", ", value.longitude.toFixed(6)] }))] })] }) }));
378
+ }
379
+ const win = window;
380
+ return (_jsxs("div", { className: styles.container, style: {
381
+ '--eureka-outline': formStyle.outlineColor,
382
+ '--eureka-primary': formStyle.primaryColor,
383
+ '--eureka-text': formStyle.textColor,
384
+ '--eureka-bg': formStyle.standAloneBackgroundColor || '#fff',
385
+ }, children: [_jsxs(Typography, { variant: "subtitle2", color: "textSecondary", style: { fontWeight: 600, marginBottom: '2px', color: formStyle.textColor }, children: [label, " ", required && _jsx("span", { style: { color: formStyle.errorColor || 'red' }, children: "*" })] }), _jsx("div", { className: styles.inputRow, children: value?.address ? (_jsxs(_Fragment, { children: [_jsx("div", { style: { flex: 1 }, children: _jsx(ErkTextField, { label: "Ubicaci\u00F3n seleccionada", value: value.address, readOnly: true, error: error, helperText: helperText, required: required, inputRef: inputRef }) }), allowedModes.includes(LocationInputMode.MANUAL) && (_jsx(Tooltip, { title: "Editar direcci\u00F3n manualmente", children: _jsx(IconButton, { onClick: () => setManualModalOpen(true), className: styles.actionButton, children: _jsx(EditIcon, { fill: "currentColor" }) }) })), _jsx(Tooltip, { title: "Limpiar selecci\u00F3n", children: _jsx(IconButton, { onClick: () => {
386
+ onChange?.(null);
387
+ setSelectedPlace(null);
388
+ setInputValue('');
389
+ if (autocompleteSessionTokenClass) {
390
+ sessionTokenRef.current = new autocompleteSessionTokenClass();
391
+ }
392
+ }, className: styles.actionButton, children: _jsx(CloseIcon, { fill: "currentColor" }) }) })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { style: { flex: 1 }, children: loadingScript ? (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '8px', height: '40px' }, children: [_jsx(CircularProgress, { size: 20 }), _jsx(Typography, { variant: "body2", children: "Cargando buscador..." })] })) : scriptError || !isGooglePlacesAllowed ? (_jsx(ErkTextField, { label: "Ingresa una ubicaci\u00F3n...", value: inputValue, onChange: (val) => {
393
+ setInputValue(val);
394
+ onChange?.({
395
+ source: LocationInputMode.MANUAL,
396
+ address: val,
397
+ });
398
+ }, error: error, helperText: helperText, required: required, inputRef: inputRef })) : (_jsx(Autocomplete, { options: options, getOptionLabel: (option) => option.description, filterOptions: (x) => x, loading: searching, value: autocompleteValue, popupIcon: searching ? _jsx(CircularProgress, { color: "inherit", size: 20 }) : undefined, noOptionsText: inputValue.length >= 3 ? 'No se encontraron resultados' : 'Escribe para buscar...', onInputChange: (_, newInputValue) => {
399
+ setInputValue(newInputValue);
400
+ }, onChange: (_, selection) => {
401
+ setSelectedPlace(selection);
402
+ if (selection) {
403
+ if (typeof selection.toPlace === 'function') {
404
+ const place = selection.toPlace();
405
+ place
406
+ .fetchFields({
407
+ fields: [
408
+ 'displayName',
409
+ 'formattedAddress',
410
+ 'location',
411
+ 'addressComponents',
412
+ ],
413
+ })
414
+ .then(() => {
415
+ const lat = place.location.lat();
416
+ const lng = place.location.lng();
417
+ const detail = parseAddressComponents(place.addressComponents ?? []);
418
+ onChange?.({
419
+ source: LocationInputMode.GOOGLE_PLACES,
420
+ latitude: lat,
421
+ longitude: lng,
422
+ name: place.displayName ?? selection.mainText,
423
+ address: place.formattedAddress,
424
+ placeId: selection.place_id,
425
+ addressDetail: detail,
426
+ });
427
+ if (autocompleteSessionTokenClass) {
428
+ sessionTokenRef.current = new autocompleteSessionTokenClass();
429
+ }
430
+ })
431
+ .catch((err) => {
432
+ console.error('Error fetching place fields:', err);
433
+ });
434
+ }
435
+ else {
436
+ const geocoderInstance = geocoderRef.current ??
437
+ (geocoderClass
438
+ ? new geocoderClass()
439
+ : win.google?.maps?.Geocoder
440
+ ? new win.google.maps.Geocoder()
441
+ : null);
442
+ if (geocoderInstance) {
443
+ geocoderInstance.geocode({ placeId: selection.place_id }, (results, status) => {
444
+ if (status === 'OK' && results?.[0]) {
445
+ const result = results[0];
446
+ const lat = result.geometry.location.lat();
447
+ const lng = result.geometry.location.lng();
448
+ const detail = parseAddressComponents(result.address_components);
449
+ onChange?.({
450
+ source: LocationInputMode.GOOGLE_PLACES,
451
+ latitude: lat,
452
+ longitude: lng,
453
+ name: selection.mainText ?? selection.description,
454
+ address: result.formatted_address,
455
+ placeId: selection.place_id,
456
+ addressDetail: detail,
457
+ });
458
+ if (autocompleteSessionTokenClass) {
459
+ sessionTokenRef.current =
460
+ new autocompleteSessionTokenClass();
461
+ }
462
+ }
463
+ });
464
+ }
465
+ }
466
+ }
467
+ else {
468
+ onChange?.(null);
469
+ if (autocompleteSessionTokenClass) {
470
+ sessionTokenRef.current = new autocompleteSessionTokenClass();
471
+ }
472
+ }
473
+ }, renderInput: (params) => (_jsx(StyledTextField, { ...params, label: "Buscar direcci\u00F3n...", error: error, helperText: helperText, required: required, inputRef: inputRef })) })) }), allowedModes.includes(LocationInputMode.GPS) && (_jsx(Tooltip, { title: "Obtener ubicaci\u00F3n por GPS", children: _jsx(IconButton, { onClick: handleGPSFetch, disabled: gpsLoading, className: styles.actionButton, children: gpsLoading ? _jsx(CircularProgress, { size: 18 }) : _jsx(LocationIcon, { fill: "currentColor" }) }) })), allowedModes.includes(LocationInputMode.MANUAL) && (_jsx(Tooltip, { title: "Ingresar direcci\u00F3n manualmente", children: _jsx(IconButton, { onClick: () => setManualModalOpen(true), className: styles.actionButton, children: _jsx(EditIcon, { fill: "currentColor" }) }) }))] })) }), gpsError && (_jsx(Typography, { variant: "caption", color: "error", style: { display: 'block', marginTop: '4px' }, children: gpsError })), _jsxs(Dialog, { open: manualModalOpen, onClose: () => setManualModalOpen(false), maxWidth: "sm", fullWidth: true, slotProps: {
474
+ popper: { sx: { zIndex: 1400 } },
475
+ paper: {
476
+ style: {
477
+ borderRadius: '20px',
478
+ padding: '16px 24px',
479
+ backgroundColor: formStyle.stepBackgroundColor || '#fff',
480
+ },
481
+ },
482
+ }, children: [_jsx(DialogTitle, { style: {
483
+ fontWeight: 700,
484
+ padding: '0 0 16px 0',
485
+ color: formStyle.textColor,
486
+ fontFamily: 'inherit',
487
+ }, children: "Ingresar Direcci\u00F3n" }), _jsx(DialogContent, { style: {
488
+ padding: '16px 0',
489
+ borderTop: `1px solid ${formStyle.outlineColor}`,
490
+ borderBottom: `1px solid ${formStyle.outlineColor}`,
491
+ }, children: _jsxs("div", { className: styles.manualGrid, children: [_jsx("div", { className: styles.manualRow, children: _jsx("div", { className: styles.col12, children: _jsx(ErkTextField, { label: "Calle / Carrera / Avenida (L\u00EDnea 1)", value: localStreet, onChange: setLocalStreet, required: true }) }) }), _jsxs("div", { className: styles.manualRow, children: [_jsx("div", { className: styles.col6, children: _jsx(ErkTextField, { label: "N\u00FAmero / Portal", value: localStreetNumber, onChange: setLocalStreetNumber }) }), _jsx("div", { className: styles.col6, children: _jsx(ErkTextField, { label: "Apto / Oficina / Interior", value: localUnit, onChange: setLocalUnit }) })] }), _jsxs("div", { className: styles.manualRow, children: [_jsx("div", { className: styles.col6, children: _jsx(ErkTextField, { label: "Barrio / Zona", value: localNeighborhood, onChange: setLocalNeighborhood }) }), _jsx("div", { className: styles.col6, children: _jsx(ErkTextField, { label: "Ciudad", value: localCity, onChange: setLocalCity, required: true }) })] }), _jsxs("div", { className: styles.manualRow, children: [_jsx("div", { className: styles.col4, children: _jsx(ErkTextField, { label: "Estado / Provincia / Dpto", value: localState, onChange: setLocalState }) }), _jsx("div", { className: styles.col4, children: _jsx(ErkTextField, { label: "C\u00F3digo Postal", value: localPostalCode, onChange: setLocalPostalCode }) }), _jsx("div", { className: styles.col4, children: _jsx(ErkTextField, { label: "Pa\u00EDs", value: localCountry, onChange: setLocalCountry, required: true }) })] })] }) }), _jsxs(DialogActions, { style: { padding: '16px 0 0 0', gap: '12px' }, children: [_jsx(ErkButton, { text: "Cancelar", variant: "outlined", onClick: () => setManualModalOpen(false), sx: {
492
+ borderColor: formStyle.outlineColor,
493
+ color: formStyle.textColor,
494
+ '&:hover': {
495
+ borderColor: formStyle.textColor,
496
+ backgroundColor: 'rgba(0, 0, 0, 0.04)',
497
+ },
498
+ } }), _jsx(ErkButton, { text: "Guardar", variant: "contained", onClick: handleSaveManual, disabled: !localStreet || !localCity || !localCountry })] })] })] }));
499
+ }
@@ -0,0 +1,115 @@
1
+ .container {
2
+ width: 100%;
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 8px;
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ .inputRow {
10
+ display: flex;
11
+ align-items: flex-start;
12
+ gap: 10px;
13
+ width: 100%;
14
+ }
15
+
16
+ .actionButton {
17
+ border: 1px solid var(--eureka-outline) !important;
18
+ border-radius: 10px !important;
19
+ padding: 5px !important;
20
+ height: 33px !important;
21
+ width: 33px !important;
22
+ background-color: var(--eureka-bg) !important;
23
+ color: var(--eureka-text) !important;
24
+ flex-shrink: 0;
25
+ transition: all 0.2s ease-in-out;
26
+ margin-top: 5px !important;
27
+ }
28
+
29
+ .actionButton:hover:not(:disabled) {
30
+ background-color: rgba(0, 0, 0, 0.04) !important;
31
+ border-color: var(--eureka-primary) !important;
32
+ color: var(--eureka-primary) !important;
33
+ }
34
+
35
+ .actionButton:disabled {
36
+ opacity: 0.5;
37
+ }
38
+
39
+ .manualGrid {
40
+ display: flex;
41
+ flex-direction: column;
42
+ gap: 12px;
43
+ width: 100%;
44
+ }
45
+
46
+ .manualRow {
47
+ display: flex;
48
+ gap: 12px;
49
+ width: 100%;
50
+ }
51
+
52
+ .col12 { width: 100%; }
53
+ .col8 { width: 66.666%; }
54
+ .col6 { width: 50%; }
55
+ .col4 { width: 33.333%; }
56
+ .col3 { width: 25%; }
57
+
58
+ @media (max-width: 600px) {
59
+ .manualRow {
60
+ flex-direction: column;
61
+ gap: 12px;
62
+ }
63
+ .col8, .col6, .col4, .col3 {
64
+ width: 100%;
65
+ }
66
+ }
67
+
68
+ .readOnlyContainer {
69
+ width: 100%;
70
+ box-sizing: border-box;
71
+ }
72
+
73
+ .readOnlyCard {
74
+ display: flex;
75
+ align-items: flex-start;
76
+ gap: 15px;
77
+ padding: 15px;
78
+ border: 1px solid var(--eureka-outline);
79
+ border-radius: 10px;
80
+ background-color: var(--eureka-bg);
81
+ }
82
+
83
+ .readOnlyIcon {
84
+ flex-shrink: 0;
85
+ margin-top: 2px;
86
+ width: 24px;
87
+ height: 24px;
88
+ }
89
+
90
+ .readOnlyDetails {
91
+ display: flex;
92
+ flex-direction: column;
93
+ gap: 4px;
94
+ }
95
+
96
+ .readOnlyLabel {
97
+ font-size: 0.75rem !important;
98
+ color: #666;
99
+ font-weight: 500 !important;
100
+ }
101
+
102
+ .readOnlyName {
103
+ font-size: 1rem !important;
104
+ font-weight: 600 !important;
105
+ }
106
+
107
+ .readOnlyAddress {
108
+ font-size: 0.9rem !important;
109
+ }
110
+
111
+ .readOnlyCoords {
112
+ font-size: 0.8rem !important;
113
+ color: #888;
114
+ font-family: monospace;
115
+ }
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ export interface ErkNumberFieldProps {
3
+ /** Controlled numeric value. null = empty field. */
4
+ value?: number | null;
5
+ onChange?: (value: number | null) => void;
6
+ label?: string;
7
+ min?: number;
8
+ max?: number;
9
+ /** Increment/decrement step. Defaults to 1. */
10
+ step?: number;
11
+ /**
12
+ * Maximum decimal places.
13
+ * - Blocks extra decimal input live (rejects the keystroke)
14
+ * - Formats display on blur via Intl.NumberFormat
15
+ * - 0 = integers only
16
+ */
17
+ decimals?: number;
18
+ required?: boolean;
19
+ error?: boolean;
20
+ /** Show red border on error. */
21
+ errorBorder?: boolean;
22
+ helperText?: React.ReactNode;
23
+ disabled?: boolean;
24
+ readOnly?: boolean;
25
+ placeholder?: string;
26
+ size?: 'small' | 'medium';
27
+ fullWidth?: boolean;
28
+ /** Static text at the end of the field (e.g. "%", "USD"). */
29
+ suffix?: React.ReactNode;
30
+ /** Show increment/decrement spinner buttons. */
31
+ spinner?: boolean;
32
+ inputRef?: React.Ref<HTMLInputElement>;
33
+ }
34
+ /**
35
+ * Pure number input built on Base UI NumberField + MUI styling.
36
+ * No form-step system dependency.
37
+ * Class wrapper for react-hook-form ref compatibility.
38
+ */
39
+ declare class ErkNumberField extends React.Component<ErkNumberFieldProps> {
40
+ render(): JSX.Element;
41
+ }
42
+ export default ErkNumberField;