@object-ui/plugin-map 17.6.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,54 +1,42 @@
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, convertSortToQueryParams as s, extractRecords as c, getRecordDisplayName as ee } from "@object-ui/core";
3
- import { ElementDataSourceGate as l, useNavigationOverlay as te, useSchemaContext as u } from "@object-ui/react";
4
- import { ObjectMapConfigSchema as d } from "@object-ui/types/zod";
5
- import { NavigationOverlay as ne, cn as f, useIsMobile as re } from "@object-ui/components";
6
- import ie, { Marker as p, NavigationControl as ae, Popup as oe } 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 m, jsxs as h } from "react/jsx-runtime";
9
- function g(e) {
9
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
10
+ function _(e) {
10
11
  return e >= -180 && e < 180 ? e : ((e + 180) % 360 + 360) % 360 - 180;
11
12
  }
12
- var _ = (e) => Math.min(85, Math.max(-85, e));
13
- function se(e) {
13
+ var v = (e) => Math.min(85, Math.max(-85, e));
14
+ function ve(e) {
14
15
  if (e.length === 0) return null;
15
- let t = e.map(([e]) => g(e)).sort((e, t) => e - t), n = e.map(([, e]) => _(e)), r = t.length, i = t[0] + 360 - t[r - 1], a = 0;
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;
16
17
  for (let e = 1; e < r; e++) {
17
18
  let n = t[e] - t[e - 1];
18
19
  n > i && (i = n, a = e);
19
20
  }
20
- let o = t[a], s = t[(a + r - 1) % r], c = a === 0 ? s : s + 360;
21
- return [[o, Math.min(...n)], [c, Math.max(...n)]];
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)]];
22
23
  }
23
- function ce(e) {
24
+ function ye(e) {
24
25
  let [[t, n], [r, i]] = e;
25
26
  return {
26
- longitude: g((t + r) / 2),
27
+ longitude: _((t + r) / 2),
27
28
  latitude: (n + i) / 2
28
29
  };
29
30
  }
30
31
  //#endregion
31
32
  //#region src/ObjectMap.tsx
32
- var le = "https://demotiles.maplibre.org/style.json", v = Object.keys(d.shape).filter((e) => e !== "style");
33
- function ue(e) {
34
- if (e.data) {
35
- let t = e.data;
36
- return Array.isArray(t) ? {
37
- provider: "value",
38
- items: t
39
- } : e.data;
40
- }
41
- return e.staticData ? {
42
- provider: "value",
43
- items: e.staticData
44
- } : e.objectName ? {
45
- provider: "object",
46
- object: e.objectName
47
- } : null;
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");
48
36
  }
49
- var y = () => globalThis.process?.env?.NODE_ENV !== "production", b = /* @__PURE__ */ new Set();
50
- function x(e) {
51
- if (!y()) return;
37
+ var b = () => globalThis.process?.env?.NODE_ENV !== "production", x = /* @__PURE__ */ new Set();
38
+ function Se(e) {
39
+ if (!b()) return;
52
40
  let t = e.filter;
53
41
  if (!t || typeof t != "object" || Array.isArray(t) || !Object.prototype.hasOwnProperty.call(t, "map")) return;
54
42
  let n = t.map;
@@ -59,32 +47,32 @@ function x(e) {
59
47
  } catch {
60
48
  r = `${e.type ?? "map"}::${e.objectName ?? ""}::<unserializable>`;
61
49
  }
62
- b.has(r) || (b.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."));
63
51
  }
64
52
  var S = /* @__PURE__ */ new Set();
65
53
  function C(e) {
66
- if (!y()) return;
54
+ if (!b()) return;
67
55
  let t = e.style;
68
56
  if (typeof t != "string" || t === "") return;
69
57
  let n = `${e.type ?? "map"}::${e.objectName ?? ""}::${t}`;
70
- 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, note that \`options.map\` is FLATTENED into the top level, so its \`style\` lands here as this same top-level key — spell it \`map: { mapStyle }\` there. objectui#5017.`));
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.`));
71
59
  }
72
60
  var w = /* @__PURE__ */ new Set();
73
61
  function T(e) {
74
- if (!y()) return;
75
- let t = v.filter((t) => e[t] !== void 0);
62
+ if (!b()) return;
63
+ let t = y.filter((t) => e[t] !== void 0);
76
64
  if (t.length === 0) return;
77
65
  let n = `${e.type ?? "map"}::${e.objectName ?? ""}::${t.join(",")}`;
78
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.`));
79
67
  }
