@sfperusacdev/sf-ui 0.2.3 → 0.2.5

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 (31) hide show
  1. package/dist/sf-ui.css +1 -1
  2. package/dist/sf-ui.js +2375 -1743
  3. package/dist/sf-ui.js.map +1 -1
  4. package/dist/sf-ui.umd.cjs +7 -7
  5. package/dist/sf-ui.umd.cjs.map +1 -1
  6. package/dist/theme/sf-tokens.css +3 -0
  7. package/dist/types/src/adapters/maps/google/google.test.d.ts +1 -0
  8. package/dist/types/src/adapters/maps/google/index.d.ts +24 -1
  9. package/dist/types/src/components/inputs/AsyncSearchSelect.d.ts +4 -1
  10. package/dist/types/src/components/inputs/FieldLeadingIcon.d.ts +6 -0
  11. package/dist/types/src/components/inputs/Input.d.ts +8 -1
  12. package/dist/types/src/components/inputs/SearchSelect.d.ts +4 -1
  13. package/dist/types/src/components/inputs/fieldClassName.d.ts +3 -1
  14. package/dist/types/src/components/maps/base/MapPointCard.d.ts +22 -0
  15. package/dist/types/src/components/maps/base/MapPointCard.test.d.ts +1 -0
  16. package/dist/types/src/components/maps/base/index.d.ts +2 -0
  17. package/dist/types/src/components/maps/forms/AddressSearchInput.test.d.ts +1 -0
  18. package/dist/types/src/components/maps/renderers/geometria.d.ts +5 -0
  19. package/dist/types/src/components/maps/renderers/google/GoogleMapRenderer.d.ts +1 -1
  20. package/dist/types/src/components/maps/renderers/leaflet/LeafletMapRenderer.d.ts +1 -1
  21. package/dist/types/src/components/maps/views/MapView.d.ts +20 -3
  22. package/dist/types/src/components/maps/views/MapView.test.d.ts +1 -0
  23. package/dist/types/src/components/table/CustomTable.d.ts +16 -1
  24. package/dist/types/src/components/table/index.d.ts +1 -1
  25. package/dist/types/src/constants/maps/index.d.ts +3 -0
  26. package/dist/types/src/contracts/maps/index.d.ts +2 -0
  27. package/dist/types/src/hooks/maps/index.d.ts +1 -0
  28. package/dist/types/src/hooks/maps/useReverseGeocode.d.ts +14 -0
  29. package/dist/types/src/services/maps/googleMapsLoader.d.ts +20 -0
  30. package/dist/types/src/types/maps/index.d.ts +61 -0
  31. package/package.json +1 -1
@@ -480,6 +480,9 @@
480
480
  --sf-map-area-fill: var(--sf-info);
481
481
  --sf-map-route-stroke: #0f766e;
482
482
  --sf-map-route-fill: #14b8a6;
483
+ --sf-map-circle-stroke: var(--sf-success);
484
+ --sf-map-circle-fill: var(--sf-success);
485
+ --sf-map-pin-bg: var(--sf-success);
483
486
  --sf-map-panel-bg: var(--sf-network-panel-bg);
484
487
  --sf-map-vertex-border: var(--sf-white);
485
488
  --sf-map-vertex-shadow: var(--sf-network-marker-shadow);
@@ -1,13 +1,22 @@
1
- import { SfLatLng, SfPlace } from '../../../types/maps';
1
+ import { SfAddressParts, SfLatLng, SfPlace } from '../../../types/maps';
2
2
  export type GoogleMapsLikeLatLng = {
3
3
  lat: () => number;
4
4
  lng: () => number;
5
5
  };
6
+ export type GoogleMapsLikeAddressComponent = {
7
+ long_name?: string;
8
+ short_name?: string;
9
+ types?: string[];
10
+ };
6
11
  export type GoogleMapsLikeGeocoderResult = {
7
12
  place_id?: string;
8
13
  formatted_address?: string;
14
+ types?: string[];
15
+ address_components?: GoogleMapsLikeAddressComponent[];
9
16
  geometry?: {
10
17
  location?: GoogleMapsLikeLatLng;
18
+ /** ROOFTOP | RANGE_INTERPOLATED | GEOMETRIC_CENTER | APPROXIMATE */
19
+ location_type?: string;
11
20
  };
12
21
  };
