@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.umd.js CHANGED
@@ -408,6 +408,9 @@
408
408
  onClose
409
409
  });
410
410
  } else {
411
+ if (!fullIdPath && pathSegments?.some((segment) => segment.includes("/"))) {
412
+ 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".`);
413
+ }
411
414
  let to = navigation.buildUrlCollectionPath(entityId ? `${fullIdPath ?? path}/${encodeEntityId(entityId)}` : fullIdPath ?? path);
412
415
  if (entityId && selectedTab) {
413
416
  to += `/${selectedTab}`;
@@ -1311,7 +1314,12 @@
1311
1314
  path: newPath,
1312
1315
  collections: collection.subcollections,
1313
1316
  currentFullPath: fullPath,
1314
- currentFullIdPath: fullIdPath,
1317
+ // The entity id is a hop in the id chain exactly as it is in the
1318
+ // other two. Without it a nested `fullIdPath` was
1319
+ // "products/locales" rather than "products/pid/locales", so any
1320
+ // URL built from it pointed at a collection that does not exist.
1321
+ // Escaped, because `fullIdPath` is URL-facing.
1322
+ currentFullIdPath: fullIdPath + "/" + encodedEntityId,
1315
1323
  currentFullUrlPath: fullUrlPath,
1316
1324
  currentPathSegments: entitySegments,
1317
1325
  contextEntityViews: props.contextEntityViews
@@ -4887,6 +4895,129 @@
4887
4895
  ...e_1
4888
4896
  };
4889
4897
  }
4898
+ function entityCacheKey(path, entityId) {
4899
+ return `${path}/${entityId === void 0 ? entityId : encodeEntityId(entityId)}`;
4900
+ }
4901
+ const LOCAL_STORAGE_PREFIX = "entity_cache::";
4902
+ const entityCache = /* @__PURE__ */ new Map();
4903
+ const isLocalStorageAvailable = typeof localStorage !== "undefined";
4904
+ function customReplacer(key) {
4905
+ const value = this[key];
4906
+ if (value instanceof Date) {
4907
+ return {
4908
+ __type: "Date",
4909
+ value: value.toISOString()
4910
+ };
4911
+ }
4912
+ if (value instanceof EntityReference) {
4913
+ return {
4914
+ __type: "EntityReference",
4915
+ id: value.id,
4916
+ path: value.path,
4917
+ databaseId: value.databaseId
4918
+ };
4919
+ }
4920
+ if (value instanceof GeoPoint) {
4921
+ return {
4922
+ __type: "GeoPoint",
4923
+ latitude: value.latitude,
4924
+ longitude: value.longitude
4925
+ };
4926
+ }
4927
+ if (value instanceof Vector) {
4928
+ return {
4929
+ __type: "Vector",
4930
+ value: value.value
4931
+ };
4932
+ }
4933
+ return value;
4934
+ }
4935
+ function customReviver(key, value) {
4936
+ if (value && typeof value === "object" && "__type" in value) {
4937
+ switch (value.__type) {
4938
+ case "Date":
4939
+ return new Date(value.value);
4940
+ case "EntityReference":
4941
+ return new EntityReference(value.id, value.path, value.databaseId);
4942
+ case "GeoPoint":
4943
+ return new GeoPoint(value.latitude, value.longitude);
4944
+ case "Vector":
4945
+ return new Vector(value.value);
4946
+ default:
4947
+ return value;
4948
+ }
4949
+ }
4950
+ return value;
4951
+ }
4952
+ function saveEntityToCache(path, data) {
4953
+ if (isLocalStorageAvailable) {
4954
+ try {
4955
+ const key = LOCAL_STORAGE_PREFIX + path;
4956
+ const entityString = JSON.stringify(data, customReplacer);
4957
+ console.debug("Saving entity to localStorage:", {
4958
+ key,
4959
+ entityString
4960
+ });
4961
+ localStorage.setItem(key, entityString);
4962
+ } catch (error) {
4963
+ console.error(`Failed to save entity for path "${path}" to localStorage:`, error);
4964
+ }
4965
+ }
4966
+ }
4967
+ function removeEntityFromMemoryCache(path) {
4968
+ entityCache.delete(path);
4969
+ }
4970
+ function saveEntityToMemoryCache(path, data) {
4971
+ entityCache.set(path, data);
4972
+ }
4973
+ function getEntityFromMemoryCache(path) {
4974
+ return entityCache.get(path);
4975
+ }
4976
+ function getEntityFromCache(path) {
4977
+ if (isLocalStorageAvailable) {
4978
+ try {
4979
+ const key = LOCAL_STORAGE_PREFIX + path;
4980
+ const entityString = localStorage.getItem(key);
4981
+ if (entityString) {
4982
+ const entity = JSON.parse(entityString, customReviver);
4983
+ return entity;
4984
+ }
4985
+ } catch (error) {
4986
+ console.error(`Failed to load entity for path "${path}" from localStorage:`, error);
4987
+ }
4988
+ }
4989
+ return void 0;
4990
+ }
4991
+ function removeEntityFromCache(path) {
4992
+ if (isLocalStorageAvailable) {
4993
+ try {
4994
+ const key = LOCAL_STORAGE_PREFIX + path;
4995
+ localStorage.removeItem(key);
4996
+ } catch (error) {
4997
+ console.error(`Failed to remove entity for path "${path}" from localStorage:`, error);
4998
+ }
4999
+ }
5000
+ }
5001
+ function flattenKeys(obj, prefix = "", result = []) {
5002
+ if (isObject(obj) || Array.isArray(obj)) {
5003
+ const plainObject = isPlainObject(obj);
5004
+ if (!plainObject && prefix) {
5005
+ result.push(prefix);
5006
+ } else {
5007
+ for (const key in obj) {
5008
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
5009
+ const newKey = prefix ? Array.isArray(obj) ? `${prefix}[${key}]` : `${prefix}.${key}` : key;
5010
+ if (isObject(obj[key]) || Array.isArray(obj[key])) {
5011
+ flattenKeys(obj[key], newKey, result);
5012
+ } else {
5013
+ result.push(newKey);
5014
+ }
5015
+ }
5016
+ }
5017
+ }
5018
+ }
5019
+ return result;
5020
+ }
4890
5021
  const CACHE = {};
