@object-ui/plugin-map 17.3.0 → 17.6.0
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/CHANGELOG.md +490 -0
- package/README.md +144 -243
- package/dist/ObjectMap.d.ts +18 -3
- package/dist/ObjectMap.d.ts.map +1 -1
- package/dist/camera.d.ts +102 -0
- package/dist/camera.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +271 -204
- package/dist/index.umd.cjs +1 -1
- package/package.json +12 -11
package/README.md
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
# @object-ui/plugin-map
|
|
2
2
|
|
|
3
|
-
Map
|
|
3
|
+
Map view plugin for Object UI.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Renders the records of an **ObjectQL query** as markers on a MapLibre map: every
|
|
6
|
+
marker comes from a record's own coordinate fields, and the first paint frames the
|
|
7
|
+
records that were fetched. It is a *view over data* — there is no authored marker
|
|
8
|
+
list, and no pin you place by hand.
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
10
|
+
Importing the package registers two component types on the `ComponentRegistry`,
|
|
11
|
+
both resolving to the same renderer:
|
|
12
|
+
|
|
13
|
+
- `object-map` — the object-bound renderer
|
|
14
|
+
- `map` — the bare spec view-type name (`ViewTypeSchema`'s `'map'`), for a node
|
|
15
|
+
authored with it directly. Inside an `ObjectView`, a `map` view is compiled to
|
|
16
|
+
an `object-map` node, so both spellings end at the same component.
|
|
13
17
|
|
|
14
18
|
## Installation
|
|
15
19
|
|
|
@@ -17,274 +21,171 @@ Map visualization plugin for Object UI - Display geographic data with interactiv
|
|
|
17
21
|
pnpm add @object-ui/plugin-map
|
|
18
22
|
```
|
|
19
23
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
### Automatic Registration (Side-Effect Import)
|
|
23
|
-
|
|
24
|
-
```typescript
|
|
25
|
-
// In your app entry point (e.g., App.tsx or main.tsx)
|
|
26
|
-
import '@object-ui/plugin-map';
|
|
27
|
-
|
|
28
|
-
// Now you can use map types in your schemas
|
|
29
|
-
const schema = {
|
|
30
|
-
type: 'map',
|
|
31
|
-
center: { lat: 37.7749, lng: -122.4194 },
|
|
32
|
-
zoom: 12,
|
|
33
|
-
markers: [
|
|
34
|
-
{ lat: 37.7749, lng: -122.4194, label: 'San Francisco' }
|
|
35
|
-
]
|
|
36
|
-
};
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Manual Registration
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
import { mapComponents } from '@object-ui/plugin-map';
|
|
43
|
-
import { ComponentRegistry } from '@object-ui/core';
|
|
44
|
-
|
|
45
|
-
// Register map components
|
|
46
|
-
Object.entries(mapComponents).forEach(([type, component]) => {
|
|
47
|
-
ComponentRegistry.register(type, component);
|
|
48
|
-
});
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Schema API
|
|
24
|
+
## Requires a bundler — plain Node cannot import this package
|
|
52
25
|
|
|
53
|
-
|
|
26
|
+
`ObjectMap` imports MapLibre's stylesheet at module scope
|
|
27
|
+
(`import 'maplibre-gl/dist/maplibre-gl.css'`), and Node has no loader for `.css` at all.
|
|
28
|
+
Importing the published entry from plain Node ESM — no bundler, no loader hooks — therefore
|
|
29
|
+
resolves and then fails during evaluation:
|
|
54
30
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
type: 'map',
|
|
60
|
-
center: { lat: number, lng: number },
|
|
61
|
-
zoom?: number, // Default: 10
|
|
62
|
-
markers?: MapMarker[],
|
|
63
|
-
layers?: MapLayer[],
|
|
64
|
-
height?: number | string,
|
|
65
|
-
onMarkerClick?: (marker) => void,
|
|
66
|
-
className?: string
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Map Marker
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
interface MapMarker {
|
|
74
|
-
lat: number;
|
|
75
|
-
lng: number;
|
|
76
|
-
label?: string;
|
|
77
|
-
icon?: string; // Icon URL or name
|
|
78
|
-
popup?: string | ReactNode;
|
|
79
|
-
color?: string; // Marker color
|
|
80
|
-
}
|
|
31
|
+
```text
|
|
32
|
+
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".css"
|
|
33
|
+
for .../maplibre-gl/dist/maplibre-gl.css
|
|
81
34
|
```
|
|
82
35
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
36
|
+
**This is a supported-configuration statement, not a bug to report.** Unbundled Node
|
|
37
|
+
consumption is not supported for style-carrying plugin packages. It was ruled that way on
|
|
38
|
+
[objectui#5384](https://github.com/objectstack-ai/objectui/issues/5384) — deliberately, over
|
|
39
|
+
the alternative of moving the stylesheet out of module scope — because a MapLibre canvas
|
|
40
|
+
without its stylesheet is not a map, and no unbundled-Node consumer exists to serve.
|
|
86
41
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
height: 500,
|
|
93
|
-
markers: [
|
|
94
|
-
{
|
|
95
|
-
lat: 40.7128,
|
|
96
|
-
lng: -74.0060,
|
|
97
|
-
label: 'New York City',
|
|
98
|
-
popup: 'The Big Apple'
|
|
99
|
-
}
|
|
100
|
-
]
|
|
101
|
-
};
|
|
102
|
-
```
|
|
42
|
+
Consume it through a host that handles CSS imports, which every supported host does: Vite,
|
|
43
|
+
webpack, or Next with the package listed in `transpilePackages`. If you have a real need to
|
|
44
|
+
import it under plain Node — SSR with no bundler, a Node-side script — please open an issue.
|
|
45
|
+
That reopens the question as a design decision rather than a defect, and the shape of your
|
|
46
|
+
consumer is the missing input.
|
|
103
47
|
|
|
104
|
-
|
|
48
|
+
## Usage
|
|
105
49
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
type: 'map',
|
|
109
|
-
center: { lat: 37.7749, lng: -122.4194 },
|
|
110
|
-
zoom: 10,
|
|
111
|
-
markers: [
|
|
112
|
-
{
|
|
113
|
-
lat: 37.7749,
|
|
114
|
-
lng: -122.4194,
|
|
115
|
-
label: 'San Francisco',
|
|
116
|
-
color: 'red',
|
|
117
|
-
popup: 'Golden Gate Bridge'
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
lat: 37.8044,
|
|
121
|
-
lng: -122.2712,
|
|
122
|
-
label: 'Oakland',
|
|
123
|
-
color: 'blue',
|
|
124
|
-
popup: 'Port of Oakland'
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
lat: 37.3382,
|
|
128
|
-
lng: -121.8863,
|
|
129
|
-
label: 'San Jose',
|
|
130
|
-
color: 'green',
|
|
131
|
-
popup: 'Silicon Valley'
|
|
132
|
-
}
|
|
133
|
-
]
|
|
134
|
-
};
|
|
135
|
-
```
|
|
50
|
+
Registration is a side effect of the import. There is no manual-registration
|
|
51
|
+
export to iterate over — the import *is* the registration.
|
|
136
52
|
|
|
137
|
-
|
|
53
|
+
```ts
|
|
54
|
+
import '@object-ui/plugin-map';
|
|
138
55
|
|
|
139
|
-
|
|
56
|
+
// Object-bound: the markers are the records the query returns.
|
|
140
57
|
const schema = {
|
|
141
|
-
type: 'map',
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
58
|
+
type: 'object-map',
|
|
59
|
+
objectName: 'stores',
|
|
60
|
+
map: {
|
|
61
|
+
latitudeField: 'lat',
|
|
62
|
+
longitudeField: 'lng',
|
|
63
|
+
titleField: 'name',
|
|
64
|
+
descriptionField: 'address',
|
|
148
65
|
},
|
|
149
|
-
onMapClick: (coordinates) => {
|
|
150
|
-
console.log('Map clicked at:', coordinates);
|
|
151
|
-
// Add new marker
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### Map with Data Binding
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
const schema = {
|
|
160
|
-
type: 'map',
|
|
161
|
-
center: { lat: 37.7749, lng: -122.4194 },
|
|
162
|
-
zoom: 10,
|
|
163
|
-
markers: '${data.locations.map(loc => ({ lat: loc.latitude, lng: loc.longitude, label: loc.name }))}',
|
|
164
|
-
onMarkerClick: (marker) => {
|
|
165
|
-
// Handle marker click
|
|
166
|
-
}
|
|
167
66
|
};
|
|
168
67
|
```
|
|
169
68
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
Connect map to ObjectStack data sources:
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
import { createObjectStackAdapter } from '@object-ui/data-objectstack';
|
|
176
|
-
|
|
177
|
-
const dataSource = createObjectStackAdapter({
|
|
178
|
-
baseUrl: 'https://api.example.com',
|
|
179
|
-
token: 'your-auth-token'
|
|
180
|
-
});
|
|
69
|
+
A literal record array instead of a query, with the same `map` block:
|
|
181
70
|
|
|
71
|
+
```ts
|
|
182
72
|
const schema = {
|
|
183
73
|
type: 'object-map',
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
popupField: 'description',
|
|
190
|
-
center: { lat: 37.7749, lng: -122.4194 },
|
|
191
|
-
zoom: 10
|
|
192
|
-
};
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
## Map Features
|
|
196
|
-
|
|
197
|
-
### Custom Markers
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
const schema = {
|
|
201
|
-
type: 'map',
|
|
202
|
-
markers: [
|
|
203
|
-
{
|
|
204
|
-
lat: 40.7128,
|
|
205
|
-
lng: -74.0060,
|
|
206
|
-
icon: '/custom-marker.png',
|
|
207
|
-
popup: {
|
|
208
|
-
type: 'card',
|
|
209
|
-
title: 'Location Details',
|
|
210
|
-
body: 'Custom popup content'
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
]
|
|
214
|
-
};
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Geolocation
|
|
218
|
-
|
|
219
|
-
```typescript
|
|
220
|
-
const schema = {
|
|
221
|
-
type: 'map',
|
|
222
|
-
useGeolocation: true, // Center map on user's location
|
|
223
|
-
zoom: 15,
|
|
224
|
-
markers: []
|
|
74
|
+
staticData: [
|
|
75
|
+
{ id: 1, name: 'San Francisco HQ', lat: 37.7749, lng: -122.4194 },
|
|
76
|
+
{ id: 2, name: 'Oakland Office', lat: 37.8044, lng: -122.2711 },
|
|
77
|
+
],
|
|
78
|
+
map: { latitudeField: 'lat', longitudeField: 'lng', titleField: 'name' },
|
|
225
79
|
};
|
|
226
80
|
```
|
|
227
81
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
82
|
+
`filter` and `sort` are the **query's** filter and order — they reach the data
|
|
83
|
+
source as `$filter` / `$orderby`, and the spec's per-element `dataSource` binding
|
|
84
|
+
is honoured as well. The map issues no row cap of its own.
|
|
85
|
+
|
|
86
|
+
## The `map` block
|
|
87
|
+
|
|
88
|
+
The declared configuration input. Every key is optional:
|
|
89
|
+
|
|
90
|
+
| Key | Description |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| `latitudeField` | Record field holding the latitude. Needs `longitudeField` alongside it; both values must be numbers. |
|
|
93
|
+
| `longitudeField` | Record field holding the longitude. |
|
|
94
|
+
| `locationField` | Single field holding both coordinates — see the formats below. Used when the lat/lng pair yields nothing. |
|
|
95
|
+
| `titleField` | Field shown as the marker title. Omitted, markers are titled `Marker`. |
|
|
96
|
+
| `descriptionField` | Field shown under the title in the marker popup. |
|
|
97
|
+
| `zoom` | Zoom level. Declaring it opts this view out of the auto-fit (see below). |
|
|
98
|
+
| `center` | `[latitude, longitude]` — a two-number **tuple**, latitude first. Declaring it opts this view out of the auto-fit. |
|
|
99
|
+
| `style` | MapLibre style URL/spec, replacing the default public demo style. |
|
|
100
|
+
|
|
101
|
+
**The block replaces the field-name defaults, it is not merged with them.** With
|
|
102
|
+
no map configuration at all the component falls back to the field names
|
|
103
|
+
`latitude` / `longitude` / `location` / `name` / `description`; the moment a `map`
|
|
104
|
+
block is present, only what it declares is read. So `map: { titleField: 'name' }`
|
|
105
|
+
on its own names no coordinate field, places nothing, and renders an empty map
|
|
106
|
+
under the excluded-records notice — the defaults do not fill the gap.
|
|
107
|
+
|
|
108
|
+
## Initial camera
|
|
109
|
+
|
|
110
|
+
There is no default zoom and no default centre. With records to show and no
|
|
111
|
+
camera declared, the map **fits the records**: their bounding box, measured along
|
|
112
|
+
the shortest arc that contains them (so a set straddling the antimeridian is
|
|
113
|
+
framed across the line, not around the far side of the planet), with 48px of
|
|
114
|
+
padding and a city-scale zoom ceiling of 12 — a single record does not become a
|
|
115
|
+
rooftop view.
|
|
116
|
+
|
|
117
|
+
Two cases sit outside the fit:
|
|
118
|
+
|
|
119
|
+
- **Nothing placeable** (empty result, or no record yielding coordinates): the
|
|
120
|
+
whole world, centred on `0, 0`.
|
|
121
|
+
- **A declared camera**: `zoom` or `center` in the `map` block wins and the fit is
|
|
122
|
+
skipped. Declaring one half keeps the other derived — `zoom` alone is applied at
|
|
123
|
+
the centre of the records, `center` alone at a continental zoom.
|
|
124
|
+
|
|
125
|
+
A `center` that is not a two-number tuple (the `{ lat, lng }` object form, say) is
|
|
126
|
+
rejected by the config schema, warned about in the console, and **not** adapted —
|
|
127
|
+
and it does not cost the view its fit.
|
|
128
|
+
|
|
129
|
+
## Coordinate formats
|
|
130
|
+
|
|
131
|
+
`locationField` reads any of:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
{ location: { lat: 37.7749, lng: -122.4194 } } // also latitude/longitude, lon
|
|
135
|
+
{ location: '37.7749,-122.4194' } // "lat,lng"
|
|
136
|
+
{ location: [37.7749, -122.4194] } // [lat, lng]
|
|
239
137
|
```
|
|
240
138
|
|
|
241
|
-
|
|
139
|
+
A record whose coordinates are missing, unparseable, or out of range (latitude
|
|
140
|
+
beyond ±90, longitude beyond ±180) is left off the map and counted in a notice
|
|
141
|
+
above it, rather than being silently dropped or rescued.
|
|
242
142
|
|
|
243
|
-
|
|
244
|
-
import type { MapSchema, MapMarker } from '@object-ui/plugin-map';
|
|
143
|
+
## What this component does not read
|
|
245
144
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
145
|
+
Keys that look plausible on a map schema but have no read site here: `markers`
|
|
146
|
+
(markers are records), `layers`, `height` (the container is a fixed responsive
|
|
147
|
+
height, 300px through 600px), `useGeolocation` (the map carries a
|
|
148
|
+
user-initiated "show my location" button instead), and per-marker `icon` /
|
|
149
|
+
`color` / `popup` styling. A `map` configuration stashed under `filter.map` — a
|
|
150
|
+
shape predating the `map` input — is no longer read either, and says so in the
|
|
151
|
+
console.
|
|
252
152
|
|
|
253
|
-
|
|
254
|
-
type: 'map',
|
|
255
|
-
center: { lat: 37.7749, lng: -122.4194 },
|
|
256
|
-
zoom: 12,
|
|
257
|
-
markers: [marker]
|
|
258
|
-
};
|
|
259
|
-
```
|
|
153
|
+
## Using `ObjectMap` directly
|
|
260
154
|
|
|
261
|
-
|
|
155
|
+
`ObjectMap` (the component), `ObjectMapRenderer` (the registered wrapper, for a
|
|
156
|
+
host that registers types itself) and the `ObjectMapProps` type are the package's
|
|
157
|
+
exports:
|
|
262
158
|
|
|
263
|
-
|
|
159
|
+
```tsx
|
|
160
|
+
import { ObjectMap } from '@object-ui/plugin-map';
|
|
264
161
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
center: { lat: 37.7749, lng: -122.4194 }
|
|
271
|
-
};
|
|
162
|
+
<ObjectMap
|
|
163
|
+
schema={{ type: 'object-map', objectName: 'stores', map: { latitudeField: 'lat', longitudeField: 'lng' } }}
|
|
164
|
+
dataSource={dataSource}
|
|
165
|
+
onMarkerClick={(record) => console.log(record)}
|
|
166
|
+
/>;
|
|
272
167
|
```
|
|
273
168
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
169
|
+
| Prop | Description |
|
|
170
|
+
| --- | --- |
|
|
171
|
+
| `schema` | The map schema — the keys above. |
|
|
172
|
+
| `dataSource` | Resolves the `object` provider. Not needed for `staticData` or an inline `data` array. |
|
|
173
|
+
| `className` | Classes for the wrapper around the map. |
|
|
174
|
+
| `data` | Records to render directly, bypassing the component's own fetch — the shape `ListView` passes when it already holds the rows. Tracked live: passing a new array after mount (e.g. once a host's own in-flight query resolves) updates the map. |
|
|
175
|
+
| `onMarkerClick` | Called with the clicked record. |
|
|
176
|
+
| `onRowClick` | Record click handler; takes priority over the `navigation` overlay. |
|
|
177
|
+
| `onEdit` / `onDelete` | Passing either adds that button to the marker popup (and to the mobile record sheet). |
|
|
178
|
+
| `enableClustering` | Forces clustering on; without it, clustering starts above 100 visible markers. |
|
|
179
|
+
| `clusterRadius` | Clustering granularity (default `50`): the grid cell is `clusterRadius / 2 ** zoom`, so a larger value groups more aggressively. |
|
|
180
|
+
|
|
181
|
+
In a schema-driven page these handlers may equally be authored on the node
|
|
182
|
+
itself: `SchemaRenderer` spreads a node's non-metadata properties onto the
|
|
183
|
+
component.
|
|
284
184
|
|
|
285
185
|
## Links
|
|
286
186
|
|
|
287
|
-
- 📚 [Documentation](https://www.objectui.org/docs/plugins/plugin-map)
|
|
187
|
+
- 📚 [Documentation](https://www.objectui.org/docs/plugins/plugin-map) — the full
|
|
188
|
+
authoring reference for the schema and the `map` block
|
|
288
189
|
- 📦 [npm package](https://www.npmjs.com/package/@object-ui/plugin-map)
|
|
289
190
|
- 📝 [Changelog](./CHANGELOG.md)
|
|
290
191
|
- 🐛 [Report an issue](https://github.com/objectstack-ai/objectui/issues)
|
package/dist/ObjectMap.d.ts
CHANGED
|
@@ -1,16 +1,31 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { ObjectMapSchema, DataSource } from '@object-ui/types';
|
|
3
3
|
export interface ObjectMapProps {
|
|
4
|
-
schema:
|
|
4
|
+
schema: ObjectMapSchema;
|
|
5
5
|
dataSource?: DataSource;
|
|
6
6
|
className?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Records to render directly, bypassing this component's own fetch — the
|
|
9
|
+
* shape `ListView` passes when it already holds the rows. Declared as its
|
|
10
|
+
* own prop (not read off the `rest` spread) so the fetch effect can depend
|
|
11
|
+
* on this one value: naming the whole `rest` object in that effect's deps
|
|
12
|
+
* instead would refetch on every render, since `rest` is a fresh object
|
|
13
|
+
* each render (objectui#5003).
|
|
14
|
+
*/
|
|
15
|
+
data?: any[];
|
|
7
16
|
onMarkerClick?: (record: any) => void;
|
|
8
17
|
onRowClick?: (record: any) => void;
|
|
9
18
|
onEdit?: (record: any) => void;
|
|
10
19
|
onDelete?: (record: any) => void;
|
|
11
20
|
/** Enable marker clustering for dense data */
|
|
12
21
|
enableClustering?: boolean;
|
|
13
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Clustering grid granularity (default: 50). NOT screen pixels — the grid
|
|
24
|
+
* cell edge is `clusterRadius / 2 ** zoom`, in coordinate DEGREES (the
|
|
25
|
+
* marker's `[lng, lat]`), so a larger value groups more aggressively and
|
|
26
|
+
* the effective granularity shrinks exponentially as zoom increases. See
|
|
27
|
+
* `clusterMarkers` below.
|
|
28
|
+
*/
|
|
14
29
|
clusterRadius?: number;
|
|
15
30
|
}
|
|
16
31
|
export declare const ObjectMap: React.FC<ObjectMapProps>;
|
package/dist/ObjectMap.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObjectMap.d.ts","sourceRoot":"","sources":["../src/ObjectMap.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;GAYG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AACjF,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"ObjectMap.d.ts","sourceRoot":"","sources":["../src/ObjectMap.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;GAYG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AACjF,OAAO,KAAK,EAAE,eAAe,EAAmB,UAAU,EAAY,MAAM,kBAAkB,CAAC;AAY/F,OAAO,kCAAkC,CAAC;AAkB1C,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACb,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACjC,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAkcD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA2jB9C,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
package/dist/camera.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Initial-camera derivation for ObjectMap (objectui#4941).
|
|
10
|
+
*
|
|
11
|
+
* A map view whose query returned records must never first-paint an empty
|
|
12
|
+
* viewport. The camera is therefore derived from the records themselves: the
|
|
13
|
+
* marker set's bounding box, handed to MapLibre so it fits the box with the
|
|
14
|
+
* real container size (`initialViewState.bounds` -> the `bounds` /
|
|
15
|
+
* `fitBoundsOptions` pair on the MapLibre `Map` constructor, which overrides
|
|
16
|
+
* `center` / `zoom`).
|
|
17
|
+
*
|
|
18
|
+
* Two things this module deliberately does NOT do:
|
|
19
|
+
*
|
|
20
|
+
* 1. It does not rescue out-of-range record coordinates. The platform's
|
|
21
|
+
* `location` value is `{ lat, lng }` with longitude bounded to [-180, 180]
|
|
22
|
+
* by `@objectstack/spec` (`data/field-value.zod.ts`), so a longitude outside
|
|
23
|
+
* that range is a producer-side defect, not a dialect this renderer should
|
|
24
|
+
* learn to read. `ObjectMap` keeps rejecting such records and counting them
|
|
25
|
+
* in its "invalid coordinates" notice. The normalization here is projection
|
|
26
|
+
* arithmetic over already-valid values, applied to the CAMERA only.
|
|
27
|
+
* 2. It does not introduce any authoring surface. The only camera declaration
|
|
28
|
+
* read anywhere is the documented `zoom` / `center` pair of the map config
|
|
29
|
+
* block (`content/docs/plugins/plugin-map.mdx`), and an author who declares
|
|
30
|
+
* it keeps winning over everything computed here.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* `[[west, south], [east, north]]` — the two-corner form MapLibre accepts as
|
|
34
|
+
* `LngLatBoundsLike`. `east` may exceed 180: that is how MapLibre expects a box
|
|
35
|
+
* crossing the antimeridian to be spelled (its own `adjustAntiMeridian` produces
|
|
36
|
+
* exactly this shape), and it is what keeps the fitted camera on the same world
|
|
37
|
+
* copy as the markers.
|
|
38
|
+
*/
|
|
39
|
+
export type MarkerBounds = [[number, number], [number, number]];
|
|
40
|
+
/** Breathing room around the fitted box, in pixels. */
|
|
41
|
+
export declare const FIT_PADDING_PX = 48;
|
|
42
|
+
/**
|
|
43
|
+
* Ceiling for the fitted zoom. A single record — or several records at one
|
|
44
|
+
* address — fits a zero-width box, and an unbounded fit answers that with the
|
|
45
|
+
* map's maximum zoom (22): a building-level view of a style whose tiles usually
|
|
46
|
+
* stop far short of it. City scale is the useful answer instead.
|
|
47
|
+
*/
|
|
48
|
+
export declare const FIT_MAX_ZOOM = 12;
|
|
49
|
+
/** Zoom used when there is nothing to fit: the whole world, not a random sea. */
|
|
50
|
+
export declare const EMPTY_VIEW_ZOOM = 2;
|
|
51
|
+
/**
|
|
52
|
+
* Zoom used when a partially declared camera pins the position but not the
|
|
53
|
+
* scale, so the box cannot drive the fit. Same value the component used before
|
|
54
|
+
* the fit existed.
|
|
55
|
+
*/
|
|
56
|
+
export declare const UNFITTED_CENTER_ZOOM = 3;
|
|
57
|
+
/**
|
|
58
|
+
* Latitude ceiling for the fitted box. Web Mercator is unbounded at the poles,
|
|
59
|
+
* so a record sitting exactly at +/-90 projects to infinity and takes the whole
|
|
60
|
+
* camera with it. Markers themselves stay at their real latitude.
|
|
61
|
+
*/
|
|
62
|
+
export declare const MAX_FIT_LATITUDE = 85;
|
|
63
|
+
/**
|
|
64
|
+
* Fold any longitude onto the [-180, 180) circle.
|
|
65
|
+
*
|
|
66
|
+
* In-range values return unchanged rather than through the modulo, which would
|
|
67
|
+
* round-trip them into a neighbouring float (-74.006 came back as
|
|
68
|
+
* -74.00599999999997) and put that drift into every fitted box.
|
|
69
|
+
*/
|
|
70
|
+
export declare function normalizeLongitude(lng: number): number;
|
|
71
|
+
/**
|
|
72
|
+
* Bounding box of a marker set, along the SHORTEST arc that contains every
|
|
73
|
+
* marker.
|
|
74
|
+
*
|
|
75
|
+
* Why the shortest arc and not `[min(lng), max(lng)]`: the naive extremes read
|
|
76
|
+
* the circle as a line, so a set straddling the antimeridian (say 179 and -179,
|
|
77
|
+
* two degrees apart) yields a 358-degree-wide box whose midpoint is 0 — the
|
|
78
|
+
* antipode of the data, half a planet away. MapLibre then places the markers in
|
|
79
|
+
* whichever world copy is nearest their previous screen position (`smartWrap`),
|
|
80
|
+
* which is how a fitted-looking camera ends up showing an empty ocean with the
|
|
81
|
+
* records sitting on a neighbouring copy of the world.
|
|
82
|
+
*
|
|
83
|
+
* The widest EMPTY gap between adjacent longitudes is the seam to cut; what
|
|
84
|
+
* remains is the shortest containing arc. `west` is the first marker east of
|
|
85
|
+
* that seam, and the span is measured eastward from it, so the box may extend
|
|
86
|
+
* past 180.
|
|
87
|
+
*
|
|
88
|
+
* @param coordinates marker coordinates in MapLibre order, `[lng, lat]`
|
|
89
|
+
* @returns the box, or `null` when there is nothing to fit
|
|
90
|
+
*/
|
|
91
|
+
export declare function computeMarkerBounds(coordinates: ReadonlyArray<readonly [number, number]>): MarkerBounds | null;
|
|
92
|
+
/**
|
|
93
|
+
* Centre of a box from {@link computeMarkerBounds}, for the paths that cannot
|
|
94
|
+
* hand the box to a fit (a declared zoom with no declared centre). The
|
|
95
|
+
* longitude is folded back onto the circle so a box crossing the antimeridian
|
|
96
|
+
* still yields a real centre rather than a value past 180.
|
|
97
|
+
*/
|
|
98
|
+
export declare function boundsCenter(bounds: MarkerBounds): {
|
|
99
|
+
longitude: number;
|
|
100
|
+
latitude: number;
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=camera.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../src/camera.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEhE,uDAAuD;AACvD,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;;;GAKG;AACH,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B,iFAAiF;AACjF,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGtD;AAKD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACpD,YAAY,GAAG,IAAI,CA8BrB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAM1F"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,CAAC;AAwB/B,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,CAgB3C,CAAC"}
|