@firecms/core 3.5.1 → 3.5.2-canary.09e1281

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.
Files changed (56) hide show
  1. package/dist/components/EntityCollectionView/CollectionCountBadge.d.ts +8 -0
  2. package/dist/components/EntityCollectionView/CollectionPathTitle.d.ts +24 -0
  3. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +5 -0
  4. package/dist/components/EntityCollectionView/EntityIdHeaderWidget.d.ts +12 -0
  5. package/dist/components/EntityCollectionView/utils.d.ts +3 -3
  6. package/dist/components/HomePage/HomePageDnD.d.ts +4 -2
  7. package/dist/{de-CBOjCvsy.js → de-CUF9f7S-.js} +2 -1
  8. package/dist/{de-CBOjCvsy.js.map → de-CUF9f7S-.js.map} +1 -1
  9. package/dist/{editor-BSo_aS1I.js → editor-WZytSO_1.js} +172 -137
  10. package/dist/editor-WZytSO_1.js.map +1 -0
  11. package/dist/{fr-DRfttcG0.js → fr-DmkMX6hp.js} +2 -1
  12. package/dist/{fr-DRfttcG0.js.map → fr-DmkMX6hp.js.map} +1 -1
  13. package/dist/{hi-DwieOZNf.js → hi-BKuaMr7w.js} +2 -1
  14. package/dist/{hi-DwieOZNf.js.map → hi-BKuaMr7w.js.map} +1 -1
  15. package/dist/index.es.js +787 -638
  16. package/dist/index.es.js.map +1 -1
  17. package/dist/index.umd.js +946 -756
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/internal/useLeavePageConfirmation.d.ts +11 -0
  20. package/dist/{it-l2KXtx9n.js → it-BwQflFGz.js} +2 -1
  21. package/dist/{it-l2KXtx9n.js.map → it-BwQflFGz.js.map} +1 -1
  22. package/dist/{pl-DPZrdKhi.js → pl-BPsAvZvc.js} +2 -1
  23. package/dist/{pl-DPZrdKhi.js.map → pl-BPsAvZvc.js.map} +1 -1
  24. package/dist/{pt-BoHpabw1.js → pt-D5q7OQp1.js} +2 -1
  25. package/dist/{pt-BoHpabw1.js.map → pt-D5q7OQp1.js.map} +1 -1
  26. package/dist/types/side_dialogs_controller.d.ts +4 -2
  27. package/dist/types/translations.d.ts +1 -0
  28. package/package.json +3 -3
  29. package/src/components/EntityCollectionView/CollectionCountBadge.tsx +17 -0
  30. package/src/components/EntityCollectionView/CollectionPathTitle.tsx +68 -0
  31. package/src/components/EntityCollectionView/EntityCollectionView.tsx +12 -111
  32. package/src/components/EntityCollectionView/EntityIdHeaderWidget.tsx +124 -0
  33. package/src/components/EntityCollectionView/utils.ts +7 -7
  34. package/src/components/HomePage/DefaultHomePage.tsx +2 -0
  35. package/src/components/HomePage/HomePageDnD.tsx +12 -4
  36. package/src/components/common/default_entity_actions.tsx +2 -1
  37. package/src/core/DefaultAppBar.tsx +12 -9
  38. package/src/core/EntityEditView.tsx +26 -1
  39. package/src/core/SideDialogs.tsx +6 -13
  40. package/src/editor/selectors/link-selector.tsx +37 -12
  41. package/src/hooks/useReferenceDialog.tsx +2 -2
  42. package/src/internal/useBuildSideDialogsController.tsx +44 -2
  43. package/src/internal/useLeavePageConfirmation.ts +25 -0
  44. package/src/locales/de.ts +1 -0
  45. package/src/locales/en.ts +1 -0
  46. package/src/locales/es.ts +1 -0
  47. package/src/locales/fr.ts +1 -0
  48. package/src/locales/hi.ts +1 -0
  49. package/src/locales/it.ts +1 -0
  50. package/src/locales/pl.ts +1 -0
  51. package/src/locales/pt.ts +1 -0
  52. package/src/types/side_dialogs_controller.tsx +4 -2
  53. package/src/types/translations.ts +1 -0
  54. package/dist/editor-BSo_aS1I.js.map +0 -1
  55. package/dist/internal/useUnsavedChangesDialog.d.ts +0 -12
  56. package/src/internal/useUnsavedChangesDialog.tsx +0 -142
