@firecms/core 3.4.0-canary.e2466ba → 3.4.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.es.js CHANGED
@@ -10,7 +10,7 @@ import { useLocation, useNavigate, Link, NavLink, Routes, Route, createBrowserRo
10
10
  import Fuse from "fuse.js";
11
11
  import equal from "react-fast-compare";
12
12
  import jsonLogic from "json-logic-js";
13
- import { useTranslation as useTranslation$1, initReactI18next, I18nextProvider } from "react-i18next";
13
+ import { useTranslation as useTranslation$1, I18nContext, initReactI18next, I18nextProvider } from "react-i18next";
14
14
  import { format } from "date-fns";
15
15
  import * as locales from "date-fns/locale";
16
16
  import { useDropzone } from "react-dropzone";
@@ -431,6 +431,9 @@ function navigateToEntity({
431
431
  onClose
432
432
  });
433
433
  } else {
434
+ if (!fullIdPath && pathSegments?.some((segment) => segment.includes("/"))) {
435
+ console.warn(`navigateToEntity: no "fullIdPath" was given and "${path}" contains an entity id with a "/", so no unambiguous URL can be built for it. The link will point at a different location. Pass "fullIdPath" — the escaped chain — alongside "path".`);
436
+ }
434
437
  let to = navigation.buildUrlCollectionPath(entityId ? `${fullIdPath ?? path}/${encodeEntityId(entityId)}` : fullIdPath ?? path);
435
438
  if (entityId && selectedTab) {
436
439
  to += `/${selectedTab}`;
@@ -1334,7 +1337,12 @@ function getNavigationEntriesFromPath(props) {
1334
1337
  path: newPath,
1335
1338
  collections: collection.subcollections,
1336
1339
  currentFullPath: fullPath,
1337
- currentFullIdPath: fullIdPath,
1340
+ // The entity id is a hop in the id chain exactly as it is in the
1341
+ // other two. Without it a nested `fullIdPath` was
1342
+ // "products/locales" rather than "products/pid/locales", so any
1343
+ // URL built from it pointed at a collection that does not exist.
1344
+ // Escaped, because `fullIdPath` is URL-facing.
1345
+ currentFullIdPath: fullIdPath + "/" + encodedEntityId,
1338
1346
  currentFullUrlPath: fullUrlPath,
1339
1347
  currentPathSegments: entitySegments,
1340
1348
  contextEntityViews: props.contextEntityViews
@@ -4910,6 +4918,129 @@ function _temp$D(e_1) {
4910
4918
  ...e_1
4911
4919
  };
4912
4920
  }
4921
+ function entityCacheKey(path, entityId) {
4922
+ return `${path}/${entityId === void 0 ? entityId : encodeEntityId(entityId)}`;
4923
+ }
4924
+ const LOCAL_STORAGE_PREFIX = "entity_cache::";
4925
+ const entityCache = /* @__PURE__ */ new Map();
4926
+ const isLocalStorageAvailable = typeof localStorage !== "undefined";
4927
+ function customReplacer(key) {
4928
+ const value = this[key];
4929
+ if (value instanceof Date) {
4930
+ return {
4931
+ __type: "Date",
4932
+ value: value.toISOString()
4933
+ };
4934
+ }
4935
+ if (value instanceof EntityReference) {
4936
+ return {
4937
+ __type: "EntityReference",
4938
+ id: value.id,
4939
+ path: value.path,
4940
+ databaseId: value.databaseId
4941
+ };
4942
+ }
4943
+ if (value instanceof GeoPoint) {
4944
+ return {
4945
+ __type: "GeoPoint",
4946
+ latitude: value.latitude,
4947
+ longitude: value.longitude
4948
+ };
4949
+ }
4950
+ if (value instanceof Vector) {
4951
+ return {
4952
+ __type: "Vector",
4953
+ value: value.value
4954
+ };
4955
+ }
4956
+ return value;
4957
+ }
4958
+ function customReviver(key, value) {
4959
+ if (value && typeof value === "object" && "__type" in value) {
4960
+ switch (value.__type) {
4961
+ case "Date":
4962
+ return new Date(value.value);
4963
+ case "EntityReference":
4964
+ return new EntityReference(value.id, value.path, value.databaseId);
4965
+ case "GeoPoint":
4966
+ return new GeoPoint(value.latitude, value.longitude);
4967
+ case "Vector":
4968
+ return new Vector(value.value);
4969
+ default:
4970
+ return value;
4971
+ }
4972
+ }
4973
+ return value;
4974
+ }
4975
+ function saveEntityToCache(path, data) {
4976
+ if (isLocalStorageAvailable) {
4977
+ try {
4978
+ const key = LOCAL_STORAGE_PREFIX + path;
4979
+ const entityString = JSON.stringify(data, customReplacer);
4980
+ console.debug("Saving entity to localStorage:", {
4981
+ key,
4982
+ entityString
4983
+ });
4984
+ localStorage.setItem(key, entityString);
4985
+ } catch (error) {
4986
+ console.error(`Failed to save entity for path "${path}" to localStorage:`, error);
4987
+ }
4988
+ }
4989
+ }
4990
+ function removeEntityFromMemoryCache(path) {
4991
+ entityCache.delete(path);
4992
+ }
4993
+ function saveEntityToMemoryCache(path, data) {
4994
+ entityCache.set(path, data);
4995
+ }
4996
+ function getEntityFromMemoryCache(path) {
4997
+ return entityCache.get(path);
4998
+ }
4999
+ function getEntityFromCache(path) {
5000
+ if (isLocalStorageAvailable) {
5001
+ try {
5002
+ const key = LOCAL_STORAGE_PREFIX + path;
5003
+ const entityString = localStorage.getItem(key);
5004
+ if (entityString) {
5005
+ const entity = JSON.parse(entityString, customReviver);
5006
+ return entity;
5007
+ }
5008
+ } catch (error) {
5009
+ console.error(`Failed to load entity for path "${path}" from localStorage:`, error);
5010
+ }
5011
+ }
5012
+ return void 0;
5013
+ }
5014
+ function removeEntityFromCache(path) {
5015
+ if (isLocalStorageAvailable) {
5016
+ try {
5017
+ const key = LOCAL_STORAGE_PREFIX + path;
5018
+ localStorage.removeItem(key);
5019
+ } catch (error) {
5020
+ console.error(`Failed to remove entity for path "${path}" from localStorage:`, error);
5021
+ }
5022
+ }
5023
+ }
5024
+ function flattenKeys(obj, prefix = "", result = []) {
5025
+ if (isObject(obj) || Array.isArray(obj)) {
5026
+ const plainObject = isPlainObject(obj);
5027
+ if (!plainObject && prefix) {
5028
+ result.push(prefix);
5029
+ } else {
5030
+ for (const key in obj) {
5031
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
5032
+ const newKey = prefix ? Array.isArray(obj) ? `${prefix}[${key}]` : `${prefix}.${key}` : key;
5033
+ if (isObject(obj[key]) || Array.isArray(obj[key])) {
5034
+ flattenKeys(obj[key], newKey, result);
5035
+ } else {
5036
+ result.push(newKey);
5037
+ }
5038
+ }
5039
+ }
5040
+ }
5041
+ }
5042
+ return result;
5043
+ }
4913
5044
  const CACHE = {};
4914
5045
  function useEntityFetch(t0) {
4915
5046
  const $ = c(23);
@@ -4974,7 +5105,7 @@ function useEntityFetch(t0) {
4974
5105
  console.error(e);
4975
5106
  }
4976
5107
  }
4977
- CACHE[`${path}/${entityId}`] = updatedEntity;
5108
+ CACHE[entityCacheKey(path, entityId)] = updatedEntity;
4978
5109
  setEntity(updatedEntity);
4979
5110
  setDataLoading(false);
4980
5111
  setDataLoadingError(void 0);
@@ -4985,8 +5116,8 @@ function useEntityFetch(t0) {
4985
5116
  setEntity(void 0);
4986
5117
  setDataLoadingError(error);
4987
5118
  };
4988
- if (entityId && useCache && CACHE[`${path}/${entityId}`]) {
4989
- setEntity(CACHE[`${path}/${entityId}`]);
5119
+ if (entityId && useCache && CACHE[entityCacheKey(path, entityId)]) {
5120
+ setEntity(CACHE[entityCacheKey(path, entityId)]);
4990
5121
  setDataLoading(false);
4991
5122
  setDataLoadingError(void 0);
4992
5123
  return _temp$C;
@@ -6898,7 +7029,7 @@ function ReferencePreviewInternal(t0) {
6898
7029
  return t4;
6899
7030
  }
6900
7031
  function ReferencePreviewExisting(t0) {
6901
- const $ = c(36);
7032
+ const $ = c(37);
6902
7033
  const {
6903
7034
  reference,
6904
7035
  collection,
@@ -6932,37 +7063,38 @@ function ReferencePreviewExisting(t0) {
6932
7063
  dataLoading
6933
7064
  } = useEntityFetch(t1);
6934
7065
  if (entity) {
6935
- referencesCache.set(reference.pathWithId, entity);
7066
+ referencesCache.set(entityCacheKey(reference.path, reference.id), entity);
6936
7067
  }
6937
7068
  let t2;
6938
- if ($[5] !== entity || $[6] !== reference.pathWithId) {
6939
- t2 = entity ?? referencesCache.get(reference.pathWithId);
7069
+ if ($[5] !== entity || $[6] !== reference.id || $[7] !== reference.path) {
7070
+ t2 = entity ?? referencesCache.get(entityCacheKey(reference.path, reference.id));
6940
7071
  $[5] = entity;
6941
- $[6] = reference.pathWithId;
6942
- $[7] = t2;
7072
+ $[6] = reference.id;
7073
+ $[7] = reference.path;
7074
+ $[8] = t2;
6943
7075
  } else {
6944
- t2 = $[7];
7076
+ t2 = $[8];
6945
7077
  }
6946
7078
  const usedEntity = t2;
6947
7079
  let body;
6948
7080
  if (!reference) {
6949
7081
  let t32;
6950
- if ($[8] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
7082
+ if ($[9] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
6951
7083
  t32 = /* @__PURE__ */ jsx(ErrorView, { error: "Reference not set" });
6952
- $[8] = t32;
7084
+ $[9] = t32;
6953
7085
  } else {
6954
- t32 = $[8];
7086
+ t32 = $[9];
6955
7087
  }
6956
7088
  body = t32;
6957
7089
  } else {
6958
7090
  if (usedEntity && !usedEntity.values) {
6959
7091
  let t32;
6960
- if ($[9] !== reference.path) {
7092
+ if ($[10] !== reference.path) {
6961
7093
  t32 = /* @__PURE__ */ jsx(ErrorView, { error: "Reference does not exist", tooltip: reference.path });
6962
- $[9] = reference.path;
6963
- $[10] = t32;
7094
+ $[10] = reference.path;
7095
+ $[11] = t32;
6964
7096
  } else {
6965
- t32 = $[10];
7097
+ t32 = $[11];
6966
7098
  }
6967
7099
  body = t32;
6968
7100
  }
@@ -6971,15 +7103,15 @@ function ReferencePreviewExisting(t0) {
6971
7103
  const t32 = disabled ? void 0 : onClick;
6972
7104
  const t4 = disabled ? void 0 : hover;
6973
7105
  let t5;
6974
- if ($[11] !== body || $[12] !== size || $[13] !== t32 || $[14] !== t4) {
7106
+ if ($[12] !== body || $[13] !== size || $[14] !== t32 || $[15] !== t4) {
6975
7107
  t5 = /* @__PURE__ */ jsx(EntityPreviewContainer, { onClick: t32, hover: t4, size, children: body });
6976
- $[11] = body;
6977
- $[12] = size;
6978
- $[13] = t32;
6979
- $[14] = t4;
6980
- $[15] = t5;
7108
+ $[12] = body;
7109
+ $[13] = size;
7110
+ $[14] = t32;
7111
+ $[15] = t4;
7112
+ $[16] = t5;
6981
7113
  } else {
6982
- t5 = $[15];
7114
+ t5 = $[16];
6983
7115
  }
6984
7116
  return t5;
6985
7117
  }
@@ -6987,21 +7119,21 @@ function ReferencePreviewExisting(t0) {
6987
7119
  const t32 = disabled ? void 0 : onClick;
6988
7120
  const t4 = disabled ? void 0 : hover;
6989
7121
  let t5;
6990
- if ($[16] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
7122
+ if ($[17] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
6991
7123
  t5 = /* @__PURE__ */ jsx(Skeleton, {});
6992
- $[16] = t5;
7124
+ $[17] = t5;
6993
7125
  } else {
6994
- t5 = $[16];
7126
+ t5 = $[17];
6995
7127
  }
6996
7128
  let t6;
6997
- if ($[17] !== size || $[18] !== t32 || $[19] !== t4) {
7129
+ if ($[18] !== size || $[19] !== t32 || $[20] !== t4) {
6998
7130
  t6 = /* @__PURE__ */ jsx(EntityPreviewContainer, { onClick: t32, hover: t4, size, children: t5 });
6999
- $[17] = size;
7000
- $[18] = t32;
7001
- $[19] = t4;
7002
- $[20] = t6;
7131
+ $[18] = size;
7132
+ $[19] = t32;
7133
+ $[20] = t4;
7134
+ $[21] = t6;
7003
7135
  } else {
7004
- t6 = $[20];
7136
+ t6 = $[21];
7005
7137
  }
7006
7138
  return t6;
7007
7139
  }
@@ -7009,39 +7141,39 @@ function ReferencePreviewExisting(t0) {
7009
7141
  const t32 = disabled ? void 0 : onClick;
7010
7142
  const t4 = disabled ? void 0 : hover;
7011
7143
  let t5;
7012
- if ($[21] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
7144
+ if ($[22] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
7013
7145
  t5 = /* @__PURE__ */ jsx(ErrorView, { error: "Entity not found" });
7014
- $[21] = t5;
7146
+ $[22] = t5;
7015
7147
  } else {
7016
- t5 = $[21];
7148
+ t5 = $[22];
7017
7149
  }
7018
7150
  let t6;
7019
- if ($[22] !== size || $[23] !== t32 || $[24] !== t4) {
7151
+ if ($[23] !== size || $[24] !== t32 || $[25] !== t4) {
7020
7152
  t6 = /* @__PURE__ */ jsx(EntityPreviewContainer, { onClick: t32, hover: t4, size, children: t5 });
7021
- $[22] = size;
7022
- $[23] = t32;
7023
- $[24] = t4;
7024
- $[25] = t6;
7153
+ $[23] = size;
7154
+ $[24] = t32;
7155
+ $[25] = t4;
7156
+ $[26] = t6;
7025
7157
  } else {
7026
- t6 = $[25];
7158
+ t6 = $[26];
7027
7159
  }
7028
7160
  return t6;
7029
7161
  }
7030
7162
  let t3;
7031
- if ($[26] !== collection || $[27] !== disabled || $[28] !== hover || $[29] !== includeEntityLink || $[30] !== includeId || $[31] !== onClick || $[32] !== previewProperties || $[33] !== size || $[34] !== usedEntity) {
7163
+ if ($[27] !== collection || $[28] !== disabled || $[29] !== hover || $[30] !== includeEntityLink || $[31] !== includeId || $[32] !== onClick || $[33] !== previewProperties || $[34] !== size || $[35] !== usedEntity) {
7032
7164
  t3 = /* @__PURE__ */ jsx(EntityPreview, { size, previewKeys: previewProperties, disabled, entity: usedEntity, collection, onClick, includeEntityLink, includeId, hover });
7033
- $[26] = collection;
7034
- $[27] = disabled;
7035
- $[28] = hover;
7036
- $[29] = includeEntityLink;
7037
- $[30] = includeId;
7038
- $[31] = onClick;
7039
- $[32] = previewProperties;
7040
- $[33] = size;
7041
- $[34] = usedEntity;
7042
- $[35] = t3;
7165
+ $[27] = collection;
7166
+ $[28] = disabled;
7167
+ $[29] = hover;
7168
+ $[30] = includeEntityLink;
7169
+ $[31] = includeId;
7170
+ $[32] = onClick;
7171
+ $[33] = previewProperties;
7172
+ $[34] = size;
7173
+ $[35] = usedEntity;
7174
+ $[36] = t3;
7043
7175
  } else {
7044
- t3 = $[35];
7176
+ t3 = $[36];
7045
7177
  }
7046
7178
  return t3;
7047
7179
  }
@@ -9204,13 +9336,12 @@ class ErrorBoundary extends React__default.Component {
9204
9336
  constructor(props) {
9205
9337
  super(props);
9206
9338
  this.state = {
9207
- hasError: false
9339
+ error: null
9208
9340
  };
9209
9341
  }
9210
9342
  // eslint-disable-next-line n/handle-callback-err
9211
9343
  static getDerivedStateFromError(error) {
9212
9344
  return {
9213
- hasError: true,
9214
9345
  error
9215
9346
  };
9216
9347
  }
@@ -9218,20 +9349,23 @@ class ErrorBoundary extends React__default.Component {
9218
9349
  console.error(error);
9219
9350
  }
9220
9351
  render() {
9221
- if (this.state.hasError) {
9222
- return /* @__PURE__ */ jsx(FallbackView, { message: this.state.error?.message });
9352
+ if (this.state.error) {
9353
+ return /* @__PURE__ */ jsx(FallbackView, { message: this.state.error.message });
9223
9354
  }
9224
9355
  return this.props.children;
9225
9356
  }
9226
9357
  }
9227
9358
  function FallbackView(t0) {
9228
- const $ = c(12);
9359
+ const $ = c(13);
9360
+ const {
9361
+ message
9362
+ } = t0;
9229
9363
  const {
9230
9364
  t
9231
9365
  } = useTranslation();
9232
9366
  let t1;
9233
9367
  if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
9234
- t1 = /* @__PURE__ */ jsx(ErrorIcon, {});
9368
+ t1 = /* @__PURE__ */ jsx(ErrorIcon, { color: "error", size: "small" });
9235
9369
  $[0] = t1;
9236
9370
  } else {
9237
9371
  t1 = $[0];
@@ -9246,7 +9380,7 @@ function FallbackView(t0) {
9246
9380
  }
9247
9381
  let t3;
9248
9382
  if ($[3] !== t2) {
9249
- t3 = /* @__PURE__ */ jsxs("div", { className: "flex items-center mb-4 text-red-500 dark:text-red-400", children: [
9383
+ t3 = /* @__PURE__ */ jsxs("div", { className: "flex items-center m-2", children: [
9250
9384
  t1,
9251
9385
  /* @__PURE__ */ jsx("div", { className: "ml-4", children: t2 })
9252
9386
  ] });
@@ -9256,32 +9390,33 @@ function FallbackView(t0) {
9256
9390
  t3 = $[4];
9257
9391
  }
9258
9392
  let t4;
9259
- if ($[5] !== t) {
9260
- t4 = t("see_console_details");
9261
- $[5] = t;
9262
- $[6] = t4;
9393
+ if ($[5] !== message || $[6] !== t) {
9394
+ t4 = message ?? t("see_console_details");
9395
+ $[5] = message;
9396
+ $[6] = t;
9397
+ $[7] = t4;
9263
9398
  } else {
9264
- t4 = $[6];
9399
+ t4 = $[7];
9265
9400
  }
9266
9401
  let t5;
9267
- if ($[7] !== t4) {
9268
- t5 = /* @__PURE__ */ jsx("div", { className: "flex justify-center text-gray-500 dark:text-gray-400", children: t4 });
9269
- $[7] = t4;
9270
- $[8] = t5;
9402
+ if ($[8] !== t4) {
9403
+ t5 = /* @__PURE__ */ jsx(Typography, { variant: "caption", children: t4 });
9404
+ $[8] = t4;
9405
+ $[9] = t5;
9271
9406
  } else {
9272
- t5 = $[8];
9407
+ t5 = $[9];
9273
9408
  }
9274
9409
  let t6;
9275
- if ($[9] !== t3 || $[10] !== t5) {
9276
- t6 = /* @__PURE__ */ jsx("div", { className: "h-full w-full bg-slate-100 dark:bg-surface-900 flex items-center justify-center p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center m-4 bg-white dark:bg-surface-800 p-8 rounded-lg shadow-sm border border-gray-200 dark:border-surface-700", children: [
9410
+ if ($[10] !== t3 || $[11] !== t5) {
9411
+ t6 = /* @__PURE__ */ jsxs("div", { className: "flex flex-col m-2", children: [
9277
9412
  t3,
9278
9413
  t5
9279
- ] }) });
9280
- $[9] = t3;
9281
- $[10] = t5;
9282
- $[11] = t6;
9414
+ ] });
9415
+ $[10] = t3;
9416
+ $[11] = t5;
9417
+ $[12] = t6;
9283
9418
  } else {
9284
- t6 = $[11];
9419
+ t6 = $[12];
9285
9420
  }
9286
9421
  return t6;
9287
9422
  }
@@ -11338,126 +11473,6 @@ const PropertyTableCell = React__default.memo(function PropertyTableCell2({
11338
11473
  function areEqual(prevProps, nextProps) {
11339
11474
  return prevProps.height === nextProps.height && prevProps.propertyKey === nextProps.propertyKey && prevProps.align === nextProps.align && prevProps.width === nextProps.width && equal(prevProps.property, nextProps.property) && equal(prevProps.value, nextProps.value) && equal(prevProps.entity.id, nextProps.entity.id) && equal(prevProps.entity.values, nextProps.entity.values) && prevProps.isDragging === nextProps.isDragging && prevProps.isDraggable === nextProps.isDraggable && prevProps.frozen === nextProps.frozen;
11340
11475
  }
11341
- const LOCAL_STORAGE_PREFIX = "entity_cache::";
11342
- const entityCache = /* @__PURE__ */ new Map();
11343
- const isLocalStorageAvailable = typeof localStorage !== "undefined";
11344
- function customReplacer(key) {
11345
- const value = this[key];
11346
- if (value instanceof Date) {
11347
- return {
11348
- __type: "Date",
11349
- value: value.toISOString()
11350
- };
11351
- }
11352
- if (value instanceof EntityReference) {
11353
- return {
11354
- __type: "EntityReference",
11355
- id: value.id,
11356
- path: value.path,
11357
- databaseId: value.databaseId
11358
- };
11359
- }
11360
- if (value instanceof GeoPoint) {
11361
- return {
11362
- __type: "GeoPoint",
11363
- latitude: value.latitude,
11364
- longitude: value.longitude
11365
- };
11366
- }
11367
- if (value instanceof Vector) {
11368
- return {
11369
- __type: "Vector",
11370
- value: value.value
11371
- };
11372
- }
11373
- return value;
11374
- }
11375
- function customReviver(key, value) {
11376
- if (value && typeof value === "object" && "__type" in value) {
11377
- switch (value.__type) {
11378
- case "Date":
11379
- return new Date(value.value);
11380
- case "EntityReference":
11381
- return new EntityReference(value.id, value.path, value.databaseId);
11382
- case "GeoPoint":
11383
- return new GeoPoint(value.latitude, value.longitude);
11384
- case "Vector":
11385
- return new Vector(value.value);
11386
- default:
11387
- return value;
11388
- }
11389
- }
11390
- return value;
11391
- }
11392
- function saveEntityToCache(path, data) {
11393
- if (isLocalStorageAvailable) {
11394
- try {
11395
- const key = LOCAL_STORAGE_PREFIX + path;
11396
- const entityString = JSON.stringify(data, customReplacer);
11397
- console.debug("Saving entity to localStorage:", {
11398
- key,
11399
- entityString
11400
- });
11401
- localStorage.setItem(key, entityString);
11402
- } catch (error) {
11403
- console.error(`Failed to save entity for path "${path}" to localStorage:`, error);
11404
- }
11405
- }
11406
- }
11407
- function removeEntityFromMemoryCache(path) {
11408
- entityCache.delete(path);
11409
- }
11410
- function saveEntityToMemoryCache(path, data) {
11411
- entityCache.set(path, data);
11412
- }
11413
- function getEntityFromMemoryCache(path) {
11414
- return entityCache.get(path);
11415
- }
11416
- function getEntityFromCache(path) {
11417
- if (isLocalStorageAvailable) {
11418
- try {
11419
- const key = LOCAL_STORAGE_PREFIX + path;
11420
- const entityString = localStorage.getItem(key);
11421
- if (entityString) {
11422
- const entity = JSON.parse(entityString, customReviver);
11423
- return entity;
11424
- }
11425
- } catch (error) {
11426
- console.error(`Failed to load entity for path "${path}" from localStorage:`, error);
11427
- }
11428
- }
11429
- return void 0;
11430
- }
11431
- function removeEntityFromCache(path) {
11432
- if (isLocalStorageAvailable) {
11433
- try {
11434
- const key = LOCAL_STORAGE_PREFIX + path;
11435
- localStorage.removeItem(key);
11436
- } catch (error) {
11437
- console.error(`Failed to remove entity for path "${path}" from localStorage:`, error);
11438
- }
11439
- }
11440
- }
11441
- function flattenKeys(obj, prefix = "", result = []) {
11442
- if (isObject(obj) || Array.isArray(obj)) {
11443
- const plainObject = isPlainObject(obj);
11444
- if (!plainObject && prefix) {
11445
- result.push(prefix);
11446
- } else {
11447
- for (const key in obj) {
11448
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
11449
- const newKey = prefix ? Array.isArray(obj) ? `${prefix}[${key}]` : `${prefix}.${key}` : key;
11450
- if (isObject(obj[key]) || Array.isArray(obj[key])) {
11451
- flattenKeys(obj[key], newKey, result);
11452
- } else {
11453
- result.push(newKey);
11454
- }
11455
- }
11456
- }
11457
- }
11458
- }
11459
- return result;
11460
- }
11461
11476
  function FieldHelperText(t0) {
11462
11477
  const $ = c(10);
11463
11478
  const {
@@ -16980,7 +16995,7 @@ function EntityForm({
16980
16995
  const [savingError, setSavingError] = useState();
16981
16996
  const autoSave = collection.formAutoSave && !collection.customId;
16982
16997
  const baseInitialValues = useMemo(() => getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs), [authController, collection, path, status, entity, customizationController.propertyConfigs]);
16983
- const localChangesDataRaw = useMemo(() => entityId ? getEntityFromCache(path + "/" + entityId) : getEntityFromCache(path + "#new"), [entityId, path]);
16998
+ const localChangesDataRaw = useMemo(() => entityId ? getEntityFromCache(entityCacheKey(path, entityId)) : getEntityFromCache(path + "#new"), [entityId, path]);
16984
16999
  const [localChangesCleared, setLocalChangesCleared] = useState(false);
16985
17000
  const localChangesBackup = getLocalChangesBackup(collection);
16986
17001
  const autoApplyLocalChanges = localChangesBackup === "auto_apply";
@@ -17042,7 +17057,7 @@ function EntityForm({
17042
17057
  onValuesModified?.(false, initialValues_0);
17043
17058
  },
17044
17059
  onValuesChangeDeferred: (values_0, controller) => {
17045
- const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
17060
+ const key = status === "new" || status === "copy" ? path + "#new" : entityCacheKey(path, entityId);
17046
17061
  if (controller.dirty && localChangesBackup !== false) {
17047
17062
  const touchedValues = removeEmptyContainers(extractTouchedValues(values_0, controller.touched));
17048
17063
  if (touchedValues && Object.keys(touchedValues).length > 0) {
@@ -17107,8 +17122,8 @@ function EntityForm({
17107
17122
  removeEntityFromMemoryCache(path + "#new");
17108
17123
  removeEntityFromCache(path + "#new");
17109
17124
  } else {
17110
- removeEntityFromMemoryCache(path + "/" + entityId);
17111
- removeEntityFromCache(path + "/" + entityId);
17125
+ removeEntityFromMemoryCache(entityCacheKey(path, entityId));
17126
+ removeEntityFromCache(entityCacheKey(path, entityId));
17112
17127
  }
17113
17128
  }
17114
17129
  const onSaveSuccess = (updatedEntity) => {
@@ -17399,7 +17414,7 @@ function EntityForm({
17399
17414
  }), noValidate: true, className: cls("flex-1 flex flex-row w-full overflow-y-auto justify-center", className), children: [
17400
17415
  /* @__PURE__ */ jsx("div", { id: `form_${path}`, className: cls("relative flex flex-row max-w-4xl lg:max-w-3xl xl:max-w-4xl 2xl:max-w-6xl w-full h-fit"), children: /* @__PURE__ */ jsxs("div", { className: cls("flex flex-col w-full pt-12 pb-16 px-4 sm:px-8 md:px-10"), children: [
17401
17416
  /* @__PURE__ */ jsxs("div", { className: "flex flex-row gap-4 self-end sticky top-4 z-10", children: [
17402
- manualApplyLocalChanges && hasLocalChanges && /* @__PURE__ */ jsx(LocalChangesMenu, { cacheKey: status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId, properties: resolvedCollection.properties, cachedData: localChangesDataRaw, formex, onClearLocalChanges: () => setLocalChangesCleared(true) }),
17417
+ manualApplyLocalChanges && hasLocalChanges && /* @__PURE__ */ jsx(LocalChangesMenu, { cacheKey: status === "new" || status === "copy" ? path + "#new" : entityCacheKey(path, entityId), properties: resolvedCollection.properties, cachedData: localChangesDataRaw, formex, onClearLocalChanges: () => setLocalChangesCleared(true) }),
17403
17418
  formex.dirty ? /* @__PURE__ */ jsx(Tooltip, { title: "This form has been modified", children: /* @__PURE__ */ jsx(Chip, { size: "small", className: "py-1", colorScheme: "orangeDarker", children: /* @__PURE__ */ jsx(EditIcon, { size: "smallest" }) }) }) : /* @__PURE__ */ jsx(Tooltip, { title: "The current form is in sync with the database", children: /* @__PURE__ */ jsx(Chip, { size: "small", className: "py-1", children: /* @__PURE__ */ jsx(CheckIcon, { size: "smallest" }) }) })
17404
17419
  ] }),
17405
17420
  formView
@@ -17490,7 +17505,7 @@ const EntityCollectionRowActions = function EntityCollectionRowActions2({
17490
17505
  const collapsedActions = actions.filter((a_0) => a_0.collapsed || a_0.collapsed === void 0);
17491
17506
  const uncollapsedActions = actions.filter((a_1) => a_1.collapsed === false);
17492
17507
  const enableLocalChangesBackup = collection ? getLocalChangesBackup(collection) : false;
17493
- const cachedData = enableLocalChangesBackup ? getEntityFromCache(fullPath + "/" + entity.id) : void 0;
17508
+ const cachedData = enableLocalChangesBackup ? getEntityFromCache(entityCacheKey(fullPath, entity.id)) : void 0;
17494
17509
  const hasDraft = (() => {
17495
17510
  if (!cachedData || typeof cachedData !== "object" || Object.keys(cachedData).length === 0) return false;
17496
17511
  const realChanges = getChanges(cachedData, entity?.values ?? {});
@@ -20319,10 +20334,10 @@ function useDataSourceTableController({
20319
20334
  const [sortBy_0, setSortBy] = React__default.useState((updateUrl ? initialSortUrl : void 0) ?? initialSortInternal);
20320
20335
  const [searchString, setSearchString] = React__default.useState(updateUrl ? initialSearchUrl : void 0);
20321
20336
  useUpdateUrl(filterValues_0, sortBy_0, searchString, updateUrl);
20322
- const collectionScroll = scrollRestoration?.getCollectionScroll(fullPath, filterValues_0);
20323
- const initialItemCount = collectionScroll?.data.length ?? pageSize;
20337
+ const collectionScroll = scrollRestoration?.getCollectionScroll(resolvedPath, filterValues_0);
20338
+ const initialItemCount = collectionScroll?.data.length || pageSize;
20324
20339
  useEffect(() => {
20325
- if (scrollRestoration) {
20340
+ if (scrollRestoration && rawData.length > 0) {
20326
20341
  scrollRestoration.updateCollectionScroll({
20327
20342
  fullPath: resolvedPath,
20328
20343
  scrollOffset: collectionScroll?.scrollOffset ?? 0,
@@ -20866,6 +20881,7 @@ const copyEntityAction = {
20866
20881
  context,
20867
20882
  fullPath,
20868
20883
  pathSegments,
20884
+ fullIdPath,
20869
20885
  highlightEntity,
20870
20886
  unhighlightEntity,
20871
20887
  openEntityMode
@@ -20880,14 +20896,14 @@ const copyEntityAction = {
20880
20896
  });
20881
20897
  const path = collection?.collectionGroup ? collection.path : fullPath ?? collection?.path ?? entity.path;
20882
20898
  const resolvedPathSegments = collection?.collectionGroup ? void 0 : pathSegments ?? entity.pathSegments;
20883
- const fullIdPath = collection?.collectionGroup ? collection.id : fullPath ?? collection?.id ?? entity.path;
20899
+ const newFullIdPath = collection?.collectionGroup ? collection.id : fullIdPath ?? collection?.id;
20884
20900
  navigateToEntity({
20885
20901
  openEntityMode,
20886
20902
  collection,
20887
20903
  entityId: entity.id,
20888
20904
  path,
20889
20905
  pathSegments: resolvedPathSegments,
20890
- fullIdPath,
20906
+ fullIdPath: newFullIdPath,
20891
20907
  copy: true,
20892
20908
  sideEntityController: context.sideEntityController,
20893
20909
  onClose: () => unhighlightEntity?.(entity),
@@ -26755,7 +26771,7 @@ const EntityCollectionView$1 = React__default.memo(function EntityCollectionView
26755
26771
  }) => {
26756
26772
  const collectionsWithPath = navigation.getParentReferencesFromPath(entity_4.path, entity_4.pathSegments);
26757
26773
  return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 w-full", children: collectionsWithPath.map((reference) => {
26758
- return /* @__PURE__ */ jsx(ReferencePreview, { reference, size: "small" }, reference.path + "/" + reference.id);
26774
+ return /* @__PURE__ */ jsx(ReferencePreview, { reference, size: "small" }, entityCacheKey(reference.path, reference.id));
26759
26775
  }) });
26760
26776
  }
26761
26777
  }] : [];
@@ -29900,7 +29916,7 @@ function EntitySidePanel(props) {
29900
29916
  }) => /* @__PURE__ */ jsxs(Fragment, { children: [
29901
29917
  /* @__PURE__ */ jsx(IconButton, { className: "self-center", size: "smallest", onClick: onClose, children: /* @__PURE__ */ jsx(CloseIcon, { size: "smallest" }) }),
29902
29918
  allowFullScreen && /* @__PURE__ */ jsx(IconButton, { className: "self-center", size: "smallest", onClick: () => {
29903
- const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
29919
+ const key = status === "new" || status === "copy" ? path + "#new" : entityCacheKey(path, entityId);
29904
29920
  saveEntityToMemoryCache(key, values);
29905
29921
  if (entityId) navigate(location.pathname + location.search);
29906
29922
  else navigate(location.pathname + location.search + "#new");
@@ -30408,7 +30424,7 @@ function EntityEditView$1({
30408
30424
  databaseId: props.databaseId,
30409
30425
  useCache: false
30410
30426
  });
30411
- const initialDirtyValues = entityId ? getEntityFromMemoryCache(props.path + "/" + entityId) : getEntityFromMemoryCache(props.path + "#new");
30427
+ const initialDirtyValues = entityId ? getEntityFromMemoryCache(entityCacheKey(props.path, entityId)) : getEntityFromMemoryCache(props.path + "#new");
30412
30428
  const authController = useAuthController();
30413
30429
  const initialStatus = props.copy ? "copy" : entityId ? "existing" : "new";
30414
30430
  const [status, setStatus] = useState(initialStatus);
@@ -30859,19 +30875,21 @@ function buildSidePanelsFromUrl(path, collections, newFlag) {
30859
30875
  return sidePanel ? [sidePanel] : [];
30860
30876
  }
30861
30877
  const propsToSidePanel = (props, buildUrlCollectionPath, resolveIdsFrom, smallLayout, customizationController, authController, locationSearch) => {
30862
- const collectionPath = removeInitialAndTrailingSlashes(props.path);
30863
- const urlPath = props.entityId ? buildUrlCollectionPath(`${collectionPath}/${encodeEntityId(props.entityId)}${props.selectedTab ? "/" + props.selectedTab : ""}${locationSearch}#${SIDE_URL_HASH}`) : buildUrlCollectionPath(`${collectionPath}${locationSearch}#${NEW_URL_HASH}`);
30878
+ const urlChain = removeInitialAndTrailingSlashes(props.fullIdPath ?? props.path);
30879
+ const urlPath = props.entityId ? buildUrlCollectionPath(`${urlChain}/${encodeEntityId(props.entityId)}${props.selectedTab ? "/" + props.selectedTab : ""}${locationSearch}#${SIDE_URL_HASH}`) : buildUrlCollectionPath(`${urlChain}${locationSearch}#${NEW_URL_HASH}`);
30864
30880
  const resolvedPanelProps = {
30865
30881
  ...props,
30866
30882
  formProps: props.formProps
30867
30883
  };
30868
30884
  const entityViewWidth = getEntityViewWidth(props, smallLayout, customizationController, authController);
30869
30885
  return {
30870
- key: `${props.path}/${props.entityId}`,
30886
+ // Built from the escaped chain so that two different chains which happen to flatten
30887
+ // to the same raw string are still two different panels.
30888
+ key: `${urlChain}/${props.entityId ? encodeEntityId(props.entityId) : ""}`,
30871
30889
  component: void 0,
30872
30890
  // Lazy render in SideDialogs for better performance
30873
30891
  urlPath,
30874
- parentUrlPath: buildUrlCollectionPath(collectionPath),
30892
+ parentUrlPath: buildUrlCollectionPath(urlChain),
30875
30893
  width: entityViewWidth,
30876
30894
  onClose: props.onClose,
30877
30895
  additional: resolvedPanelProps
@@ -39236,12 +39254,27 @@ function FireCMSi18nProvider({
39236
39254
  translations,
39237
39255
  children
39238
39256
  }) {
39257
+ const parentI18n = useContext(I18nContext)?.i18n;
39258
+ const parentInstance = parentI18n?.hasResourceBundle?.("en", FIRECMS_NS) ? parentI18n : void 0;
39239
39259
  const i18nRef = useRef(null);
39240
39260
  const [ready, setReady] = React__default.useState(false);
39241
39261
  if (!i18nRef.current) {
39242
39262
  const instance = i18next.createInstance();
39243
39263
  const resources = buildResources(translations);
39244
- let initialLocale = locale;
39264
+ if (parentInstance) {
39265
+ const inherited = parentInstance.services?.resourceStore?.data ?? {};
39266
+ for (const [lang, namespaces] of Object.entries(inherited)) {
39267
+ const bundle = namespaces?.[FIRECMS_NS];
39268
+ if (!bundle) continue;
39269
+ resources[lang] = {
39270
+ [FIRECMS_NS]: {
39271
+ ...bundle,
39272
+ ...translations?.[lang] ?? {}
39273
+ }
39274
+ };
39275
+ }
39276
+ }
39277
+ let initialLocale = parentInstance?.language ?? locale;
39245
39278
  if (typeof window !== "undefined") {
39246
39279
  const stored = localStorage.getItem(FIRECMS_LOCALE_STORAGE_KEY);
39247
39280
  if (stored) initialLocale = stored;
@@ -39266,6 +39299,17 @@ function FireCMSi18nProvider({
39266
39299
  });
39267
39300
  i18nRef.current = instance;
39268
39301
  }
39302
+ useEffect(() => {
39303
+ if (!parentInstance || !i18nRef.current) return;
39304
+ const instance_0 = i18nRef.current;
39305
+ const follow = (lng_0) => {
39306
+ if (instance_0.language !== lng_0) instance_0.changeLanguage(lng_0);
39307
+ };
39308
+ parentInstance.on("languageChanged", follow);
39309
+ return () => {
39310
+ parentInstance.off("languageChanged", follow);
39311
+ };
39312
+ }, [parentInstance]);
39269
39313
  useEffect(() => {
39270
39314
  if (i18nRef.current && i18nRef.current.language !== locale) {
39271
39315
  const hasUserPreference = typeof window !== "undefined" && Boolean(localStorage.getItem(FIRECMS_LOCALE_STORAGE_KEY));
@@ -39277,11 +39321,11 @@ function FireCMSi18nProvider({
39277
39321
  useEffect(() => {
39278
39322
  if (!i18nRef.current) return;
39279
39323
  const resources_0 = buildResources(translations);
39280
- for (const [lang, bundle] of Object.entries(resources_0)) {
39324
+ for (const [lang_0, bundle_0] of Object.entries(resources_0)) {
39281
39325
  i18nRef.current.addResourceBundle(
39282
- lang,
39326
+ lang_0,
39283
39327
  FIRECMS_NS,
39284
- bundle[FIRECMS_NS],
39328
+ bundle_0[FIRECMS_NS],
39285
39329
  true,
39286
39330
  // deep merge
39287
39331
  true