useDocyrusMapView
Map backed by a Docyrus data source. Detects the location field and builds markers from logos, icons or the default pin, with clustering, a viewport filter, basemaps and drag-to-move with reverse geocoding.
Installation
pnpm dlx @docyrus/cli add @docyrus/rn-hooks-use-docyrus-map-viewpnpm add @docyrus/api-client @docyrus/app-utils @tanstack/react-query react-native-maps expo-location (optional)This is a port of the web hook with the same signature. It builds on useDocyrusDataGrid for the saved views, toolbar, filters and search, then requests only the columns the map needs.
The rendering is rewritten for native. DocyrusMapView draws everything on the compound Map, which uses react-native-maps. It includes:
- clustered markers showing a logo, a Docyrus icon or the default pin
- callouts
- drag-to-move
- zoom and locate controls
- a basemap switcher
- a "Search this area" pill
The web names DocyrusLeafletMapView and DocyrusGoogleMapView are exported as aliases. DocyrusGoogleMapView forces the Google provider.
Usage
import { useDocyrusClient } from '@docyrus/signin/react-native';
import {
DocyrusMapView,
useDocyrusMapView
} from '@/hooks/docyrus-native/use-docyrus-map-view';
export function OrganizationsMap() {
const client = useDocyrusClient();
const mapView = useDocyrusMapView({
client: client!,
appSlug: 'base',
dataSourceSlug: 'organization',
locationFieldSlug: 'map_location',
titleFieldSlug: 'name',
descriptionFieldSlugs: ['address', 'city', 'status'],
markerImageFieldSlug: 'company_logo',
markerIconFieldSlug: 'company_icon',
enableViewportFilter: true,
viewportFilterMode: 'manual',
enableMarkerDrag: true,
basemaps: ['street', 'satellite', 'hybrid', 'terrain'],
googleMapsApiKey: process.env.EXPO_PUBLIC_GOOGLE_MAPS_API_KEY,
persistState: true
});
return (
<>
{mapView.toolbar}
<DocyrusMapView
className="h-[480px]"
markers={mapView.markers}
allMarkersCount={mapView.allMarkers.length}
center={mapView.center}
bounds={mapView.bounds}
defaultZoom={mapView.defaultZoom}
focusedZoom={mapView.focusedZoom}
fitBoundsPadding={mapView.fitBoundsPadding}
selectedMarkerId={mapView.selectedMarkerId}
onSelectMarker={mapView.setSelectedMarkerId}
onViewportChange={mapView.setViewportBounds}
isViewportStale={mapView.isViewportStale}
onSearchThisArea={mapView.searchThisArea}
draggableMarkers={mapView.enableMarkerDrag}
onMarkerMove={mapView.handleMarkerMove}
basemaps={mapView.availableBasemaps}
activeBasemap={mapView.activeBasemap}
onBasemapChange={mapView.setActiveBasemap}
onMapClick={mapView.onMapClick} />
</>
);
}API Reference
Options (UseDocyrusMapViewOptions<TData>)
Extends every useDocyrusDataGrid option except data, enableItemsQuery, showSelectColumn and enableRowMarkers.
| Prop | Type | Default | Description |
|---|---|---|---|
client | RestApiClient | — | Authenticated Docyrus API client (required) |
appSlug | string | — | App slug (required) |
dataSourceSlug | string | — | Data source slug (required) |
data | TData[] | — | Pre-loaded rows. When set, the items query is skipped |
collection | DocyrusDataGridCollection<TData> | — | Custom list adapter |
locationFieldSlug | string | first field-locationSelect | Location field |
titleFieldSlug | string | name, title, … or the first text field | Marker title field |
descriptionFieldSlugs | string[] | description, details, address, city, … | Fields for the callout description |
markerImageFieldSlug | string | — | Image / file field used as the marker image |
markerIconFieldSlug | string | field-icon / icon | Docyrus icon field |
markerIconColorFieldSlug | string | — | Icon color field (hex, rgb() or a Tailwind token) |
markerIconColor | string | — | Icon color when the color field is empty |
markerPriority | 'image' | 'icon' | 'image' | Which one to show when a record has both |
enableViewportFilter | boolean | false | Limits markers / mappedItems to the viewport |
viewportFilterMode | 'auto' | 'manual' | 'auto' | manual waits for searchThisArea() |
enableMarkerDrag | boolean | false | Makes markers draggable (long-press, then drag) |
onMarkerMove | (marker, newPosition) => void | Promise<void> | PATCH location | Replaces the default PATCH |
markerTemplate | string | — | {{field_slug}} template for the callout text |
getMarker | (item, context) => DocyrusMapMarker<TData> | null | — | Replaces the built-in marker builder |
defaultCenter | DocyrusMapPoint | { lat: 39.9334, lng: 32.8597 } | Center when there are no markers |
defaultZoom | number | 6 | Zoom when there are no markers |
focusedZoom | number | 14 | Zoom for a single marker |
fitBoundsPadding | number | 48 | Padding in px for the one-time fit to all markers |
autoSelectSingleMarker | boolean | true | Selects the marker automatically when there is only one |
googleMapsApiKey | string | null | — | Enables Google reverse geocoding. There is no env fallback on native |
onMapClick | (position: DocyrusMapPoint) => void | — | Called on a tap on the map, then again with address once it resolves |
basemaps | Array<DocyrusBasemapId | DocyrusBasemapDefinition> | false | — | Basemaps in the picker. Omit to hide the picker |
defaultBasemap | string | first entry | Initial basemap |
enableGroupMenu / enableRowHeightMenu / enableDisplayMenu | boolean | false | Grid toolbar menus |
staleTime | number | 30000 | React Query stale time in ms |
onReload | () => void | — | Called after a toolbar reload |
persistState | boolean | { storage?: 'session' | 'local'; key?: string } | — | Saves the grid's view parameters and the basemap (…:map) for each saved view |
Result (UseDocyrusMapViewResult<TData>)
| Key | Type | Description |
|---|---|---|
markers / allMarkers | DocyrusMapMarker<TData>[] | Markers after / before the viewport filter |
items / mappedItems / unmappedItems | TData[] | All records / records with a location / records without one |
counts | { total, mapped, unmapped } | Record counts |
viewportBounds / setViewportBounds | DocyrusMapBounds | null / setter | Last settled bounds. Wire the setter to onViewportChange |
isViewportStale | boolean | Manual mode: the map has moved since the last search |
searchThisArea / clearViewportFilter | () => void | Applies / clears the viewport filter |
enableMarkerDrag / handleMarkerMove | boolean / (marker, pos) => Promise<void> | Drag-to-move |
availableBasemaps / activeBasemap / setActiveBasemap | — | Basemap state |
locationField / titleField / descriptionFields | DataSourceField | Resolved fields |
hasLocationField | boolean | Whether a location field was found |
requestedColumns / resolvedListParams | — | Query payload |
center / bounds / defaultZoom / focusedZoom / fitBoundsPadding | — | Camera inputs for DocyrusMapView |
selectedMarkerId / selectedMarker / setSelectedMarkerId / selectMarker / clearSelectedMarker | — | Selection |
googleMapsApiKey | string | null | The resolved key |
onMapClick | (position) => void | undefined | Passed through from options |
isLoading / error / reload | — | Loading, error and reload |
table / toolbar / views / fields / dataSource / activeViewId / setActiveViewId | — | Passed through from useDocyrusDataGrid |
DocyrusMapView props
| Prop | Type | Default | Description |
|---|---|---|---|
markers | DocyrusMapMarker<TData>[] | — | Markers to render (required) |
center | DocyrusMapPoint | — | Initial center (required) |
bounds | DocyrusMapBounds | null | — | The map fits these bounds once, the first time they are available |
defaultZoom | number | 6 | Initial zoom without bounds |
focusedZoom | number | 14 | Zoom used for a single point |
fitBoundsPadding | number | 48 | Fit padding in px |
selectedMarkerId | string | null | — | Highlighted marker |
onSelectMarker | (id | null) => void | — | Called on a marker press, and with null when the map is tapped |
onMapClick | (position) => void | — | Called on a tap on empty map, then again with address |
renderPopup | (marker) => ReactNode | — | Custom callout content |
onEditMarker | (marker) => void | — | Called when the callout is pressed |
onViewportChange | (bounds) => void | — | Called when the map settles |
draggableMarkers | boolean | — | Makes markers draggable |
onMarkerMove | (marker, position) => void | Promise<void> | — | Called when a marker is dropped |
allMarkersCount | number | — | Keeps the map mounted when the viewport has no markers |
basemaps / activeBasemap / onBasemapChange | — | — | Layers control; shown when there are 2 or more basemaps |
renderMarkerIcon | (marker, isSelected) => ReactNode | — | Custom marker view |
emptyState | ReactNode | t('ui.mapView.emptyMarkers') | Shown when there is no data |
className | string | h-[420px] | Container classes. Give it a height |
provider | 'default' | 'google' | 'default' | Map provider (native-only) |
isViewportStale / onSearchThisArea | boolean / () => void | — | "Search this area" pill (native-only) |
enableClustering | boolean | true | Clusters nearby markers (native-only) |
showZoomControl / showLocateControl | boolean | true | Map controls (native-only) |
googleMapsApiKey | string | null | — | Reverse-geocoding key for onMapClick (native-only) |
DocyrusGoogleMapView also accepts apiKey, defaultMapType ('roadmap' | 'satellite' | 'hybrid' | 'terrain') and getMarkerIcon(marker, isSelected) => string | { url, size } | null.
Helpers
reverseGeocode(lat, lng, apiKey?)returnsPromise<string | null>. It tries the optionalexpo-locationpeer first, then the Google Geocoding REST API when a key is given, then OpenStreetMap Nominatim. It never throws.getExpoLocationModule()returns the optionalexpo-locationmodule, ornull.DEFAULT_DOCYRUS_BASEMAPSholds the built-instreet/satellite/hybrid/terraindefinitions.
Native deltas
- There is ONE
DocyrusMapViewon react-native-maps instead of the web Leaflet and Google Maps views. Both web names are exported as aliases. DocyrusBasemapDefinitionaddsmapType('standard' | 'satellite' | 'hybrid' | 'terrain'). The Leaflet tile fields are optional and ignored.DocyrusMapMarkeraddsicon(the raw Docyrus icon id), which is rendered withDocyrusIcon.- The map fits to the markers only once, like web, so refetches and drags never move the user's viewport. The viewport is not persisted.
expo-locationis an optional peer. Without it, reverse geocoding uses Google REST (with a key) or Nominatim.
Translation Keys
| Key | Fallback |
|---|---|
ui.mapView.emptyMarkers | No mapped records yet. |
ui.mapView.searchThisArea | Search this area |
ui.mapView.tapToEdit | Tap to edit |
Type Exports
| Type | Description |
|---|---|
UseDocyrusMapViewOptions<TData> / UseDocyrusMapViewResult<TData> | Hook options and result |
DocyrusMapMarker<TData> / DocyrusMapMarkerBuilderContext<TData> / DocyrusMapMarkerIcon | Marker types |
DocyrusMapPoint / DocyrusMapBounds / DocyrusLocationValue / DocyrusNormalizedLocation | Geometry types |
DocyrusBasemapId / DocyrusBasemapDefinition / DocyrusGoogleMapType / DocyrusMapProvider | Basemap types |
DocyrusMapViewProps<TData> / DocyrusGoogleMapViewProps<TData> | View props |
useDocyrusLogActivity
Docyrus data engine for LogActivityForm. Fetches comments, users, task and event options and the record's status field, and runs the comment, task, event and two-call status mutations.
useDocyrusPivotCalendar
PivotCalendar backed by a Docyrus data source. Aggregates per day or month in the database through a pivot matrix, optionally grouped by a relation, and turns a tapped value into a ready-to-run drilldown query.