@firecms/core 3.0.0-canary.168 → 3.0.0-canary.169

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 (96) hide show
  1. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +2 -1
  2. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
  3. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +15 -1
  4. package/dist/components/EntityCollectionTable/index.d.ts +1 -1
  5. package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +4 -0
  6. package/dist/components/SelectableTable/SelectableTable.d.ts +12 -2
  7. package/dist/components/UnsavedChangesDialog.d.ts +8 -0
  8. package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -0
  9. package/dist/components/common/index.d.ts +1 -1
  10. package/dist/components/common/{useDataSourceEntityCollectionTableController.d.ts → useDataSourceTableController.d.ts} +10 -2
  11. package/dist/components/common/useDebouncedCallback.d.ts +1 -0
  12. package/dist/components/common/useScrollRestoration.d.ts +14 -0
  13. package/dist/contexts/BreacrumbsContext.d.ts +8 -0
  14. package/dist/core/EntityEditView.d.ts +25 -7
  15. package/dist/core/FireCMSRouter.d.ts +4 -0
  16. package/dist/core/NavigationRoutes.d.ts +0 -1
  17. package/dist/core/index.d.ts +2 -1
  18. package/dist/hooks/data/useEntityFetch.d.ts +2 -1
  19. package/dist/hooks/useBreadcrumbsController.d.ts +26 -0
  20. package/dist/hooks/useBuildNavigationController.d.ts +0 -1
  21. package/dist/index.es.js +2344 -1305
  22. package/dist/index.es.js.map +1 -1
  23. package/dist/index.umd.js +2344 -1306
  24. package/dist/index.umd.js.map +1 -1
  25. package/dist/internal/useUnsavedChangesDialog.d.ts +7 -9
  26. package/dist/routes/CustomCMSRoute.d.ts +4 -0
  27. package/dist/routes/FireCMSRoute.d.ts +1 -0
  28. package/dist/routes/HomePageRoute.d.ts +3 -0
  29. package/dist/types/collections.d.ts +18 -6
  30. package/dist/types/datasource.d.ts +2 -2
  31. package/dist/types/dialogs_controller.d.ts +3 -7
  32. package/dist/types/entities.d.ts +1 -1
  33. package/dist/types/entity_actions.d.ts +1 -6
  34. package/dist/types/navigation.d.ts +28 -12
  35. package/dist/types/plugins.d.ts +1 -0
  36. package/dist/types/side_entity_controller.d.ts +1 -1
  37. package/dist/util/entity_cache.d.ts +23 -0
  38. package/dist/util/navigation_from_path.d.ts +5 -1
  39. package/dist/util/navigation_utils.d.ts +12 -1
  40. package/package.json +5 -5
  41. package/src/components/CircularProgressCenter.tsx +1 -1
  42. package/src/components/DeleteEntityDialog.tsx +1 -1
  43. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +40 -19
  44. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +10 -4
  45. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +18 -1
  46. package/src/components/EntityCollectionTable/index.tsx +1 -1
  47. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -3
  48. package/src/components/EntityCollectionView/EntityCollectionView.tsx +79 -38
  49. package/src/components/NotFoundPage.tsx +2 -2
  50. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -5
  51. package/src/components/SelectableTable/SelectableTable.tsx +18 -3
  52. package/src/components/UnsavedChangesDialog.tsx +42 -0
  53. package/src/components/VirtualTable/VirtualTable.tsx +19 -2
  54. package/src/components/VirtualTable/VirtualTableProps.tsx +15 -0
  55. package/src/components/VirtualTable/fields/VirtualTableInput.tsx +1 -1
  56. package/src/components/common/default_entity_actions.tsx +24 -10
  57. package/src/components/common/index.ts +1 -1
  58. package/src/components/common/{useDataSourceEntityCollectionTableController.tsx → useDataSourceTableController.tsx} +122 -13
  59. package/src/components/common/useDebouncedCallback.tsx +20 -0
  60. package/src/components/common/useScrollRestoration.tsx +68 -0
  61. package/src/contexts/BreacrumbsContext.tsx +38 -0
  62. package/src/contexts/DialogsProvider.tsx +2 -3
  63. package/src/core/DefaultAppBar.tsx +28 -4
  64. package/src/core/EntityEditView.tsx +457 -296
  65. package/src/core/EntitySidePanel.tsx +72 -6
  66. package/src/core/FireCMS.tsx +7 -4
  67. package/src/core/FireCMSRouter.tsx +17 -0
  68. package/src/core/NavigationRoutes.tsx +15 -28
  69. package/src/core/SideDialogs.tsx +10 -5
  70. package/src/core/index.tsx +4 -2
  71. package/src/hooks/data/save.ts +1 -1
  72. package/src/hooks/data/useCollectionFetch.tsx +1 -1
  73. package/src/hooks/data/useEntityFetch.tsx +5 -1
  74. package/src/hooks/useBreadcrumbsController.tsx +31 -0
  75. package/src/hooks/useBuildNavigationController.tsx +59 -21
  76. package/src/hooks/useLargeLayout.tsx +0 -35
  77. package/src/hooks/useResolvedNavigationFrom.tsx +4 -6
  78. package/src/internal/useBuildSideDialogsController.tsx +4 -2
  79. package/src/internal/useBuildSideEntityController.tsx +157 -77
  80. package/src/internal/useUnsavedChangesDialog.tsx +126 -92
  81. package/src/routes/CustomCMSRoute.tsx +21 -0
  82. package/src/routes/FireCMSRoute.tsx +230 -0
  83. package/src/routes/HomePageRoute.tsx +17 -0
  84. package/src/types/collections.ts +19 -6
  85. package/src/types/datasource.ts +2 -1
  86. package/src/types/dialogs_controller.tsx +3 -7
  87. package/src/types/entities.ts +1 -1
  88. package/src/types/entity_actions.tsx +1 -7
  89. package/src/types/navigation.ts +35 -16
  90. package/src/types/plugins.tsx +1 -0
  91. package/src/types/side_entity_controller.tsx +1 -1
  92. package/src/util/entity_cache.ts +196 -0
  93. package/src/util/navigation_from_path.ts +10 -2
  94. package/src/util/navigation_utils.ts +53 -1
  95. package/src/util/parent_references_from_path.ts +3 -3
  96. /package/src/util/{common.tsx → common.ts} +0 -0
