@object-ui/plugin-map 17.6.0 → 18.0.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,52 @@
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, useObjectTranslation as le, useResolvedFilter as ue, useSchemaContext as de, useSettledSchema as fe } from "@object-ui/react";
4
+ import { ObjectMapConfigSchema as f } from "@object-ui/types/zod";
5
+ import { NavigationOverlay as pe, cn as p, useIsMobile as me } from "@object-ui/components";
6
+ import { usePermissions as he } from "@object-ui/permissions";
7
+ import ge, { Marker as m, NavigationControl as _e, Popup as ve } 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 ye(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 be(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;
33
+ var xe = "https://demotiles.maplibre.org/style.json";
34
+ function Se() {
35
+ if (typeof document > "u") return !0;
36
+ try {
37
+ let e = document.createElement("canvas").getContext("webgl2");
38
+ return e ? (e.getExtension("WEBGL_lose_context")?.loseContext(), !0) : !1;
39
+ } catch {
40
+ return !1;
40
41
  }
41
- return e.staticData ? {
42
- provider: "value",
43
- items: e.staticData
44
- } : e.objectName ? {
45
- provider: "object",
46
- object: e.objectName
47
- } : null;
48
42
  }
49
- var y = () => globalThis.process?.env?.NODE_ENV !== "production", b = /* @__PURE__ */ new Set();
50
- function x(e) {
51
- if (!y()) return;
43
+ var y = Object.keys(f.shape).filter((e) => e !== "style");
44
+ function Ce(e) {
45
+ return u(e, "view-data");
46
+ }
47
+ var b = () => globalThis.process?.env?.NODE_ENV !== "production", x = /* @__PURE__ */ new Set();
48
+ function we(e) {
49
+ if (!b()) return;
52
50
  let t = e.filter;
53
51
  if (!t || typeof t != "object" || Array.isArray(t) || !Object.prototype.hasOwnProperty.call(t, "map")) return;
54
52
  let n = t.map;
@@ -59,32 +57,32 @@ function x(e) {
59
57
  } catch {
60
58
  r = `${e.type ?? "map"}::${e.objectName ?? ""}::<unserializable>`;
61
59
  }
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."));
60
+ 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
61
  }
64
62
  var S = /* @__PURE__ */ new Set();
65
63
  function C(e) {
66
- if (!y()) return;
64
+ if (!b()) return;
67
65
  let t = e.style;
68
66
  if (typeof t != "string" || t === "") return;
69
67
  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.`));
68
+ 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
69
  }
72
70
  var w = /* @__PURE__ */ new Set();
73
71
  function T(e) {
74
- if (!y()) return;
75
- let t = v.filter((t) => e[t] !== void 0);
72
+ if (!b()) return;
73
+ let t = y.filter((t) => e[t] !== void 0);
76
74
  if (t.length === 0) return;
77
75
  let n = `${e.type ?? "map"}::${e.objectName ?? ""}::${t.join(",")}`;
78
76
  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
77
  }
80
- function de(e) {
81
- x(e), C(e);
78
+ function Te(e) {
79
+ we(e), C(e);
82
80
  let t = e.mapStyle || e.map?.style, n = e.map ?? null;
83
81
  if (n) {
84
- let r = d.safeParse(n);
82
+ let r = f.safeParse(n);
85
83
  return r.success || console.warn("[ObjectMap] Invalid map configuration:", r.error.format()), T(e), {
86
84
  ...n,
87
- style: n.style || t
85
+ style: t
88
86
  };
89
87
  }
90
88
  return e.locationField || e.latitudeField ? {
@@ -96,15 +94,12 @@ function de(e) {
96
94
  zoom: e.zoom,
97
95
  center: e.center,
98
96
  style: t
99
- } : {
100
- latitudeField: "latitude",
101
- longitudeField: "longitude",
102
- locationField: "location",
103
- descriptionField: "description",
104
- style: t
105
- };
97
+ } : { style: t };
98
+ }
99
+ function Ee(e) {
100
+ return !!e.locationField || !!(e.latitudeField && e.longitudeField);
106
101
  }
107
- function fe(e, t) {
102
+ function De(e, t) {
108
103
  if (t.latitudeField && t.longitudeField) {
109
104
  let n = e[t.latitudeField], r = e[t.longitudeField];
110
105
  if (typeof n == "number" && typeof r == "number") return [n, r];
@@ -126,7 +121,7 @@ function fe(e, t) {
126
121
  }
127
122
  return null;
128
123
  }
129
- function pe(e, t, n = 50) {
124
+ function Oe(e, t, n = 50) {
130
125
  if (e.length <= 1 || t >= 15) return e.map((e) => ({
131
126
  id: e.id,
132
127
  coordinates: e.coordinates,
@@ -157,96 +152,114 @@ function pe(e, t, n = 50) {
157
152
  }
158
153
  }), a;
159
154
  }
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(() => {
155
+ var E = ({ schema: a, dataSource: c, className: u, data: d, onMarkerClick: le, onRowClick: de, onEdit: f, onDelete: _, enableClustering: v, clusterRadius: y = 50 }) => {
156
+ let [b, x] = i([]), [we, S] = i(!0), [C, w] = i(null), [T, E] = i(null), [D, O] = i(null), [k, ke] = i(""), Ae = me(), A = r(null), [j, je] = i(null), [Me, M] = i(null), [Ne, N] = i(!1), [Pe, Fe] = i(null), Ie = e((e) => {
157
+ Fe(e?.error?.message || "Failed to load the map style/tiles.");
158
+ }, []), [Le] = i(Se), Re = e(() => {
164
159
  if (typeof navigator > "u" || !navigator.geolocation) {
165
- F("Geolocation is not available in this browser.");
160
+ M("Geolocation is not available in this browser.");
166
161
  return;
167
162
  }
168
- L(!0), F(null), navigator.geolocation.getCurrentPosition((e) => {
163
+ N(!0), M(null), navigator.geolocation.getCurrentPosition((e) => {
169
164
  let { longitude: t, latitude: n } = e.coords;
170
- _e({
165
+ je({
171
166
  lng: t,
172
167
  lat: n
173
- }), L(!1);
168
+ }), N(!1);
174
169
  try {
175
- M.current?.flyTo({
170
+ A.current?.flyTo({
176
171
  center: [t, n],
177
172
  zoom: 12,
178
173
  duration: 800
179
174
  });
180
175
  } catch {}
181
176
  }, (e) => {
182
- L(!1), F(e.message || "Unable to retrieve location.");
177
+ N(!1), M(e.message || "Unable to retrieve location.");
183
178
  }, {
184
179
  enableHighAccuracy: !1,
185
180
  timeout: 8e3,
186
181
  maximumAge: 6e4
187
182
  });
188
- }, []), z = ue(a), B = n(() => z, [JSON.stringify(z)]), V = n(() => de(a), [a]), H = B?.provider === "value";
183
+ }, []), P = n(() => Ce(a), [a]), F = n(() => Te(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, ze = ie(a, P), { ready: Be, def: B } = fe(ze ?? "", I ? void 0 : c), V = he(), H = !Array.isArray(d) && L === "object", U = oe(H ? R : void 0), Ve = r(U), He = r(null), W = r(0), Ue = se(), G = ue(a.filter, Ue);
189
184
  t(() => {
190
- (async () => {
185
+ if (H && !Be) {
186
+ S(!0);
187
+ return;
188
+ }
189
+ let e = [
190
+ d,
191
+ L,
192
+ R,
193
+ z,
194
+ c,
195
+ I,
196
+ G,
197
+ a.sort,
198
+ B,
199
+ V
200
+ ], t = He.current, n = t !== null && t.length === e.length && e.every((e, n) => Object.is(e, t[n])), r = U !== Ve.current;
201
+ Ve.current = U;
202
+ let i = r && n, ne = ++W.current, l = () => W.current === ne, u = (t, n) => {
203
+ l() && (x(t), E(n), w(null), He.current = e);
204
+ };
205
+ return (async () => {
191
206
  try {
192
- if (T(!0), Array.isArray(d)) {
193
- C(d), T(!1);
207
+ if (i || S(!0), Array.isArray(d)) {
208
+ u(d, null);
194
209
  return;
195
210
  }
196
- if (H && B?.provider === "value") {
197
- C(B.items), T(!1);
211
+ if (I && L === "value") {
212
+ let e = await new o({ items: z ?? [] }).find("", {
213
+ $filter: G,
214
+ $orderby: te(a.sort),
215
+ ...re()
216
+ }), t = s(e);
217
+ u(t.rows, t);
198
218
  return;
199
219
  }
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);
220
+ if (!c || typeof c.find != "function") throw Error("DataSource required for the object provider");
221
+ if (L === "object") {
222
+ let e = R, t = ee(B?.fields), n = V?.isLoaded ? t.filter((t) => V.checkField(e, t, "read")) : t, r = await c.find(e, {
223
+ $filter: G,
224
+ $orderby: te(a.sort),
225
+ ...re(),
226
+ ...n.length > 0 ? { $expand: n } : {}
227
+ }), i = s(r);
228
+ u(i.rows, i);
229
+ }
210
230
  } catch (e) {
211
- D(e), T(!1);
231
+ i ? console.error("[ObjectMap] Failed to refresh data:", e) : l() && w(e);
232
+ } finally {
233
+ l() && S(!1);
212
234
  }
213
- })();
235
+ })(), () => {
236
+ W.current += 1;
237
+ };
214
238
  }, [
215
239
  d,
216
- B,
217
- l,
218
- H,
219
- a.filter,
240
+ L,
241
+ R,
242
+ z,
243
+ c,
244
+ I,
245
+ G,
220
246
  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
247
+ Be,
248
+ B,
249
+ V,
250
+ U,
251
+ H
239
252
  ]);
240
- let { markers: U, invalidCount: W } = n(() => {
253
+ let { markers: K, invalidCount: q } = n(() => {
241
254
  let e = 0;
242
255
  return {
243
- markers: S.map((t, n) => {
244
- let r = fe(t, V);
256
+ markers: b.map((t, n) => {
257
+ let r = De(t, F);
245
258
  if (!r) return e++, null;
246
- let i = ee(O, t, {
247
- titleField: V.titleField,
259
+ let i = ne(B, t, {
260
+ titleField: F.titleField,
248
261
  fallback: "Marker"
249
- }), a = V.descriptionField ? t[V.descriptionField] : void 0, [o, s] = r;
262
+ }), a = l(t, F.descriptionField), [o, s] = r;
250
263
  return !isFinite(o) || !isFinite(s) || o < -90 || o > 90 || s < -180 || s > 180 ? (e++, null) : {
251
264
  id: t.id || t._id || `marker-${n}`,
252
265
  title: i,
@@ -258,166 +271,168 @@ var E = ({ schema: a, dataSource: l, className: u, data: d, onMarkerClick: g, on
258
271
  invalidCount: e
259
272
  };
260
273
  }, [
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) => {
274
+ b,
275
+ F,
276
+ B
277
+ ]), J = n(() => K.find((e) => e.id === D), [K, D]), [Y, We] = i(F.zoom || 3), Ge = e((e) => {
265
278
  try {
266
279
  let t = e?.target?.getZoom?.();
267
- typeof t == "number" && Number.isFinite(t) && xe(Math.round(t));
280
+ typeof t == "number" && Number.isFinite(t) && We(Math.round(t));
268
281
  } catch {}
269
- }, []), q = te({
282
+ }, []), X = ce({
270
283
  navigation: a.navigation,
271
284
  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) => ({
285
+ onRowClick: de
286
+ }), Ke = (e) => {
287
+ O(e.id), X.handleClick(e.data), le?.(e.data);
288
+ }, Z = n(() => {
289
+ if (!k.trim()) return K;
290
+ let e = k.toLowerCase();
291
+ return K.filter((t) => t.title?.toLowerCase().includes(e) || t.description?.toLowerCase().includes(e));
292
+ }, [K, k]), qe = n(() => v ?? (a.enableClustering || Z.length > 100) ? Oe(Z, Y, y) : Z.map((e) => ({
278
293
  id: e.id,
279
294
  coordinates: e.coordinates,
280
295
  markers: [e],
281
296
  isCluster: !1
282
297
  })), [
283
- J,
284
- K,
285
- b,
286
- x,
298
+ Z,
299
+ Y,
300
+ v,
301
+ y,
287
302
  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,
303
+ ]), Q = n(() => ye(Z.map((e) => e.coordinates)), [Z]), $ = typeof F.center?.[0] == "number" ? F.center[0] : void 0, Je = typeof F.center?.[1] == "number" ? F.center[1] : void 0, Ye = typeof F.zoom == "number" ? F.zoom : void 0, Xe = $ !== void 0 || Je !== void 0 || Ye !== void 0, Ze = n(() => {
304
+ if (Q && !Xe) return {
305
+ bounds: Q,
291
306
  fitBoundsOptions: {
292
307
  padding: 48,
293
308
  maxZoom: 12
294
309
  }
295
310
  };
296
- let e = Y ? ce(Y) : {
311
+ let e = Q ? be(Q) : {
297
312
  longitude: 0,
298
313
  latitude: 0
299
314
  };
300
315
  return {
301
- longitude: Z ?? e.longitude,
302
- latitude: X ?? e.latitude,
303
- zoom: Q ?? (Y ? 3 : 2)
316
+ longitude: Je ?? e.longitude,
317
+ latitude: $ ?? e.latitude,
318
+ zoom: Ye ?? (Q ? 3 : 2)
304
319
  };
305
320
  }, [
306
- Y,
321
+ Q,
322
+ Xe,
323
+ Je,
307
324
  $,
308
- Z,
309
- X,
310
- Q
325
+ Ye
311
326
  ]);
312
- return w ? /* @__PURE__ */ m("div", {
313
- className: f("min-w-0 overflow-hidden", u),
314
- children: /* @__PURE__ */ m("div", {
327
+ return Ee(F) ? we ? /* @__PURE__ */ h("div", {
328
+ className: p("min-w-0 overflow-hidden", u),
329
+ children: /* @__PURE__ */ h("div", {
315
330
  className: "flex items-center justify-center h-96 bg-muted rounded-lg border",
316
- children: /* @__PURE__ */ m("div", {
331
+ children: /* @__PURE__ */ h("div", {
317
332
  className: "text-muted-foreground",
318
333
  children: "Loading map..."
319
334
  })
320
335
  })
321
- }) : E ? /* @__PURE__ */ m("div", {
322
- className: f("min-w-0 overflow-hidden", u),
323
- children: /* @__PURE__ */ m("div", {
336
+ }) : C ? /* @__PURE__ */ h("div", {
337
+ className: p("min-w-0 overflow-hidden", u),
338
+ children: /* @__PURE__ */ h("div", {
324
339
  className: "flex items-center justify-center h-96 bg-muted rounded-lg border",
325
- children: /* @__PURE__ */ h("div", {
340
+ children: /* @__PURE__ */ g("div", {
326
341
  className: "text-destructive",
327
- children: ["Error: ", E.message]
342
+ children: ["Error: ", C.message]
328
343
  })
329
344
  })
330
- }) : /* @__PURE__ */ h("div", {
331
- className: f("min-w-0 overflow-hidden", u),
345
+ }) : /* @__PURE__ */ g("div", {
346
+ className: p("min-w-0 overflow-hidden", u),
332
347
  children: [
333
- W > 0 && /* @__PURE__ */ m("div", {
348
+ q > 0 && /* @__PURE__ */ h("div", {
334
349
  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.`
350
+ children: `${q} record${q === 1 ? "" : "s"} with missing or invalid coordinates excluded from the map.`
336
351
  }),
337
- U.length > 0 && /* @__PURE__ */ m("div", {
352
+ K.length > 0 && /* @__PURE__ */ h("div", {
338
353
  className: "mb-2",
339
- children: /* @__PURE__ */ m("input", {
354
+ children: /* @__PURE__ */ h("input", {
340
355
  type: "text",
341
- value: A,
342
- onChange: (e) => ge(e.target.value),
356
+ value: k,
357
+ onChange: (e) => ke(e.target.value),
343
358
  placeholder: "Search locations…",
344
359
  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
360
  })
346
361
  }),
347
- /* @__PURE__ */ h("div", {
362
+ /* @__PURE__ */ g("div", {
348
363
  className: "relative border rounded-lg overflow-hidden bg-muted h-[300px] sm:h-[400px] md:h-[500px] lg:h-[600px] w-full",
349
364
  children: [
350
- /* @__PURE__ */ h(ie, {
365
+ Le ? /* @__PURE__ */ g(ge, {
351
366
  ref: (e) => {
352
- M.current = e;
367
+ A.current = e;
353
368
  },
354
- initialViewState: we,
369
+ initialViewState: Ze,
355
370
  style: {
356
371
  width: "100%",
357
372
  height: "100%"
358
373
  },
359
- mapStyle: V.style || le,
374
+ mapStyle: F.style || xe,
360
375
  touchZoomRotate: !0,
361
376
  dragRotate: !0,
362
377
  touchPitch: !0,
363
- onLoad: Se,
364
- onZoom: (e) => xe(Math.round(e.viewState.zoom)),
365
- onError: ye,
378
+ onLoad: Ge,
379
+ onZoom: (e) => We(Math.round(e.viewState.zoom)),
380
+ onError: Ie,
366
381
  children: [
367
- R && /* @__PURE__ */ h("div", {
382
+ Pe && /* @__PURE__ */ g("div", {
368
383
  role: "alert",
369
384
  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
385
  children: [
371
386
  "Map failed to load (",
372
- R,
387
+ Pe,
373
388
  "). Markers below are still listed in the search box;",
374
389
  " ",
375
- V.style ? "check the configured map style URL." : "configure a custom map style for production use."
390
+ F.style ? "check the configured map style URL." : "configure a custom map style for production use."
376
391
  ]
377
392
  }),
378
- /* @__PURE__ */ m(ae, {
393
+ /* @__PURE__ */ h(_e, {
379
394
  position: "top-right",
380
395
  showCompass: !0,
381
396
  showZoom: !0
382
397
  }),
383
- /* @__PURE__ */ m("button", {
398
+ /* @__PURE__ */ h("button", {
384
399
  type: "button",
385
- onClick: be,
386
- disabled: I,
400
+ onClick: Re,
401
+ disabled: Ne,
387
402
  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
403
  "aria-label": "Show my location",
389
404
  "data-testid": "map-geolocate",
390
- children: /* @__PURE__ */ m("span", {
405
+ children: /* @__PURE__ */ h("span", {
391
406
  "aria-hidden": "true",
392
- children: I ? "⏳" : "🧭"
407
+ children: Ne ? "⏳" : "🧭"
393
408
  })
394
409
  }),
395
- N && /* @__PURE__ */ m(p, {
396
- longitude: N.lng,
397
- latitude: N.lat,
410
+ j && /* @__PURE__ */ h(m, {
411
+ longitude: j.lng,
412
+ latitude: j.lat,
398
413
  anchor: "center",
399
- children: /* @__PURE__ */ m("div", {
414
+ children: /* @__PURE__ */ h("div", {
400
415
  className: "size-3 rounded-full bg-blue-500 ring-4 ring-blue-500/30 shadow",
401
416
  "aria-label": "Your location",
402
417
  "data-testid": "map-user-location"
403
418
  })
404
419
  }),
405
- Ce.map((e) => e.isCluster ? /* @__PURE__ */ m(p, {
420
+ qe.map((e) => e.isCluster ? /* @__PURE__ */ h(m, {
406
421
  longitude: e.coordinates[0],
407
422
  latitude: e.coordinates[1],
408
423
  anchor: "center",
409
424
  onClick: (t) => {
410
425
  t.originalEvent.stopPropagation();
411
426
  try {
412
- let t = Math.min(20, Math.max(K + 2, 8));
413
- M.current?.flyTo({
427
+ let t = Math.min(20, Math.max(Y + 2, 8));
428
+ A.current?.flyTo({
414
429
  center: [e.coordinates[0], e.coordinates[1]],
415
430
  zoom: t,
416
431
  duration: 600
417
432
  });
418
433
  } catch {}
419
434
  },
420
- children: /* @__PURE__ */ m("div", {
435
+ children: /* @__PURE__ */ h("div", {
421
436
  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
437
  style: {
423
438
  width: Math.min(48, 24 + e.markers.length * 2),
@@ -427,48 +442,46 @@ var E = ({ schema: a, dataSource: l, className: u, data: d, onMarkerClick: g, on
427
442
  "data-testid": "map-cluster",
428
443
  children: e.markers.length
429
444
  })
430
- }, e.id) : /* @__PURE__ */ m(p, {
445
+ }, e.id) : /* @__PURE__ */ h(m, {
431
446
  longitude: e.coordinates[0],
432
447
  latitude: e.coordinates[1],
433
448
  anchor: "bottom",
434
449
  onClick: (t) => {
435
- t.originalEvent.stopPropagation();
436
- let n = e.markers[0];
437
- k(n.id), q.handleClick(n.data), g?.(n.data);
450
+ t.originalEvent.stopPropagation(), Ke(e.markers[0]);
438
451
  },
439
- children: /* @__PURE__ */ m("div", {
452
+ children: /* @__PURE__ */ h("div", {
440
453
  className: "text-2xl cursor-pointer hover:scale-110 transition-transform",
441
454
  children: "📍"
442
455
  })
443
456
  }, e.id)),
444
- G && !j && /* @__PURE__ */ m(oe, {
445
- longitude: G.coordinates[0],
446
- latitude: G.coordinates[1],
457
+ J && !Ae && /* @__PURE__ */ h(ve, {
458
+ longitude: J.coordinates[0],
459
+ latitude: J.coordinates[1],
447
460
  anchor: "top",
448
- onClose: () => k(null),
461
+ onClose: () => O(null),
449
462
  closeOnClick: !1,
450
- children: /* @__PURE__ */ h("div", {
463
+ children: /* @__PURE__ */ g("div", {
451
464
  className: "p-2 min-w-[150px] sm:min-w-[200px]",
452
465
  children: [
453
- /* @__PURE__ */ m("h3", {
466
+ /* @__PURE__ */ h("h3", {
454
467
  className: "font-bold text-sm mb-1",
455
- children: G.title
468
+ children: J.title
456
469
  }),
457
- G.description && /* @__PURE__ */ m("p", {
470
+ J.description && /* @__PURE__ */ h("p", {
458
471
  className: "text-xs text-muted-foreground",
459
- children: G.description
472
+ children: J.description
460
473
  }),
461
- /* @__PURE__ */ h("div", {
474
+ /* @__PURE__ */ g("div", {
462
475
  className: "mt-2 text-xs flex gap-2",
463
- children: [v && /* @__PURE__ */ m("button", {
476
+ children: [f && /* @__PURE__ */ h("button", {
464
477
  type: "button",
465
478
  className: "text-blue-500 hover:underline",
466
- onClick: () => v(G.data),
479
+ onClick: () => f(J.data),
467
480
  children: "Edit"
468
- }), y && /* @__PURE__ */ m("button", {
481
+ }), _ && /* @__PURE__ */ h("button", {
469
482
  type: "button",
470
483
  className: "text-red-500 hover:underline",
471
- onClick: () => y(G.data),
484
+ onClick: () => _(J.data),
472
485
  children: "Delete"
473
486
  })]
474
487
  })
@@ -476,70 +489,97 @@ var E = ({ schema: a, dataSource: l, className: u, data: d, onMarkerClick: g, on
476
489
  })
477
490
  })
478
491
  ]
492
+ }) : /* @__PURE__ */ g("div", {
493
+ className: "flex h-full flex-col",
494
+ "data-testid": "map-webgl2-fallback",
495
+ children: [/* @__PURE__ */ h("div", {
496
+ role: "alert",
497
+ className: "p-2 text-sm text-center text-amber-900 bg-amber-50 border-b border-amber-200",
498
+ children: "Map failed to load (this browser does not provide WebGL2, which the map needs). The records are listed below; turn on hardware acceleration or use a browser with WebGL2 to see the map."
499
+ }), /* @__PURE__ */ g("ul", {
500
+ className: "min-h-0 flex-1 overflow-y-auto divide-y bg-background",
501
+ "aria-label": "Locations",
502
+ children: [Z.length === 0 && /* @__PURE__ */ h("li", {
503
+ className: "px-3 py-2 text-sm text-muted-foreground",
504
+ children: "No locations to list."
505
+ }), Z.map((e) => /* @__PURE__ */ h("li", { children: /* @__PURE__ */ g("button", {
506
+ type: "button",
507
+ onClick: () => Ke(e),
508
+ className: p("block w-full px-3 py-2 text-left hover:bg-accent focus:outline-none focus-visible:bg-accent", e.id === D && "bg-accent"),
509
+ children: [/* @__PURE__ */ h("span", {
510
+ className: "block truncate text-sm font-medium",
511
+ children: e.title
512
+ }), e.description && /* @__PURE__ */ h("span", {
513
+ className: "block truncate text-xs text-muted-foreground",
514
+ children: e.description
515
+ })]
516
+ }) }, e.id))]
517
+ })]
479
518
  }),
480
- G && j && /* @__PURE__ */ h("div", {
519
+ J && Ae && /* @__PURE__ */ g("div", {
481
520
  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
521
  role: "dialog",
483
522
  "aria-label": "Location details",
484
523
  "data-testid": "map-mobile-record-sheet",
485
524
  children: [
486
- /* @__PURE__ */ m("div", {
525
+ /* @__PURE__ */ h("div", {
487
526
  className: "mx-auto mb-2 h-1 w-10 rounded-full bg-muted",
488
527
  "aria-hidden": "true"
489
528
  }),
490
- /* @__PURE__ */ h("div", {
529
+ /* @__PURE__ */ g("div", {
491
530
  className: "flex items-start justify-between gap-3",
492
- children: [/* @__PURE__ */ h("div", {
531
+ children: [/* @__PURE__ */ g("div", {
493
532
  className: "min-w-0 flex-1",
494
- children: [/* @__PURE__ */ m("h3", {
533
+ children: [/* @__PURE__ */ h("h3", {
495
534
  className: "text-sm font-semibold truncate",
496
- children: G.title
497
- }), G.description && /* @__PURE__ */ m("p", {
535
+ children: J.title
536
+ }), J.description && /* @__PURE__ */ h("p", {
498
537
  className: "mt-1 text-xs text-muted-foreground line-clamp-3",
499
- children: G.description
538
+ children: J.description
500
539
  })]
501
- }), /* @__PURE__ */ m("button", {
540
+ }), /* @__PURE__ */ h("button", {
502
541
  type: "button",
503
- onClick: () => k(null),
542
+ onClick: () => O(null),
504
543
  className: "text-muted-foreground hover:text-foreground text-lg leading-none px-2",
505
544
  "aria-label": "Close details",
506
545
  "data-testid": "map-mobile-record-close",
507
546
  children: "×"
508
547
  })]
509
548
  }),
510
- /* @__PURE__ */ h("div", {
549
+ /* @__PURE__ */ g("div", {
511
550
  className: "mt-3 flex flex-wrap gap-2 text-xs",
512
- children: [v && /* @__PURE__ */ m("button", {
551
+ children: [f && /* @__PURE__ */ h("button", {
513
552
  type: "button",
514
553
  className: "px-3 py-1.5 rounded-md border bg-card hover:bg-accent",
515
- onClick: () => v(G.data),
554
+ onClick: () => f(J.data),
516
555
  children: "Edit"
517
- }), y && /* @__PURE__ */ m("button", {
556
+ }), _ && /* @__PURE__ */ h("button", {
518
557
  type: "button",
519
558
  className: "px-3 py-1.5 rounded-md border border-destructive/30 text-destructive hover:bg-destructive/10",
520
- onClick: () => y(G.data),
559
+ onClick: () => _(J.data),
521
560
  children: "Delete"
522
561
  })]
523
562
  })
524
563
  ]
525
564
  }),
526
- P && /* @__PURE__ */ m("div", {
565
+ Me && /* @__PURE__ */ h("div", {
527
566
  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
567
+ children: Me
529
568
  })
530
569
  ]
531
570
  }),
532
- q.isOverlay && /* @__PURE__ */ m(ne, {
533
- ...q,
571
+ T && /* @__PURE__ */ h(ae, { result: T }),
572
+ X.isOverlay && /* @__PURE__ */ h(pe, {
573
+ ...X,
534
574
  title: "Location Details",
535
- children: (e) => /* @__PURE__ */ m("div", {
575
+ children: (e) => /* @__PURE__ */ h("div", {
536
576
  className: "space-y-3",
537
- children: Object.entries(e).map(([e, t]) => /* @__PURE__ */ h("div", {
577
+ children: Object.entries(e).map(([e, t]) => /* @__PURE__ */ g("div", {
538
578
  className: "flex flex-col",
539
- children: [/* @__PURE__ */ m("span", {
579
+ children: [/* @__PURE__ */ h("span", {
540
580
  className: "text-xs font-medium text-muted-foreground uppercase tracking-wide",
541
581
  children: e.replace(/_/g, " ")
542
- }), /* @__PURE__ */ m("span", {
582
+ }), /* @__PURE__ */ h("span", {
543
583
  className: "text-sm",
544
584
  children: String(t ?? "—")
545
585
  })]
@@ -547,55 +587,105 @@ var E = ({ schema: a, dataSource: l, className: u, data: d, onMarkerClick: g, on
547
587
  })
548
588
  })
549
589
  ]
590
+ }) : /* @__PURE__ */ h("div", {
591
+ className: p("min-w-0 overflow-hidden", u),
592
+ children: /* @__PURE__ */ h("div", {
593
+ className: "flex items-center justify-center h-96 bg-muted rounded-lg border p-4 text-center",
594
+ "data-testid": "map-missing-location-binding",
595
+ role: "alert",
596
+ children: /* @__PURE__ */ g("div", {
597
+ className: "text-destructive",
598
+ children: [
599
+ "Map configuration required — declare ",
600
+ /* @__PURE__ */ h("code", {
601
+ className: "font-mono",
602
+ children: "map.locationField"
603
+ }),
604
+ " ",
605
+ "or ",
606
+ /* @__PURE__ */ h("code", {
607
+ className: "font-mono",
608
+ children: "map.latitudeField"
609
+ }),
610
+ " +",
611
+ " ",
612
+ /* @__PURE__ */ h("code", {
613
+ className: "font-mono",
614
+ children: "map.longitudeField"
615
+ })
616
+ ]
617
+ })
618
+ })
550
619
  });
551
620
  }, D = {
552
621
  filter: !0,
553
622
  sort: !0
554
- }, O = ({ schema: e, ...t }) => {
555
- let { dataSource: n } = u() || {};
556
- return /* @__PURE__ */ m(l, {
623
+ }, O = c(({ schema: e, ...t }) => {
624
+ let { dataSource: n } = de() || {}, { t: r } = le();
625
+ return /* @__PURE__ */ h(d, {
557
626
  schema: e,
558
627
  mapping: D,
559
628
  dataSource: n,
560
629
  testId: "object-map",
561
- errorTitle: "This map’s data source could not be resolved",
562
- children: (e) => /* @__PURE__ */ m(E, {
630
+ errorTitle: r("element.map.sourceUnresolved", { defaultValue: "This map’s data source could not be resolved" }),
631
+ children: (e) => /* @__PURE__ */ h(E, {
563
632
  schema: e,
564
633
  dataSource: n,
565
634
  ...t
566
635
  })
567
636
  });
568
- };
637
+ });
569
638
  a.register("object-map", O, {
570
639
  namespace: "plugin-map",
571
640
  label: "Object Map",
572
641
  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
- }]
642
+ inputs: [
643
+ {
644
+ name: "objectName",
645
+ type: "string",
646
+ 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."
647
+ },
648
+ {
649
+ name: "map",
650
+ type: "object",
651
+ 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`."
652
+ },
653
+ {
654
+ name: "data",
655
+ type: "object",
656
+ 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`. 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`."
657
+ },
658
+ {
659
+ name: "staticData",
660
+ type: "array",
661
+ 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."
662
+ },
663
+ {
664
+ name: "filter",
665
+ type: "array",
666
+ 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."
667
+ },
668
+ {
669
+ name: "sort",
670
+ type: "array",
671
+ description: "Marker order in `[{ field, order }]` form, ordering the rows the map plots. Lowered to `$orderby` on the same query."
672
+ },
673
+ {
674
+ name: "mapStyle",
675
+ type: "string",
676
+ 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"
677
+ },
678
+ {
679
+ name: "navigation",
680
+ type: "object",
681
+ 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."
682
+ },
683
+ {
684
+ name: "enableClustering",
685
+ type: "boolean",
686
+ description: "Group nearby markers into one numbered cluster. Absent, the map clusters only above 100 markers; `false` turns clustering off at any count."
687
+ }
688
+ ]
599
689
  });
600
690
  //#endregion
601
691
  export { E as ObjectMap, O as ObjectMapRenderer };