From b7cd0bd878060306e0ac892f53a134da3b7937ba Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Mon, 1 Jun 2026 20:48:26 +0800 Subject: [PATCH] fix(fields): render location/geolocation cells as coordinates, not [Object] A `location` (or `geolocation`) field rendered as "[Object]" in grids and detail views because it fell through to the text cell renderer. Add a LocationCellRenderer that formats `{ lat, lng }` / `{ latitude, longitude }`, `"lat,lng"` strings, and `[lat, lng]` arrays as "lat, lng" with a pin icon, falling back to compact JSON otherwise. Co-Authored-By: Claude Opus 4.8 --- packages/fields/src/index.tsx | 39 ++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/packages/fields/src/index.tsx b/packages/fields/src/index.tsx index d022aa9ec..99e621d8d 100644 --- a/packages/fields/src/index.tsx +++ b/packages/fields/src/index.tsx @@ -10,7 +10,7 @@ import React from 'react'; import type { FieldMetadata, SelectOptionMetadata } from '@object-ui/types'; import { ComponentRegistry } from '@object-ui/core'; import { Badge, Avatar, AvatarFallback, Button, Checkbox, EmptyValue, cn } from '@object-ui/components'; -import { Check, X, Copy, Phone as PhoneIcon } from 'lucide-react'; +import { Check, X, Copy, Phone as PhoneIcon, MapPin } from 'lucide-react'; import { useObjectTranslation } from '@object-ui/react'; import { SchemaRendererContext as _SchemaRendererContext } from '@object-ui/react'; @@ -1389,6 +1389,39 @@ export function ColorSwatchCellRenderer({ value }: CellRendererProps): React.Rea ); } +/** + * Renders a `location`/`geolocation` value as readable coordinates with a pin. + * Accepts `{ lat, lng }` / `{ latitude, longitude }`, a `"lat,lng"` string, + * or a `[lat, lng]` array. Falls back to compact JSON for anything else. + */ +export function LocationCellRenderer({ value }: CellRendererProps): React.ReactElement { + if (value == null || value === '') return ; + let lat: number | undefined; + let lng: number | undefined; + if (typeof value === 'object' && !Array.isArray(value)) { + const v = value as Record; + lat = typeof v.lat === 'number' ? v.lat : typeof v.latitude === 'number' ? v.latitude : undefined; + lng = typeof v.lng === 'number' ? v.lng : typeof v.lon === 'number' ? v.lon : typeof v.longitude === 'number' ? v.longitude : undefined; + } else if (Array.isArray(value) && value.length === 2) { + lat = Number(value[0]); + lng = Number(value[1]); + } else if (typeof value === 'string') { + const parts = value.split(',').map((s) => parseFloat(s.trim())); + if (parts.length === 2 && !isNaN(parts[0]) && !isNaN(parts[1])) { + [lat, lng] = parts; + } + } + if (typeof lat === 'number' && typeof lng === 'number' && !isNaN(lat) && !isNaN(lng)) { + return ( + + + ); + } + return ; +} + /** * Get the appropriate cell renderer for a field type */ @@ -1443,8 +1476,8 @@ export function getCellRenderer(fieldType: string): React.FC owner: UserCellRenderer, password: () => ••••••, secret: () => ••••••, - location: TextCellRenderer, // Default fallback - geolocation: JsonCellRenderer, + location: LocationCellRenderer, + geolocation: LocationCellRenderer, address: JsonCellRenderer, color: ColorSwatchCellRenderer, json: JsonCellRenderer,