@@ -9,14 +9,15 @@ import {
9
9
  FilterValues,
10
10
  FireCMSContext,
11
11
  SelectedCellProps,
12
- User
12
+ User,
13
+ WhereFilterOp
13
14
  } from "../../types";
14
15
  import { useDebouncedData } from "./useDebouncedData";
15
- import equal from "react-fast-compare"
16
+ import { ScrollRestorationController } from "./useScrollRestoration";
16
17
 
17
18
  const DEFAULT_PAGE_SIZE = 50;
18
19
 
19
- export type DataSourceEntityCollectionTableControllerProps<M extends Record<string, any> = any> = {
20
+ export type DataSourceTableControllerProps<M extends Record<string, any> = any> = {
20
21
  /**
21
22
  * Full path where the data of this table is located
22
23
  */
@@ -37,6 +38,14 @@ export type DataSourceEntityCollectionTableControllerProps<M extends Record<stri
37
38
  * Force filter to be applied to the table.
38
39
  */
39
40
  forceFilter?: FilterValues<string>;
41
+
42
+ scrollRestoration?: ScrollRestorationController;
43
+
44
+ /**
45
+ * When set to true the filters and sort will be updated in the URL
46
+ */
47
+ updateUrl?: boolean;
48
+
40
49
  }
41
50
 
42
51
  /**
@@ -48,18 +57,22 @@ export type DataSourceEntityCollectionTableControllerProps<M extends Record<stri
48
57
  *
49
58
  * @param fullPath
50
59
  * @param collection
60
+ * @param scrollRestoration
51
61
  * @param entitiesDisplayedFirst
52
62
  * @param lastDeleteTimestamp
53
63
  * @param forceFilterFromProps
64
+ * @param updateUrl
54
65
  */
55
- export function useDataSourceEntityCollectionTableController<M extends Record<string, any> = any, USER extends User = User>(
66
+ export function useDataSourceTableController<M extends Record<string, any> = any, USER extends User = User>(
56
67
  {
57
68
  fullPath,
58
69
  collection,
70
+ scrollRestoration,
59
71
  entitiesDisplayedFirst,
60
72
  lastDeleteTimestamp,
61
73
  forceFilter: forceFilterFromProps,
62
- }: DataSourceEntityCollectionTableControllerProps<M>)
74
+ updateUrl
75
+ }: DataSourceTableControllerProps<M>)
63
76
  : EntityTableController<M> {
64
77
 
65
78
  const {
@@ -71,14 +84,13 @@ export function useDataSourceEntityCollectionTableController<M extends Record<st
71
84
  const [popupCell, setPopupCell] = React.useState<SelectedCellProps<M> | undefined>(undefined);
72
85
  const navigation = useNavigationController();
73
86
  const dataSource = useDataSource(collection);
74
- const resolvedPath = useMemo(() => navigation.resolveAliasesFrom(fullPath), [fullPath, navigation.resolveAliasesFrom]);
87
+ const resolvedPath = useMemo(() => navigation.resolveIdsFrom(fullPath), [fullPath, navigation.resolveIdsFrom]);
75
88
 
76
89
  const forceFilter = forceFilterFromProps ?? forceFilterFromCollection;
77
90
  const paginationEnabled = collection.pagination === undefined || Boolean(collection.pagination);
78
91
  const pageSize = typeof collection.pagination === "number" ? collection.pagination : DEFAULT_PAGE_SIZE;
79
92
 
80
93
  const [searchString, setSearchString] = React.useState<string | undefined>();
81
- const [itemCount, setItemCount] = React.useState<number | undefined>(paginationEnabled ? pageSize : undefined);
82
94
 
83
95
  const checkFilterCombination = useCallback((filterValues: FilterValues<any>,
84
96
  sortBy?: [string, "asc" | "desc"]) => {
@@ -92,6 +104,21 @@ export function useDataSourceEntityCollectionTableController<M extends Record<st
92
104
  })
93
105
  }, []);