13
22
  export declare const googleLatLngToSfLatLng: (point: GoogleMapsLikeLatLng) => SfLatLng;
@@ -15,4 +24,18 @@ export declare const sfLatLngToGoogleLiteral: (point: SfLatLng) => {
15
24
  lat: number;
16
25
  lng: number;
17
26
  };
27
+ /**
28
+ * Partes de la dirección a partir de uno o varios resultados de Google (los de una geocodificación inversa vienen
29
+ * del más al menos preciso). Vía y número salen del mismo resultado; el resto, del primero que lo traiga.
30
+ * En Perú Google da el distrito como `locality`, la provincia como nivel 2 y el departamento como nivel 1
31
+ * (en Lima Metropolitana el nivel 1 es "Provincia de Lima").
32
+ */
33
+ export declare const googleAddressParts: (results: GoogleMapsLikeGeocoderResult[], conVia?: GoogleMapsLikeGeocoderResult | undefined) => SfAddressParts;
18
34
  export declare const googleGeocoderResultToSfPlace: (result: GoogleMapsLikeGeocoderResult) => SfPlace | null;
35
+ /**
36
+ * Lo que hay en un punto según la geocodificación inversa de Google. La dirección sale del resultado con vía
37
+ * **más cercano** al punto: Google devuelve la dirección con número más cercana que conoce, que puede estar a una
38
+ * cuadra (calles sin numeración), y no siempre la pone primero. `parts.lineDistanceM` dice a cuántos metros está,
39
+ * para que quien la muestre la presente como referencia si no es exacta. La ubicación es el punto pedido.
40
+ */
41
+ export declare const googleReverseResultsToSfPlace: (point: SfLatLng, results: GoogleMapsLikeGeocoderResult[]) => SfPlace | null;
@@ -1,4 +1,5 @@
1
1
  import { FieldValues, Path, UseFormReturn } from 'react-hook-form';
2
+ import { IconType } from 'react-icons';
2
3
  import { SelectOption } from './SelectOption';