@@ -45,8 +45,8 @@ export function useReferenceDialog<M extends Record<string, any>>(referenceDialo
45
45
  }, [navigation, referenceDialogProps, sideDialogsController]);
46
46
 
47
47
  const close = useCallback(() => {
48
- sideDialogsController.close();
49
- }, [sideDialogsController]);
48
+ sideDialogsController.close(`reference_${referenceDialogProps.path}`);
49
+ }, [sideDialogsController, referenceDialogProps.path]);
50
50
 
51
51
  return {
52
52
  open,
@@ -14,6 +14,9 @@ export function useBuildSideDialogsController(): SideDialogsController {
14
14
  const routesStore = useRef<Record<string, SideDialogPanelProps>>({});
15
15
  const routesCount = useRef<number>(0);
16
16
 
17
+ // The history entry each panel without a URL was opened on, by panel key.
18
+ const entryKeysOfPanelsWithoutUrl = useRef<Record<string, string>>({});
19
+
17
20
  const updateSidePanels = (newPanels: SideDialogPanelProps[]) => {
18
21
  sidePanelsRef.current = newPanels;
19
22
  setSidePanels(newPanels);
@@ -22,20 +25,55 @@ export function useBuildSideDialogsController(): SideDialogsController {
22
25
  useEffect(() => {
23
26
  const state = location.state as any;
24
27
  const panelKeys: string[] = state?.panels ?? [];
25
- const newPanels = panelKeys
28
+ const panelsInHistory = panelKeys
26
29
  .map(key => routesStore.current[key])
27
30
  .filter(p => Boolean(p)) as SideDialogPanelProps[];
31
+
32
+ // A panel without a URL, like a reference dialog, pushes no history entry, so the
33
+ // entry under it does not list it, and going back to that entry, as closing a
34
+ // panel opened on top of it does, would drop it. It stays open for as long as
35
+ // what it was opened over does: the panel below it or, with none, the entry itself.
36
+ const keptPanels: SideDialogPanelProps[] = [];
37
+ let matched = 0;
38
+ for (const panel of sidePanelsRef.current) {
39
+ if (panelsInHistory[matched]?.key === panel.key) {
40
+ keptPanels.push(panel);
41
+ matched++;
42
+ } else if (!panel.urlPath &&
43
+ (keptPanels.length > 0 || entryKeysOfPanelsWithoutUrl.current[panel.key] === location.key)) {
44
+ keptPanels.push(panel);
45
+ } else {
46
+ break;
47
+ }
48
+ }
49
+ const newPanels = [...keptPanels, ...panelsInHistory.slice(matched)];
50
+
28
51
  if (!equal(sidePanelsRef.current.map(p => p.key), newPanels.map(p => p.key))) {
29
52
  updateSidePanels(newPanels);
30
53
  }
31
54
  }, [location]);
32
55
 
33
- const close = useCallback(() => {
56
+ const close = useCallback((key?: string) => {
34
57
 
35
58
  const currentPanels = sidePanelsRef.current;
36
59
  if (currentPanels.length === 0)
37
60
  return;
38
61
 
62
+ // A panel can ask to close itself while another one is open on top of it: the
63
+ // reference dialog does when an entity created from it is saved. Closing the top
64
+ // one instead would close that entity, and then the entity's own close would take
65
+ // the panel under the dialog with it. Only a panel without a URL can be taken out
66
+ // of the middle, since it has no history entry of its own. Should two panels share
67
+ // a key (a reference dialog opened from an entity created in another one for the
68
+ // same collection), it is the upper one.
69
+ const index = key !== undefined ? currentPanels.findLastIndex(p => p.key === key) : -1;
70
+ if (index !== -1 && index < currentPanels.length - 1 && !currentPanels[index].urlPath) {
71
+ updateSidePanels(currentPanels.filter((_, i) => i !== index));
72
+ if (routesCount.current > 0)
73
+ routesCount.current--;
74
+ return;
75
+ }
76
+
39
77
  const lastSidePanel = currentPanels[currentPanels.length - 1];
40
78
  const updatedPanels = [...currentPanels.slice(0, -1)];
41
79
  updateSidePanels(updatedPanels);
@@ -65,6 +103,8 @@ export function useBuildSideDialogsController(): SideDialogsController {
65
103
 
66
104
  newPanels.forEach((panel) => {
67
105
  routesStore.current[panel.key] = panel;
106
+ if (!panel.urlPath)
107
+ entryKeysOfPanelsWithoutUrl.current[panel.key] = location.key;
68
108
  });
69
109
  routesCount.current = routesCount.current + newPanels.length;
70
110
 
@@ -95,6 +135,8 @@ export function useBuildSideDialogsController(): SideDialogsController {
95
135
  const newPanels: SideDialogPanelProps[] = Array.isArray(panelProps) ? panelProps : [panelProps];
96
136
  newPanels.forEach((panel) => {
97
137
  routesStore.current[panel.key] = panel;
138
+ if (!panel.urlPath)
139
+ entryKeysOfPanelsWithoutUrl.current[panel.key] = location.key;
98
140
  });
99
141
 
100
142
  const baseLocation = (location.state as any)?.base_location ?? location;
@@ -0,0 +1,25 @@
1
+ import { useEffect } from "react";
2
+
3
+ /**
4
+ * Asks the browser to confirm leaving the page (reload, tab close, a link that
5
+ * loads another document) while `when` is true.
6
+ *
7
+ * Navigation inside the app is not intercepted: unsaved entity values are kept
8
+ * in the dirty entity cache, so they survive it. Cancelling link clicks here
9
+ * used to leave plain and new-tab links doing nothing, with no dialog shown.
10
+ *
11
+ * @param when - Whether leaving the page should be confirmed.
12
+ */
13
+ export function useLeavePageConfirmation(when: boolean): void {
14
+ useEffect(() => {
15
+ if (!when) return;
16
+
17
+ const handleBeforeUnload = (e: BeforeUnloadEvent) => {
18
+ e.preventDefault();
19
+ e.returnValue = "";
20
+ };
21
+
22
+ window.addEventListener("beforeunload", handleBeforeUnload);
23
+ return () => window.removeEventListener("beforeunload", handleBeforeUnload);
24
+ }, [when]);
25
+ }
package/src/locales/de.ts CHANGED
@@ -651,6 +651,7 @@ export const de: FireCMSTranslations = {
651
651
  editor_cancel: "Abbrechen",
652
652
  editor_remove_link: "Link entfernen",
653
653
  editor_paste_or_type_link: "Link einfügen oder eintippen",
654
+ editor_invalid_url: "Ungültige URL",
654
655
  editor_open_in_new_window: "In neuem Fenster öffnen",
655
656
  editor_bold: "Fett",
656
657
  editor_italic: "Kursiv",
package/src/locales/en.ts CHANGED
@@ -665,6 +665,7 @@ export const en: FireCMSTranslations = {
665
665
  editor_cancel: "Cancel",
666
666
  editor_remove_link: "Remove link",
667
667
  editor_paste_or_type_link: "Paste or type link",
668
+ editor_invalid_url: "Invalid URL",
668
669
  editor_open_in_new_window: "Open in new window",
669
670
  editor_bold: "Bold",
670
671
  editor_italic: "Italic",
package/src/locales/es.ts CHANGED
@@ -663,6 +663,7 @@ export const es: FireCMSTranslations = {
663
663
  editor_cancel: "Cancelar",
664
664
  editor_remove_link: "Eliminar enlace",
665
665
  editor_paste_or_type_link: "Pega o escribe el enlace",
666
+ editor_invalid_url: "URL no válida",
666
667
  editor_open_in_new_window: "Abrir en una ventana nueva",
667
668
  editor_bold: "Negrita",
668
669
  editor_italic: "Cursiva",
package/src/locales/fr.ts CHANGED
@@ -651,6 +651,7 @@ export const fr: FireCMSTranslations = {
651
651
  editor_cancel: "Annuler",
652
652
  editor_remove_link: "Supprimer le lien",
653
653
  editor_paste_or_type_link: "Coller ou taper le lien",
654
+ editor_invalid_url: "URL invalide",
654
655
  editor_open_in_new_window: "Ouvrir dans une nouvelle fenêtre",
655
656
  editor_bold: "Gras",
656
657
  editor_italic: "Italique",
package/src/locales/hi.ts CHANGED
@@ -651,6 +651,7 @@ export const hi: FireCMSTranslations = {
651
651
  editor_cancel: "रद्द करें",
652
652
  editor_remove_link: "लिंक हटाएं",
653
653
  editor_paste_or_type_link: "लिंक पेस्ट करें या टाइप करें",
654
+ editor_invalid_url: "अमान्य URL",
654
655
  editor_open_in_new_window: "नई विंडो में खोलें",
655
656
  editor_bold: "बोल्ड",
656
657
  editor_italic: "इटैलिक",
package/src/locales/it.ts CHANGED
@@ -651,6 +651,7 @@ export const it: FireCMSTranslations = {
651
651
  editor_cancel: "Annulla",
652
652
  editor_remove_link: "Rimuovi link",
653
653
  editor_paste_or_type_link: "Incolla o digita il link",
654
+ editor_invalid_url: "URL non valido",
654
655
  editor_open_in_new_window: "Apri in una nuova finestra",
655
656
  editor_bold: "Grassetto",
656
657
  editor_italic: "Corsivo",
package/src/locales/pl.ts CHANGED
@@ -655,6 +655,7 @@ export const pl: FireCMSTranslations = {
655
655
  editor_cancel: "Anuluj",
656
656
  editor_remove_link: "Usuń link",
657
657
  editor_paste_or_type_link: "Wklej lub wpisz link",
658
+ editor_invalid_url: "Nieprawidłowy URL",
658
659
  editor_open_in_new_window: "Otwórz w nowym oknie",
659
660
  editor_bold: "Pogrubienie",
660
661
  editor_italic: "Kursywa",
package/src/locales/pt.ts CHANGED
@@ -660,6 +660,7 @@ export const pt: FireCMSTranslations = {
660
660
  editor_cancel: "Cancelar",
661
661
  editor_remove_link: "Remover ligação",
662
662
  editor_paste_or_type_link: "Cole ou escreva a ligação",
663
+ editor_invalid_url: "URL inválido",
663
664
  editor_open_in_new_window: "Abrir numa nova janela",
664
665
  editor_bold: "Negrito",
665
666
  editor_italic: "Itálico",
@@ -8,9 +8,11 @@ import { EntityFormProps } from "../form";
8
8
  export interface SideDialogsController {
9
9
 
10
10
  /**
11
- * Close the last panel
11
+ * Close the last panel.
12
+ * When the `key` of a panel without a URL is given, that panel is closed
13
+ * instead, even if other panels are open on top of it.
12
14
  */
13
- close: () => void;
15
+ close: (key?: string) => void;
14
16
 
15
17
  /**
16
18
  * List of side panels currently open
@@ -708,6 +708,7 @@ export interface FireCMSTranslations {
708
708
  editor_cancel: string;
709
709
  editor_remove_link: string;
710
710
  editor_paste_or_type_link: string;
711
+ editor_invalid_url: string;
711
712
  editor_open_in_new_window: string;
712
713
  editor_bold: string;
713
714
  editor_italic: string;