94
106
 
107
+ const onScroll = ({
108
+ scrollOffset
109
+ }: {
110
+ scrollOffset: number
111
+ }) => {
112
+ if (scrollRestoration) {
113
+ scrollRestoration.updateCollectionScroll({
114
+ fullPath: resolvedPath,
115
+ scrollOffset,
116
+ data: rawData,
117
+ filters: filterValues
118
+ });
119
+ }
120
+ }
121
+
95
122
  const initialSortInternal = useMemo(() => {
96
123
  if (initialSort && forceFilter && !checkFilterCombination(forceFilter, initialSort)) {
97
124
  console.warn("Initial sort is not compatible with the force filter. Ignoring initial sort");
@@ -100,21 +127,38 @@ export function useDataSourceEntityCollectionTableController<M extends Record<st
100
127
  return initialSort;
101
128
  }, [initialSort, forceFilter]);
102
129
 
130
+ const {
131
+ filterValues: initialFilteUrl,
132
+ sortBy: initialSortUrl,
133
+ } = parseFilterAndSort(window.location.search);
134
+
135
+ const [filterValues, setFilterValues] = React.useState<FilterValues<Extract<keyof M, string>> | undefined>(forceFilter ?? initialFilteUrl ?? initialFilter ?? undefined);
136
+ const [sortBy, setSortBy] = React.useState<[Extract<keyof M, string>, "asc" | "desc"] | undefined>(initialSortUrl ?? initialSortInternal);
137
+
138
+ useUpdateUrl(filterValues, sortBy, searchString, updateUrl);
139
+
140
+ const collectionScroll = scrollRestoration?.getCollectionScroll(fullPath, filterValues);
141
+ const initialItemCount = collectionScroll?.data.length ?? pageSize;
142
+
103
143
  useEffect(() => {
104
- if (!equal(forceFilter, filterValues)) {
105
- setFilterValues(forceFilter)
144
+ if (scrollRestoration) {
145
+ scrollRestoration.updateCollectionScroll({
146
+ fullPath: resolvedPath,
147
+ scrollOffset: collectionScroll?.scrollOffset ?? 0,
148
+ data: rawData,
149
+ filters: filterValues
150
+ });
106
151
  }
107
- }, [forceFilter]);
152
+ }, []);
108
153
 
109
- const [filterValues, setFilterValues] = React.useState<FilterValues<Extract<keyof M, string>> | undefined>(forceFilter ?? initialFilter ?? undefined);
110
- const [sortBy, setSortBy] = React.useState<[Extract<keyof M, string>, "asc" | "desc"] | undefined>(initialSortInternal);
154
+ const [itemCount, setItemCount] = React.useState<number | undefined>(paginationEnabled ? initialItemCount : undefined);
111
155
 
