@firecms/core 3.0.0 → 3.1.0-canary.1df3b2c

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 (185) hide show
  1. package/README.md +1 -1
  2. package/dist/components/AIIcon.d.ts +16 -0
  3. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +7 -1
  4. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  5. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +14 -0
  6. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +6 -0
  7. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +5 -4
  8. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +6 -0
  9. package/dist/components/EntityCollectionView/Board.d.ts +2 -0
  10. package/dist/components/EntityCollectionView/BoardColumn.d.ts +42 -0
  11. package/dist/components/EntityCollectionView/BoardColumnTitle.d.ts +9 -0
  12. package/dist/components/EntityCollectionView/BoardSortableList.d.ts +14 -0
  13. package/dist/components/EntityCollectionView/EntityBoardCard.d.ts +26 -0
  14. package/dist/components/EntityCollectionView/EntityCard.d.ts +19 -0
  15. package/dist/components/EntityCollectionView/EntityCollectionBoardView.d.ts +20 -0
  16. package/dist/components/EntityCollectionView/EntityCollectionCardView.d.ts +31 -0
  17. package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +2 -2
  18. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +7 -3
  19. package/dist/components/EntityCollectionView/FiltersDialog.d.ts +14 -0
  20. package/dist/components/EntityCollectionView/ViewModeToggle.d.ts +49 -0
  21. package/dist/components/EntityCollectionView/board_types.d.ts +105 -0
  22. package/dist/components/EntityCollectionView/useBoardDataController.d.ts +60 -0
  23. package/dist/components/SelectableTable/SelectableTable.d.ts +5 -1
  24. package/dist/components/SelectableTable/filters/DateTimeFilterField.d.ts +2 -1
  25. package/dist/components/VirtualTable/VirtualTable.performance.test.d.ts +1 -0
  26. package/dist/components/VirtualTable/VirtualTableCell.d.ts +6 -0
  27. package/dist/components/VirtualTable/VirtualTableHeader.d.ts +2 -0
  28. package/dist/components/VirtualTable/VirtualTableHeaderRow.d.ts +1 -1
  29. package/dist/components/VirtualTable/VirtualTableProps.d.ts +11 -0
  30. package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +1 -0
  31. package/dist/components/VirtualTable/types.d.ts +2 -0
  32. package/dist/components/index.d.ts +3 -0
  33. package/dist/contexts/index.d.ts +10 -0
  34. package/dist/core/DrawerNavigationGroup.d.ts +45 -0
  35. package/dist/core/index.d.ts +1 -0
  36. package/dist/form/components/LocalChangesMenu.d.ts +2 -2
  37. package/dist/form/components/StorageUploadProgress.d.ts +1 -1
  38. package/dist/form/validation.d.ts +3 -2
  39. package/dist/hooks/useBreadcrumbsController.d.ts +16 -0
  40. package/dist/hooks/useCollapsedGroups.d.ts +4 -1
  41. package/dist/index.es.js +5397 -1768
  42. package/dist/index.es.js.map +1 -1
  43. package/dist/index.umd.js +5391 -1763
  44. package/dist/index.umd.js.map +1 -1
  45. package/dist/preview/PropertyPreviewProps.d.ts +5 -0
  46. package/dist/preview/components/DatePreview.d.ts +13 -3
  47. package/dist/preview/components/ImagePreview.d.ts +5 -1
  48. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  49. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  50. package/dist/preview/property_previews/ArrayOfStorageComponentsPreview.d.ts +1 -1
  51. package/dist/preview/property_previews/ArrayOfStringsPreview.d.ts +1 -1
  52. package/dist/preview/property_previews/SkeletonPropertyComponent.d.ts +1 -1
  53. package/dist/types/collections.d.ts +42 -2
  54. package/dist/types/datasource.d.ts +0 -1
  55. package/dist/types/entities.d.ts +1 -0
  56. package/dist/types/plugins.d.ts +46 -1
  57. package/dist/types/properties.d.ts +268 -4
  58. package/dist/types/storage.d.ts +8 -0
  59. package/dist/util/__tests__/conditions.test.d.ts +1 -0
  60. package/dist/util/__tests__/objects.test.d.ts +1 -0
  61. package/dist/util/conditions.d.ts +26 -0
  62. package/dist/util/entities.d.ts +1 -2
  63. package/dist/util/index.d.ts +2 -1
  64. package/dist/util/property_utils.d.ts +2 -1
  65. package/dist/util/resolutions.d.ts +1 -1
  66. package/dist/util/useStorageUploadController.d.ts +1 -1
  67. package/package.json +11 -7
  68. package/src/app/Scaffold.tsx +14 -15
  69. package/src/components/AIIcon.tsx +39 -0
  70. package/src/components/ArrayContainer.tsx +1 -4
  71. package/src/components/ClearFilterSortButton.tsx +19 -16
  72. package/src/components/ConfirmationDialog.tsx +0 -2
  73. package/src/components/DeleteEntityDialog.tsx +2 -4
  74. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +74 -41
  75. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +130 -79
  76. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +121 -104
  77. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +132 -103
  78. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +20 -42
  79. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +90 -49
  80. package/src/components/EntityCollectionView/Board.tsx +324 -0
  81. package/src/components/EntityCollectionView/BoardColumn.tsx +158 -0
  82. package/src/components/EntityCollectionView/BoardColumnTitle.tsx +45 -0
  83. package/src/components/EntityCollectionView/BoardSortableList.tsx +172 -0
  84. package/src/components/EntityCollectionView/EntityBoardCard.tsx +212 -0
  85. package/src/components/EntityCollectionView/EntityCard.tsx +231 -0
  86. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +713 -0
  87. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +244 -0
  88. package/src/components/EntityCollectionView/EntityCollectionView.tsx +485 -203
  89. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +31 -19
  90. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +84 -15
  91. package/src/components/EntityCollectionView/FiltersDialog.tsx +249 -0
  92. package/src/components/EntityCollectionView/ViewModeToggle.tsx +202 -0
  93. package/src/components/EntityCollectionView/board_types.ts +113 -0
  94. package/src/components/EntityCollectionView/useBoardDataController.tsx +490 -0
  95. package/src/components/EntityPreview.tsx +1 -1
  96. package/src/components/ErrorTooltip.tsx +2 -1
  97. package/src/components/HomePage/DefaultHomePage.tsx +47 -10
  98. package/src/components/HomePage/HomePageDnD.tsx +56 -41
  99. package/src/components/HomePage/NavigationCard.tsx +20 -18
  100. package/src/components/HomePage/NavigationGroup.tsx +17 -16
  101. package/src/components/HomePage/RenameGroupDialog.tsx +0 -2
  102. package/src/components/HomePage/SmallNavigationCard.tsx +10 -9
  103. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -10
  104. package/src/components/ReferenceWidget.tsx +2 -4
  105. package/src/components/SelectableTable/SelectableTable.tsx +75 -67
  106. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +7 -6
  107. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +39 -40
  108. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +38 -38
  109. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +49 -58
  110. package/src/components/UnsavedChangesDialog.tsx +0 -2
  111. package/src/components/UserDisplay.tsx +4 -4
  112. package/src/components/VirtualTable/VirtualTable.performance.test.tsx +386 -0
  113. package/src/components/VirtualTable/VirtualTable.tsx +172 -21
  114. package/src/components/VirtualTable/VirtualTableCell.tsx +18 -2
  115. package/src/components/VirtualTable/VirtualTableHeader.tsx +20 -11
  116. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +158 -42
  117. package/src/components/VirtualTable/VirtualTableProps.tsx +14 -1
  118. package/src/components/VirtualTable/VirtualTableRow.tsx +1 -1
  119. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +3 -0
  120. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +17 -4
  121. package/src/components/VirtualTable/types.tsx +2 -0
  122. package/src/components/common/useColumnsIds.tsx +95 -3
  123. package/src/components/index.tsx +4 -0
  124. package/src/contexts/BreacrumbsContext.tsx +15 -8
  125. package/src/contexts/index.ts +10 -0
  126. package/src/core/DefaultAppBar.tsx +39 -26
  127. package/src/core/DefaultDrawer.tsx +42 -56
  128. package/src/core/DrawerNavigationGroup.tsx +118 -0
  129. package/src/core/DrawerNavigationItem.tsx +4 -3
  130. package/src/core/EntityEditView.tsx +41 -43
  131. package/src/core/SideDialogs.tsx +4 -2
  132. package/src/core/index.tsx +1 -0
  133. package/src/form/EntityForm.tsx +3 -10
  134. package/src/form/PropertyFieldBinding.tsx +58 -43
  135. package/src/form/components/LocalChangesMenu.tsx +6 -6
  136. package/src/form/components/StorageItemPreview.tsx +2 -1
  137. package/src/form/components/StorageUploadProgress.tsx +4 -3
  138. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +0 -1
  139. package/src/form/field_bindings/DateTimeFieldBinding.tsx +17 -16
  140. package/src/form/field_bindings/KeyValueFieldBinding.tsx +0 -1
  141. package/src/form/field_bindings/MapFieldBinding.tsx +69 -67
  142. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +21 -17
  143. package/src/form/field_bindings/TextFieldBinding.tsx +71 -35
  144. package/src/form/validation.ts +245 -160
  145. package/src/hooks/useBreadcrumbsController.tsx +18 -0
  146. package/src/hooks/useBuildNavigationController.tsx +42 -19
  147. package/src/hooks/useCollapsedGroups.ts +12 -4
  148. package/src/internal/useBuildDataSource.ts +69 -34
  149. package/src/internal/useBuildSideDialogsController.tsx +11 -8
  150. package/src/internal/useBuildSideEntityController.tsx +2 -4
  151. package/src/internal/useRestoreScroll.tsx +26 -14
  152. package/src/preview/PropertyPreview.tsx +40 -32
  153. package/src/preview/PropertyPreviewProps.tsx +6 -0
  154. package/src/preview/components/DatePreview.tsx +72 -4
  155. package/src/preview/components/EmptyValue.tsx +1 -1
  156. package/src/preview/components/ImagePreview.tsx +37 -21
  157. package/src/preview/components/ReferencePreview.tsx +6 -1
  158. package/src/preview/components/StorageThumbnail.tsx +16 -12
  159. package/src/preview/components/UrlComponentPreview.tsx +28 -25
  160. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +9 -7
  161. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +11 -9
  162. package/src/preview/property_previews/ArrayPropertyPreview.tsx +26 -24
  163. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +61 -56
  164. package/src/routes/CustomCMSRoute.tsx +1 -0
  165. package/src/routes/FireCMSRoute.tsx +26 -13
  166. package/src/types/collections.ts +48 -3
  167. package/src/types/datasource.ts +54 -56
  168. package/src/types/entities.ts +10 -0
  169. package/src/types/plugins.tsx +51 -1
  170. package/src/types/properties.ts +357 -27
  171. package/src/types/storage.ts +9 -0
  172. package/src/util/__tests__/conditions.test.ts +506 -0
  173. package/src/util/__tests__/objects.test.ts +196 -0
  174. package/src/util/callbacks.ts +6 -3
  175. package/src/util/collections.ts +51 -6
  176. package/src/util/conditions.ts +339 -0
  177. package/src/util/entities.ts +28 -29
  178. package/src/util/entity_cache.ts +2 -1
  179. package/src/util/index.ts +2 -1
  180. package/src/util/objects.ts +31 -13
  181. package/src/util/{references.ts → previews.ts} +14 -0
  182. package/src/util/property_utils.tsx +36 -10
  183. package/src/util/resolutions.ts +57 -55
  184. package/src/util/useStorageUploadController.tsx +11 -1
  185. /package/dist/util/{references.d.ts → previews.d.ts} +0 -0