4891
5022
  function useEntityFetch(t0) {
4892
5023
  const $ = reactCompilerRuntime.c(23);
@@ -4951,7 +5082,7 @@
4951
5082
  console.error(e);
4952
5083
  }
4953
5084
  }
4954
- CACHE[`${path}/${entityId}`] = updatedEntity;
5085
+ CACHE[entityCacheKey(path, entityId)] = updatedEntity;
4955
5086
  setEntity(updatedEntity);
4956
5087
  setDataLoading(false);
4957
5088
  setDataLoadingError(void 0);
@@ -4962,8 +5093,8 @@
4962
5093
  setEntity(void 0);
4963
5094
  setDataLoadingError(error);
4964
5095
  };
4965
- if (entityId && useCache && CACHE[`${path}/${entityId}`]) {
4966
- setEntity(CACHE[`${path}/${entityId}`]);
5096
+ if (entityId && useCache && CACHE[entityCacheKey(path, entityId)]) {
5097
+ setEntity(CACHE[entityCacheKey(path, entityId)]);
4967
5098
  setDataLoading(false);
4968
5099
  setDataLoadingError(void 0);
4969
5100
  return _temp$C;
@@ -6875,7 +7006,7 @@
6875
7006
  return t4;
6876
7007
  }
6877
7008
  function ReferencePreviewExisting(t0) {
6878
- const $ = reactCompilerRuntime.c(36);
7009
+ const $ = reactCompilerRuntime.c(37);
6879
7010
  const {
6880
7011
  reference,
6881
7012
  collection,
@@ -6909,37 +7040,38 @@
6909
7040
  dataLoading
6910
7041
  } = useEntityFetch(t1);
6911
7042
  if (entity) {
6912
- referencesCache.set(reference.pathWithId, entity);
7043
+ referencesCache.set(entityCacheKey(reference.path, reference.id), entity);
6913
7044
  }
6914
7045
  let t2;
6915
- if ($[5] !== entity || $[6] !== reference.pathWithId) {
6916
- t2 = entity ?? referencesCache.get(reference.pathWithId);
7046
+ if ($[5] !== entity || $[6] !== reference.id || $[7] !== reference.path) {
7047
+ t2 = entity ?? referencesCache.get(entityCacheKey(reference.path, reference.id));
6917
7048
  $[5] = entity;
6918
- $[6] = reference.pathWithId;
6919
- $[7] = t2;
7049
+ $[6] = reference.id;
7050
+ $[7] = reference.path;
7051
+ $[8] = t2;
6920
7052
  } else {
6921
- t2 = $[7];
7053
+ t2 = $[8];
6922
7054
  }
6923
7055
  const usedEntity = t2;
6924
7056
  let body;
6925
7057
  if (!reference) {
6926
7058
  let t32;
6927
- if ($[8] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
7059
+ if ($[9] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
6928
7060
  t32 = /* @__PURE__ */ jsxRuntime.jsx(ErrorView, { error: "Reference not set" });
6929
- $[8] = t32;
7061
+ $[9] = t32;
6930
7062
  } else {
6931
- t32 = $[8];
7063
+ t32 = $[9];
6932
7064
  }
6933
7065
  body = t32;
6934
7066
  } else {
6935
7067
  if (usedEntity && !usedEntity.values) {
6936
7068
  let t32;
6937
- if ($[9] !== reference.path) {
7069
+ if ($[10] !== reference.path) {
6938
7070
  t32 = /* @__PURE__ */ jsxRuntime.jsx(ErrorView, { error: "Reference does not exist", tooltip: reference.path });
6939
- $[9] = reference.path;
6940
- $[10] = t32;
7071
+ $[10] = reference.path;
7072
+ $[11] = t32;
6941
7073
  } else {
6942
- t32 = $[10];
7074
+ t32 = $[11];
6943
7075
  }
6944
7076
  body = t32;
6945
7077
  }
@@ -6948,15 +7080,15 @@
6948
7080
  const t32 = disabled ? void 0 : onClick;
6949
7081
  const t4 = disabled ? void 0 : hover;
6950
7082
  let t5;
6951
- if ($[11] !== body || $[12] !== size || $[13] !== t32 || $[14] !== t4) {
7083
+ if ($[12] !== body || $[13] !== size || $[14] !== t32 || $[15] !== t4) {
6952
7084
  t5 = /* @__PURE__ */ jsxRuntime.jsx(EntityPreviewContainer, { onClick: t32, hover: t4, size, children: body });
6953
- $[11] = body;
6954
- $[12] = size;
6955
- $[13] = t32;
6956
- $[14] = t4;
6957
- $[15] = t5;
7085
+ $[12] = body;
7086
+ $[13] = size;
7087
+ $[14] = t32;
7088
+ $[15] = t4;
7089
+ $[16] = t5;
6958
7090
  } else {
6959
- t5 = $[15];
7091
+ t5 = $[16];
6960
7092
  }
6961
7093
  return t5;
6962
7094
  }
@@ -6964,21 +7096,21 @@
6964
7096
  const t32 = disabled ? void 0 : onClick;
6965
7097
  const t4 = disabled ? void 0 : hover;
6966
7098
  let t5;
6967
- if ($[16] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
7099
+ if ($[17] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
6968
7100
  t5 = /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, {});
6969
- $[16] = t5;
7101
+ $[17] = t5;
6970
7102
  } else {
6971
- t5 = $[16];
7103
+ t5 = $[17];
6972
7104
  }
6973
7105
  let t6;
6974
- if ($[17] !== size || $[18] !== t32 || $[19] !== t4) {
7106
+ if ($[18] !== size || $[19] !== t32 || $[20] !== t4) {
6975
7107
  t6 = /* @__PURE__ */ jsxRuntime.jsx(EntityPreviewContainer, { onClick: t32, hover: t4, size, children: t5 });
6976
- $[17] = size;
6977
- $[18] = t32;
6978
- $[19] = t4;
6979
- $[20] = t6;
7108
+ $[18] = size;
7109
+ $[19] = t32;
7110
+ $[20] = t4;
7111
+ $[21] = t6;
6980
7112
  } else {
6981
- t6 = $[20];
7113
+ t6 = $[21];
6982
7114
  }
6983
7115
  return t6;
6984
7116
  }
@@ -6986,39 +7118,39 @@
6986
7118
  const t32 = disabled ? void 0 : onClick;
6987
7119
  const t4 = disabled ? void 0 : hover;
6988
7120
  let t5;
6989
- if ($[21] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
7121
+ if ($[22] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
6990
7122
  t5 = /* @__PURE__ */ jsxRuntime.jsx(ErrorView, { error: "Entity not found" });
6991
- $[21] = t5;
7123
+ $[22] = t5;
6992
7124
  } else {
6993
- t5 = $[21];
7125
+ t5 = $[22];
6994
7126
  }
6995
7127
  let t6;
6996
- if ($[22] !== size || $[23] !== t32 || $[24] !== t4) {
7128
+ if ($[23] !== size || $[24] !== t32 || $[25] !== t4) {
6997
7129
  t6 = /* @__PURE__ */ jsxRuntime.jsx(EntityPreviewContainer, { onClick: t32, hover: t4, size, children: t5 });
6998
- $[22] = size;
6999
- $[23] = t32;
7000
- $[24] = t4;
7001
- $[25] = t6;
7130
+ $[23] = size;
7131
+ $[24] = t32;
7132
+ $[25] = t4;
7133
+ $[26] = t6;
7002
7134
  } else {
7003
- t6 = $[25];
7135
+ t6 = $[26];
7004
7136
  }
7005
7137
  return t6;
7006
7138
  }
7007
7139
  let t3;
7008
- if ($[26] !== collection || $[27] !== disabled || $[28] !== hover || $[29] !== includeEntityLink || $[30] !== includeId || $[31] !== onClick || $[32] !== previewProperties || $[33] !== size || $[34] !== usedEntity) {
7140
+ if ($[27] !== collection || $[28] !== disabled || $[29] !== hover || $[30] !== includeEntityLink || $[31] !== includeId || $[32] !== onClick || $[33] !== previewProperties || $[34] !== size || $[35] !== usedEntity) {
7009
7141
  t3 = /* @__PURE__ */ jsxRuntime.jsx(EntityPreview, { size, previewKeys: previewProperties, disabled, entity: usedEntity, collection, onClick, includeEntityLink, includeId, hover });
7010
- $[26] = collection;
7011
- $[27] = disabled;
7012
- $[28] = hover;
7013
- $[29] = includeEntityLink;
7014
- $[30] = includeId;
7015
- $[31] = onClick;
7016
- $[32] = previewProperties;
7017
- $[33] = size;
7018
- $[34] = usedEntity;
7019
- $[35] = t3;
7142
+ $[27] = collection;
7143
+ $[28] = disabled;
7144
+ $[29] = hover;
7145
+ $[30] = includeEntityLink;
7146
+ $[31] = includeId;
7147
+ $[32] = onClick;
7148
+ $[33] = previewProperties;
7149
+ $[34] = size;
7150
+ $[35] = usedEntity;
7151
+ $[36] = t3;
7020
7152
  } else {
7021
- t3 = $[35];
7153
+ t3 = $[36];
7022
7154
  }
7023
7155
  return t3;
7024
7156
  }
@@ -9181,13 +9313,12 @@
9181
9313
  constructor(props) {
9182
9314
  super(props);
9183
9315
  this.state = {
9184
- hasError: false
9316
+ error: null
9185
9317
  };
9186
9318
  }
9187
9319
  // eslint-disable-next-line n/handle-callback-err
9188
9320
  static getDerivedStateFromError(error) {
9189
9321
  return {
9190
- hasError: true,
9191
9322
  error
9192
9323
  };
9193
9324
  }
@@ -9195,20 +9326,23 @@
9195
9326
  console.error(error);
9196
9327
  }
9197
9328
  render() {
9198
- if (this.state.hasError) {
9199
- return /* @__PURE__ */ jsxRuntime.jsx(FallbackView, { message: this.state.error?.message });
9329
+ if (this.state.error) {
9330
+ return /* @__PURE__ */ jsxRuntime.jsx(FallbackView, { message: this.state.error.message });
9200
9331
  }
9201
9332
  return this.props.children;
9202
9333
  }
9203
9334
  }
9204
9335
  function FallbackView(t0) {
9205
- const $ = reactCompilerRuntime.c(12);
9336
+ const $ = reactCompilerRuntime.c(13);
9337
+ const {
9338
+ message
9339
+ } = t0;
9206
9340
  const {
9207
9341
  t
9208
9342
  } = useTranslation();
9209
9343
  let t1;
9210
9344
  if ($[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel")) {
9211
- t1 = /* @__PURE__ */ jsxRuntime.jsx(ui.ErrorIcon, {});
9345
+ t1 = /* @__PURE__ */ jsxRuntime.jsx(ui.ErrorIcon, { color: "error", size: "small" });
9212
9346
  $[0] = t1;
9213
9347
  } else {
9214
9348
  t1 = $[0];
@@ -9223,7 +9357,7 @@
9223
9357
  }
9224
9358
  let t3;
9225
9359
  if ($[3] !== t2) {
9226
- t3 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center mb-4 text-red-500 dark:text-red-400", children: [
9360
+ t3 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center m-2", children: [
9227
9361
  t1,
9228
9362
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-4", children: t2 })
9229
9363
  ] });
@@ -9233,32 +9367,33 @@
9233
9367
  t3 = $[4];
9234
9368
  }
9235
9369
  let t4;
9236
- if ($[5] !== t) {
9237
- t4 = t("see_console_details");
9238
- $[5] = t;
9239
- $[6] = t4;
9370
+ if ($[5] !== message || $[6] !== t) {
9371
+ t4 = message ?? t("see_console_details");
9372
+ $[5] = message;
9373
+ $[6] = t;
9374
+ $[7] = t4;
9240
9375
  } else {
9241
- t4 = $[6];
9376
+ t4 = $[7];
9242
9377
  }
9243
9378
  let t5;
9244
- if ($[7] !== t4) {
9245
- t5 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center text-gray-500 dark:text-gray-400", children: t4 });
9246
- $[7] = t4;
9247
- $[8] = t5;
9379
+ if ($[8] !== t4) {
9380
+ t5 = /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", children: t4 });
9381
+ $[8] = t4;
9382
+ $[9] = t5;
9248
9383
  } else {
9249
- t5 = $[8];
9384
+ t5 = $[9];
9250
9385
  }
9251
9386
  let t6;
9252
- if ($[9] !== t3 || $[10] !== t5) {
9253
- t6 = /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full bg-slate-100 dark:bg-surface-900 flex items-center justify-center p-4", children: /* @__PURE__ */ jsxRuntime.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: [
9387
+ if ($[10] !== t3 || $[11] !== t5) {
9388
+ t6 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col m-2", children: [
9254
9389
  t3,
9255
9390
  t5
9256
- ] }) });
9257
- $[9] = t3;
9258
- $[10] = t5;
9259
- $[11] = t6;
9391
+ ] });
9392
+ $[10] = t3;
9393
+ $[11] = t5;
9394
+ $[12] = t6;
9260
9395
  } else {
9261
- t6 = $[11];
9396
+ t6 = $[12];
9262
9397
  }
