@object-ui/plugin-map 17.5.0 → 17.7.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/dist/index.js CHANGED
@@ -1,44 +1,43 @@
1
1
  import { useCallback as e, useEffect as t, useMemo as n, useRef as r, useState as i } from "react";
2
- import { ComponentRegistry as a, buildExpandFields as o, extractRecords as s } from "@object-ui/core";
3
- import { ElementDataSourceGate as c, useNavigationOverlay as l, useSchemaContext as u } from "@object-ui/react";
4
- import { NavigationOverlay as ee, cn as d, useIsMobile as f } from "@object-ui/components";
5
- import { z as p } from "zod";
6
- import te, { Marker as m, NavigationControl as h, Popup as ne } from "react-map-gl/maplibre";
2
+ import { ComponentRegistry as a, ValueDataSource as o, applyNonGridRowCeiling as s, buildExpandFields as ee, convertSortToQueryParams as te, elementDataSourceBlock as c, getRecordDisplayName as ne, nonGridRowCeilingQuery as re, recordDisplayValueAt as l, resolveRecordSourceConfig as u, resolveRecordSourceObjectName as ie } from "@object-ui/core";
3
+ import { ElementDataSourceGate as d, NonGridRowCeilingNote as ae, useDataInvalidation as oe, useFilterScope as se, useNavigationOverlay as ce, useResolvedFilter as le, useSchemaContext as ue, useSettledSchema as de } from "@object-ui/react";
4
+ import { ObjectMapConfigSchema as f } from "@object-ui/types/zod";
5
+ import { NavigationOverlay as fe, cn as p, useIsMobile as pe } from "@object-ui/components";
6
+ import { usePermissions as me } from "@object-ui/permissions";
7
+ import he, { Marker as m, NavigationControl as ge, Popup as _e } from "react-map-gl/maplibre";
7
8
  import "maplibre-gl/dist/maplibre-gl.css";