80
- function de(e) {
81
- x(e), C(e);
68
+ function Ce(e) {
69
+ Se(e), C(e);
82
70
  let t = e.mapStyle || e.map?.style, n = e.map ?? null;
83
71
  if (n) {
84
- let r = d.safeParse(n);
72
+ let r = f.safeParse(n);
85
73
  return r.success || console.warn("[ObjectMap] Invalid map configuration:", r.error.format()), T(e), {
86
74
  ...n,
87
- style: n.style || t
75
+ style: t
88
76
  };
89
77
  }
90
78
  return e.locationField || e.latitudeField ? {
@@ -96,15 +84,12 @@ function de(e) {
96
84
  zoom: e.zoom,
97
85
  center: e.center,
98
86
  style: t
99
- } : {
100
- latitudeField: "latitude",
101
- longitudeField: "longitude",
102
- locationField: "location",
103
- descriptionField: "description",
104
- style: t
105
- };
87
+ } : { style: t };
106
88
  }
107
- function fe(e, t) {
89
+ function we(e) {
90
+ return !!e.locationField || !!(e.latitudeField && e.longitudeField);
91
+ }
92
+ function Te(e, t) {
108
93
  if (t.latitudeField && t.longitudeField) {
109
94
  let n = e[t.latitudeField], r = e[t.longitudeField];
110
95
  if (typeof n == "number" && typeof r == "number") return [n, r];
@@ -126,7 +111,7 @@ function fe(e, t) {
126
111
  }
127
112
  return null;
128
113
  }
129
- function pe(e, t, n = 50) {
114
+ function Ee(e, t, n = 50) {
130
115
  if (e.length <= 1 || t >= 15) return e.map((e) => ({
131
116
  id: e.id,
132
117
  coordinates: e.coordinates,
@@ -157,96 +142,114 @@ function pe(e, t, n = 50) {
157
142
  }
158
143
  }), a;
159
144
  }
160
- var E = ({ schema: a, dataSource: l, className: u, data: d, onMarkerClick: g, onRowClick: _, onEdit: v, onDelete: y, enableClustering: b, clusterRadius: x = 50 }) => {
161
- let [S, C] = i([]), [w, T] = i(!0), [E, D] = i(null), [O, me] = i(null), [he, k] = i(null), [A, ge] = i(""), j = re(), M = r(null), [N, _e] = i(null), [P, F] = i(null), [I, L] = i(!1), [R, ve] = i(null), ye = e((e) => {
162
- ve(e?.error?.message || "Failed to load the map style/tiles.");
163
- }, []), be = 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(() => {
164
149
  if (typeof navigator > "u" || !navigator.geolocation) {
165
- F("Geolocation is not available in this browser.");
150
+ M("Geolocation is not available in this browser.");
166
151
  return;
167
152
  }
168
- L(!0), F(null), navigator.geolocation.getCurrentPosition((e) => {
153
+ N(!0), M(null), navigator.geolocation.getCurrentPosition((e) => {
169
154
  let { longitude: t, latitude: n } = e.coords;
170
- _e({
155
+ Ae({
171
156
  lng: t,
172
157
  lat: n
173
- }), L(!1);
158
+ }), N(!1);
174
159
  try {
175
- M.current?.flyTo({
160
+ A.current?.flyTo({
176
161
  center: [t, n],
177
162
  zoom: 12,
178
163
  duration: 800
179
164
  });
180
165
  } catch {}
181
166
  }, (e) => {
182
- L(!1), F(e.message || "Unable to retrieve location.");
167
+ N(!1), M(e.message || "Unable to retrieve location.");
183
168
  }, {
184
169
  enableHighAccuracy: !1,
185
170
  timeout: 8e3,
186
171
  maximumAge: 6e4
187
172
  });
188
- }, []), z = ue(a), B = n(() => z, [JSON.stringify(z)]), V = n(() => de(a), [a]), H = B?.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);
189
174
  t(() => {
190
- (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 () => {
191
196
  try {
192
- if (T(!0), Array.isArray(d)) {
193
- C(d), T(!1);
197
+ if (i || C(!0), Array.isArray(d)) {
198
+ u(d, null);
194
199
  return;
195
200
  }
196
- if (H && B?.provider === "value") {
197
- C(B.items), T(!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);
198
208
  return;
199
209
  }
200
- if (!l || typeof l.find != "function") throw Error("DataSource required for object/api providers");
201
- if (B?.provider === "object") {
202
- let e = B.object, t = o(O?.fields), n = await l.find(e, {
203
- $filter: a.filter,
204
- $orderby: s(a.sort),
205
- ...t.length > 0 ? { $expand: t } : {}
206
- }), r = c(n);
207
- C(r);
208
- } else B?.provider === "api" && (console.warn("API provider not yet implemented for ObjectMap"), C([]));
209
- T(!1);
210
+ if (!c || typeof c.find != "function") throw Error("DataSource required for object/api providers");
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));
210
220
  } catch (e) {
211
- D(e), T(!1);
221
+ i ? console.error("[ObjectMap] Failed to refresh data:", e) : l() && T(e);
222
+ } finally {
223
+ l() && C(!1);
212
224
  }
213
- })();
225
+ })(), () => {
226
+ W.current += 1;
227
+ };
214
228
  }, [
215
229
  d,
216
- B,
217
- l,
218
- H,
219
- a.filter,
230
+ L,
231
+ R,
232
+ z,
233
+ c,
234
+ I,
235
+ G,
220
236
  a.sort,
221
- O
222
- ]), t(() => {
223
- !H && l && (async () => {
224
- try {
225
- if (!l) return;
226
- let e = B?.provider === "object" ? B.object : a.objectName;
227
- if (!e) return;
228
- let t = await l.getObjectSchema(e);
229
- me(t);
230
- } catch (e) {
231
- console.error("Failed to fetch object schema:", e);
232
- }
233
- })();
234
- }, [
235
- a.objectName,
236
- l,
237
- H,
238
- B
237
+ Re,
238
+ B,
239
+ V,
240
+ U,
241
+ H
239
242
  ]);
240
- let { markers: U, invalidCount: W } = n(() => {
243
+ let { markers: K, invalidCount: q } = n(() => {
241
244
  let e = 0;
242
245
  return {
243
- markers: S.map((t, n) => {
244
- let r = fe(t, V);
246
+ markers: x.map((t, n) => {
247
+ let r = Te(t, F);
245
248
  if (!r) return e++, null;
246
- let i = ee(O, t, {
247
- titleField: V.titleField,
249
+ let i = ne(B, t, {
250
+ titleField: F.titleField,
248
251
  fallback: "Marker"
249
- }), a = V.descriptionField ? t[V.descriptionField] : void 0, [o, s] = r;
252
+ }), a = l(t, F.descriptionField), [o, s] = r;
250
253
  return !isFinite(o) || !isFinite(s) || o < -90 || o > 90 || s < -180 || s > 180 ? (e++, null) : {
251
254
  id: t.id || t._id || `marker-${n}`,
252
255
  title: i,
@@ -258,166 +261,166 @@ var E = ({ schema: a, dataSource: l, className: u, data: d, onMarkerClick: g, on
258
261
  invalidCount: e
259
262
  };
260
263
  }, [
261
- S,
262
- V,
263
- O
264
- ]), G = n(() => U.find((e) => e.id === he), [U, he]), [K, xe] = i(V.zoom || 3), Se = e((e) => {
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) => {
265
268
  try {
266
269
  let t = e?.target?.getZoom?.();
267
- typeof t == "number" && Number.isFinite(t) && xe(Math.round(t));
270
+ typeof t == "number" && Number.isFinite(t) && He(Math.round(t));
268
271
  } catch {}
269
- }, []), q = te({
272
+ }, []), X = ce({
270
273
  navigation: a.navigation,
271
274
  objectName: a.objectName,
272
- onRowClick: _
273
- }), J = n(() => {
274
- if (!A.trim()) return U;
275
- let e = A.toLowerCase();
276
- return U.filter((t) => t.title?.toLowerCase().includes(e) || t.description?.toLowerCase().includes(e));
277
- }, [U, A]), Ce = n(() => b ?? (a.enableClustering || J.length > 100) ? pe(J, K, x) : J.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) => ({
278
281
  id: e.id,
279
282
  coordinates: e.coordinates,
280
283
  markers: [e],
281
284
  isCluster: !1
282
285
  })), [
283
- J,
284
- K,
286
+ Z,
287
+ Y,
288
+ y,
285
289
  b,
286
- x,
287
290
  a
288
- ]), Y = n(() => se(J.map((e) => e.coordinates)), [J]), X = typeof V.center?.[0] == "number" ? V.center[0] : void 0, Z = typeof V.center?.[1] == "number" ? V.center[1] : void 0, Q = typeof V.zoom == "number" ? V.zoom : void 0, $ = X !== void 0 || Z !== void 0 || Q !== void 0, we = n(() => {
289
- if (Y && !$) return {
290
- bounds: Y,
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,
291
294
  fitBoundsOptions: {
292
295
  padding: 48,
293
296
  maxZoom: 12
294
297
  }
295
298
  };
296
- let e = Y ? ce(Y) : {
299
+ let e = Q ? ye(Q) : {
297
300
  longitude: 0,
298
301
  latitude: 0
299
302
  };
300
303
  return {
301
- longitude: Z ?? e.longitude,
302
- latitude: X ?? e.latitude,
303
- zoom: Q ?? (Y ? 3 : 2)
304
+ longitude: Ge ?? e.longitude,
305
+ latitude: $ ?? e.latitude,
306
+ zoom: Ke ?? (Q ? 3 : 2)
304
307
  };
305
308
  }, [
306
- Y,
309
+ Q,
310
+ qe,
311
+ Ge,
307
312
  $,
308
- Z,
309
- X,
310
- Q
313
+ Ke
311
314
  ]);
312
- return w ? /* @__PURE__ */ m("div", {
313
- className: f("min-w-0 overflow-hidden", u),
314
- children: /* @__PURE__ */ m("div", {
315
+ return we(F) ? S ? /* @__PURE__ */ h("div", {
316
+ className: p("min-w-0 overflow-hidden", u),
317
+ children: /* @__PURE__ */ h("div", {
315
318
  className: "flex items-center justify-center h-96 bg-muted rounded-lg border",
316
- children: /* @__PURE__ */ m("div", {
319
+ children: /* @__PURE__ */ h("div", {
317
320
  className: "text-muted-foreground",
318
321
  children: "Loading map..."
319
322
  })
320
323
  })
321
- }) : E ? /* @__PURE__ */ m("div", {
322
- className: f("min-w-0 overflow-hidden", u),
323
- children: /* @__PURE__ */ m("div", {
324
+ }) : w ? /* @__PURE__ */ h("div", {
325
+ className: p("min-w-0 overflow-hidden", u),
326
+ children: /* @__PURE__ */ h("div", {
324
327
  className: "flex items-center justify-center h-96 bg-muted rounded-lg border",
325
- children: /* @__PURE__ */ h("div", {
328
+ children: /* @__PURE__ */ g("div", {
326
329
  className: "text-destructive",
327
- children: ["Error: ", E.message]
330
+ children: ["Error: ", w.message]
328
331
  })
329
332
  })
330
- }) : /* @__PURE__ */ h("div", {
331
- className: f("min-w-0 overflow-hidden", u),
333
+ }) : /* @__PURE__ */ g("div", {
334
+ className: p("min-w-0 overflow-hidden", u),
332
335
  children: [
333
- W > 0 && /* @__PURE__ */ m("div", {
336
+ q > 0 && /* @__PURE__ */ h("div", {
334
337
  className: "mb-2 p-2 text-sm text-yellow-800 bg-yellow-50 border border-yellow-200 rounded",
335
- children: `${W} record${W === 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.`
336
339
  }),
337
- U.length > 0 && /* @__PURE__ */ m("div", {
340
+ K.length > 0 && /* @__PURE__ */ h("div", {
338
341
  className: "mb-2",
339
- children: /* @__PURE__ */ m("input", {
342
+ children: /* @__PURE__ */ h("input", {
340
343
  type: "text",
341
- value: A,
342
- onChange: (e) => ge(e.target.value),
344
+ value: k,
345
+ onChange: (e) => Oe(e.target.value),
343
346
  placeholder: "Search locations…",
344
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"
345
348
  })
346
349
  }),
347
- /* @__PURE__ */ h("div", {
350
+ /* @__PURE__ */ g("div", {
348
351
  className: "relative border rounded-lg overflow-hidden bg-muted h-[300px] sm:h-[400px] md:h-[500px] lg:h-[600px] w-full",
349
352
  children: [
350
- /* @__PURE__ */ h(ie, {
353
+ /* @__PURE__ */ g(he, {
351
354
  ref: (e) => {
352
- M.current = e;
355
+ A.current = e;
353
356
  },
354
- initialViewState: we,
357
+ initialViewState: Je,
355
358
  style: {
356
359
  width: "100%",
357
360
  height: "100%"
358
361
  },
359
- mapStyle: V.style || le,
362
+ mapStyle: F.style || be,
360
363
  touchZoomRotate: !0,
361
364
  dragRotate: !0,
362
365
  touchPitch: !0,
363
- onLoad: Se,
364
- onZoom: (e) => xe(Math.round(e.viewState.zoom)),
365
- onError: ye,
366
+ onLoad: Ue,
367
+ onZoom: (e) => He(Math.round(e.viewState.zoom)),
368
+ onError: Fe,
366
369
  children: [
367
- R && /* @__PURE__ */ h("div", {
370
+ Ne && /* @__PURE__ */ g("div", {
368
371
  role: "alert",
369
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",
370
373
  children: [
371
374
  "Map failed to load (",
372
- R,
375
+ Ne,
373
376
  "). Markers below are still listed in the search box;",
374
377
  " ",
375
- V.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."
376
379
  ]
377
380
  }),
378
- /* @__PURE__ */ m(ae, {
381
+ /* @__PURE__ */ h(ge, {
379
382
  position: "top-right",
380
383
  showCompass: !0,
381
384
  showZoom: !0
382
385
  }),
383
- /* @__PURE__ */ m("button", {
386
+ /* @__PURE__ */ h("button", {
384
387
  type: "button",
385
- onClick: be,
386
- disabled: I,
388
+ onClick: Ie,
389
+ disabled: Me,
387
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",
388
391
  "aria-label": "Show my location",
389
392
  "data-testid": "map-geolocate",
390
- children: /* @__PURE__ */ m("span", {
393
+ children: /* @__PURE__ */ h("span", {
391
394
  "aria-hidden": "true",
392
- children: I ? "⏳" : "🧭"
395
+ children: Me ? "⏳" : "🧭"
393
396
  })
394
397
  }),
395
- N && /* @__PURE__ */ m(p, {
396
- longitude: N.lng,
397
- latitude: N.lat,
398
+ j && /* @__PURE__ */ h(m, {
399
+ longitude: j.lng,
400
+ latitude: j.lat,
398
401
  anchor: "center",
399
- children: /* @__PURE__ */ m("div", {
402
+ children: /* @__PURE__ */ h("div", {
400
403
  className: "size-3 rounded-full bg-blue-500 ring-4 ring-blue-500/30 shadow",
401
404
  "aria-label": "Your location",
402
405
  "data-testid": "map-user-location"
403
406
  })
404
407
  }),
405
- Ce.map((e) => e.isCluster ? /* @__PURE__ */ m(p, {
408
+ We.map((e) => e.isCluster ? /* @__PURE__ */ h(m, {
406
409
  longitude: e.coordinates[0],
407
410
  latitude: e.coordinates[1],
408
411
  anchor: "center",
409
412
  onClick: (t) => {
410
413
  t.originalEvent.stopPropagation();
411
414
  try {
412
- let t = Math.min(20, Math.max(K + 2, 8));
413
- M.current?.flyTo({
415
+ let t = Math.min(20, Math.max(Y + 2, 8));
416
+ A.current?.flyTo({
414
417
  center: [e.coordinates[0], e.coordinates[1]],
415
418
  zoom: t,
416
419
  duration: 600
417
420
  });
418
421
  } catch {}
419
422
  },
420
- children: /* @__PURE__ */ m("div", {
423
+ children: /* @__PURE__ */ h("div", {
421
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",
422
425
  style: {
423
426
  width: Math.min(48, 24 + e.markers.length * 2),
@@ -427,48 +430,48 @@ var E = ({ schema: a, dataSource: l, className: u, data: d, onMarkerClick: g, on
427
430
  "data-testid": "map-cluster",
428
431
  children: e.markers.length
429
432
  })
430
- }, e.id) : /* @__PURE__ */ m(p, {
433
+ }, e.id) : /* @__PURE__ */ h(m, {
431
434
  longitude: e.coordinates[0],
432
435
  latitude: e.coordinates[1],
433
436
  anchor: "bottom",
434
437
  onClick: (t) => {
435
438
  t.originalEvent.stopPropagation();
436
439
  let n = e.markers[0];
437
- k(n.id), q.handleClick(n.data), g?.(n.data);
440
+ O(n.id), X.handleClick(n.data), ue?.(n.data);
438
441
  },
439
- children: /* @__PURE__ */ m("div", {
442
+ children: /* @__PURE__ */ h("div", {
440
443
  className: "text-2xl cursor-pointer hover:scale-110 transition-transform",
441
444
  children: "📍"
442
445
  })
443
446
  }, e.id)),
444
- G && !j && /* @__PURE__ */ m(oe, {
445
- longitude: G.coordinates[0],
446
- latitude: G.coordinates[1],
447
+ J && !ke && /* @__PURE__ */ h(_e, {
448
+ longitude: J.coordinates[0],
449
+ latitude: J.coordinates[1],
447
450
  anchor: "top",
448
- onClose: () => k(null),
451
+ onClose: () => O(null),
449
452
  closeOnClick: !1,
450
- children: /* @__PURE__ */ h("div", {
453
+ children: /* @__PURE__ */ g("div", {
451
454
  className: "p-2 min-w-[150px] sm:min-w-[200px]",
452
455
  children: [
453
- /* @__PURE__ */ m("h3", {
456
+ /* @__PURE__ */ h("h3", {
454
457
  className: "font-bold text-sm mb-1",
455
- children: G.title
458
+ children: J.title
456
459
  }),
457
- G.description && /* @__PURE__ */ m("p", {
460
+ J.description && /* @__PURE__ */ h("p", {
458
461
  className: "text-xs text-muted-foreground",
459
- children: G.description
462
+ children: J.description
460
463
  }),
461
- /* @__PURE__ */ h("div", {
464
+ /* @__PURE__ */ g("div", {
462
465
  className: "mt-2 text-xs flex gap-2",
463
- children: [v && /* @__PURE__ */ m("button", {
466
+ children: [_ && /* @__PURE__ */ h("button", {
464
467
  type: "button",
465
468
  className: "text-blue-500 hover:underline",
466
- onClick: () => v(G.data),
469
+ onClick: () => _(J.data),
467
470
  children: "Edit"
468
- }), y && /* @__PURE__ */ m("button", {
471
+ }), v && /* @__PURE__ */ h("button", {
469
472
  type: "button",
470
473
  className: "text-red-500 hover:underline",
471
- onClick: () => y(G.data),
474
+ onClick: () => v(J.data),
472
475
  children: "Delete"
473
476
  })]
474
477
  })
@@ -477,69 +480,70 @@ var E = ({ schema: a, dataSource: l, className: u, data: d, onMarkerClick: g, on
477
480
  })
478
481
  ]
479
482
  }),
480
- G && j && /* @__PURE__ */ h("div", {
483
+ J && ke && /* @__PURE__ */ g("div", {
481
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)]",
482
485
  role: "dialog",
483
486
  "aria-label": "Location details",
484
487
  "data-testid": "map-mobile-record-sheet",
485
488
  children: [
486
- /* @__PURE__ */ m("div", {
489
+ /* @__PURE__ */ h("div", {
487
490
  className: "mx-auto mb-2 h-1 w-10 rounded-full bg-muted",
488
491
  "aria-hidden": "true"
489
492
  }),
490
- /* @__PURE__ */ h("div", {
493
+ /* @__PURE__ */ g("div", {
491
494
  className: "flex items-start justify-between gap-3",
492
- children: [/* @__PURE__ */ h("div", {
495
+ children: [/* @__PURE__ */ g("div", {
493
496
  className: "min-w-0 flex-1",
494
- children: [/* @__PURE__ */ m("h3", {
497
+ children: [/* @__PURE__ */ h("h3", {
495
498
  className: "text-sm font-semibold truncate",
496
- children: G.title
497
- }), G.description && /* @__PURE__ */ m("p", {
499
+ children: J.title
500
+ }), J.description && /* @__PURE__ */ h("p", {
498
501
  className: "mt-1 text-xs text-muted-foreground line-clamp-3",
499
- children: G.description
502
+ children: J.description
500
503
  })]
501
- }), /* @__PURE__ */ m("button", {
504
+ }), /* @__PURE__ */ h("button", {
502
505
  type: "button",
503
- onClick: () => k(null),
506
+ onClick: () => O(null),
504
507
  className: "text-muted-foreground hover:text-foreground text-lg leading-none px-2",
505
508
  "aria-label": "Close details",
506
509
  "data-testid": "map-mobile-record-close",
507
510
  children: "×"
508
511
  })]
509
512
  }),
510
- /* @__PURE__ */ h("div", {
513
+ /* @__PURE__ */ g("div", {
511
514
  className: "mt-3 flex flex-wrap gap-2 text-xs",
512
- children: [v && /* @__PURE__ */ m("button", {
515
+ children: [_ && /* @__PURE__ */ h("button", {
513
516
  type: "button",
514
517
  className: "px-3 py-1.5 rounded-md border bg-card hover:bg-accent",
515
- onClick: () => v(G.data),
518
+ onClick: () => _(J.data),
516
519
  children: "Edit"
517
- }), y && /* @__PURE__ */ m("button", {
520
+ }), v && /* @__PURE__ */ h("button", {
518
521
  type: "button",
519
522
  className: "px-3 py-1.5 rounded-md border border-destructive/30 text-destructive hover:bg-destructive/10",
520
- onClick: () => y(G.data),
523
+ onClick: () => v(J.data),
521
524
  children: "Delete"
522
525
  })]
523
526
  })
524
527
  ]
525
528
  }),
526
- P && /* @__PURE__ */ m("div", {
529
+ je && /* @__PURE__ */ h("div", {
527
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",
528
- children: P
531
+ children: je
529
532
  })
530
533
  ]
531
534
  }),
532
- q.isOverlay && /* @__PURE__ */ m(ne, {
533
- ...q,
535
+ E && /* @__PURE__ */ h(ae, { result: E }),
536
+ X.isOverlay && /* @__PURE__ */ h(fe, {
537
+ ...X,
534
538
  title: "Location Details",
535
- children: (e) => /* @__PURE__ */ m("div", {
539
+ children: (e) => /* @__PURE__ */ h("div", {
536
540
  className: "space-y-3",
537
- children: Object.entries(e).map(([e, t]) => /* @__PURE__ */ h("div", {
541
+ children: Object.entries(e).map(([e, t]) => /* @__PURE__ */ g("div", {
538
542
  className: "flex flex-col",
539
- children: [/* @__PURE__ */ m("span", {
543
+ children: [/* @__PURE__ */ h("span", {
540
544
  className: "text-xs font-medium text-muted-foreground uppercase tracking-wide",
541
545
  children: e.replace(/_/g, " ")
542
- }), /* @__PURE__ */ m("span", {
546
+ }), /* @__PURE__ */ h("span", {
543
547
  className: "text-sm",
544
548
  children: String(t ?? "—")
545
549
  })]
@@ -547,55 +551,105 @@ var E = ({ schema: a, dataSource: l, className: u, data: d, onMarkerClick: g, on
547
551
  })
548
552
  })
549
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
+ })
550
583
  });
551
- }, D = {
584
+ }, De = {
552
585
  filter: !0,
553
586
  sort: !0
554
- }, O = ({ schema: e, ...t }) => {
555
- let { dataSource: n } = u() || {};
556
- return /* @__PURE__ */ m(l, {
587
+ }, D = c(({ schema: e, ...t }) => {
588
+ let { dataSource: n } = ue() || {};
589
+ return /* @__PURE__ */ h(d, {
557
590
  schema: e,
558
- mapping: D,
591
+ mapping: De,
559
592
  dataSource: n,
560
593
  testId: "object-map",
561
594
  errorTitle: "This map’s data source could not be resolved",
562
- children: (e) => /* @__PURE__ */ m(E, {
595
+ children: (e) => /* @__PURE__ */ h(E, {
563
596
  schema: e,
564
597
  dataSource: n,
565
598
  ...t
566
599
  })
567
600
  });
568
- };
569
- a.register("object-map", O, {
601
+ });
602
+ a.register("object-map", D, {
570
603
  namespace: "plugin-map",
571
604
  label: "Object Map",
572
605
  category: "view",
573
- inputs: [{
574
- name: "objectName",
575
- type: "string",
576
- label: "Object Name",
577
- required: !0
578
- }, {
579
- name: "map",
580
- type: "object",
581
- label: "Map Config",
582
- description: "latitudeField, longitudeField, titleField"
583
- }]
584
- }), a.register("map", O, {
585
- namespace: "view",
586
- label: "Map View",
587
- category: "view",
588
- inputs: [{
589
- name: "objectName",
590
- type: "string",
591
- label: "Object Name",
592
- required: !0
593
- }, {
594
- name: "map",
595
- type: "object",
596
- label: "Map Config",
597
- description: "latitudeField, longitudeField, titleField"
598
- }]
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
+ ]
599
653
  });
600
654
  //#endregion
601
- export { E as ObjectMap, O as ObjectMapRenderer };
655
+ export { E as ObjectMap, D as ObjectMapRenderer };