3
4
  type AsyncSearchSelectProps<TFormValues extends FieldValues> = {
4
5
  form: UseFormReturn<TFormValues>;
@@ -15,6 +16,8 @@ type AsyncSearchSelectProps<TFormValues extends FieldValues> = {
15
16
  readOnly?: boolean;
16
17
  small?: boolean;
17
18
  onChange?: (value: string | string[]) => void;
19
+ /** Icono a la izquierda, en su propio compartimento. */
20
+ leadingIcon?: IconType;
18
21
  };
19
- export declare const AsyncSearchSelect: <TFormValues extends FieldValues>({ form, name, loadOptions, label, info, required, multiple, className, placeholder, searchPlaceholder, emptyMessage, readOnly, small, onChange, }: AsyncSearchSelectProps<TFormValues>) => import("react").JSX.Element;
22
+ export declare const AsyncSearchSelect: <TFormValues extends FieldValues>({ form, name, loadOptions, label, info, required, multiple, className, placeholder, searchPlaceholder, emptyMessage, readOnly, small, onChange, leadingIcon, }: AsyncSearchSelectProps<TFormValues>) => import("react").JSX.Element;
20
23
  export {};
@@ -0,0 +1,6 @@
1
+ import { IconType } from 'react-icons';
2
+ /** Icono a la izquierda del campo, en su propio compartimento con línea divisoria. */
3
+ export declare const FieldLeadingIcon: ({ icon: Icon, small }: {
4
+ icon: IconType;
5
+ small?: boolean;
6
+ }) => import("react").JSX.Element;
@@ -18,6 +18,8 @@ type InputProps<TFormValues extends FieldValues> = {
18
18
  step?: number | string;
19
19
  onChange?: (value: string) => void;
20
20
  icon?: IconType;
21
+ /** Icono a la izquierda, en su propio compartimento. `icon` sigue siendo el de la derecha. */
22
+ leadingIcon?: IconType;
21
23
  small?: boolean;
22
24
  onIconClick?: () => Promise<void> | void;
23
25
  onRefresh?: () => Promise<void> | void;
@@ -26,6 +28,11 @@ type InputProps<TFormValues extends FieldValues> = {
26
28
  * para experiencias de una pregunta por pantalla. Usa clases propias de este componente; NO
27
29
  * modifica `fieldClassName.ts` ni afecta a ningún otro input. */
28
30
  variant?: InputVariant;
31
+ /**
32
+ * Valores que se ofrecen mientras se escribe (lista nativa del navegador, `<datalist>`). El texto sigue siendo
33
+ * libre: sirve para que un mismo valor no se escriba de tres formas ("Urb Primavera", "Urb. Primavera"…).
34
+ */
35
+ suggestions?: string[];
29
36
  };
30
- export declare const Input: <TFormValues extends FieldValues>({ form, name, label, info, required, type, className, placeholder, readOnly, min, max, step, onChange, icon, small, onIconClick, onRefresh, variant, }: InputProps<TFormValues>) => React.JSX.Element;
37
+ export declare const Input: <TFormValues extends FieldValues>({ form, name, label, info, required, type, className, placeholder, readOnly, min, max, step, onChange, icon, leadingIcon, small, onIconClick, onRefresh, variant, suggestions, }: InputProps<TFormValues>) => React.JSX.Element;
31
38
  export {};
@@ -1,4 +1,5 @@
1
1
  import { FieldValues, Path, UseFormReturn } from 'react-hook-form';
2
+ import { IconType } from 'react-icons';
2
3
  import { SelectOption } from './SelectOption';
3
4
  type SearchSelectProps<TFormValues extends FieldValues> = {
4
5
  form: UseFormReturn<TFormValues>;
@@ -19,6 +20,8 @@ type SearchSelectProps<TFormValues extends FieldValues> = {
19
20
  onRefresh?: () => Promise<void> | void;
20
21
  loading?: boolean;
21
22
  onOpen?: () => void;
23
+ /** Icono a la izquierda, en su propio compartimento. */
24
+ leadingIcon?: IconType;
22
25
  };
23
- export declare const SearchSelect: <TFormValues extends FieldValues>({ form, name, options, label, info, required, multiple, className, placeholder, searchPlaceholder, emptyMessage, readOnly, small, maxChips, onChange, onRefresh, onOpen, loading, }: SearchSelectProps<TFormValues>) => import("react").JSX.Element;
26
+ export declare const SearchSelect: <TFormValues extends FieldValues>({ form, name, options, label, info, required, multiple, className, placeholder, searchPlaceholder, emptyMessage, readOnly, small, maxChips, onChange, onRefresh, onOpen, loading, leadingIcon, }: SearchSelectProps<TFormValues>) => import("react").JSX.Element;
24
27
  export {};
@@ -1,8 +1,10 @@
1
- export declare const fieldClassName: ({ small, error, className, withRightIcon, disabled, readOnly, }: {
1
+ export declare const fieldClassName: ({ small, error, className, withRightIcon, withLeftIcon, disabled, readOnly, }: {
2
2
  small?: boolean;
3
3
  error?: boolean;
4
4
  className?: string;
5
5
  withRightIcon?: boolean;
6
+ /** Deja sitio al icono de la izquierda (`FieldLeadingIcon`). */
7
+ withLeftIcon?: boolean;
6
8
  disabled?: boolean;
7
9
  readOnly?: boolean;
8
10
  }) => string;
@@ -0,0 +1,22 @@
1
+ import { ReactNode } from 'react';
2
+ export type MapPointCardItem = {
3
+ key: string;
4
+ label: string;
5
+ /** Sin valor la fila no se pinta. */
6
+ value?: ReactNode;
7
+ /** Botón al lado del valor, p. ej. "Usar" cuando el campo ya tiene algo escrito a mano. */
8
+ actionLabel?: string;
9
+ onAction?: () => void;
10
+ };
11
+ export type MapPointCardProps = {
12
+ title?: string;
13
+ items: MapPointCardItem[];
14
+ loading?: boolean;
15
+ error?: string | null;
16
+ /** Aviso destacado al pie (p. ej. otro registro muy cerca). */
17
+ warning?: ReactNode;
18
+ onClose?: () => void;
19
+ className?: string;
20
+ };
21
+ /** Tarjeta con lo que hay en el punto pulsado de un mapa. Va en `MapView.panel`. */
22
+ export declare const MapPointCard: ({ title, items, loading, error, warning, onClose, className, }: MapPointCardProps) => import("react").JSX.Element;
@@ -8,3 +8,5 @@ export { MapToolbar } from './MapToolbar';
8
8
  export type { MapToolbarMapType, MapToolbarProps } from './MapToolbar';
9
9
  export { MapVehiclePopup } from './MapVehiclePopup';
10
10
  export type { MapVehiclePopupProps } from './MapVehiclePopup';
11
+ export { MapPointCard } from './MapPointCard';
12
+ export type { MapPointCardItem, MapPointCardProps } from './MapPointCard';
@@ -0,0 +1,5 @@
1
+ import { SfLatLng, SfMapCircle } from '../../../types/maps';
2
+ /** Punto del borde inferior-derecho del círculo, donde se ancla su etiqueta (igual en Leaflet y Google). */
3
+ export declare const sfCircleLabelAnchor: (circle: SfMapCircle) => SfLatLng;
4
+ /** Contorno cerrado del círculo (para trazarlo punteado donde el motor no sabe hacerlo). */
5
+ export declare const sfCircleRing: (circle: SfMapCircle, lados?: number) => SfLatLng[];
@@ -2,5 +2,5 @@ import { SfGoogleMapsEngineConfig, SfMapRendererProps } from '../../../../types/
2
2
  type GoogleMapRendererProps = SfMapRendererProps & {
3
3
  config?: SfGoogleMapsEngineConfig;
4
4
  };
5
- export declare const GoogleMapRenderer: ({ config, center, zoom, markers, routes, areas, height, className, readOnly, onClick, onMarkerClick, onMarkerDoubleClick, onMarkerDrag, onMarkerDragEnd, }: GoogleMapRendererProps) => import("react").JSX.Element;
5
+ export declare const GoogleMapRenderer: ({ config, center, zoom, markers, routes, areas, circles, mapType, view, onViewChange, zoomControl, height, className, readOnly, onClick, onMarkerClick, onMarkerDoubleClick, onMarkerDrag, onMarkerDragEnd, }: GoogleMapRendererProps) => import("react").JSX.Element;
6
6
  export {};
@@ -3,5 +3,5 @@ type LeafletMapRendererProps = SfMapRendererProps & {
3
3
  tileUrl?: string;
4
4
  attribution?: string;
5
5
  };
6
- export declare const LeafletMapRenderer: ({ center, zoom, markers, routes, areas, height, className, readOnly, tileUrl, attribution, onClick, onMarkerClick, onMarkerDoubleClick, onMarkerDrag, onMarkerDragEnd, }: LeafletMapRendererProps) => import("react").JSX.Element;
6
+ export declare const LeafletMapRenderer: ({ center, zoom, markers, routes, areas, circles, mapType, view, onViewChange, zoomControl, height, className, readOnly, tileUrl, attribution, onClick, onMarkerClick, onMarkerDoubleClick, onMarkerDrag, onMarkerDragEnd, }: LeafletMapRendererProps) => import("react").JSX.Element;
7
7
  export {};
@@ -1,9 +1,26 @@
1
- import { SfMapMarker, SfMapRendererProps, SfMapShape } from '../../../types/maps';
2
- export type MapViewProps = Omit<SfMapRendererProps, "markers"> & {
1
+ import { ReactNode } from 'react';
2
+ import { SfLatLng, SfMapCircle, SfMapMarker, SfMapRendererProps, SfMapShape, SfMapType } from '../../../types/maps';
3
+ export type MapViewProps = Omit<SfMapRendererProps, "markers" | "view" | "onViewChange" | "zoomControl"> & {
3
4
  title?: string;
4
5
  description?: string;
5
6
  markers?: SfMapMarker[];
6
7
  routes?: SfMapShape[];
7
8
  areas?: SfMapShape[];
9
+ circles?: SfMapCircle[];
10
+ /** Con `onMapTypeChange` se pinta el selector Mapa / Satélite arriba a la izquierda. */
11
+ mapType?: SfMapType;
12
+ onMapTypeChange?: (mapType: SfMapType) => void;
13
+ /** Controles propios arriba a la derecha: zoom, recentrar (si hay `recenterTo`) y capas (si hay `onMapTypeChange`). */
14
+ showControls?: boolean;
15
+ /** Destino del botón recentrar. */
16
+ recenterTo?: SfLatLng | null;
17
+ /** Cuando cambia, el mapa se centra ahí (p. ej. el resultado de una búsqueda). */
18
+ focus?: SfLatLng | null;
19
+ /** Zoom mínimo al recentrar o al ir a `focus`. */
20
+ focusZoom?: number;
21
+ /** Tarjeta abajo a la derecha (leyenda). */
22
+ legend?: ReactNode;
23
+ /** Panel arriba a la izquierda, bajo el selector de capas (p. ej. `MapPointCard`). */
24
+ panel?: ReactNode;
8
25
  };
9
- export declare const MapView: ({ title, description, center, zoom, markers, routes, areas, height, className, readOnly, onClick, onMarkerClick, onMarkerDoubleClick, onMarkerDrag, onMarkerDragEnd, }: MapViewProps) => import("react").JSX.Element;
26
+ export declare const MapView: ({ title, description, center, zoom, markers, routes, areas, circles, mapType, onMapTypeChange, showControls, recenterTo, focus, focusZoom, legend, panel, height, className, readOnly, onClick, onMarkerClick, onMarkerDoubleClick, onMarkerDrag, onMarkerDragEnd, }: MapViewProps) => import("react").JSX.Element;
@@ -11,6 +11,14 @@ import { CustomTableController } from './customTableController';
11
11
  * Todos se apoyan en tokens `--sf-*` para reaccionar al tema activo.
12
12
  */
13
13
  export type TableHeaderVariant = "default" | "brand" | "soft" | "neon" | "premium" | "matrix";
14
+ /** Estado de una paginación que resuelve el backend (limit/offset). `page` es 1-based. */
15
+ export type CustomTableServerPagination = {
16
+ totalItems: number;
17
+ page: number;
18
+ pageSize: number;
19
+ onPageChange: (page: number) => void;
20
+ onPageSizeChange: (pageSize: number) => void;
21
+ };
14
22
  type BuiltInAggregation = "sum" | "min" | "max" | "mean" | "median" | "unique" | "uniqueCount" | "count";
15
23
  type TableAggregation = BuiltInAggregation | AggregationFn<unknown>;
16
24
  type CustomColumn<TData> = {
@@ -67,6 +75,13 @@ type CustomTableProps<TData extends Record<string, unknown>, TDetail extends Rec
67
75
  paginationVariant?: PaginationVariant | "legacy";
68
76
  /** Opciones del selector "Mostrar N registros por página". Default: 10/25/50/100/200. */
69
77
  pageSizeOptions?: number[];
78
+ /**
79
+ * Paginación en servidor: `data` ya es UNA página que trajo el backend. El
80
+ * CustomTable no la recorta y dibuja el mismo paginador dentro de la tarjeta
81
+ * con estos valores. Excluye a `enablePagination` (paginación en cliente).
82
+ * Las pantallas arrancan con `pageSize` 25 (regla del producto).
83
+ */
84
+ serverPagination?: CustomTableServerPagination;
70
85
  fillParentHeight?: boolean;
71
86
  maxBodyHeightPx?: number;
72
87
  className?: string;
@@ -122,5 +137,5 @@ type CustomTableProps<TData extends Record<string, unknown>, TDetail extends Rec
122
137
  detail?: CustomTableDetail<TData, TDetail>;
123
138
  controller?: CustomTableController<TData>;
124
139
  };
125
- export declare function CustomTable<TData extends Record<string, unknown>, TDetail extends Record<string, unknown> = Record<string, unknown>>({ id, stateStore, data, columns, grouping, defaultGrouping, enableGrouping, enableSorting, enableFiltering, enablePagination, pageSize, paginationVariant, pageSizeOptions, fillParentHeight, maxBodyHeightPx, className, emptyMessage, title, subtitle, sidePanel, onGroupingChange, toolbarSlot, belowToolbarSlot, loading, loadingMessage, isFetching, error, onRetry, onRefresh, onRowClick, enableRowSelection, getRowActions, getBulkActions, bulkActionsSlot, onSelectionChange, headerVariant, searchPlaceholder, embedded, detail, controller, }: CustomTableProps<TData, TDetail>): import("react").JSX.Element;
140
+ export declare function CustomTable<TData extends Record<string, unknown>, TDetail extends Record<string, unknown> = Record<string, unknown>>({ id, stateStore, data, columns, grouping, defaultGrouping, enableGrouping, enableSorting, enableFiltering, enablePagination, pageSize, paginationVariant, pageSizeOptions, serverPagination, fillParentHeight, maxBodyHeightPx, className, emptyMessage, title, subtitle, sidePanel, onGroupingChange, toolbarSlot, belowToolbarSlot, loading, loadingMessage, isFetching, error, onRetry, onRefresh, onRowClick, enableRowSelection, getRowActions, getBulkActions, bulkActionsSlot, onSelectionChange, headerVariant, searchPlaceholder, embedded, detail, controller, }: CustomTableProps<TData, TDetail>): import("react").JSX.Element;
126
141
  export type { CustomColumn, CustomTableDetail, CustomTableProps, TableAggregation };
@@ -1,5 +1,5 @@
1
1
  export { CustomTable } from './CustomTable';
2
- export type { CustomColumn, CustomTableDetail, CustomTableProps, TableHeaderVariant } from './CustomTable';
2
+ export type { CustomColumn, CustomTableDetail, CustomTableProps, CustomTableServerPagination, TableHeaderVariant, } from './CustomTable';
3
3
  export { AsyncCustomTable } from './AsyncCustomTable';
4
4
  export type { AsyncCustomTableProps } from './AsyncCustomTable';
5
5
  export { PaginationRangeInput } from './PaginationRangeInput';
@@ -4,5 +4,8 @@ export declare const SF_DEFAULT_MAP_CENTER: SfLatLng;
4
4
  export declare const SF_DEFAULT_MAP_ZOOM = 13;
5
5
  export declare const SF_OSM_TILE_URL = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
6
6
  export declare const SF_OSM_ATTRIBUTION = "&copy; <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\" rel=\"noreferrer\">OpenStreetMap</a>";
7
+ /** Imagen satelital de Esri World Imagery: sin clave, exige la atribución de abajo. */
8
+ export declare const SF_SATELLITE_TILE_URL = "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}";
9
+ export declare const SF_SATELLITE_ATTRIBUTION = "Imagen &copy; <a href=\"https://www.esri.com\" target=\"_blank\" rel=\"noreferrer\">Esri</a>, Maxar, Earthstar Geographics";
7
10
  export declare const SF_LEAFLET_ENGINE_CAPABILITIES: SfMapEngineCapabilities;
8
11
  export declare const SF_GOOGLE_ENGINE_CAPABILITIES: SfMapEngineCapabilities;
@@ -9,6 +9,8 @@ export interface IGeocoderService {
9
9
  capabilities: SfGeocodingCapabilities;
10
10
  geocode(query: string): Promise<SfPlace[]>;
11
11
  reverseGeocode(point: SfLatLng): Promise<SfPlace | null>;
12
+ /** Nombre y dirección de un lugar del mapa base (`SfMapClickEvent.placeId`). Solo lo ofrece Google. */
13
+ placeDetails?(placeId: string): Promise<SfPlace | null>;
12
14
  }
13
15
  export interface IRouteService {
14
16
  id: string;
@@ -1,3 +1,4 @@
1
1
  export { useAddressSearch } from './useAddressSearch';
2
2
  export { useMapEngine } from './useMapEngine';
3
3
  export { useMapsConfig } from './useMapsConfig';
4
+ export { useReverseGeocode } from './useReverseGeocode';
@@ -0,0 +1,14 @@
1
+ import { SfLatLng, SfPlace } from '../../types/maps';
2
+ /**
3
+ * Qué hay en un punto del mapa: dirección y partes (geocodificación inversa) y, si se pulsó un lugar del mapa
4
+ * base (`SfMapClickEvent.placeId`), su nombre. Solo vale la última consulta: `lookup` resuelve `null` si otra
5
+ * más nueva la reemplazó, así quien llama no pisa datos con una respuesta vieja.
6
+ */
7
+ export declare const useReverseGeocode: () => {
8
+ place: SfPlace | null;
9
+ loading: boolean;
10
+ error: string | null;
11
+ lookup: (point: SfLatLng, placeId?: string) => Promise<SfPlace | null>;
12
+ show: (next: SfPlace | null) => void;
13
+ clear: () => void;
14
+ };
@@ -8,11 +8,19 @@ export type GoogleMapInstance = {
8
8
  lng: number;
9
9
  }) => void;
10
10
  setZoom?: (zoom: number) => void;
11
+ setMapTypeId?: (mapTypeId: string) => void;
12
+ getCenter?: () => {
13
+ lat: () => number;
14
+ lng: () => number;
15
+ } | undefined;
16
+ getZoom?: () => number | undefined;
11
17
  addListener?: (eventName: string, handler: (event: {
12
18
  latLng?: {
13
19
  lat: () => number;
14
20
  lng: () => number;
15
21
  };
22
+ placeId?: string;
23
+ stop?: () => void;
16
24
  }) => void) => GoogleMapsListener;
17
25
  };
18
26
  type GoogleMapsNamespace = {
@@ -56,9 +64,21 @@ type GoogleMapsNamespace = {
56
64
  lng: number;
57
65
  }[]) => void;
58
66
  };
67
+ Circle?: new (options: Record<string, unknown>) => {
68
+ setMap?: (map: unknown | null) => void;
69
+ setOptions?: (options: Record<string, unknown>) => void;
70
+ };
71
+ Point?: new (x: number, y: number) => unknown;
72
+ Size?: new (width: number, height: number) => unknown;
59
73
  event?: {
60
74
  clearInstanceListeners: (instance: unknown) => void;
61
75
  };
76
+ places?: {
77
+ PlacesService?: new (container: HTMLElement) => {
78
+ getDetails: (request: Record<string, unknown>, callback: (result: unknown | null, status: string) => void) => void;
79
+ };
80
+ };
81
+ importLibrary?: (name: string) => Promise<unknown>;
62
82
  };
63
83
  };
64
84
  export declare const getGoogleMapsNamespace: () => GoogleMapsNamespace | undefined;
@@ -7,18 +7,61 @@ export type SfLatLng = {
7
7
  lat: number;
8
8
  lng: number;
9
9
  };
10
+ /** Capa base del mapa: calles (`roadmap`) o imagen satelital. */
11
+ export type SfMapType = "roadmap" | "satellite";
12
+ /** Encuadre del mapa. Con `view` el mapa es controlado: sigue este encuadre y avisa con `onViewChange`. */
13
+ export type SfMapView = {
14
+ center: SfLatLng;
15
+ zoom: number;
16
+ };
17
+ /** Círculo en metros reales (geocercas). `label` se pinta fijo junto al borde. */
18
+ export type SfMapCircle = {
19
+ id: string;
20
+ center: SfLatLng;
21
+ radiusM: number;
22
+ color?: string;
23
+ fillColor?: string;
24
+ dashed?: boolean;
25
+ label?: string;
26
+ };
10
27
  export type SfBounds = {
11
28
  north: number;
12
29
  south: number;
13
30
  east: number;
14
31
  west: number;
15
32
  };
33
+ /** Partes de una dirección, con los nombres de la división política del Perú. */
34
+ export type SfAddressParts = {
35
+ /** Vía: "Jirón Independencia", "Av. Larco". */
36
+ street?: string;
37
+ /** Número o cuadra: "179", "cuadra 4". */
38
+ number?: string;
39
+ /** Vía y número, listo para un campo Dirección. */
40
+ line?: string;
41
+ /**
42
+ * Metros entre el punto consultado y la dirección de `line` (geocodificación inversa). Google da la dirección con
43
+ * número más cercana que conoce: en calles sin numeración puede estar a una cuadra. Sin valor = no se sabe.
44
+ */
45
+ lineDistanceM?: number;
46
+ /** Urbanización, barrio o sector. */
47
+ neighborhood?: string;
48
+ /** Ciudad o localidad como se escribe en una dirección ("Chepén"); el distrito puede venir sin tildes ("Chepen"). */
49
+ locality?: string;
50
+ district?: string;
51
+ province?: string;
52
+ department?: string;
53
+ country?: string;
54
+ postalCode?: string;
55
+ };
16
56
  export type SfPlace = {
17
57
  id: string;
18
58
  label: string;
19
59
  address?: string;
20
60
  location: SfLatLng;
21
61
  provider?: string;
62
+ /** Nombre del lugar (comercio, colegio, plaza) cuando el punto es uno. */
63
+ name?: string;
64
+ parts?: SfAddressParts;
22
65
  raw?: unknown;
23
66
  };
24
67
  export type SfAddress = SfPlace;
@@ -71,6 +114,15 @@ export type SfMapMarker = {
71
114
  export type SfMapMarkerOptions = {
72
115
  draggable?: boolean;
73
116
  selectable?: boolean;
117
+ /**
118
+ * `dot` (punto, el de siempre), `pin` (marcador de ubicación) o `badge` (círculo del color `color`, con `text`
119
+ * adentro si lo hay: p. ej. el orden de una parada; sin texto es un punto pequeño).
120
+ */
121
+ variant?: "dot" | "pin" | "badge";
122
+ /** Texto dentro del marcador `badge` (1–3 caracteres). */
123
+ text?: string;
124
+ /** Muestra `label` fijo sobre el marcador. */
125
+ showLabel?: boolean;
74
126
  };
75
127
  export type SfMapShape = {
76
128
  id: string;
@@ -81,6 +133,8 @@ export type SfMapShape = {
81
133
  };
82
134
  export type SfMapClickEvent = {
83
135
  location: SfLatLng;
136
+ /** Lugar del mapa base que se pulsó (comercio, colegio…). Solo lo informa el motor Google. */
137
+ placeId?: string;
84
138
  };
85
139
  export type SfMarkerClickEvent = {
86
140
  marker: SfMapMarker;
@@ -143,6 +197,13 @@ export type SfMapRendererProps = {
143
197
  markers?: SfMapMarker[];
144
198
  routes?: SfMapShape[];
145
199
  areas?: SfMapShape[];
200
+ circles?: SfMapCircle[];
201
+ mapType?: SfMapType;
202
+ /** Encuadre controlado. Sin `view`, `center`/`zoom` solo valen al montar (no recentra al mover puntos). */
203
+ view?: SfMapView;
204
+ onViewChange?: (view: SfMapView) => void;
205
+ /** Control de zoom propio del motor. `MapView` lo apaga cuando pinta sus propios controles. */
206
+ zoomControl?: boolean;
146
207
  height?: number | string;
147
208
  className?: string;
148
209
  readOnly?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sfperusacdev/sf-ui",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "SF UI — librería de componentes React (inputs, tablas, hooks) para aplicaciones SF Perú.",
5
5
  "type": "module",
6
6
  "main": "./dist/sf-ui.umd.cjs",