9263
9398
  return t6;
9264
9399
  }
@@ -11315,126 +11450,6 @@
11315
11450
  function areEqual(prevProps, nextProps) {
11316
11451
  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;
11317
11452
  }
11318
- const LOCAL_STORAGE_PREFIX = "entity_cache::";
11319
- const entityCache = /* @__PURE__ */ new Map();
11320
- const isLocalStorageAvailable = typeof localStorage !== "undefined";
11321
- function customReplacer(key) {
11322
- const value = this[key];
11323
- if (value instanceof Date) {
11324
- return {
11325
- __type: "Date",
11326
- value: value.toISOString()
11327
- };
11328
- }
11329
- if (value instanceof EntityReference) {
11330
- return {
11331
- __type: "EntityReference",
11332
- id: value.id,
11333
- path: value.path,
11334
- databaseId: value.databaseId
11335
- };
11336
- }
11337
- if (value instanceof GeoPoint) {
11338
- return {
11339
- __type: "GeoPoint",
11340
- latitude: value.latitude,
11341
- longitude: value.longitude
11342
- };
11343
- }
11344
- if (value instanceof Vector) {
11345
- return {
11346
- __type: "Vector",
11347
- value: value.value
11348
- };
11349
- }
11350
- return value;
11351
- }
11352
- function customReviver(key, value) {
11353
- if (value && typeof value === "object" && "__type" in value) {
11354
- switch (value.__type) {
11355
- case "Date":
11356
- return new Date(value.value);
11357
- case "EntityReference":
11358
- return new EntityReference(value.id, value.path, value.databaseId);
11359
- case "GeoPoint":
11360
- return new GeoPoint(value.latitude, value.longitude);
11361
- case "Vector":
11362
- return new Vector(value.value);
11363
- default:
11364
- return value;
11365
- }
11366
- }
11367
- return value;
11368
- }
11369
- function saveEntityToCache(path, data) {
11370
- if (isLocalStorageAvailable) {
11371
- try {
11372
- const key = LOCAL_STORAGE_PREFIX + path;
11373
- const entityString = JSON.stringify(data, customReplacer);
11374
- console.debug("Saving entity to localStorage:", {
11375
- key,
11376
- entityString
11377
- });
11378
- localStorage.setItem(key, entityString);
11379
- } catch (error) {
11380
- console.error(`Failed to save entity for path "${path}" to localStorage:`, error);
11381
- }
11382
- }
11383
- }
11384
- function removeEntityFromMemoryCache(path) {
11385
- entityCache.delete(path);
11386
- }
11387
- function saveEntityToMemoryCache(path, data) {
11388
- entityCache.set(path, data);
11389
- }
11390
- function getEntityFromMemoryCache(path) {
11391
- return entityCache.get(path);
11392
- }
11393
- function getEntityFromCache(path) {
11394
- if (isLocalStorageAvailable) {
11395
- try {
11396
- const key = LOCAL_STORAGE_PREFIX + path;
11397
- const entityString = localStorage.getItem(key);
11398
- if (entityString) {
11399
- const entity = JSON.parse(entityString, customReviver);
11400
- return entity;
11401
- }
11402
- } catch (error) {
11403
- console.error(`Failed to load entity for path "${path}" from localStorage:`, error);
11404
- }
11405
- }
11406
- return void 0;
11407
- }
11408
- function removeEntityFromCache(path) {
11409
- if (isLocalStorageAvailable) {
11410
- try {
11411
- const key = LOCAL_STORAGE_PREFIX + path;
11412
- localStorage.removeItem(key);
11413
- } catch (error) {
11414
- console.error(`Failed to remove entity for path "${path}" from localStorage:`, error);
11415
- }
11416
- }
11417
- }
11418
- function flattenKeys(obj, prefix = "", result = []) {
11419
- if (isObject(obj) || Array.isArray(obj)) {
11420
- const plainObject = isPlainObject(obj);
11421
- if (!plainObject && prefix) {
11422
- result.push(prefix);
11423
- } else {
11424
- for (const key in obj) {
11425
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
11426
- const newKey = prefix ? Array.isArray(obj) ? `${prefix}[${key}]` : `${prefix}.${key}` : key;
11427
- if (isObject(obj[key]) || Array.isArray(obj[key])) {
11428
- flattenKeys(obj[key], newKey, result);
11429
- } else {
11430
- result.push(newKey);
11431
- }
11432
- }
11433
- }
11434
- }
11435
- }
11436
- return result;
11437
- }
11438
11453
  function FieldHelperText(t0) {
11439
11454
  const $ = reactCompilerRuntime.c(10);
11440
11455
  const {
@@ -16957,7 +16972,7 @@
16957
16972
  const [savingError, setSavingError] = React.useState();
16958
16973
  const autoSave = collection.formAutoSave && !collection.customId;
16959
16974
  const baseInitialValues = React.useMemo(() => getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs), [authController, collection, path, status, entity, customizationController.propertyConfigs]);
16960
- const localChangesDataRaw = React.useMemo(() => entityId ? getEntityFromCache(path + "/" + entityId) : getEntityFromCache(path + "#new"), [entityId, path]);
16975
+ const localChangesDataRaw = React.useMemo(() => entityId ? getEntityFromCache(entityCacheKey(path, entityId)) : getEntityFromCache(path + "#new"), [entityId, path]);
16961
16976
  const [localChangesCleared, setLocalChangesCleared] = React.useState(false);
16962
16977
  const localChangesBackup = getLocalChangesBackup(collection);
16963
16978
  const autoApplyLocalChanges = localChangesBackup === "auto_apply";
@@ -17019,7 +17034,7 @@
17019
17034
  onValuesModified?.(false, initialValues_0);
17020
17035
  },
