@rebasepro/admin 0.11.0 → 0.11.1-canary.g8caabf3
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/README.md +1 -1
- package/dist/{CollectionEditorDialog-o7F_zbGY.js → CollectionEditorDialog-id__fOqM.js} +4 -4
- package/dist/CollectionEditorDialog-id__fOqM.js.map +1 -0
- package/dist/{PropertyEditView-DgB2b22q.js → PropertyEditView-BLPT_e-N.js} +2 -2
- package/dist/{PropertyEditView-DgB2b22q.js.map → PropertyEditView-BLPT_e-N.js.map} +1 -1
- package/dist/{RouterCollectionsStudioView-DaLRgPkj.js → RouterCollectionsStudioView-DwkGdgyN.js} +4 -4
- package/dist/{RouterCollectionsStudioView-DaLRgPkj.js.map → RouterCollectionsStudioView-DwkGdgyN.js.map} +1 -1
- package/dist/collection_editor/CollectionEditorDialogsContext.d.ts +1 -1
- package/dist/collection_editor/_cms_internals.d.ts +1 -1
- package/dist/collection_editor/extensibility_types.d.ts +1 -1
- package/dist/collection_editor_ui.js +4 -4
- package/dist/components/CollectionEditorDialogs.d.ts +2 -2
- package/dist/components/CollectionViewBinding/CollectionListViewBinding.d.ts +2 -2
- package/dist/components/DefaultDrawer.d.ts +3 -3
- package/dist/components/DrawerNavigationGroup.d.ts +8 -1
- package/dist/components/DrawerNavigationItem.d.ts +7 -1
- package/dist/components/EditViewBinding.d.ts +1 -1
- package/dist/components/RebaseAdmin.d.ts +1 -1
- package/dist/components/RebaseAuthGate.d.ts +1 -1
- package/dist/components/RebaseLayout.d.ts +5 -1
- package/dist/components/RebaseNavigation.d.ts +2 -2
- package/dist/components/RebaseRouteDefs.d.ts +1 -1
- package/dist/components/RebaseShell.d.ts +6 -1
- package/dist/components/SidePanelProvider.d.ts +2 -2
- package/dist/components/app/Scaffold.d.ts +11 -0
- package/dist/{export-BvqhjdEb.js → export-lZhpezjk.js} +3 -3
- package/dist/export-lZhpezjk.js.map +1 -0
- package/dist/form/EntityForm.d.ts +2 -2
- package/dist/form/EntityFormBinding.d.ts +5 -5
- package/dist/{history-Dz20hH38.js → history-DkP1tce0.js} +2 -2
- package/dist/{history-Dz20hH38.js.map → history-DkP1tce0.js.map} +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/useAdminContext.d.ts +29 -0
- package/dist/{import-DINSB-Vu.js → import-DQPGsiIw.js} +2 -2
- package/dist/{import-DINSB-Vu.js.map → import-DQPGsiIw.js.map} +1 -1
- package/dist/index.js +60 -38
- package/dist/index.js.map +1 -1
- package/dist/types/components/EntityFormProps.d.ts +1 -1
- package/dist/types/fields.d.ts +1 -1
- package/dist/{util-BhhfHY1M.js → util-DBxSfWpl.js} +45 -35
- package/dist/util-DBxSfWpl.js.map +1 -0
- package/package.json +9 -9
- package/src/collection_editor/CollectionEditorDialogsContext.tsx +1 -1
- package/src/collection_editor/ConfigControllerProvider.tsx +1 -1
- package/src/collection_editor/_cms_internals.ts +1 -1
- package/src/collection_editor/extensibility_types.ts +1 -1
- package/src/collection_editor/index.ts +2 -2
- package/src/collection_editor/ui/collection_editor/EntityActionsEditTab.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/SubcollectionsEditTab.tsx +2 -2
- package/src/components/CollectionEditorDialogs.tsx +2 -2
- package/src/components/CollectionTableBinding/CollectionRowActions.tsx +5 -4
- package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +8 -7
- package/src/components/CollectionViewBinding/CollectionViewActions.tsx +2 -2
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +2 -2
- package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +2 -2
- package/src/components/DefaultDrawer.tsx +19 -8
- package/src/components/DeleteEntityDialog.tsx +2 -2
- package/src/components/DetailViewBinding.tsx +21 -1
- package/src/components/DrawerNavigationGroup.tsx +28 -4
- package/src/components/DrawerNavigationItem.tsx +19 -5
- package/src/components/EditFormActions.tsx +4 -3
- package/src/components/EditViewBinding.tsx +1 -1
- package/src/components/HomePage/ContentHomePage.tsx +4 -4
- package/src/components/RebaseAdmin.tsx +3 -3
- package/src/components/RebaseAuthGate.tsx +1 -1
- package/src/components/RebaseLayout.tsx +8 -2
- package/src/components/RebaseNavigation.tsx +9 -9
- package/src/components/RebaseRouteDefs.tsx +2 -2
- package/src/components/RebaseShell.tsx +10 -1
- package/src/components/RelationSelector.tsx +2 -2
- package/src/components/SidePanelProvider.tsx +2 -2
- package/src/components/app/Scaffold.tsx +16 -1
- package/src/data_export/export/ExportCollectionAction.tsx +2 -2
- package/src/form/EntityForm.tsx +2 -2
- package/src/form/EntityFormActions.tsx +2 -1
- package/src/form/EntityFormBinding.tsx +7 -7
- package/src/hooks/index.ts +1 -1
- package/src/hooks/navigation/useNavigationResolution.ts +2 -2
- package/src/hooks/{useCMSContext.tsx → useAdminContext.tsx} +10 -10
- package/src/hooks/useResolvedNavigationFrom.tsx +2 -2
- package/src/index.ts +4 -4
- package/src/preview/components/RelationPreview.tsx +1 -1
- package/src/types/components/EntityFormProps.tsx +2 -2
- package/src/types/fields.tsx +1 -1
- package/dist/CollectionEditorDialog-o7F_zbGY.js.map +0 -1
- package/dist/export-BvqhjdEb.js.map +0 -1
- package/dist/hooks/useCMSContext.d.ts +0 -29
- package/dist/util-BhhfHY1M.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/admin",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.11.
|
|
4
|
+
"version": "0.11.1-canary.g8caabf3",
|
|
5
5
|
"description": "Rebase CMS — content management views, forms, and routing",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -83,14 +83,14 @@
|
|
|
83
83
|
"react-dropzone": "^15.0.0",
|
|
84
84
|
"react-use-measure": "^2.1.7",
|
|
85
85
|
"zod": "^4.4.3",
|
|
86
|
-
"@rebasepro/admin-types": "0.11.
|
|
87
|
-
"@rebasepro/
|
|
88
|
-
"@rebasepro/forms": "0.11.
|
|
89
|
-
"@rebasepro/
|
|
90
|
-
"@rebasepro/
|
|
91
|
-
"@rebasepro/
|
|
92
|
-
"@rebasepro/
|
|
93
|
-
"@rebasepro/
|
|
86
|
+
"@rebasepro/admin-types": "0.11.1-canary.g8caabf3",
|
|
87
|
+
"@rebasepro/app": "0.11.1-canary.g8caabf3",
|
|
88
|
+
"@rebasepro/forms": "0.11.1-canary.g8caabf3",
|
|
89
|
+
"@rebasepro/common": "0.11.1-canary.g8caabf3",
|
|
90
|
+
"@rebasepro/types": "0.11.1-canary.g8caabf3",
|
|
91
|
+
"@rebasepro/ui": "0.11.1-canary.g8caabf3",
|
|
92
|
+
"@rebasepro/utils": "0.11.1-canary.g8caabf3",
|
|
93
|
+
"@rebasepro/inference": "0.11.1-canary.g8caabf3"
|
|
94
94
|
},
|
|
95
95
|
"peerDependencies": {
|
|
96
96
|
"react": ">=19.0.0",
|
|
@@ -5,7 +5,7 @@ import React, { createContext, useContext } from "react";
|
|
|
5
5
|
* sets and CollectionEditorDialogs reads.
|
|
6
6
|
*
|
|
7
7
|
* This indirection exists because ConfigControllerProvider is a root-scope
|
|
8
|
-
* plugin provider that wraps <RebaseShell>, therefore living ABOVE the
|
|
8
|
+
* plugin provider that wraps <RebaseShell>, therefore living ABOVE the admin
|
|
9
9
|
* context providers (CollectionRegistryContext, NavigationStateContext,
|
|
10
10
|
* UrlContext). The actual dialog components need those contexts, so they
|
|
11
11
|
* must render inside the shell tree.
|
|
@@ -245,7 +245,7 @@ parentEntityIds,
|
|
|
245
245
|
}, [onAnalyticsEvent, pathSuggestions]);
|
|
246
246
|
|
|
247
247
|
// Build the dialog props objects that will be consumed by
|
|
248
|
-
// CollectionEditorDialogs (rendered inside RebaseShell where
|
|
248
|
+
// CollectionEditorDialogs (rendered inside RebaseShell where admin
|
|
249
249
|
// contexts are available).
|
|
250
250
|
const collectionDialogProps = useMemo(() => {
|
|
251
251
|
if (!currentDialog) return undefined;
|
|
@@ -160,7 +160,7 @@ export interface CollectionEditorExtensionProps {
|
|
|
160
160
|
* reading from React contexts.
|
|
161
161
|
*
|
|
162
162
|
* Use this when embedding the collection editor outside of the Rebase
|
|
163
|
-
*
|
|
163
|
+
* admin, where the Rebase context providers are not available.
|
|
164
164
|
*
|
|
165
165
|
* Default: false (standard Rebase mode).
|
|
166
166
|
*/
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Collection Editor — moved from @rebasepro/studio
|
|
2
|
-
// This module provides the visual schema/collection editor for the
|
|
2
|
+
// This module provides the visual schema/collection editor for the admin.
|
|
3
3
|
//
|
|
4
4
|
// IMPORTANT: Heavy UI components (CollectionEditorDialog, CollectionStudioView,
|
|
5
5
|
// CollectionsStudioView, PropertyForm, PropertyFormDialog) are NOT re-exported
|
|
6
|
-
// here to keep the main
|
|
6
|
+
// here to keep the main admin bundle lean. Import them from
|
|
7
7
|
// "@rebasepro/admin/collection_editor_ui" instead — they live in a separate chunk
|
|
8
8
|
// that is loaded on demand.
|
|
9
9
|
|
|
@@ -145,7 +145,7 @@ export function EntityActionsEditTab({
|
|
|
145
145
|
title={<>Remove this action?</>}
|
|
146
146
|
body={<>This will <b>not
|
|
147
147
|
delete any data</b>, only
|
|
148
|
-
the action in the
|
|
148
|
+
the action in the admin</>}/>}
|
|
149
149
|
|
|
150
150
|
<EntityActionsSelectDialog
|
|
151
151
|
open={addEntityActionDialogOpen}
|
|
@@ -27,7 +27,7 @@ export function GetCodeDialog({
|
|
|
27
27
|
<DialogContent>
|
|
28
28
|
|
|
29
29
|
<Typography variant={"body2"} className={"my-4 mb-8"}>
|
|
30
|
-
If you want to customise the collection in code, you can add this collection code to your
|
|
30
|
+
If you want to customise the collection in code, you can add this collection code to your admin
|
|
31
31
|
app configuration.
|
|
32
32
|
More info in the <a
|
|
33
33
|
rel="noopener noreferrer"
|
|
@@ -214,7 +214,7 @@ export function SubcollectionsEditTab({
|
|
|
214
214
|
title={<>Delete this subcollection?</>}
|
|
215
215
|
body={<> This will <b>not
|
|
216
216
|
delete any data</b>, only
|
|
217
|
-
the collection in the
|
|
217
|
+
the collection in the admin</>}/>}
|
|
218
218
|
{viewToDelete &&
|
|
219
219
|
<ConfirmationDialog open={Boolean(viewToDelete)}
|
|
220
220
|
onAccept={() => {
|
|
@@ -225,7 +225,7 @@ export function SubcollectionsEditTab({
|
|
|
225
225
|
title={<>Remove this view?</>}
|
|
226
226
|
body={<>This will <b>not
|
|
227
227
|
delete any data</b>, only
|
|
228
|
-
the view in the
|
|
228
|
+
the view in the admin</>}/>}
|
|
229
229
|
|
|
230
230
|
<CollectionEditorDialog
|
|
231
231
|
open={Boolean(currentDialog)}
|
|
@@ -16,12 +16,12 @@ const PropertyFormDialog = lazy(() =>
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Renders the CollectionEditorDialog and PropertyFormDialog inside
|
|
19
|
-
* the RebaseShell tree where
|
|
19
|
+
* the RebaseShell tree where admin-internal contexts
|
|
20
20
|
* (CollectionRegistryContext, NavigationStateContext, UrlContext)
|
|
21
21
|
* are available.
|
|
22
22
|
*
|
|
23
23
|
* Both dialog components are loaded dynamically via React.lazy so that
|
|
24
|
-
* production
|
|
24
|
+
* production admin bundles that never open the collection editor don't pay
|
|
25
25
|
* the download/parse cost up-front.
|
|
26
26
|
*
|
|
27
27
|
* The dialog state (open/close, props) is managed by ConfigControllerProvider
|
|
@@ -16,9 +16,10 @@ import {
|
|
|
16
16
|
Tooltip
|
|
17
17
|
} from "@rebasepro/ui";
|
|
18
18
|
import { useTranslation } from "@rebasepro/app";
|
|
19
|
+
import { getIcon } from "@rebasepro/app";
|
|
19
20
|
import { getEntityFromCache } from "@rebasepro/app";
|
|
20
21
|
import { getLocalChangesBackup } from "@rebasepro/app";
|
|
21
|
-
import {
|
|
22
|
+
import { useAdminContext } from "../../hooks/useAdminContext";
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
*
|
|
@@ -79,7 +80,7 @@ export const CollectionRowActions = function CollectionRowActions({
|
|
|
79
80
|
isDraggable?: boolean;
|
|
80
81
|
}) {
|
|
81
82
|
|
|
82
|
-
const context =
|
|
83
|
+
const context = useAdminContext();
|
|
83
84
|
const sidePanelCtrl = context.sidePanelController;
|
|
84
85
|
const { t } = useTranslation();
|
|
85
86
|
|
|
@@ -140,7 +141,7 @@ export const CollectionRowActions = function CollectionRowActions({
|
|
|
140
141
|
});
|
|
141
142
|
}}
|
|
142
143
|
size={iconSize}>
|
|
143
|
-
{action.icon}
|
|
144
|
+
{getIcon(action.icon, undefined, undefined, "smallest")}
|
|
144
145
|
</IconButton>;
|
|
145
146
|
if (isEditAction && hasDraft) {
|
|
146
147
|
iconButton = (
|
|
@@ -183,7 +184,7 @@ export const CollectionRowActions = function CollectionRowActions({
|
|
|
183
184
|
openEntityMode: openEntityMode ?? collection?.openEntityMode
|
|
184
185
|
});
|
|
185
186
|
}}>
|
|
186
|
-
{action.icon}
|
|
187
|
+
{getIcon(action.icon, undefined, undefined, "smallest")}
|
|
187
188
|
{action.name}
|
|
188
189
|
</MenuItem>
|
|
189
190
|
))}
|
|
@@ -22,9 +22,10 @@ import {
|
|
|
22
22
|
import { useAnalyticsController } from "@rebasepro/app";
|
|
23
23
|
import { getEntityPreviewKeys } from "../../util/previews";
|
|
24
24
|
import { IconForView } from "@rebasepro/app";
|
|
25
|
+
import { getIcon } from "@rebasepro/app";
|
|
25
26
|
import { getValueInPath } from "@rebasepro/utils";
|
|
26
27
|
import { useCollectionSlotKeys, resolveEntitySlots, type CollectionSlotKeys } from "./usePreviewSlots";
|
|
27
|
-
import {
|
|
28
|
+
import { useAdminContext } from "../../hooks/useAdminContext";
|
|
28
29
|
import { resolveEntityAction } from "../../util/resolutions";
|
|
29
30
|
|
|
30
31
|
export type CollectionListViewBindingProps<M extends Record<string, unknown> = Record<string, unknown>> = {
|
|
@@ -265,8 +266,8 @@ year: diffDays > 365 ? "numeric" : undefined });
|
|
|
265
266
|
}
|
|
266
267
|
|
|
267
268
|
/**
|
|
268
|
-
* Classic
|
|
269
|
-
* Designed to be the most familiar, stereotypical
|
|
269
|
+
* Classic admin list view for displaying entities.
|
|
270
|
+
* Designed to be the most familiar, stereotypical admin content management view:
|
|
270
271
|
* - Clean rows with checkbox, icon/avatar, title, metadata, and actions
|
|
271
272
|
* - Column-sortable headers
|
|
272
273
|
* - Infinite scroll
|
|
@@ -289,7 +290,7 @@ export function CollectionListViewBinding<M extends Record<string, unknown> = Re
|
|
|
289
290
|
const authController = useAuthController();
|
|
290
291
|
const customizationController = useCustomizationController();
|
|
291
292
|
const analyticsController = useAnalyticsController();
|
|
292
|
-
const context =
|
|
293
|
+
const context = useAdminContext();
|
|
293
294
|
|
|
294
295
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
295
296
|
const [containerWidth, setContainerWidth] = useState(1200);
|
|
@@ -622,7 +623,7 @@ const ListRow = React.memo(function ListRow<M extends Record<string, unknown>>({
|
|
|
622
623
|
isLast: boolean;
|
|
623
624
|
isActive?: boolean;
|
|
624
625
|
listViewActions?: EntityAction[];
|
|
625
|
-
context?: ReturnType<typeof
|
|
626
|
+
context?: ReturnType<typeof useAdminContext>;
|
|
626
627
|
path?: string;
|
|
627
628
|
selectionController?: SelectionController<M>;
|
|
628
629
|
openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
|
|
@@ -864,7 +865,7 @@ const ListRow = React.memo(function ListRow<M extends Record<string, unknown>>({
|
|
|
864
865
|
openEntityMode: openEntityMode ?? collection?.openEntityMode ?? "full_screen"
|
|
865
866
|
});
|
|
866
867
|
}}>
|
|
867
|
-
{action.icon}
|
|
868
|
+
{getIcon(action.icon, undefined, undefined, "smallest")}
|
|
868
869
|
</IconButton>
|
|
869
870
|
</Tooltip>
|
|
870
871
|
))}
|
|
@@ -888,7 +889,7 @@ const ListRow = React.memo(function ListRow<M extends Record<string, unknown>>({
|
|
|
888
889
|
isLast: boolean;
|
|
889
890
|
isActive?: boolean;
|
|
890
891
|
listViewActions?: EntityAction[];
|
|
891
|
-
context?: ReturnType<typeof
|
|
892
|
+
context?: ReturnType<typeof useAdminContext>;
|
|
892
893
|
path?: string;
|
|
893
894
|
selectionController?: SelectionController<M>;
|
|
894
895
|
openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
|
|
@@ -13,7 +13,7 @@ const ImportCollectionAction = lazy(() => import("../../data_import/import").the
|
|
|
13
13
|
const ExportCollectionAction = lazy(() => import("../../data_export/export").then(m => ({ default: m.ExportCollectionAction })));
|
|
14
14
|
import { EditorCollectionAction } from "../../collection_editor/ui/EditorCollectionAction";
|
|
15
15
|
import { useCollectionEditorController } from "../../collection_editor/useCollectionEditorController";
|
|
16
|
-
import {
|
|
16
|
+
import { useAdminContext } from "../../hooks/useAdminContext";
|
|
17
17
|
|
|
18
18
|
export type CollectionViewActionsProps<M extends Record<string, unknown>> = {
|
|
19
19
|
collection: AdminCollection<M>;
|
|
@@ -52,7 +52,7 @@ export function CollectionViewActions<M extends Record<string, unknown>>({
|
|
|
52
52
|
children,
|
|
53
53
|
openNewDocument
|
|
54
54
|
}: CollectionViewActionsProps<M>) {
|
|
55
|
-
const context =
|
|
55
|
+
const context = useAdminContext();
|
|
56
56
|
|
|
57
57
|
const { canCreate, canDelete } = usePermissions();
|
|
58
58
|
|
|
@@ -75,7 +75,7 @@ import { addRecentId, getRecentIds } from "./utils";
|
|
|
75
75
|
import { isViewMode, OpenEntityMode, resolveOpenEntityMode, VIEW_MODE_PARAM } from "../../util/view_mode";
|
|
76
76
|
import { mergeDeep } from "@rebasepro/utils";
|
|
77
77
|
import { useBreadcrumbsController } from "../../hooks/useBreadcrumbsController";
|
|
78
|
-
import {
|
|
78
|
+
import { useAdminContext } from "../../hooks/useAdminContext";
|
|
79
79
|
import { useCollectionRegistryController } from "../../hooks/navigation/contexts/CollectionRegistryContext";
|
|
80
80
|
import { useSidePanel } from "../../hooks/useSidePanel";
|
|
81
81
|
import { useUrlController } from "../../hooks/navigation/contexts/UrlContext";
|
|
@@ -167,7 +167,7 @@ const CollectionViewBindingInner = React.memo(
|
|
|
167
167
|
) {
|
|
168
168
|
|
|
169
169
|
const { t } = useTranslation();
|
|
170
|
-
const context =
|
|
170
|
+
const context = useAdminContext();
|
|
171
171
|
const collectionRegistry = useCollectionRegistryController();
|
|
172
172
|
const urlController = useUrlController();
|
|
173
173
|
const breadcrumbs = useBreadcrumbsController();
|
|
@@ -11,7 +11,7 @@ import { FilterPresetsButton } from "./FilterPresetsButton";
|
|
|
11
11
|
import { toArray } from "@rebasepro/utils";
|
|
12
12
|
import { useNavigate } from "react-router-dom";
|
|
13
13
|
import { useUrlController } from "../../hooks/navigation/contexts/UrlContext";
|
|
14
|
-
import {
|
|
14
|
+
import { useAdminContext } from "../../hooks/useAdminContext";
|
|
15
15
|
import { withViewMode } from "../../util/view_mode";
|
|
16
16
|
|
|
17
17
|
export type CollectionViewStartActionsProps<M extends Record<string, unknown>> = {
|
|
@@ -43,7 +43,7 @@ export function CollectionViewStartActions<M extends Record<string, unknown>>({
|
|
|
43
43
|
openNewDocument
|
|
44
44
|
}: CollectionViewStartActionsProps<M>) {
|
|
45
45
|
|
|
46
|
-
const context =
|
|
46
|
+
const context = useAdminContext();
|
|
47
47
|
const largeLayout = useLargeLayout();
|
|
48
48
|
const { t } = useTranslation();
|
|
49
49
|
|
|
@@ -33,9 +33,9 @@ import { LanguageToggle, RebaseLogo } from "@rebasepro/app";
|
|
|
33
33
|
import { useApp } from "./app/useApp";
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* Default drawer used in the
|
|
36
|
+
* Default drawer used in the admin.
|
|
37
37
|
*
|
|
38
|
-
* When no `children` are provided, renders the full
|
|
38
|
+
* When no `children` are provided, renders the full admin navigation
|
|
39
39
|
* (collection groups, mode switch, plugin slots).
|
|
40
40
|
*
|
|
41
41
|
* When `children` **are** provided, renders the shared drawer shell
|
|
@@ -60,7 +60,7 @@ export function DefaultDrawer({
|
|
|
60
60
|
logoDestination?: string;
|
|
61
61
|
/**
|
|
62
62
|
* Custom navigation content for the drawer.
|
|
63
|
-
* When provided, replaces the default
|
|
63
|
+
* When provided, replaces the default admin navigation (collection groups,
|
|
64
64
|
* mode switch, slots). The shared shell (logo, scroll area, footer
|
|
65
65
|
* actions, collapse toggle) is still rendered around it.
|
|
66
66
|
*/
|
|
@@ -115,7 +115,7 @@ export function DefaultDrawer({
|
|
|
115
115
|
? "linear-gradient(to bottom, transparent 0, black 20px, black calc(100% - 20px), transparent 100%)"
|
|
116
116
|
: "linear-gradient(to bottom, black 0, black calc(100% - 20px), transparent 100%)"
|
|
117
117
|
}}>
|
|
118
|
-
{children ?? <
|
|
118
|
+
{children ?? <AdminNavigationContent />}
|
|
119
119
|
</div>
|
|
120
120
|
|
|
121
121
|
{footerActions !== null && (
|
|
@@ -138,11 +138,11 @@ export function DefaultDrawer({
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
|
-
* Default
|
|
141
|
+
* Default admin navigation content — rendered inside DefaultDrawer when no
|
|
142
142
|
* custom `children` are provided. Contains the mode switch, navigation
|
|
143
143
|
* groups, and header / footer plugin slots.
|
|
144
144
|
*/
|
|
145
|
-
function
|
|
145
|
+
function AdminNavigationContent() {
|
|
146
146
|
|
|
147
147
|
const {
|
|
148
148
|
drawerHovered,
|
|
@@ -166,7 +166,7 @@ function CMSNavigationContent() {
|
|
|
166
166
|
const allNavigationEntries = navigationState.topLevelNavigation?.navigationEntries ?? [];
|
|
167
167
|
|
|
168
168
|
// Build a set of Studio-only dev view slugs so we can distinguish them from
|
|
169
|
-
//
|
|
169
|
+
// admin custom views added via <RebaseAdmin views={...}> or plugins.
|
|
170
170
|
const studioViewSlugs = useMemo(() => {
|
|
171
171
|
const slugs = new Set<string>();
|
|
172
172
|
(registry.studioConfig?.devViews ?? []).forEach(v => slugs.add(v.slug));
|
|
@@ -176,7 +176,7 @@ function CMSNavigationContent() {
|
|
|
176
176
|
}, [registry.studioConfig, registry.cmsConfig?.collectionEditor]);
|
|
177
177
|
|
|
178
178
|
// Studio mode shows Studio dev views + admin entries (Users/Roles).
|
|
179
|
-
// Content mode shows collections,
|
|
179
|
+
// Content mode shows collections, admin custom views, and admin entries — but not Studio dev views.
|
|
180
180
|
const filteredEntries = adminModeController.mode === "studio"
|
|
181
181
|
? allNavigationEntries.filter(e => (e.type === "view" && studioViewSlugs.has(e.slug)) || e.type === "admin")
|
|
182
182
|
: allNavigationEntries.filter(e => e.type !== "view" || !studioViewSlugs.has(e.slug));
|
|
@@ -189,6 +189,16 @@ function CMSNavigationContent() {
|
|
|
189
189
|
...[...entryGroups].filter(g => !orderedGroups.includes(g))
|
|
190
190
|
];
|
|
191
191
|
|
|
192
|
+
// Group header icons, declared on `navigationGroupMappings`. Keyed by name
|
|
193
|
+
// because that is what a navigation entry's `group` holds.
|
|
194
|
+
const groupIcons = useMemo(() => {
|
|
195
|
+
const icons = new Map<string, string>();
|
|
196
|
+
(registry.cmsConfig?.navigationGroupMappings ?? []).forEach(mapping => {
|
|
197
|
+
if (mapping.icon) icons.set(mapping.name, mapping.icon);
|
|
198
|
+
});
|
|
199
|
+
return icons;
|
|
200
|
+
}, [registry.cmsConfig?.navigationGroupMappings]);
|
|
201
|
+
|
|
192
202
|
// Collapsible groups state - using "drawer" namespace for independent state from home page
|
|
193
203
|
const collapsedDefaults = useMemo(
|
|
194
204
|
() => buildCollapsedDefaults(registry.cmsConfig?.navigationGroupMappings, "drawer"),
|
|
@@ -244,6 +254,7 @@ context });
|
|
|
244
254
|
tooltipsOpen={tooltipsOpen}
|
|
245
255
|
adminMenuOpen={adminMenuOpen}
|
|
246
256
|
onItemClick={onItemClick}
|
|
257
|
+
icon={groupIcons.get(group)}
|
|
247
258
|
/>
|
|
248
259
|
);
|
|
249
260
|
})}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
useSnackbarController,
|
|
11
11
|
useTranslation
|
|
12
12
|
} from "@rebasepro/app";
|
|
13
|
-
import {
|
|
13
|
+
import { useAdminContext } from "../hooks";
|
|
14
14
|
import { EntityViewBinding } from "./EntityViewBinding";
|
|
15
15
|
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
16
16
|
|
|
@@ -54,7 +54,7 @@ export function DeleteEntityDialog<M extends Record<string, unknown>>({
|
|
|
54
54
|
const { t } = useTranslation();
|
|
55
55
|
const [loading, setLoading] = useState(false);
|
|
56
56
|
|
|
57
|
-
const context =
|
|
57
|
+
const context = useAdminContext();
|
|
58
58
|
const entityOrEntities = Array.isArray(entityOrEntitiesToDelete) && entityOrEntitiesToDelete.length === 1
|
|
59
59
|
? entityOrEntitiesToDelete[0]
|
|
60
60
|
: entityOrEntitiesToDelete;
|
|
@@ -11,6 +11,7 @@ import { EntityViewBinding } from "./EntityViewBinding";
|
|
|
11
11
|
import { CircularProgressCenter, iconSize } from "@rebasepro/ui";
|
|
12
12
|
import {
|
|
13
13
|
Alert,
|
|
14
|
+
ArrowLeftIcon,
|
|
14
15
|
Button,
|
|
15
16
|
CenteredView,
|
|
16
17
|
cls,
|
|
@@ -470,7 +471,24 @@ entityId }
|
|
|
470
471
|
);
|
|
471
472
|
});
|
|
472
473
|
|
|
473
|
-
|
|
474
|
+
// Full screen is the one layout with no way out of its own: a side panel and a
|
|
475
|
+
// dialog can be dismissed, and a split keeps the list beside it, but opening an
|
|
476
|
+
// entity full screen replaces the collection entirely and left browser Back as
|
|
477
|
+
// the only route back to it — which is not an affordance the page shows, and is
|
|
478
|
+
// wrong anyway once the user has moved between tabs inside the entity.
|
|
479
|
+
const backToCollectionButton = layout === "full_screen" ? (
|
|
480
|
+
<Tooltip title={t("back")}>
|
|
481
|
+
<IconButton
|
|
482
|
+
size="small"
|
|
483
|
+
aria-label={t("back")}
|
|
484
|
+
onClick={() => navigate(urlController.buildUrlCollectionPath(path))}
|
|
485
|
+
>
|
|
486
|
+
<ArrowLeftIcon size={iconSize.smallest} />
|
|
487
|
+
</IconButton>
|
|
488
|
+
</Tooltip>
|
|
489
|
+
) : null;
|
|
490
|
+
|
|
491
|
+
const shouldShowTopBar = Boolean(barActions) || hasAdditionalViews || layout === "side_panel" || layout === "dialog" || Boolean(backToCollectionButton);
|
|
474
492
|
|
|
475
493
|
const fullScreenButton = !barActions && (layout === "side_panel" || layout === "split" || layout === "dialog") && entityId ? (
|
|
476
494
|
<Tooltip title={"Open full screen"}>
|
|
@@ -562,6 +580,8 @@ entityId }
|
|
|
562
580
|
{shouldShowTopBar && <div
|
|
563
581
|
className={cls("h-[52px] items-center flex overflow-hidden w-full border-b pl-2 pr-2 flex bg-surface-50 dark:bg-surface-900", defaultBorderMixin)}>
|
|
564
582
|
|
|
583
|
+
{backToCollectionButton}
|
|
584
|
+
|
|
565
585
|
{fullScreenButton}
|
|
566
586
|
|
|
567
587
|
{barActions?.({
|
|
@@ -2,7 +2,7 @@ import type { NavigationEntry } from "@rebasepro/admin-types";
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { ChevronDownIcon, cls, iconSize, Typography } from "@rebasepro/ui";
|
|
4
4
|
|
|
5
|
-
import { IconForView } from "@rebasepro/app";
|
|
5
|
+
import { IconForView, getIcon } from "@rebasepro/app";
|
|
6
6
|
import { DrawerNavigationItem } from "./DrawerNavigationItem";
|
|
7
7
|
import { useTranslation, useComponentOverride } from "@rebasepro/app";
|
|
8
8
|
|
|
@@ -47,6 +47,13 @@ export interface DrawerNavigationGroupProps {
|
|
|
47
47
|
* Hide the group header (title and expandable panel)
|
|
48
48
|
*/
|
|
49
49
|
hideHeader?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Lucide icon name for the group header, from `NavigationGroupMapping.icon`.
|
|
52
|
+
*
|
|
53
|
+
* Supplying one moves the visual anchor from the rows to the group: the header
|
|
54
|
+
* gets the icon, and the entries below trade theirs for an indent.
|
|
55
|
+
*/
|
|
56
|
+
icon?: string;
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
/**
|
|
@@ -63,10 +70,17 @@ export function DrawerNavigationGroup({
|
|
|
63
70
|
adminMenuOpen,
|
|
64
71
|
headerActions,
|
|
65
72
|
onItemClick,
|
|
66
|
-
hideHeader
|
|
73
|
+
hideHeader,
|
|
74
|
+
icon
|
|
67
75
|
}: DrawerNavigationGroupProps) {
|
|
68
76
|
const { t } = useTranslation();
|
|
69
77
|
const ResolvedDrawerNavigationItem = useComponentOverride("Shell.DrawerNavigationItem", DrawerNavigationItem);
|
|
78
|
+
|
|
79
|
+
// Indent the entries only when the group itself is carrying the icon. Without a
|
|
80
|
+
// header there is nothing to indent under, and collapsed to a rail the entry
|
|
81
|
+
// icons are the only affordance left, so both cases keep the original rows.
|
|
82
|
+
const groupIcon = !hideHeader && drawerOpen ? getIcon(icon, undefined, undefined, "small") : undefined;
|
|
83
|
+
const indentEntries = Boolean(groupIcon);
|
|
70
84
|
return (
|
|
71
85
|
<div
|
|
72
86
|
className={"my-2 mx-2 flex flex-col"}
|
|
@@ -83,14 +97,23 @@ export function DrawerNavigationGroup({
|
|
|
83
97
|
<ChevronDownIcon
|
|
84
98
|
size={iconSize.small}
|
|
85
99
|
className={cls(
|
|
86
|
-
"text-surface-
|
|
100
|
+
"text-surface-500 dark:text-surface-400 transition-transform duration-200 mr-1",
|
|
87
101
|
collapsed ? "-rotate-90" : "rotate-0"
|
|
88
102
|
)}
|
|
89
103
|
/>
|
|
104
|
+
{groupIcon && (
|
|
105
|
+
<span className="shrink-0 mr-2 flex items-center text-surface-600 dark:text-surface-300 [&>svg]:size-4">
|
|
106
|
+
{groupIcon}
|
|
107
|
+
</span>
|
|
108
|
+
)}
|
|
90
109
|
<Typography
|
|
91
110
|
variant={"caption"}
|
|
92
111
|
color={"secondary"}
|
|
93
|
-
|
|
112
|
+
// A category label is the thing you scan to find anything else in
|
|
113
|
+
// the drawer, so it is read far more often than it is read *past*.
|
|
114
|
+
// At 11px uppercase in surface-400 it sat below the contrast of the
|
|
115
|
+
// rows it labels, which inverted that.
|
|
116
|
+
className="font-semibold text-[12px] uppercase tracking-wide flex-grow line-clamp-1 text-surface-600 dark:text-surface-300"
|
|
94
117
|
>
|
|
95
118
|
{(group || t("views_group"))}
|
|
96
119
|
</Typography>
|
|
@@ -122,6 +145,7 @@ export function DrawerNavigationGroup({
|
|
|
122
145
|
onClick={() => onItemClick?.(entry)}
|
|
123
146
|
url={entry.url}
|
|
124
147
|
name={entry.name}
|
|
148
|
+
indented={indentEntries}
|
|
125
149
|
/>
|
|
126
150
|
))}
|
|
127
151
|
</div>
|
|
@@ -10,7 +10,8 @@ export function DrawerNavigationItem({
|
|
|
10
10
|
adminMenuOpen,
|
|
11
11
|
tooltipsOpen,
|
|
12
12
|
url,
|
|
13
|
-
onClick
|
|
13
|
+
onClick,
|
|
14
|
+
indented = false
|
|
14
15
|
}: {
|
|
15
16
|
icon: React.ReactElement,
|
|
16
17
|
name: string,
|
|
@@ -19,12 +20,25 @@ export function DrawerNavigationItem({
|
|
|
19
20
|
adminMenuOpen?: boolean,
|
|
20
21
|
url: string,
|
|
21
22
|
onClick?: () => void,
|
|
23
|
+
/**
|
|
24
|
+
* Render as a child of an icon-bearing group: no icon of its own, indented to
|
|
25
|
+
* sit under the group's label. Set by {@link DrawerNavigationGroup} only when
|
|
26
|
+
* the group actually has an icon and the drawer is expanded.
|
|
27
|
+
*/
|
|
28
|
+
indented?: boolean,
|
|
22
29
|
}) {
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
// Indented rows give up the icon rather than keeping it *and* indenting: a
|
|
32
|
+
// per-row icon repeated down a group is what flattens the hierarchy, since
|
|
33
|
+
// every row then reads at the weight of the category above it. The indent is
|
|
34
|
+
// the same 44px the icon occupied, so labels stay on the original grid and the
|
|
35
|
+
// rail width does not change.
|
|
36
|
+
const iconWrap = indented
|
|
37
|
+
? <div className={"shrink-0 w-[44px] h-[30px]"} aria-hidden={true}/>
|
|
38
|
+
: <div
|
|
39
|
+
className={"shrink-0 flex items-center justify-center w-[44px] h-[30px] text-surface-500 dark:text-text-secondary-dark [&>svg]:size-4 group-hover/nav:text-primary transition-colors duration-150"}>
|
|
40
|
+
{icon}
|
|
41
|
+
</div>;
|
|
28
42
|
|
|
29
43
|
const listItem = <div>
|
|
30
44
|
<NavLink
|
|
@@ -20,7 +20,8 @@ import { SideDialogController, useSideDialogContext } from "./SideDialogs";
|
|
|
20
20
|
import { FormexController } from "@rebasepro/forms";
|
|
21
21
|
import { ErrorTooltip } from "@rebasepro/app";
|
|
22
22
|
import { usePermissions } from "@rebasepro/app";
|
|
23
|
-
import {
|
|
23
|
+
import { getIcon } from "@rebasepro/app";
|
|
24
|
+
import { useAdminContext } from "../hooks/useAdminContext";
|
|
24
25
|
import { useChildViewSource } from "../hooks/useChildViewSource";
|
|
25
26
|
|
|
26
27
|
export function EditFormActions({
|
|
@@ -40,7 +41,7 @@ export function EditFormActions({
|
|
|
40
41
|
}: EntityFormActionsProps) {
|
|
41
42
|
|
|
42
43
|
const { canCreate, canDelete } = usePermissions();
|
|
43
|
-
const context =
|
|
44
|
+
const context = useAdminContext();
|
|
44
45
|
const sidePanelController = context.sidePanelController;
|
|
45
46
|
const sideDialogContext = useSideDialogContext();
|
|
46
47
|
const customizationController = useCustomizationController();
|
|
@@ -371,7 +372,7 @@ function EntityActionButton({
|
|
|
371
372
|
});
|
|
372
373
|
}
|
|
373
374
|
}}>
|
|
374
|
-
{loading ? <CircularProgress size={"smallest"}/> : action.icon}
|
|
375
|
+
{loading ? <CircularProgress size={"smallest"}/> : getIcon(action.icon, undefined, undefined, "smallest")}
|
|
375
376
|
</IconButton>
|
|
376
377
|
</Tooltip>;
|
|
377
378
|
}
|
|
@@ -70,7 +70,7 @@ export type OnTabChangeParams<M extends Record<string, unknown>> = {
|
|
|
70
70
|
|
|
71
71
|
export interface EditViewBindingProps<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
72
72
|
/**
|
|
73
|
-
* The
|
|
73
|
+
* The admin path of the entity, e.g. "users" or "products".
|
|
74
74
|
*/
|
|
75
75
|
path: string;
|
|
76
76
|
collection: AdminCollection<M>;
|
|
@@ -24,7 +24,7 @@ import { useRestoreScroll } from "@rebasepro/app";
|
|
|
24
24
|
|
|
25
25
|
import { SchemaDriftBanner } from "@rebasepro/app";
|
|
26
26
|
import { useBreadcrumbsController } from "../../hooks/useBreadcrumbsController";
|
|
27
|
-
import {
|
|
27
|
+
import { useAdminContext } from "../../hooks/useAdminContext";
|
|
28
28
|
|
|
29
29
|
export const DEFAULT_GROUP_NAME = "Views";
|
|
30
30
|
export const ADMIN_GROUP_NAME = "Admin";
|
|
@@ -43,7 +43,7 @@ export function ContentHomePage({
|
|
|
43
43
|
hiddenGroups?: string[];
|
|
44
44
|
}) {
|
|
45
45
|
|
|
46
|
-
const context =
|
|
46
|
+
const context = useAdminContext();
|
|
47
47
|
const { navigationStateController } = context;
|
|
48
48
|
const customizationController = useCustomizationController();
|
|
49
49
|
const adminModeController = useAdminModeController();
|
|
@@ -65,7 +65,7 @@ export function ContentHomePage({
|
|
|
65
65
|
} = navigationStateController.topLevelNavigation || {};
|
|
66
66
|
|
|
67
67
|
// Build a set of Studio-only dev view slugs so we can distinguish them from
|
|
68
|
-
//
|
|
68
|
+
// admin custom views added via <RebaseAdmin views={...}> or plugins.
|
|
69
69
|
const studioViewSlugs = useMemo(() => {
|
|
70
70
|
const slugs = new Set<string>();
|
|
71
71
|
(registry.studioConfig?.devViews ?? []).forEach(v => slugs.add(v.slug));
|
|
@@ -74,7 +74,7 @@ export function ContentHomePage({
|
|
|
74
74
|
}, [registry.studioConfig, registry.cmsConfig?.collectionEditor]);
|
|
75
75
|
|
|
76
76
|
// Studio mode shows Studio dev views + admin entries (Users/Roles).
|
|
77
|
-
// Content mode shows collections,
|
|
77
|
+
// Content mode shows collections, admin custom views, and admin entries — but not Studio dev views.
|
|
78
78
|
const rawNavigationEntries = useMemo(() => {
|
|
79
79
|
if (adminModeController.mode === "studio") {
|
|
80
80
|
return unFilteredNavigationEntries.filter(e => (e.type === "view" && studioViewSlugs.has(e.slug)) || e.type === "admin");
|