112
156
  const sortByProperty = sortBy ? sortBy[0] : undefined;
113
157
  const currentSort = sortBy ? sortBy[1] : undefined;
114
158
 
115
159
  const context: FireCMSContext<USER> = useFireCMSContext();
116
160
 
117
- const [rawData, setRawData] = useState<Entity<M>[]>([]);
161
+ const [rawData, setRawData] = useState<Entity<M>[]>(collectionScroll?.data ?? []);
118
162
 
119
163
  const [dataLoading, setDataLoading] = useState<boolean>(false);
120
164
  const [dataLoadingError, setDataLoadingError] = useState<Error | undefined>();
@@ -227,6 +271,8 @@ export function useDataSourceEntityCollectionTableController<M extends Record<st
227
271
  clearFilter,
228
272
  itemCount,
229
273
  setItemCount,
274
+ initialScroll: collectionScroll?.scrollOffset,
275
+ onScroll,
230
276
  paginationEnabled,
231
277
  pageSize,
232
278
  checkFilterCombination,
@@ -234,3 +280,66 @@ export function useDataSourceEntityCollectionTableController<M extends Record<st
234
280
  setPopupCell
235
281
  }
236
282
  }
283
+
284
+ function useUpdateUrl<M extends Record<string, any> = any>(
285
+ filterValues: FilterValues<Extract<keyof M, string>> | undefined,
286
+ sortBy: [Extract<keyof M, string>, "asc" | "desc"] | undefined,
287
+ searchString: string | undefined,
288
+ updateUrl: boolean | undefined
289
+ ) {
290
+
291
+ useEffect(() => {
292
+ if (updateUrl) {
293
+ const newUrl = encodeFilterAndSort(filterValues, sortBy);
294
+ const search = searchString ? `&search=${encodeURIComponent(searchString)}` : "";
295
+ const state = `${newUrl}${search}`;
296
+ const hash = window.location.hash;
297
+ if (state === "")
298
+ window.history.replaceState({}, "", `${window.location.pathname}${hash}`);
299
+ else
300
+ window.history.replaceState({}, "", `?${state}${hash}`);
301
+ }
302
+ }, [filterValues, sortBy, searchString, updateUrl]);
303
+ }
304
+
305
+ function encodeFilterAndSort(filterValues?: FilterValues<string> | undefined, sortBy?: [string, "asc" | "desc"] | undefined) {
306
+ const entries: Record<string, string> = {};
307
+ if (sortBy) {
308
+ entries["__sort"] = encodeURIComponent(sortBy[0]);
309
+ entries["__sort_order"] = encodeURIComponent(sortBy[1]);
310
+ }
311
+ if (filterValues) {
312
+ Object.entries(filterValues).forEach(([key, value]) => {
313
+ if (value) {
314
+ entries[encodeURIComponent(`${key}_op`)] = encodeURIComponent(value[0]);
315
+ entries[encodeURIComponent(`${key}_value`)] = encodeURIComponent(value[1]);
316
+ }
317
+ });
318
+ }
319
+ if (!Object.keys(entries).length) {
320
+ return "";
321
+ }
322
+ return Object.entries(entries).map(([key, value]) => `${key}=${value}`).join("&");
323
+ }
324
+
325
+ function parseFilterAndSort<M>(search: string): {
326
+ filterValues: FilterValues<string> | undefined,
327
+ sortBy?: [Extract<keyof M, string>, "asc" | "desc"]
328
+ } {
329
+ const entries = new URLSearchParams(search);
330
+ const filterValues: FilterValues<string> = {};
331
+ let sortBy: [string, "asc" | "desc"] | undefined = undefined;
332
+ entries.forEach((value, key) => {
333
+ if (key === "__sort") {
334
+ sortBy = [value, entries.get("__sort_order") as "asc" | "desc"];
335
+ } else if (key.endsWith("_op")) {
336
+ const filterValue = entries.get(`${key.replace("_op", "_value")}`);
337
+ filterValues[key.replace("_op", "")] = [value as WhereFilterOp, filterValue as string];
338
+ }
339
+ });
340
+
341
+ return {
342
+ filterValues: Object.keys(filterValues).length ? filterValues : undefined,
343
+ sortBy
344
+ }
345
+ }
@@ -0,0 +1,20 @@
1
+ import { useCallback, useRef } from "react";
2
+
3
+ export function useDebounceCallback<T extends (...args: any[]) => any>(
4
+ callback?: T,
5
+ delay?: number
6
+ ): T {
7
+ const timeoutRef = useRef<number | null>(null);
8
+
9
+ const debouncedCallback = useCallback((...args: Parameters<T>) => {
10
+ if (timeoutRef.current !== null) {
11
+ clearTimeout(timeoutRef.current);
12
+ }
13
+
14
+ timeoutRef.current = window.setTimeout(() => {
15
+ callback?.(...args);
16
+ }, delay ?? 200);
17
+ }, [callback, delay]);
18
+
19
+ return debouncedCallback as T;
20
+ }
@@ -0,0 +1,68 @@
1
+ import { Entity, FilterValues } from "../../types";
2
+
3
+ const collectionScrollCache = new Map<string, { scrollOffset: number, data: Entity<any>[] }>();
4
+
5
+ export type ScrollRestorationController = {
6
+
7
+ getCollectionScroll: (fullPath: string,
8
+ filters?: FilterValues<any>) => {
9
+ scrollOffset: number,
10
+ data: Entity<any>[]
11
+ } | undefined;
12
+
13
+ updateCollectionScroll: (props: {
14
+ fullPath: string,
15
+ scrollOffset: number,
16
+ filters?: FilterValues<any>;
17
+ data: Entity<any>[]
18
+ }) => void;
19
+
20
+ }
21
+
22
+ export function useScrollRestoration(): ScrollRestorationController {
23
+
24
+ const updateCollectionScroll = ({
25
+ fullPath,
26
+ filters,
27
+ scrollOffset,
28
+ data
29
+ }: {
30
+ fullPath: string;
31
+ filters?: FilterValues<any>;
32
+ sort?: [string, "asc" | "desc"];
33
+ scrollOffset: number;
34
+ data: Entity<any>[]
35
+ }) => {
36
+ collectionScrollCache.set(
37
+ createCacheKey(fullPath, filters),
38
+ {
39
+ scrollOffset,
40
+ data
41
+ })
42
+ }
43
+
44
+ const getCollectionScroll = (fullPath: string,
45
+ filters?: FilterValues<any>) => {
46
+ return collectionScrollCache.get(createCacheKey(fullPath, filters));
47
+ }
48
+
49
+ return {
50
+ getCollectionScroll,
51
+ updateCollectionScroll
52
+ }
53
+ }
54
+
55
+ function createCacheKey(fullPath: string, filters?: FilterValues<any>) {
56
+
57
+ if (!filters) {
58
+ return fullPath;
59
+ }
60
+
61
+ // codify the filters into a url friendly string
62
+ const filtersString = filters ? Object.keys(filters).map(key => {
63
+ const value = JSON.stringify(filters[key]);
64
+ return `${key}=${value}`;
65
+ }).join("&") : "";
66
+
67
+ return `${fullPath}?${filtersString}`;
68
+ }
@@ -0,0 +1,38 @@
1
+ import React, { useState } from "react";
2
+ import { BreadcrumbEntry, BreadcrumbsController } from "../hooks/useBreadcrumbsController";
3
+
4
+ const DEFAULT_BREADCRUMBS_CONTROLLER = {
5
+ breadcrumbs: [],
6
+ set: (props: {
7
+ breadcrumbs: BreadcrumbEntry[];
8
+ }) => {
9
+ }
10
+ };
11
+
12
+ export const BreadcrumbContext = React.createContext<BreadcrumbsController>(DEFAULT_BREADCRUMBS_CONTROLLER);
13
+
14
+ interface BreadcrumbsProviderProps {
15
+ children: React.ReactNode;
16
+ }
17
+
18
+ export const BreadcrumbsProvider: React.FC<BreadcrumbsProviderProps> = ({ children }) => {
19
+
20
+ const [breadcrumbs, setBreadcrumbs] = useState<BreadcrumbEntry[]>([]);
21
+
22
+ const set = (props: {
23
+ breadcrumbs: BreadcrumbEntry[];
24
+ }) => {
25
+ setBreadcrumbs(props.breadcrumbs);
26
+ };
27
+
28
+ return (
29
+ <BreadcrumbContext.Provider
30
+ value={{
31
+ breadcrumbs,
32
+ set
33
+ }}
34
+ >
35
+ {children}
36
+ </BreadcrumbContext.Provider>
37
+ );
38
+ };
@@ -8,7 +8,7 @@ export const DialogsProvider: React.FC<PropsWithChildren<{}>> = ({ children }) =
8
8
  const [dialogEntries, setDialogEntries] = useState<DialogControllerEntryProps[]>([]);