17021
17036
  onValuesChangeDeferred: (values_0, controller) => {
17022
- const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
17037
+ const key = status === "new" || status === "copy" ? path + "#new" : entityCacheKey(path, entityId);
17023
17038
  if (controller.dirty && localChangesBackup !== false) {
17024
17039
  const touchedValues = removeEmptyContainers(extractTouchedValues(values_0, controller.touched));
17025
17040
  if (touchedValues && Object.keys(touchedValues).length > 0) {
@@ -17084,8 +17099,8 @@
17084
17099
  removeEntityFromMemoryCache(path + "#new");
17085
17100
  removeEntityFromCache(path + "#new");
17086
17101
  } else {
17087
- removeEntityFromMemoryCache(path + "/" + entityId);
17088
- removeEntityFromCache(path + "/" + entityId);
17102
+ removeEntityFromMemoryCache(entityCacheKey(path, entityId));
17103
+ removeEntityFromCache(entityCacheKey(path, entityId));
17089
17104
  }
17090
17105
  }
17091
17106
  const onSaveSuccess = (updatedEntity) => {
@@ -17376,7 +17391,7 @@
17376
17391
  }), noValidate: true, className: ui.cls("flex-1 flex flex-row w-full overflow-y-auto justify-center", className), children: [
17377
17392
  /* @__PURE__ */ jsxRuntime.jsx("div", { id: `form_${path}`, className: ui.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__ */ jsxRuntime.jsxs("div", { className: ui.cls("flex flex-col w-full pt-12 pb-16 px-4 sm:px-8 md:px-10"), children: [
17378
17393
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-row gap-4 self-end sticky top-4 z-10", children: [
17379
- manualApplyLocalChanges && hasLocalChanges && /* @__PURE__ */ jsxRuntime.jsx(LocalChangesMenu, { cacheKey: status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId, properties: resolvedCollection.properties, cachedData: localChangesDataRaw, formex: formex$1, onClearLocalChanges: () => setLocalChangesCleared(true) }),
17394
+ manualApplyLocalChanges && hasLocalChanges && /* @__PURE__ */ jsxRuntime.jsx(LocalChangesMenu, { cacheKey: status === "new" || status === "copy" ? path + "#new" : entityCacheKey(path, entityId), properties: resolvedCollection.properties, cachedData: localChangesDataRaw, formex: formex$1, onClearLocalChanges: () => setLocalChangesCleared(true) }),
17380
17395
  formex$1.dirty ? /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: "This form has been modified", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Chip, { size: "small", className: "py-1", colorScheme: "orangeDarker", children: /* @__PURE__ */ jsxRuntime.jsx(ui.EditIcon, { size: "smallest" }) }) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { title: "The current form is in sync with the database", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Chip, { size: "small", className: "py-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.CheckIcon, { size: "smallest" }) }) })
17381
17396
  ] }),
17382
17397
  formView
@@ -17467,7 +17482,7 @@
17467
17482
  const collapsedActions = actions.filter((a_0) => a_0.collapsed || a_0.collapsed === void 0);
17468
17483
  const uncollapsedActions = actions.filter((a_1) => a_1.collapsed === false);
17469
17484
  const enableLocalChangesBackup = collection ? getLocalChangesBackup(collection) : false;
17470
- const cachedData = enableLocalChangesBackup ? getEntityFromCache(fullPath + "/" + entity.id) : void 0;
17485
+ const cachedData = enableLocalChangesBackup ? getEntityFromCache(entityCacheKey(fullPath, entity.id)) : void 0;
17471
17486
  const hasDraft = (() => {
17472
17487
  if (!cachedData || typeof cachedData !== "object" || Object.keys(cachedData).length === 0) return false;
17473
17488
  const realChanges = getChanges(cachedData, entity?.values ?? {});
@@ -20296,10 +20311,10 @@
20296
20311
  const [sortBy_0, setSortBy] = React.useState((updateUrl ? initialSortUrl : void 0) ?? initialSortInternal);
20297
20312
  const [searchString, setSearchString] = React.useState(updateUrl ? initialSearchUrl : void 0);
20298
20313
  useUpdateUrl(filterValues_0, sortBy_0, searchString, updateUrl);
20299
- const collectionScroll = scrollRestoration?.getCollectionScroll(fullPath, filterValues_0);
20300
- const initialItemCount = collectionScroll?.data.length ?? pageSize;
20314
+ const collectionScroll = scrollRestoration?.getCollectionScroll(resolvedPath, filterValues_0);
20315
+ const initialItemCount = collectionScroll?.data.length || pageSize;
20301
20316
  React.useEffect(() => {
20302
- if (scrollRestoration) {
20317
+ if (scrollRestoration && rawData.length > 0) {
20303
20318
  scrollRestoration.updateCollectionScroll({
20304
20319
  fullPath: resolvedPath,
20305
20320
  scrollOffset: collectionScroll?.scrollOffset ?? 0,
@@ -20843,6 +20858,7 @@
20843
20858
  context,
20844
20859
  fullPath,
20845
20860
  pathSegments,
20861
+ fullIdPath,
20846
20862
  highlightEntity,
20847
20863
  unhighlightEntity,
20848
20864
  openEntityMode
@@ -20857,14 +20873,14 @@
20857
20873
  });
20858
20874
  const path = collection?.collectionGroup ? collection.path : fullPath ?? collection?.path ?? entity.path;
20859
20875
  const resolvedPathSegments = collection?.collectionGroup ? void 0 : pathSegments ?? entity.pathSegments;
20860
- const fullIdPath = collection?.collectionGroup ? collection.id : fullPath ?? collection?.id ?? entity.path;
20876
+ const newFullIdPath = collection?.collectionGroup ? collection.id : fullIdPath ?? collection?.id;
20861
20877
  navigateToEntity({
20862
20878
  openEntityMode,
20863
20879
  collection,
20864
20880
  entityId: entity.id,
20865
20881
  path,
20866
20882
  pathSegments: resolvedPathSegments,
20867
- fullIdPath,
20883
+ fullIdPath: newFullIdPath,
20868
20884
  copy: true,
20869
20885
  sideEntityController: context.sideEntityController,
20870
20886
  onClose: () => unhighlightEntity?.(entity),
@@ -26732,7 +26748,7 @@
26732
26748
  }) => {
26733
26749
  const collectionsWithPath = navigation.getParentReferencesFromPath(entity_4.path, entity_4.pathSegments);
26734
26750
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2 w-full", children: collectionsWithPath.map((reference) => {
26735
- return /* @__PURE__ */ jsxRuntime.jsx(ReferencePreview, { reference, size: "small" }, reference.path + "/" + reference.id);
26751
+ return /* @__PURE__ */ jsxRuntime.jsx(ReferencePreview, { reference, size: "small" }, entityCacheKey(reference.path, reference.id));
26736
26752
  }) });
26737
26753
  }
26738
26754
  }] : [];
@@ -29877,7 +29893,7 @@
29877
29893
  }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
29878
29894
  /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { className: "self-center", size: "smallest", onClick: onClose, children: /* @__PURE__ */ jsxRuntime.jsx(ui.CloseIcon, { size: "smallest" }) }),
29879
29895
  allowFullScreen && /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { className: "self-center", size: "smallest", onClick: () => {
29880
- const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
29896
+ const key = status === "new" || status === "copy" ? path + "#new" : entityCacheKey(path, entityId);
29881
29897
  saveEntityToMemoryCache(key, values);
29882
29898
  if (entityId) navigate(location.pathname + location.search);
29883
29899
  else navigate(location.pathname + location.search + "#new");
@@ -30385,7 +30401,7 @@
30385
30401
  databaseId: props.databaseId,
30386
30402
  useCache: false
30387
30403
  });
30388
- const initialDirtyValues = entityId ? getEntityFromMemoryCache(props.path + "/" + entityId) : getEntityFromMemoryCache(props.path + "#new");
30404
+ const initialDirtyValues = entityId ? getEntityFromMemoryCache(entityCacheKey(props.path, entityId)) : getEntityFromMemoryCache(props.path + "#new");
30389
30405
  const authController = useAuthController();
30390
30406
  const initialStatus = props.copy ? "copy" : entityId ? "existing" : "new";
30391
30407
  const [status, setStatus] = React.useState(initialStatus);
@@ -30836,19 +30852,21 @@
30836
30852
  return sidePanel ? [sidePanel] : [];
30837
30853
  }
30838
30854
  const propsToSidePanel = (props, buildUrlCollectionPath, resolveIdsFrom, smallLayout, customizationController, authController, locationSearch) => {
30839
- const collectionPath = removeInitialAndTrailingSlashes(props.path);
30840
- const urlPath = props.entityId ? buildUrlCollectionPath(`${collectionPath}/${encodeEntityId(props.entityId)}${props.selectedTab ? "/" + props.selectedTab : ""}${locationSearch}#${SIDE_URL_HASH}`) : buildUrlCollectionPath(`${collectionPath}${locationSearch}#${NEW_URL_HASH}`);
30855
+ const urlChain = removeInitialAndTrailingSlashes(props.fullIdPath ?? props.path);
30856
+ const urlPath = props.entityId ? buildUrlCollectionPath(`${urlChain}/${encodeEntityId(props.entityId)}${props.selectedTab ? "/" + props.selectedTab : ""}${locationSearch}#${SIDE_URL_HASH}`) : buildUrlCollectionPath(`${urlChain}${locationSearch}#${NEW_URL_HASH}`);
30841
30857
  const resolvedPanelProps = {
30842
30858
  ...props,
30843
30859
  formProps: props.formProps
30844
30860
  };
30845
30861
  const entityViewWidth = getEntityViewWidth(props, smallLayout, customizationController, authController);
30846
30862
  return {
30847
- key: `${props.path}/${props.entityId}`,
30863
+ // Built from the escaped chain so that two different chains which happen to flatten
30864
+ // to the same raw string are still two different panels.
30865
+ key: `${urlChain}/${props.entityId ? encodeEntityId(props.entityId) : ""}`,
30848
30866
  component: void 0,
30849
30867
  // Lazy render in SideDialogs for better performance
30850
30868
  urlPath,
30851
- parentUrlPath: buildUrlCollectionPath(collectionPath),
30869
+ parentUrlPath: buildUrlCollectionPath(urlChain),
30852
30870
  width: entityViewWidth,
30853
30871
  onClose: props.onClose,
30854
30872
  additional: resolvedPanelProps
@@ -39213,12 +39231,27 @@
39213
39231
  translations,
39214
39232
  children
39215
39233
  }) {
39234
+ const parentI18n = React.useContext(reactI18next.I18nContext)?.i18n;
39235
+ const parentInstance = parentI18n?.hasResourceBundle?.("en", FIRECMS_NS) ? parentI18n : void 0;
39216
39236
  const i18nRef = React.useRef(null);
39217
39237
  const [ready, setReady] = React.useState(false);
39218
39238
  if (!i18nRef.current) {
39219
39239
  const instance = i18next.createInstance();
39220
39240
  const resources = buildResources(translations);
39221
- let initialLocale = locale;
39241
+ if (parentInstance) {
39242
+ const inherited = parentInstance.services?.resourceStore?.data ?? {};
39243
+ for (const [lang, namespaces] of Object.entries(inherited)) {
39244
+ const bundle = namespaces?.[FIRECMS_NS];
39245
+ if (!bundle) continue;
39246
+ resources[lang] = {
39247
+ [FIRECMS_NS]: {
39248
+ ...bundle,
39249
+ ...translations?.[lang] ?? {}
39250
+ }
39251
+ };
39252
+ }
39253
+ }
39254
+ let initialLocale = parentInstance?.language ?? locale;
39222
39255
  if (typeof window !== "undefined") {
39223
39256
  const stored = localStorage.getItem(FIRECMS_LOCALE_STORAGE_KEY);
39224
39257
  if (stored) initialLocale = stored;
@@ -39243,6 +39276,17 @@
39243
39276
  });
39244
39277
  i18nRef.current = instance;
39245
39278
  }
39279
+ React.useEffect(() => {
39280
+ if (!parentInstance || !i18nRef.current) return;
39281
+ const instance_0 = i18nRef.current;
39282
+ const follow = (lng_0) => {
39283
+ if (instance_0.language !== lng_0) instance_0.changeLanguage(lng_0);
39284
+ };
39285
+ parentInstance.on("languageChanged", follow);
39286
+ return () => {
39287
+ parentInstance.off("languageChanged", follow);
39288
+ };
39289
+ }, [parentInstance]);
39246
39290
  React.useEffect(() => {
39247
39291
  if (i18nRef.current && i18nRef.current.language !== locale) {
39248
39292
  const hasUserPreference = typeof window !== "undefined" && Boolean(localStorage.getItem(FIRECMS_LOCALE_STORAGE_KEY));
@@ -39254,11 +39298,11 @@
39254
39298
  React.useEffect(() => {
39255
39299
  if (!i18nRef.current) return;
39256
39300
  const resources_0 = buildResources(translations);
39257
- for (const [lang, bundle] of Object.entries(resources_0)) {
39301
+ for (const [lang_0, bundle_0] of Object.entries(resources_0)) {
39258
39302
  i18nRef.current.addResourceBundle(
39259
- lang,
39303
+ lang_0,
39260
39304
  FIRECMS_NS,
39261
- bundle[FIRECMS_NS],
39305
+ bundle_0[FIRECMS_NS],
39262
39306
  true,
39263
39307
  // deep merge
39264
39308
  true