8
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
9
- //#region src/ObjectMap.tsx
10
- var v = p.object({
11
- latitudeField: p.string().optional(),
12
- longitudeField: p.string().optional(),
13
- locationField: p.string().optional(),
14
- titleField: p.string().optional(),
15
- descriptionField: p.string().optional(),
16
- zoom: p.number().optional(),
17
- center: p.tuple([p.number(), p.number()]).optional(),
18
- style: p.string().optional()
19
- }), re = "https://demotiles.maplibre.org/style.json";
20
- function ie(e) {
21
- return e.data ? e.data : e.staticData ? {
22
- provider: "value",
23
- items: e.staticData
24
- } : e.objectName ? {
25
- provider: "object",
26
- object: e.objectName
27
- } : null;
9
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
10
+ function _(e) {
11
+ return e >= -180 && e < 180 ? e : ((e + 180) % 360 + 360) % 360 - 180;
28
12
  }
29
- function y(e) {
30
- if (e) {
31
- if (typeof e == "string") {
32
- let t = e.split(" "), n = t[0], r = t[1]?.toLowerCase() === "desc" ? "desc" : "asc";
33
- return { [n]: r };
34
- }
35
- if (Array.isArray(e)) return e.reduce((e, t) => (t.field && t.order && (e[t.field] = t.order), e), {});
13
+ var v = (e) => Math.min(85, Math.max(-85, e));
14
+ function ve(e) {
15
+ if (e.length === 0) return null;
16
+ let t = e.map(([e]) => _(e)).sort((e, t) => e - t), n = e.map(([, e]) => v(e)), r = t.length, i = t[0] + 360 - t[r - 1], a = 0;
17
+ for (let e = 1; e < r; e++) {
18
+ let n = t[e] - t[e - 1];
19
+ n > i && (i = n, a = e);
36
20
  }
21
+ let o = t[a], s = t[(a + r - 1) % r], ee = a === 0 ? s : s + 360;
22
+ return [[o, Math.min(...n)], [ee, Math.max(...n)]];
23
+ }
24
+ function ye(e) {
25
+ let [[t, n], [r, i]] = e;
26
+ return {
27
+ longitude: _((t + r) / 2),
28
+ latitude: (n + i) / 2
29
+ };
30
+ }
31
+ //#endregion
32
+ //#region src/ObjectMap.tsx
33
+ var be = "https://demotiles.maplibre.org/style.json", y = Object.keys(f.shape).filter((e) => e !== "style");
34
+ function xe(e) {
35
+ return u(e, "view-data");
37
36
  }
38
37
  var b = () => globalThis.process?.env?.NODE_ENV !== "production", x = /* @__PURE__ */ new Set();
39
- function S(e) {
38
+ function Se(e) {
40
39
  if (!b()) return;
41
- let t = e?.filter;
40
+ let t = e.filter;
42
41
  if (!t || typeof t != "object" || Array.isArray(t) || !Object.prototype.hasOwnProperty.call(t, "map")) return;
43
42
  let n = t.map;
44
43
  if (!n || typeof n != "object") return;
@@ -48,41 +47,49 @@ function S(e) {
48
47
  } catch {
49
48
  r = `${e.type ?? "map"}::${e.objectName ?? ""}::<unserializable>`;
50
49
  }
51
- x.has(r) || (x.add(r), console.warn("[ObjectMap] `filter.map` is no longer read as map configuration, so this map is rendering with the DEFAULT field names (`latitude` / `longitude` / `name`). `filter` is the query filter; the map config belongs under the declared `map` input — move it to `schema.map` (`{ type: 'object-map', map: { latitudeField, longitudeField, titleField } }`). The old spelling was never documented and could not survive a `dataSource` binding, whose merged filter is an `and` node with no `map` key at all (objectstack#7121). If `map` is genuinely a field you are filtering on, ignore this — the filter itself is passed through untouched. objectui#4034."));
50
+ x.has(r) || (x.add(r), console.warn("[ObjectMap] `filter.map` is no longer read as map configuration, so this map has NO coordinate binding and renders the \"Map configuration required\" refusal (objectui#8169 — the `latitude` / `longitude` / `location` defaults are gone). `filter` is the query filter; the map config belongs under the declared `map` input — move it to `schema.map` (`{ type: 'object-map', map: { latitudeField, longitudeField, titleField } }`). The old spelling was never documented and could not survive a `dataSource` binding, whose merged filter is an `and` node with no `map` key at all (objectstack#7121). If `map` is genuinely a field you are filtering on, ignore this — the filter itself is passed through untouched. objectui#4034."));
52
51
  }
52
+ var S = /* @__PURE__ */ new Set();
53
53
  function C(e) {
54
- S(e);
55
- let t = e.style || e.mapStyle || e.map?.style;
56
- if (e.locationField || e.latitudeField) return {
54
+ if (!b()) return;
55
+ let t = e.style;
56
+ if (typeof t != "string" || t === "") return;
57
+ let n = `${e.type ?? "map"}::${e.objectName ?? ""}::${t}`;
58
+ S.has(n) || (S.add(n), console.warn(`[ObjectMap] A top-level \`style\` is NOT read as a map style, so this map is rendering with the DEFAULT public demo tiles and \`${t}\` was dropped. \`style\` is the base schema's INLINE CSS key (a record of CSS properties), which every node may carry; the map style is \`mapStyle\` — named that way precisely to avoid this collision. Write \`mapStyle: '${t}'\` at the top level, or \`map: { style: '${t}' }\` in the declared config block. On a view, write that same declared \`map: { style: ... }\`: ObjectView / ListView flatten \`options.map\` through a whitelist of the keys \`ObjectMapConfigSchema\` declares, and that whitelist carries \`style\` out under the flat spelling \`mapStyle\` (objectui#9950), so the declared block arrives and is read. \`mapStyle\` is the TOP-LEVEL spelling only — inside the \`map\` block it is undeclared, and the flatten drops it. objectui#5017.`));
59
+ }
60
+ var w = /* @__PURE__ */ new Set();
61
+ function T(e) {
62
+ if (!b()) return;
63
+ let t = y.filter((t) => e[t] !== void 0);
64
+ if (t.length === 0) return;
65
+ let n = `${e.type ?? "map"}::${e.objectName ?? ""}::${t.join(",")}`;
66
+ w.has(n) || (w.add(n), console.warn(`[ObjectMap] The \`map\` block configures this map, so these top-level keys are IGNORED: ${t.map((e) => `\`${e}\``).join(", ")}. The \`map\` block is the authoring shape; the flat top-level spelling is the internal form ObjectView/ListView produce when they flatten \`options.map\`, and what the author wrote outranks it. Move anything you still need into \`map\`, or drop the \`map\` block. objectui#5018.`));
67
+ }
68
+ function Ce(e) {
69
+ Se(e), C(e);
70
+ let t = e.mapStyle || e.map?.style, n = e.map ?? null;
71
+ if (n) {
72
+ let r = f.safeParse(n);
73
+ return r.success || console.warn("[ObjectMap] Invalid map configuration:", r.error.format()), T(e), {
74
+ ...n,
75
+ style: t
76
+ };
77
+ }
78
+ return e.locationField || e.latitudeField ? {
57
79
  locationField: e.locationField,
58
80
  latitudeField: e.latitudeField,
59
81
  longitudeField: e.longitudeField,
60
- titleField: e.titleField || "name",
82
+ titleField: e.titleField,
61
83
  descriptionField: e.descriptionField,
62
84
  zoom: e.zoom,
63
85
  center: e.center,
64
86
  style: t
65
- };
66
- let n = e.map ? e.map : null;
67
- if (n) {
68
- let e = v.safeParse(n);
69
- return e.success || console.warn("[ObjectMap] Invalid map configuration:", e.error.format()), {
70
- ...n,
71
- style: n.style || t
72
- };
73
- }
74
- return {
75
- latitudeField: "latitude",
76
- longitudeField: "longitude",
77
- locationField: "location",
78
- titleField: "name",
79
- descriptionField: "description",
80
- zoom: 10,
81
- center: [0, 0],
82
- style: t
83
- };
87
+ } : { style: t };
88
+ }
89
+ function we(e) {
90
+ return !!e.locationField || !!(e.latitudeField && e.longitudeField);
84
91
  }
85
- function ae(e, t) {
92
+ function Te(e, t) {
86
93
  if (t.latitudeField && t.longitudeField) {
87
94
  let n = e[t.latitudeField], r = e[t.longitudeField];
88
95
  if (typeof n == "number" && typeof r == "number") return [n, r];
@@ -104,7 +111,7 @@ function ae(e, t) {
104
111
  }
105
112
  return null;
106
113
  }
107
- function oe(e, t, n = 50) {
114
+ function Ee(e, t, n = 50) {
108
115
  if (e.length <= 1 || t >= 15) return e.map((e) => ({
109
116
  id: e.id,
110
117
  coordinates: e.coordinates,
@@ -135,102 +142,114 @@ function oe(e, t, n = 50) {
135
142
  }
136
143
  }), a;
137
144
  }
138
- var w = ({ schema: a, dataSource: c, className: u, onMarkerClick: p, onRowClick: v, onEdit: b, onDelete: x, enableClustering: S, clusterRadius: w = 50, ...T }) => {
139
- let [E, D] = i([]), [se, O] = i(!0), [k, A] = i(null), [j, M] = i(null), [N, P] = i(null), [F, ce] = i(""), I = f(), L = r(null), [R, le] = i(null), [z, B] = i(null), [V, H] = i(!1), [U, ue] = i(null), de = e((e) => {
140
- ue(e?.error?.message || "Failed to load the map style/tiles.");
141
- }, []), fe = e(() => {
145
+ var E = ({ schema: a, dataSource: c, className: u, data: d, onMarkerClick: ue, onRowClick: f, onEdit: _, onDelete: v, enableClustering: y, clusterRadius: b = 50 }) => {
146
+ let [x, Se] = i([]), [S, C] = i(!0), [w, T] = i(null), [E, De] = i(null), [D, O] = i(null), [k, Oe] = i(""), ke = pe(), A = r(null), [j, Ae] = i(null), [je, M] = i(null), [Me, N] = i(!1), [Ne, Pe] = i(null), Fe = e((e) => {
147
+ Pe(e?.error?.message || "Failed to load the map style/tiles.");
148
+ }, []), Ie = e(() => {
142
149
  if (typeof navigator > "u" || !navigator.geolocation) {
143
- B("Geolocation is not available in this browser.");
150
+ M("Geolocation is not available in this browser.");
144
151
  return;
145
152
  }
146
- H(!0), B(null), navigator.geolocation.getCurrentPosition((e) => {
153
+ N(!0), M(null), navigator.geolocation.getCurrentPosition((e) => {
147
154
  let { longitude: t, latitude: n } = e.coords;
148
- le({
155
+ Ae({
149
156
  lng: t,
150
157
  lat: n
151
- }), H(!1);
158
+ }), N(!1);
152
159
  try {
153
- L.current?.flyTo({
160
+ A.current?.flyTo({
154
161
  center: [t, n],
155
162
  zoom: 12,
156
163
  duration: 800
157
164
  });
158
165
  } catch {}
159
166
  }, (e) => {
160
- H(!1), B(e.message || "Unable to retrieve location.");
167
+ N(!1), M(e.message || "Unable to retrieve location.");
161
168
  }, {
162
169
  enableHighAccuracy: !1,
163
170
  timeout: 8e3,
164
171
  maximumAge: 6e4
165
172
  });
166
- }, []), W = ie(a), G = n(() => W, [JSON.stringify(W)]), K = C(a), q = G?.provider === "value";
173
+ }, []), P = n(() => xe(a), [a]), F = n(() => Ce(a), [a]), I = P?.provider === "value", L = P?.provider, R = P?.provider === "object" ? P.object : void 0, z = P?.provider === "value" ? P.items : void 0, Le = ie(a, P), { ready: Re, def: B } = de(Le ?? "", I ? void 0 : c), V = me(), H = !Array.isArray(d) && L === "object", U = oe(H ? R : void 0), ze = r(U), Be = r(null), W = r(0), Ve = se(), G = le(a.filter, Ve);
167
174
  t(() => {
168
- (async () => {
175
+ if (H && !Re) {
176
+ C(!0);
177
+ return;
178
+ }
179
+ let e = [
180
+ d,
181
+ L,
182
+ R,
183
+ z,
184
+ c,
185
+ I,
186
+ G,
187
+ a.sort,
188
+ B,
189
+ V
190
+ ], t = Be.current, n = t !== null && t.length === e.length && e.every((e, n) => Object.is(e, t[n])), r = U !== ze.current;
191
+ ze.current = U;
192
+ let i = r && n, ne = ++W.current, l = () => W.current === ne, u = (t, n) => {
193
+ l() && (Se(t), De(n), T(null), Be.current = e);
194
+ };
195
+ return (async () => {
169
196
  try {
170
- if (O(!0), T.data) {
171
- let e = T.data;
172
- if (Array.isArray(e)) {
173
- D(e), O(!1);
174
- return;
175
- }
176
- }
177
- if (a.data) {
178
- let e = a.data;
179
- if (Array.isArray(e)) {
180
- D(e), O(!1);
181
- return;
182
- }
197
+ if (i || C(!0), Array.isArray(d)) {
198
+ u(d, null);
199
+ return;
183
200
  }
184
- if (q && G?.provider === "value") {
185
- D(G.items), O(!1);
201
+ if (I && L === "value") {
202
+ let e = await new o({ items: z ?? [] }).find("", {
203
+ $filter: G,
204
+ $orderby: te(a.sort),
205
+ ...re()
206
+ }), t = s(e);
207
+ u(t.rows, t);
186
208
  return;
187
209
  }
188
210
  if (!c || typeof c.find != "function") throw Error("DataSource required for object/api providers");
189
- if (G?.provider === "object") {
190
- let e = G.object, t = o(j?.fields), n = await c.find(e, {
191
- $filter: a.filter,
192
- $orderby: y(a.sort),
193
- ...t.length > 0 ? { $expand: t } : {}
194
- }), r = s(n);
195
- D(r);
196
- } else G?.provider === "api" && (console.warn("API provider not yet implemented for ObjectMap"), D([]));
197
- O(!1);
211
+ if (L === "object") {
212
+ let e = R, t = ee(B?.fields), n = V?.isLoaded ? t.filter((t) => V.checkField(e, t, "read")) : t, r = await c.find(e, {
213
+ $filter: G,
214
+ $orderby: te(a.sort),
215
+ ...re(),
216
+ ...n.length > 0 ? { $expand: n } : {}
217
+ }), i = s(r);
218
+ u(i.rows, i);
219
+ } else L === "api" && (console.warn("API provider not yet implemented for ObjectMap"), u([], null));
198
220
  } catch (e) {
199
- A(e), O(!1);
221
+ i ? console.error("[ObjectMap] Failed to refresh data:", e) : l() && T(e);
222
+ } finally {
223
+ l() && C(!1);
200
224
  }
201
- })();
225
+ })(), () => {
226
+ W.current += 1;
227
+ };
202
228
  }, [
203
- G,
229
+ d,
230
+ L,
231
+ R,
232
+ z,
204
233
  c,
205
- q,
206
- a.filter,
234
+ I,
235
+ G,
207
236
  a.sort,
208
- j
209
- ]), t(() => {
210
- !q && c && (async () => {
211
- try {
212
- if (!c) return;
213
- let e = G?.provider === "object" ? G.object : a.objectName;
214
- if (!e) return;
215
- let t = await c.getObjectSchema(e);
216
- M(t);
217
- } catch (e) {
218
- console.error("Failed to fetch object schema:", e);
219
- }
220
- })();
221
- }, [
222
- a.objectName,
223
- c,
224
- q,
225
- G
237
+ Re,
238
+ B,
239
+ V,
240
+ U,
241
+ H
226
242
  ]);
227
- let { markers: J, invalidCount: Y } = n(() => {
243
+ let { markers: K, invalidCount: q } = n(() => {
228
244
  let e = 0;
229
245
  return {
230
- markers: E.map((t, n) => {
231
- let r = ae(t, K);
246
+ markers: x.map((t, n) => {
247
+ let r = Te(t, F);
232
248
  if (!r) return e++, null;
233
- let i = K.titleField ? t[K.titleField] : "Marker", a = K.descriptionField ? t[K.descriptionField] : void 0, [o, s] = r;
249
+ let i = ne(B, t, {
250
+ titleField: F.titleField,
251
+ fallback: "Marker"
252
+ }), a = l(t, F.descriptionField), [o, s] = r;
234
253
  return !isFinite(o) || !isFinite(s) || o < -90 || o > 90 || s < -180 || s > 180 ? (e++, null) : {
235
254
  id: t.id || t._id || `marker-${n}`,
236
255
  title: i,
@@ -241,146 +260,167 @@ var w = ({ schema: a, dataSource: c, className: u, onMarkerClick: p, onRowClick:
241
260
  }).filter((e) => e !== null),
242
261
  invalidCount: e
243
262
  };
244
- }, [E, K]), X = n(() => J.find((e) => e.id === N), [J, N]), [Z, pe] = i(K.zoom || 3), Q = l({
263
+ }, [
264
+ x,
265
+ F,
266
+ B
267
+ ]), J = n(() => K.find((e) => e.id === D), [K, D]), [Y, He] = i(F.zoom || 3), Ue = e((e) => {
268
+ try {
269
+ let t = e?.target?.getZoom?.();
270
+ typeof t == "number" && Number.isFinite(t) && He(Math.round(t));
271
+ } catch {}
272
+ }, []), X = ce({
245
273
  navigation: a.navigation,
246
274
  objectName: a.objectName,
247
- onRowClick: v
248
- }), $ = n(() => {
249
- if (!F.trim()) return J;
250
- let e = F.toLowerCase();
251
- return J.filter((t) => t.title?.toLowerCase().includes(e) || t.description?.toLowerCase().includes(e));
252
- }, [J, F]), me = n(() => S ?? (a.enableClustering || $.length > 100) ? oe($, Z, w) : $.map((e) => ({
275
+ onRowClick: f
276
+ }), Z = n(() => {
277
+ if (!k.trim()) return K;
278
+ let e = k.toLowerCase();
279
+ return K.filter((t) => t.title?.toLowerCase().includes(e) || t.description?.toLowerCase().includes(e));
280
+ }, [K, k]), We = n(() => y ?? (a.enableClustering || Z.length > 100) ? Ee(Z, Y, b) : Z.map((e) => ({
253
281
  id: e.id,
254
282
  coordinates: e.coordinates,
255
283
  markers: [e],
256
284
  isCluster: !1
257
285
  })), [
258
- $,
259
286
  Z,
260
- S,
261
- w,
287
+ Y,
288
+ y,
289
+ b,
262
290
  a
263
- ]), he = n(() => {
264
- if (!$.length) return {
265
- longitude: K.center?.[1] || 0,
266
- latitude: K.center?.[0] || 0,
267
- zoom: K.zoom || 2
291
+ ]), Q = n(() => ve(Z.map((e) => e.coordinates)), [Z]), $ = typeof F.center?.[0] == "number" ? F.center[0] : void 0, Ge = typeof F.center?.[1] == "number" ? F.center[1] : void 0, Ke = typeof F.zoom == "number" ? F.zoom : void 0, qe = $ !== void 0 || Ge !== void 0 || Ke !== void 0, Je = n(() => {
292
+ if (Q && !qe) return {
293
+ bounds: Q,
294
+ fitBoundsOptions: {
295
+ padding: 48,
296
+ maxZoom: 12
297
+ }
298
+ };
299
+ let e = Q ? ye(Q) : {
300
+ longitude: 0,
301
+ latitude: 0
268
302
  };
269
- let e = $.map((e) => e.coordinates[0]), t = $.map((e) => e.coordinates[1]), n = Math.min(...e), r = Math.max(...e), i = Math.min(...t), a = Math.max(...t);
270
303
  return {
271
- longitude: (n + r) / 2,
272
- latitude: (i + a) / 2,
273
- zoom: K.zoom || 3
304
+ longitude: Ge ?? e.longitude,
305
+ latitude: $ ?? e.latitude,
306
+ zoom: Ke ?? (Q ? 3 : 2)
274
307
  };
275
- }, [$, K]);
276
- return se ? /* @__PURE__ */ g("div", {
277
- className: d("min-w-0 overflow-hidden", u),
278
- children: /* @__PURE__ */ g("div", {
308
+ }, [
309
+ Q,
310
+ qe,
311
+ Ge,
312
+ $,
313
+ Ke
314
+ ]);
315
+ return we(F) ? S ? /* @__PURE__ */ h("div", {
316
+ className: p("min-w-0 overflow-hidden", u),
317
+ children: /* @__PURE__ */ h("div", {
279
318
  className: "flex items-center justify-center h-96 bg-muted rounded-lg border",
280
- children: /* @__PURE__ */ g("div", {
319
+ children: /* @__PURE__ */ h("div", {
281
320
  className: "text-muted-foreground",
282
321
  children: "Loading map..."
283
322
  })
284
323
  })
285
- }) : k ? /* @__PURE__ */ g("div", {
286
- className: d("min-w-0 overflow-hidden", u),
287
- children: /* @__PURE__ */ g("div", {
324
+ }) : w ? /* @__PURE__ */ h("div", {
325
+ className: p("min-w-0 overflow-hidden", u),
326
+ children: /* @__PURE__ */ h("div", {
288
327
  className: "flex items-center justify-center h-96 bg-muted rounded-lg border",
289
- children: /* @__PURE__ */ _("div", {
328
+ children: /* @__PURE__ */ g("div", {
290
329
  className: "text-destructive",
291
- children: ["Error: ", k.message]
330
+ children: ["Error: ", w.message]
292
331
  })
293
332
  })
294
- }) : /* @__PURE__ */ _("div", {
295
- className: d("min-w-0 overflow-hidden", u),
333
+ }) : /* @__PURE__ */ g("div", {
334
+ className: p("min-w-0 overflow-hidden", u),
296
335
  children: [
297
- Y > 0 && /* @__PURE__ */ g("div", {
336
+ q > 0 && /* @__PURE__ */ h("div", {
298
337
  className: "mb-2 p-2 text-sm text-yellow-800 bg-yellow-50 border border-yellow-200 rounded",
299
- children: `${Y} record${Y === 1 ? "" : "s"} with missing or invalid coordinates excluded from the map.`
338
+ children: `${q} record${q === 1 ? "" : "s"} with missing or invalid coordinates excluded from the map.`
300
339
  }),
301
- J.length > 0 && /* @__PURE__ */ g("div", {
340
+ K.length > 0 && /* @__PURE__ */ h("div", {
302
341
  className: "mb-2",
303
- children: /* @__PURE__ */ g("input", {
342
+ children: /* @__PURE__ */ h("input", {
304
343
  type: "text",
305
- value: F,
306
- onChange: (e) => ce(e.target.value),
344
+ value: k,
345
+ onChange: (e) => Oe(e.target.value),
307
346
  placeholder: "Search locations…",
308
347
  className: "w-full px-3 py-2 text-sm border rounded-md bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
309
348
  })
310
349
  }),
311
- /* @__PURE__ */ _("div", {
350
+ /* @__PURE__ */ g("div", {
312
351
  className: "relative border rounded-lg overflow-hidden bg-muted h-[300px] sm:h-[400px] md:h-[500px] lg:h-[600px] w-full",
313
352
  children: [
314
- /* @__PURE__ */ _(te, {
353
+ /* @__PURE__ */ g(he, {
315
354
  ref: (e) => {
316
- L.current = e;
355
+ A.current = e;
317
356
  },
318
- initialViewState: he,
357
+ initialViewState: Je,
319
358
  style: {
320
359
  width: "100%",
321
360
  height: "100%"
322
361
  },
323
- mapStyle: K.style || re,
362
+ mapStyle: F.style || be,
324
363
  touchZoomRotate: !0,
325
364
  dragRotate: !0,
326
365
  touchPitch: !0,
327
- onZoom: (e) => pe(Math.round(e.viewState.zoom)),
328
- onError: de,
366
+ onLoad: Ue,
367
+ onZoom: (e) => He(Math.round(e.viewState.zoom)),
368
+ onError: Fe,
329
369
  children: [
330
- U && /* @__PURE__ */ _("div", {
370
+ Ne && /* @__PURE__ */ g("div", {
331
371
  role: "alert",
332
372
  className: "absolute inset-x-0 top-0 z-20 p-2 text-sm text-center text-amber-900 bg-amber-50 border-b border-amber-200",
333
373
  children: [
334
374
  "Map failed to load (",
335
- U,
375
+ Ne,
336
376
  "). Markers below are still listed in the search box;",
337
377
  " ",
338
- K.style ? "check the configured map style URL." : "configure a custom map style for production use."
378
+ F.style ? "check the configured map style URL." : "configure a custom map style for production use."
339
379
  ]
340
380
  }),
341
- /* @__PURE__ */ g(h, {
381
+ /* @__PURE__ */ h(ge, {
342
382
  position: "top-right",
343
383
  showCompass: !0,
344
384
  showZoom: !0
345
385
  }),
346
- /* @__PURE__ */ g("button", {
386
+ /* @__PURE__ */ h("button", {
347
387
  type: "button",
348
- onClick: fe,
349
- disabled: V,
388
+ onClick: Ie,
389
+ disabled: Me,
350
390
  className: "absolute top-2 left-2 z-10 inline-flex items-center justify-center size-9 rounded-md bg-background/95 border shadow-sm hover:bg-background disabled:opacity-50",
351
391
  "aria-label": "Show my location",
352
392
  "data-testid": "map-geolocate",
353
- children: /* @__PURE__ */ g("span", {
393
+ children: /* @__PURE__ */ h("span", {
354
394
  "aria-hidden": "true",
355
- children: V ? "⏳" : "🧭"
395
+ children: Me ? "⏳" : "🧭"
356
396
  })
357
397
  }),
358
- R && /* @__PURE__ */ g(m, {
359
- longitude: R.lng,
360
- latitude: R.lat,
398
+ j && /* @__PURE__ */ h(m, {
399
+ longitude: j.lng,
400
+ latitude: j.lat,
361
401
  anchor: "center",
362
- children: /* @__PURE__ */ g("div", {
402
+ children: /* @__PURE__ */ h("div", {
363
403
  className: "size-3 rounded-full bg-blue-500 ring-4 ring-blue-500/30 shadow",
364
404
  "aria-label": "Your location",
365
405
  "data-testid": "map-user-location"
366
406
  })
367
407
  }),
368
- me.map((e) => e.isCluster ? /* @__PURE__ */ g(m, {
408
+ We.map((e) => e.isCluster ? /* @__PURE__ */ h(m, {
369
409
  longitude: e.coordinates[0],
370
410
  latitude: e.coordinates[1],
371
411
  anchor: "center",
372
412
  onClick: (t) => {
373
413
  t.originalEvent.stopPropagation();
374
414
  try {
375
- let t = Math.min(20, Math.max(Z + 2, 8));
376
- L.current?.flyTo({
415
+ let t = Math.min(20, Math.max(Y + 2, 8));
416
+ A.current?.flyTo({
377
417
  center: [e.coordinates[0], e.coordinates[1]],
378
418
  zoom: t,
379
419
  duration: 600
380
420
  });
381
421
  } catch {}
382
422
  },
383
- children: /* @__PURE__ */ g("div", {
423
+ children: /* @__PURE__ */ h("div", {
384
424
  className: "flex items-center justify-center rounded-full bg-primary text-primary-foreground font-bold text-xs cursor-pointer hover:scale-110 transition-transform shadow-md",
385
425
  style: {
386
426
  width: Math.min(48, 24 + e.markers.length * 2),
@@ -390,48 +430,48 @@ var w = ({ schema: a, dataSource: c, className: u, onMarkerClick: p, onRowClick:
390
430
  "data-testid": "map-cluster",
391
431
  children: e.markers.length
392
432
  })
393
- }, e.id) : /* @__PURE__ */ g(m, {
433
+ }, e.id) : /* @__PURE__ */ h(m, {
394
434
  longitude: e.coordinates[0],
395
435
  latitude: e.coordinates[1],
396
436
  anchor: "bottom",
397
437
  onClick: (t) => {
398
438
  t.originalEvent.stopPropagation();
399
439
  let n = e.markers[0];
400
- P(n.id), Q.handleClick(n.data), p?.(n.data);
440
+ O(n.id), X.handleClick(n.data), ue?.(n.data);
401
441
  },
402
- children: /* @__PURE__ */ g("div", {
442
+ children: /* @__PURE__ */ h("div", {
403
443
  className: "text-2xl cursor-pointer hover:scale-110 transition-transform",
404
444
  children: "📍"
405
445
  })
406
446
  }, e.id)),
407
- X && !I && /* @__PURE__ */ g(ne, {
408
- longitude: X.coordinates[0],
409
- latitude: X.coordinates[1],
447
+ J && !ke && /* @__PURE__ */ h(_e, {
448
+ longitude: J.coordinates[0],
449
+ latitude: J.coordinates[1],
410
450
  anchor: "top",
411
- onClose: () => P(null),
451
+ onClose: () => O(null),
412
452
  closeOnClick: !1,
413
- children: /* @__PURE__ */ _("div", {
453
+ children: /* @__PURE__ */ g("div", {
414
454
  className: "p-2 min-w-[150px] sm:min-w-[200px]",
415
455
  children: [
416
- /* @__PURE__ */ g("h3", {
456
+ /* @__PURE__ */ h("h3", {
417
457
  className: "font-bold text-sm mb-1",
418
- children: X.title
458
+ children: J.title
419
459
  }),
420
- X.description && /* @__PURE__ */ g("p", {
460
+ J.description && /* @__PURE__ */ h("p", {
421
461
  className: "text-xs text-muted-foreground",
422
- children: X.description
462
+ children: J.description
423
463
  }),
424
- /* @__PURE__ */ _("div", {
464
+ /* @__PURE__ */ g("div", {
425
465
  className: "mt-2 text-xs flex gap-2",
426
- children: [b && /* @__PURE__ */ g("button", {
466
+ children: [_ && /* @__PURE__ */ h("button", {
427
467
  type: "button",
428
468
  className: "text-blue-500 hover:underline",
429
- onClick: () => b(X.data),
469
+ onClick: () => _(J.data),
430
470
  children: "Edit"
431
- }), x && /* @__PURE__ */ g("button", {
471
+ }), v && /* @__PURE__ */ h("button", {
432
472
  type: "button",
433
473
  className: "text-red-500 hover:underline",
434
- onClick: () => x(X.data),
474
+ onClick: () => v(J.data),
435
475
  children: "Delete"
436
476
  })]
437
477
  })
@@ -440,69 +480,70 @@ var w = ({ schema: a, dataSource: c, className: u, onMarkerClick: p, onRowClick:
440
480
  })
441
481
  ]
442
482
  }),
443
- X && I && /* @__PURE__ */ _("div", {
483
+ J && ke && /* @__PURE__ */ g("div", {
444
484
  className: "absolute inset-x-0 bottom-0 z-30 bg-background border-t shadow-lg rounded-t-2xl p-4 pb-[max(env(safe-area-inset-bottom),1rem)]",
445
485
  role: "dialog",
446
486
  "aria-label": "Location details",
447
487
  "data-testid": "map-mobile-record-sheet",
448
488
  children: [
449
- /* @__PURE__ */ g("div", {
489
+ /* @__PURE__ */ h("div", {
450
490
  className: "mx-auto mb-2 h-1 w-10 rounded-full bg-muted",
451
491
  "aria-hidden": "true"
452
492
  }),
453
- /* @__PURE__ */ _("div", {
493
+ /* @__PURE__ */ g("div", {
454
494
  className: "flex items-start justify-between gap-3",
455
- children: [/* @__PURE__ */ _("div", {
495
+ children: [/* @__PURE__ */ g("div", {
456
496
  className: "min-w-0 flex-1",
457
- children: [/* @__PURE__ */ g("h3", {
497
+ children: [/* @__PURE__ */ h("h3", {
458
498
  className: "text-sm font-semibold truncate",
459
- children: X.title
460
- }), X.description && /* @__PURE__ */ g("p", {
499
+ children: J.title
500
+ }), J.description && /* @__PURE__ */ h("p", {
461
501
  className: "mt-1 text-xs text-muted-foreground line-clamp-3",
462
- children: X.description
502
+ children: J.description
463
503
  })]
464
- }), /* @__PURE__ */ g("button", {
504
+ }), /* @__PURE__ */ h("button", {
465
505
  type: "button",
466
- onClick: () => P(null),
506
+ onClick: () => O(null),
467
507
  className: "text-muted-foreground hover:text-foreground text-lg leading-none px-2",
468
508
  "aria-label": "Close details",
469
509
  "data-testid": "map-mobile-record-close",
470
510
  children: "×"
471
511
  })]
472
512
  }),
473
- /* @__PURE__ */ _("div", {
513
+ /* @__PURE__ */ g("div", {
474
514
  className: "mt-3 flex flex-wrap gap-2 text-xs",
475
- children: [b && /* @__PURE__ */ g("button", {
515
+ children: [_ && /* @__PURE__ */ h("button", {
476
516
  type: "button",
477
517
  className: "px-3 py-1.5 rounded-md border bg-card hover:bg-accent",
478
- onClick: () => b(X.data),
518
+ onClick: () => _(J.data),
479
519
  children: "Edit"
480
- }), x && /* @__PURE__ */ g("button", {
520
+ }), v && /* @__PURE__ */ h("button", {
481
521
  type: "button",
482
522
  className: "px-3 py-1.5 rounded-md border border-destructive/30 text-destructive hover:bg-destructive/10",
483
- onClick: () => x(X.data),
523
+ onClick: () => v(J.data),
484
524
  children: "Delete"
485
525
  })]
486
526
  })
487
527
  ]
488
528
  }),
489
- z && /* @__PURE__ */ g("div", {
529
+ je && /* @__PURE__ */ h("div", {
490
530
  className: "absolute top-2 left-14 right-2 z-10 text-xs px-3 py-1.5 rounded-md bg-destructive/10 text-destructive border border-destructive/30",
491
- children: z
531
+ children: je
492
532
  })
493
533
  ]
494
534
  }),
495
- Q.isOverlay && /* @__PURE__ */ g(ee, {
496
- ...Q,
535
+ E && /* @__PURE__ */ h(ae, { result: E }),
536
+ X.isOverlay && /* @__PURE__ */ h(fe, {
537
+ ...X,
497
538
  title: "Location Details",
498
- children: (e) => /* @__PURE__ */ g("div", {
539
+ children: (e) => /* @__PURE__ */ h("div", {
499
540
  className: "space-y-3",
500
- children: Object.entries(e).map(([e, t]) => /* @__PURE__ */ _("div", {
541
+ children: Object.entries(e).map(([e, t]) => /* @__PURE__ */ g("div", {
501
542
  className: "flex flex-col",
502
- children: [/* @__PURE__ */ g("span", {
543
+ children: [/* @__PURE__ */ h("span", {
503
544
  className: "text-xs font-medium text-muted-foreground uppercase tracking-wide",
504
545
  children: e.replace(/_/g, " ")
505
- }), /* @__PURE__ */ g("span", {
546
+ }), /* @__PURE__ */ h("span", {
506
547
  className: "text-sm",
507
548
  children: String(t ?? "—")
508
549
  })]
@@ -510,55 +551,105 @@ var w = ({ schema: a, dataSource: c, className: u, onMarkerClick: p, onRowClick:
510
551
  })
511
552
  })
512
553
  ]
554
+ }) : /* @__PURE__ */ h("div", {
555
+ className: p("min-w-0 overflow-hidden", u),
556
+ children: /* @__PURE__ */ h("div", {
557
+ className: "flex items-center justify-center h-96 bg-muted rounded-lg border p-4 text-center",
558
+ "data-testid": "map-missing-location-binding",
559
+ role: "alert",
560
+ children: /* @__PURE__ */ g("div", {
561
+ className: "text-destructive",
562
+ children: [
563
+ "Map configuration required — declare ",
564
+ /* @__PURE__ */ h("code", {
565
+ className: "font-mono",
566
+ children: "map.locationField"
567
+ }),
568
+ " ",
569
+ "or ",
570
+ /* @__PURE__ */ h("code", {
571
+ className: "font-mono",
572
+ children: "map.latitudeField"
573
+ }),
574
+ " +",
575
+ " ",
576
+ /* @__PURE__ */ h("code", {
577
+ className: "font-mono",
578
+ children: "map.longitudeField"
579
+ })
580
+ ]
581
+ })
582
+ })
513
583
  });
514
- }, T = {
584
+ }, De = {
515
585
  filter: !0,
516
586
  sort: !0
517
- }, E = ({ schema: e, ...t }) => {
518
- let { dataSource: n } = u() || {};
519
- return /* @__PURE__ */ g(c, {
587
+ }, D = c(({ schema: e, ...t }) => {
588
+ let { dataSource: n } = ue() || {};
589
+ return /* @__PURE__ */ h(d, {
520
590
  schema: e,
521
- mapping: T,
591
+ mapping: De,
522
592
  dataSource: n,
523
593
  testId: "object-map",
524
594
  errorTitle: "This map’s data source could not be resolved",
525
- children: (e) => /* @__PURE__ */ g(w, {
595
+ children: (e) => /* @__PURE__ */ h(E, {
526
596
  schema: e,
527
597
  dataSource: n,
528
598
  ...t
529
599
  })
530
600
  });
531
- };
532
- a.register("object-map", E, {
601
+ });
602
+ a.register("object-map", D, {
533
603
  namespace: "plugin-map",
534
604
  label: "Object Map",
535
605
  category: "view",
536
- inputs: [{
537
- name: "objectName",
538
- type: "string",
539
- label: "Object Name",
540
- required: !0
541
- }, {
542
- name: "map",
543
- type: "object",
544
- label: "Map Config",
545
- description: "latitudeField, longitudeField, titleField"
546
- }]
547
- }), a.register("map", E, {
548
- namespace: "view",
549
- label: "Map View",
550
- category: "view",
551
- inputs: [{
552
- name: "objectName",
553
- type: "string",
554
- label: "Object Name",
555
- required: !0
556
- }, {
557
- name: "map",
558
- type: "object",
559
- label: "Map Config",
560
- description: "latitudeField, longitudeField, titleField"
561
- }]
606
+ inputs: [
607
+ {
608
+ name: "objectName",
609
+ type: "string",
610
+ description: "ObjectQL object name. The record source is one of `data`, `staticData` and `objectName`, or the node’s `dataSource` binding; the `object-map` schema refuses a block that declares none of them."
611
+ },
612
+ {
613
+ name: "map",
614
+ type: "object",
615
+ description: "The field configuration: `latitudeField` / `longitudeField` (or one `locationField` holding a `\"lat,lng\"` string, a `{ lat, lng }` object or a `[lat, lng]` pair) place each record’s marker, and a record with no readable coordinates is not plotted; `titleField` and `descriptionField` name what the marker popup shows; `zoom` and `center` (`[latitude, longitude]`) set the initial camera, which otherwise fits the markers; `style` is a MapLibre style URL, read after the node’s `mapStyle`."
616
+ },
617
+ {
618
+ name: "data",
619
+ type: "object",
620
+ description: "A `{ provider, … }` data-source configuration, read FIRST on the record-source ladder: a map carrying one never reaches `staticData` and never queries `objectName`. `{ provider: 'value', items }` plots those rows and `{ provider: 'object', object }` queries that object, both narrowed by `filter` and ordered by `sort`. The `api` provider is not implemented on the map and plots no markers. A bare array is not this key’s shape and is not a record source: the map falls through to `staticData`, then `objectName`, so inline rows belong under `staticData`."
621
+ },
622
+ {
623
+ name: "staticData",
624
+ type: "array",
625
+ description: "Inline records, read SECOND on the record-source ladder: a `data` configuration wins and this key is then never reached, while `objectName` is read AFTER it, so a map carrying both plots these rows and never queries that object. `filter` and `sort` narrow and order these rows exactly as they do fetched ones."
626
+ },
627
+ {
628
+ name: "filter",
629
+ type: "array",
630
+ description: "Base query filter in the rule-array form `[{ field, operator, value }, ...]`, narrowing the markers the map plots — fetched rows and inline (`staticData`, `{ provider: 'value' }`) rows alike. Context tokens such as `{current_user_id}` are resolved first, then the filter is lowered to `$filter` on the query. The MongoDB-style record form is not this key’s shape."
631
+ },
632
+ {
633
+ name: "sort",
634
+ type: "array",
635
+ description: "Marker order in `[{ field, order }]` form, ordering the rows the map plots. Lowered to `$orderby` on the same query."
636
+ },
637
+ {
638
+ name: "mapStyle",
639
+ type: "string",
640
+ description: "MapLibre style URL or spec, overriding the public demo tiles. Read before `map.style`; NOT the base node `style`, which is an inline CSS record"
641
+ },
642
+ {
643
+ name: "navigation",
644
+ type: "object",
645
+ description: "What a marker click opens — the `{ mode, size, openNewTab, preventNavigation }` block a list view declares. With the key ABSENT a click opens nothing: this renderer supplies no drawer default. `mode` is an overlay (`drawer`, `modal`, `split`, `popover`), `new_window`, `page` or `none`, and a block written without `mode` takes the spec’s `page` default. `drawer`, `modal` and `popover` open the marker’s record in that overlay, but `split` opens nothing, because the map hands the split shell no main panel; `new_window` opens the record page in a new tab; `none` opens nothing. `page` opens the record page of the map’s `objectName` through the record navigator the host publishes (the console publishes one on its custom pages, record pages and list views); under a host that publishes none, such as an embedded renderer, or on a map that names no `objectName`, there is no record page to open and the click opens nothing. `preventNavigation: true` opens nothing whatever the mode, `openNewTab: true` opens the record page in a new tab and outranks every mode except `none`, and `size` sets the overlay width. A click handler from a parent view outranks the whole key."
646
+ },
647
+ {
648
+ name: "enableClustering",
649
+ type: "boolean",
650
+ description: "Group nearby markers into one numbered cluster. Absent, the map clusters only above 100 markers; `false` turns clustering off at any count."
651
+ }
652
+ ]
562
653
  });
563
654
  //#endregion
564
- export { w as ObjectMap, E as ObjectMapRenderer };
655
+ export { E as ObjectMap, D as ObjectMapRenderer };