9
9
  const dialogEntriesRef = useRef<DialogControllerEntryProps[]>(dialogEntries);
10
10
 
11
- const updateDialogEntries = (newPanels: DialogControllerEntryProps<any>[]) => {
11
+ const updateDialogEntries = (newPanels: DialogControllerEntryProps[]) => {
12
12
  dialogEntriesRef.current = newPanels;
13
13
  setDialogEntries(newPanels);
14
14
  };
@@ -23,7 +23,7 @@ export const DialogsProvider: React.FC<PropsWithChildren<{}>> = ({ children }) =
23
23
 
24
24
  }, [dialogEntries]);
25
25
 
26
- const open = useCallback(<T extends object = object>(dialogEntry: DialogControllerEntryProps<T>) => {
26
+ const open = useCallback((dialogEntry: DialogControllerEntryProps) => {
27
27
 
28
28
  const updatedPanels = [...dialogEntriesRef.current, dialogEntry];
29
29
  updateDialogEntries(updatedPanels);
@@ -46,7 +46,6 @@ export const DialogsProvider: React.FC<PropsWithChildren<{}>> = ({ children }) =
46
46
  key={`dialog_${i}`}
47
47
  open={true}
48
48
  closeDialog={close}
49
- {...entry.props}
50
49
  />)}
51
50
  </DialogsControllerContext.Provider>
52
51
  );
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
 
3
- import { Link as ReactLink, useNavigate } from "react-router-dom";
3
+ import { Link, useNavigate } from "react-router-dom";
4
4
  import { ErrorBoundary, FireCMSLogo } from "../components";
5
5
  import {
6
6
  Avatar,
@@ -17,6 +17,7 @@ import {
17
17
  import { useAuthController, useLargeLayout, useModeController, useNavigationController } from "../hooks";
18
18
  import { User } from "../types";
19
19
  import { useApp } from "../app/useApp";
20
+ import { useBreadcrumbsController } from "../hooks/useBreadcrumbsController";
20
21
 
21
22
  export type DefaultAppBarProps<ADDITIONAL_PROPS = object> = {
22
23
 
@@ -65,6 +66,8 @@ export const DefaultAppBar = function DefaultAppBar({
65
66
  } = useApp();
66
67
  const navigation = useNavigationController();
67
68
 
69
+ const breadcrumbs = useBreadcrumbsController();
70
+
68
71
  const authController = useAuthController();
69
72
  const {
70
73
  mode,
@@ -108,8 +111,8 @@ export const DefaultAppBar = function DefaultAppBar({
108
111
  className)}>
109
112
 
110
113
 
111
- {navigation && <div className="mr-8 hidden lg:block">
112
- <ReactLink
114
+ {navigation && <div className="mr-2 hidden lg:block">
115
+ <Link
113
116
  className="visited:text-inherit visited:dark:text-inherit block"
114
117
  to={navigation?.basePath ?? "/"}
115
118
  >
@@ -127,7 +130,28 @@ export const DefaultAppBar = function DefaultAppBar({
127
130
  </Typography>
128
131
  : title}
129
132
  </div>
130
- </ReactLink>
133
+ </Link>
134
+ </div>}
135
+
136
+ {breadcrumbs.breadcrumbs && <div className="mr-8 hidden lg:block">
137
+ <div className={"flex flex-row gap-2"}>
138
+ {breadcrumbs.breadcrumbs.map((breadcrumb, index) => {
139
+ return <React.Fragment key={breadcrumb.url + "_" + index}>
140
+ <Typography variant={"caption"} color={"secondary"}>
141
+ /
142
+ </Typography>
143
+ <Link
144
+ key={index}
145
+ className="visited:text-inherit visited:dark:text-inherit block"
146
+ to={breadcrumb.url}
147
+ >
148
+ <Typography variant={"caption"} color={"secondary"}>
149
+ {breadcrumb.title}
150
+ </Typography>
151
+ </Link>
152
+ </React.Fragment>;
153
+ })}
154
+ </div>
131
155
  </div>}
132
156
 
133
157
  {startAdornment}