@@ -1,11 +1,11 @@
1
- import React, { useState } from "react";
1
+ import React, { useCallback, useState } from "react";
2
2
  import { BreadcrumbEntry, BreadcrumbsController } from "../hooks/useBreadcrumbsController";
3
3
 
4
- const DEFAULT_BREADCRUMBS_CONTROLLER = {
4
+ const DEFAULT_BREADCRUMBS_CONTROLLER: BreadcrumbsController = {
5
5
  breadcrumbs: [],
6
- set: (props: {
7
- breadcrumbs: BreadcrumbEntry[];
8
- }) => {
6
+ set: () => {
7
+ },
8
+ updateCount: () => {
9
9
  }
10
10
  };
11
11
 
@@ -19,17 +19,24 @@ export const BreadcrumbsProvider: React.FC<BreadcrumbsProviderProps> = ({ childr
19
19
 
20
20
  const [breadcrumbs, setBreadcrumbs] = useState<BreadcrumbEntry[]>([]);
21
21
 
22
- const set = (props: {
22
+ const set = useCallback((props: {
23
23
  breadcrumbs: BreadcrumbEntry[];
24
24
  }) => {
25
25
  setBreadcrumbs(props.breadcrumbs);
26
- };
26
+ }, []);
27
+
28
+ const updateCount = useCallback((id: string, count: number | null | undefined) => {
29
+ setBreadcrumbs(prev => prev.map(entry =>
30
+ entry.id === id ? { ...entry, count } : entry
31
+ ));
32
+ }, []);
27
33
 
28
34
  return (
29
35
  <BreadcrumbContext.Provider
30
36
  value={{
31
37
  breadcrumbs,
32
- set
38
+ set,
39
+ updateCount
33
40
  }}
34
41
  >
35
42
  {children}
@@ -1,3 +1,13 @@
1
1
  export * from "./SnackbarProvider";
2
2
  export * from "./ModeController";
3
3
  export * from "./AuthControllerContext";
4
+ export * from "./DataSourceContext";
5
+ export * from "./NavigationContext";
6
+ export * from "./CustomizationControllerContext";
7
+ export * from "./SideEntityControllerContext";
8
+ export * from "./SideDialogsControllerContext";
9
+ export * from "./AnalyticsContext";
10
+ export * from "./StorageSourceContext";
11
+ export * from "./UserConfigurationPersistenceContext";
12
+ export * from "./DialogsProvider";
13
+ export * from "./InternalUserManagementContext";
@@ -57,16 +57,16 @@ export type DefaultAppBarProps<ADDITIONAL_PROPS = object> = {
57
57
 
58
58
  */
59
59
  export const DefaultAppBar = function DefaultAppBar({
60
- title,
61
- endAdornment,
62
- startAdornment,
63
- dropDownActions,
64
- includeModeToggle = true,
65
- className,
66
- style,
67
- user: userProp,
68
- logo: logoProp,
69
- }: DefaultAppBarProps) {
60
+ title,
61
+ endAdornment,
62
+ startAdornment,
63
+ dropDownActions,
64
+ includeModeToggle = true,
65
+ className,
66
+ style,
67
+ user: userProp,
68
+ logo: logoProp,
69
+ }: DefaultAppBarProps) {
70
70
 
71
71
  const {
72
72
  hasDrawer,
@@ -103,7 +103,7 @@ export const DefaultAppBar = function DefaultAppBar({
103
103
  </Avatar>;
104
104
  } else if (user === undefined || authController.initialLoading) {
105
105
  avatarComponent = <div className={"p-1 flex justify-center"}>
106
- <Skeleton className={"w-10 h-10 rounded-full"}/>
106
+ <Skeleton className={"w-10 h-10 rounded-full"} />
107
107
  </div>;
108
108
  } else {
109
109
  avatarComponent = null;
@@ -131,13 +131,13 @@ export const DefaultAppBar = function DefaultAppBar({
131
131
  <div className={"flex flex-row gap-4"}>
132
132
  {!hasDrawer && (logo
133
133
  ? <img src={logo}
134
- alt="Logo"
135
- className={cls("w-[32px] h-[32px] object-contain")}/>
136
- : <FireCMSLogo width={"32px"} height={"32px"}/>)}
134
+ alt="Logo"
135
+ className={cls("w-[32px] h-[32px] object-contain")} />
136
+ : <FireCMSLogo width={"32px"} height={"32px"} />)}
137
137
 
138
138
  {typeof title === "string"
139
139
  ? <Typography variant="subtitle1"
140
- noWrap>
140
+ noWrap>
141
141
  {title}
142
142
  </Typography>
143
143
  : title}
@@ -146,7 +146,7 @@ export const DefaultAppBar = function DefaultAppBar({
146
146
  </div>}
147
147
 
148
148
  {(breadcrumbs.breadcrumbs ?? []).length > 0 && <div className="mr-8 hidden lg:block">
149
- <div className={"flex flex-row gap-2"}>
149
+ <div className={"flex flex-row gap-2 items-center"}>
150
150
  {breadcrumbs.breadcrumbs.map((breadcrumb, index) => {
151
151
  return <React.Fragment key={breadcrumb.url + "_" + index}>
152
152
  <Typography variant={"caption"} color={"secondary"}>
@@ -157,18 +157,31 @@ export const DefaultAppBar = function DefaultAppBar({
157
157
  className="visited:text-inherit visited:dark:text-inherit block"
158
158
  to={breadcrumb.url}
159
159
  >
160
- <Typography variant={"caption"} color={"secondary"}>
161
- {breadcrumb.title}
162
- </Typography>
160
+ <div className="flex flex-row items-center gap-2 whitespace-nowrap">
161
+ <Typography variant={"body2"}>
162
+ {breadcrumb.title}
163
+ </Typography>
164
+ {/* Show count badge for collection breadcrumbs: undefined = not applicable, null = loading, number = count */}
165
+ {breadcrumb.count !== undefined && (
166
+ breadcrumb.count !== null ? (
167
+ <span className="text-xs text-surface-accent-500 dark:text-surface-accent-400 bg-surface-100 dark:bg-surface-700 px-1 py-0 rounded">
168
+ {breadcrumb.count}
169
+ </span>
170
+ ) : (
171
+ <Skeleton className="w-8 h-4 rounded-md" />
172
+ )
173
+ )}
174
+ </div>
163
175
  </Link>
164
176
  </React.Fragment>;
165
177
  })}
166
178
  </div>
167
179
  </div>}
168
180
 
181
+
169
182
  {startAdornment}
170
183
 
171
- <div className={"flex-grow"}/>
184
+ <div className={"flex-grow"} />
172
185
 
173
186
  {endAdornment &&
174
187
  <ErrorBoundary>
@@ -182,13 +195,13 @@ export const DefaultAppBar = function DefaultAppBar({
182
195
  aria-label="Open drawer"
183
196
  size="large">
184
197
  {mode === "dark"
185
- ? <DarkModeIcon/>
186
- : <LightModeIcon/>}
198
+ ? <DarkModeIcon />
199
+ : <LightModeIcon />}
187
200
  </IconButton>}>
188
- <MenuItem onClick={() => setMode("dark")}><DarkModeIcon size={"smallest"}/> Dark</MenuItem>
189
- <MenuItem onClick={() => setMode("light")}><LightModeIcon size={"smallest"}/> Light </MenuItem>
201
+ <MenuItem onClick={() => setMode("dark")}><DarkModeIcon size={"smallest"} /> Dark</MenuItem>
202
+ <MenuItem onClick={() => setMode("light")}><LightModeIcon size={"smallest"} /> Light </MenuItem>
190
203
  <MenuItem onClick={() => setMode("system")}> <BrightnessMediumIcon
191
- size={"smallest"}/>System</MenuItem>
204
+ size={"smallest"} />System</MenuItem>
192
205
  </Menu>}
193
206
 
194
207
  <Menu trigger={avatarComponent}>
@@ -208,7 +221,7 @@ export const DefaultAppBar = function DefaultAppBar({
208
221
  // replace current route with home
209
222
  navigate("/");
210
223
  }}>
211
- <LogoutIcon/>
224
+ <LogoutIcon />
212
225
  Log Out
213
226
  </MenuItem>}
214
227
 
@@ -1,13 +1,13 @@
1
- import React, { useCallback } from "react";
1
+ import React from "react";
2
2
 
3
- import { useLargeLayout, useNavigationController } from "../hooks";
3
+ import { useCollapsedGroups, useLargeLayout, useNavigationController } from "../hooks";
4
4
 
5
5
  import { Link, useNavigate } from "react-router-dom";
6
6
  import { CMSAnalyticsEvent, NavigationEntry, NavigationResult } from "../types";
7
7
  import { IconForView } from "../util";
8
- import { cls, IconButton, Menu, MenuItem, MoreVertIcon, Tooltip, Typography } from "@firecms/ui";
8
+ import { cls, IconButton, Menu, MenuItem, MoreVertIcon, Tooltip } from "@firecms/ui";
9
9
  import { useAnalyticsController } from "../hooks/useAnalyticsController";
10
- import { DrawerNavigationItem } from "./DrawerNavigationItem";
10
+ import { DrawerNavigationGroup } from "./DrawerNavigationGroup";
11
11
  import { FireCMSLogo } from "../components";
12
12
  import { useApp } from "../app/useApp";
13
13
 
@@ -16,9 +16,9 @@ import { useApp } from "../app/useApp";
16
16
  * @group Core
17
17
  */
18
18
  export function DefaultDrawer({
19
- className,
20
- style,
21
- }: {
19
+ className,
20
+ style,
21
+ }: {
22
22
  className?: string
23
23
  style?: React.CSSProperties,
24
24
  }) {
@@ -50,20 +50,10 @@ export function DefaultDrawer({
50
50
  const adminViews = navigationEntries.filter(e => e.type === "admin") ?? [];
51
51
  const groupsWithoutAdmin = groups.filter(g => g !== "Admin");
52
52
 
53
- const buildGroupHeader = useCallback((group?: string) => {
54
- if (!drawerOpen) return <div className="w-full"/>;
55
- return <div
56
- className="pl-6 pr-8 py-4 flex flex-row items-center">
57
- <Typography variant={"caption"}
58
- color={"secondary"}
59
- className="font-medium flex-grow line-clamp-1">
60
- {group ? group.toUpperCase() : "Views".toUpperCase()}
61
- </Typography>
53
+ // Collapsible groups state - using "drawer" namespace for independent state from home page
54
+ const { isGroupCollapsed, toggleGroupCollapsed } = useCollapsedGroups(groupsWithoutAdmin, "drawer");
62
55
 
63
- </div>;
64
- }, [drawerOpen]);
65
-
66
- const onClick = (view: NavigationEntry) => {
56
+ const onItemClick = (view: NavigationEntry) => {
67
57
  const eventName: CMSAnalyticsEvent = view.type === "collection"
68
58
  ? "drawer_navigate_to_collection"
69
59
  : (view.type === "view" ? "drawer_navigate_to_view" : "unmapped_event");
@@ -76,33 +66,29 @@ export function DefaultDrawer({
76
66
  <>
77
67
  <div className={cls("flex flex-col h-full relative flex-grow w-full", className)} style={style}>
78
68
 
79
- <DrawerLogo logo={logo}/>
69
+ <DrawerLogo logo={logo} />
80
70
 
81
71
  <div className={"mt-4 flex-grow overflow-scroll no-scrollbar"}
82
- style={{
83
- maskImage: "linear-gradient(to bottom, transparent 0, black 20px, black calc(100% - 20px), transparent 100%)",
84
- }}>
85
-
86
- {groupsWithoutAdmin.map((group) => (
87
- <div
88
- className={"bg-surface-50 dark:bg-surface-800 dark:bg-opacity-30 my-4 rounded-lg ml-3 mr-1"}
89
- key={`drawer_group_${group}`}>
90
- {buildGroupHeader(group)}
91
- {Object.values(navigationEntries)
92
- .filter(e => e.group === group)
93
- .map((view) =>
94
- <DrawerNavigationItem
95
- key={view.id}
96
- icon={<IconForView collectionOrView={view.collection ?? view.view}
97
- size={"small"}/>}
98
- tooltipsOpen={tooltipsOpen}
99
- adminMenuOpen={adminMenuOpen}
100
- drawerOpen={drawerOpen}
101
- onClick={() => onClick(view)}
102
- url={view.url}
103
- name={view.name}/>)}
104
- </div>
105
- ))}
72
+ style={{
73
+ maskImage: "linear-gradient(to bottom, transparent 0, black 20px, black calc(100% - 20px), transparent 100%)",
74
+ }}>
75
+
76
+ {groupsWithoutAdmin.map((group) => {
77
+ const entriesInGroup = Object.values(navigationEntries).filter(e => e.group === group);
78
+ return (
79
+ <DrawerNavigationGroup
80
+ key={`drawer_group_${group}`}
81
+ group={group}
82
+ entries={entriesInGroup}
83
+ collapsed={isGroupCollapsed(group)}
84
+ onToggleCollapsed={() => toggleGroupCollapsed(group)}
85
+ drawerOpen={drawerOpen}
86
+ tooltipsOpen={tooltipsOpen}
87
+ adminMenuOpen={adminMenuOpen}
88
+ onItemClick={onItemClick}
89
+ />
90
+ );
91
+ })}
106
92
 
107
93
  </div>
108
94
 
@@ -115,9 +101,9 @@ export function DefaultDrawer({
115
101
  shape={"square"}
116
102
  className={"m-4 text-surface-900 dark:text-white w-fit"}>
117
103
  <Tooltip title={"Admin"}
118
- open={tooltipsOpen}
119
- side={"right"} sideOffset={28}>
120
- <MoreVertIcon/>
104
+ open={tooltipsOpen}
105
+ side={"right"} sideOffset={28}>
106
+ <MoreVertIcon />
121
107
  </Tooltip>
122
108
  {drawerOpen && <div
123
109
  className={cls(
@@ -132,10 +118,10 @@ export function DefaultDrawer({
132
118
  <MenuItem
133
119
  onClick={(event) => {
134
120
  event.preventDefault();
135
- navigate(entry.url); // Consistent use of entry.url for navigation
121
+ navigate(entry.url);
136
122
  }}
137
123
  key={entry.id}>
138
- {<IconForView collectionOrView={entry.view}/>}
124
+ {<IconForView collectionOrView={entry.view} />}
139
125
  {entry.name}
140
126
  </MenuItem>)}
141
127
 
@@ -167,17 +153,17 @@ export function DrawerLogo({ logo }: {
167
153
  className={cls("cursor-pointer rounded ml-3 mr-1")}>
168
154
 
169
155
  <Tooltip title={"Home"}
170
- sideOffset={20}
171
- side="right">
156
+ sideOffset={20}
157
+ side="right">
172
158
  <Link
173
159
  className={"block"}
174
160
  to={navigation.basePath}>
175
161
  {logo
176
162
  ? <img src={logo}
177
- alt="Logo"
178
- className={cls("max-w-full max-h-full transition-all object-contain",
179
- drawerOpen ? "w-[96px] h-[96px]" : "w-[32px] h-[32px]")}/>
180
- : <FireCMSLogo/>}
163
+ alt="Logo"
164
+ className={cls("max-w-full max-h-full transition-all object-contain",
165
+ drawerOpen ? "w-[96px] h-[96px]" : "w-[32px] h-[32px]")} />
166
+ : <FireCMSLogo />}
181
167
 
182
168
  </Link>
183
169
  </Tooltip>
@@ -0,0 +1,118 @@
1
+ import React from "react";
2
+ import { cls, ExpandMoreIcon, Typography } from "@firecms/ui";
3
+ import { NavigationEntry } from "../types";
4
+ import { IconForView } from "../util";
5
+ import { DrawerNavigationItem } from "./DrawerNavigationItem";
6
+
7
+ export interface DrawerNavigationGroupProps {
8
+ /**
9
+ * Group name to display in header
10
+ */
11
+ group: string;
12
+ /**
13
+ * Navigation entries in this group
14
+ */
15
+ entries: NavigationEntry[];
16
+ /**
17
+ * Whether the group is collapsed
18
+ */
19
+ collapsed: boolean;
20
+ /**
21
+ * Callback when collapse state should toggle
22
+ */
23
+ onToggleCollapsed: () => void;
24
+ /**
25
+ * Whether the drawer is in open (expanded) state
26
+ */
27
+ drawerOpen: boolean;
28
+ /**
29
+ * Whether tooltips should be shown (drawer closed + hovered)
30
+ */
31
+ tooltipsOpen: boolean;
32
+ /**
33
+ * Whether admin menu is open (used to control tooltip visibility)
34
+ */
35
+ adminMenuOpen?: boolean;
36
+ /**
37
+ * Optional actions to render in the group header (e.g., "Add collection" button)
38
+ */
39
+ headerActions?: React.ReactNode;
40
+ /**
41
+ * Optional callback when a navigation item is clicked
42
+ */
43
+ onItemClick?: (entry: NavigationEntry) => void;
44
+ }
45
+
46
+ /**
47
+ * Shared drawer navigation group component used by both DefaultDrawer and FireCMSCloudDrawer.
48
+ * Renders a collapsible group with header and navigation items.
49
+ */
50
+ export function DrawerNavigationGroup({
51
+ group,
52
+ entries,
53
+ collapsed,
54
+ onToggleCollapsed,
55
+ drawerOpen,
56
+ tooltipsOpen,
57
+ adminMenuOpen,
58
+ headerActions,
59
+ onItemClick
60
+ }: DrawerNavigationGroupProps) {
61
+ return (
62
+ <div
63
+ className={"bg-surface-50 dark:bg-surface-800/30 my-4 rounded-lg ml-3 mr-1"}
64
+ key={`drawer_group_${group}`}
65
+ >
66
+ {/* Group Header */}
67
+ {drawerOpen ? (
68
+ <div
69
+ className="pl-4 pr-2 py-2 flex flex-row items-center cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-700/50 rounded-t-lg transition-colors"
70
+ onClick={onToggleCollapsed}
71
+ >
72
+ <ExpandMoreIcon
73
+ size={"smallest"}
74
+ className={cls(
75
+ "text-surface-500 dark:text-surface-400 transition-transform duration-200 mr-1",
76
+ collapsed ? "-rotate-90" : "rotate-0"
77
+ )}
78
+ />
79
+ <Typography
80
+ variant={"caption"}
81
+ color={"secondary"}
82
+ className="font-medium flex-grow line-clamp-1"
83
+ >
84
+ {(group || "Views").toUpperCase()}
85
+ </Typography>
86
+ {headerActions && (
87
+ <div onClick={(e) => e.stopPropagation()}>
88
+ {headerActions}
89
+ </div>
90
+ )}
91
+ </div>
92
+ ) : (
93
+ <div className="w-full" />
94
+ )}
95
+
96
+ {/* Collapsible Content */}
97
+ <div
98
+ className={cls(
99
+ "overflow-hidden transition-all duration-200 ease-in-out",
100
+ collapsed ? "max-h-0 opacity-0" : "max-h-[2000px] opacity-100"
101
+ )}
102
+ >
103
+ {entries.map((entry) => (
104
+ <DrawerNavigationItem
105
+ key={entry.id}
106
+ icon={<IconForView collectionOrView={entry.collection ?? entry.view} size={18} />}
107
+ tooltipsOpen={!collapsed && tooltipsOpen}
108
+ adminMenuOpen={adminMenuOpen}
109
+ drawerOpen={drawerOpen}
110
+ onClick={() => onItemClick?.(entry)}
111
+ url={entry.url}
112
+ name={entry.name}
113
+ />
114
+ ))}
115
+ </div>
116
+ </div>
117
+ );
118
+ }
@@ -34,13 +34,13 @@ export function DrawerNavigationItem({
34
34
  transition: drawerOpen ? "width 150ms ease-in" : undefined
35
35
  }}
36
36
  className={({ isActive }: any) => cls("rounded-lg truncate",
37
- "hover:bg-surface-accent-300 hover:bg-opacity-75 dark:hover:bg-surface-accent-800 dark:hover:bg-opacity-75 text-text-primary dark:text-surface-200 hover:text-surface-900 hover:dark:text-white",
37
+ "hover:bg-surface-accent-300 hover:bg-opacity-75 hover:bg-surface-accent-300/75 dark:hover:bg-surface-accent-800 dark:hover:bg-opacity-75 dark:hover:bg-surface-accent-800/75 text-text-primary dark:text-surface-200 hover:text-surface-900 hover:dark:text-white hover:bg-surface-accent-300/75 dark:hover:bg-surface-accent-800/75",
38
38
  "flex flex-row items-center mr-8",
39
39
  // "transition-all ease-in-out delay-100 duration-300",
40
40
  // drawerOpen ? "w-full" : "w-18",
41
41
  drawerOpen ? "pl-4 h-10" : "pl-4 h-9",
42
42
  "font-semibold text-xs",
43
- isActive ? "bg-surface-accent-200 bg-opacity-60 dark:bg-surface-800 dark:bg-opacity-50" : ""
43
+ isActive ? "bg-surface-accent-200 bg-opacity-60 dark:bg-surface-800 dark:bg-opacity-50 bg-surface-accent-200/60 dark:bg-surface-800/50" : ""
44
44
  )}
45
45
  to={url}
46
46
  >
@@ -49,8 +49,9 @@ export function DrawerNavigationItem({
49
49
 
50
50
  <div
51
51
  className={cls(
52
+ "text-text-primary dark:text-surface-200",
52
53
  drawerOpen ? "opacity-100" : "opacity-0 hidden",
53
- "ml-4 font-inherit text-inherit"
54
+ "ml-4 font-inherit"
54
55
  )}>
55
56
  {name.toUpperCase()}
56
57
  </div>