@rebasepro/admin 0.13.0 → 0.13.1-canary.g18cfeb7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{CollectionEditorDialog-CjXkWZ64.js → CollectionEditorDialog-CUFXevVg.js} +3 -3
- package/dist/{CollectionEditorDialog-CjXkWZ64.js.map → CollectionEditorDialog-CUFXevVg.js.map} +1 -1
- package/dist/{PropertyEditView-W8I3YV89.js → PropertyEditView-DDUC7giL.js} +2 -2
- package/dist/{PropertyEditView-W8I3YV89.js.map → PropertyEditView-DDUC7giL.js.map} +1 -1
- package/dist/{RouterCollectionsStudioView-BsLTNoqX.js → RouterCollectionsStudioView-vzS848Gf.js} +9 -13
- package/dist/RouterCollectionsStudioView-vzS848Gf.js.map +1 -0
- package/dist/collection_editor/serializable_types.d.ts +1 -0
- package/dist/collection_editor_ui.js +4 -4
- package/dist/components/AdditionalFieldValue.d.ts +25 -0
- package/dist/components/CollectionViewBinding/CollectionViewBinding.d.ts +16 -0
- package/dist/components/CollectionViewBinding/SlotValue.d.ts +63 -0
- package/dist/components/CollectionViewBinding/SplitListCloseButton.d.ts +17 -4
- package/dist/components/CollectionViewBinding/SplitListShowButton.d.ts +12 -0
- package/dist/components/CollectionViewBinding/usePreviewSlots.d.ts +55 -5
- package/dist/components/DetailViewBinding.d.ts +15 -0
- package/dist/components/DrawerNavigationGroup.d.ts +9 -6
- package/dist/components/DrawerNavigationItem.d.ts +16 -6
- package/dist/components/EditViewBinding.d.ts +30 -1
- package/dist/components/EntityDisplayHeader.d.ts +53 -0
- package/dist/components/EntityIdentityBar.d.ts +43 -1
- package/dist/components/EntityViewBinding.d.ts +60 -6
- package/dist/components/RebaseLayout.d.ts +1 -1
- package/dist/components/RebaseShell.d.ts +1 -1
- package/dist/components/RelationSelector.d.ts +8 -1
- package/dist/components/UserSelector.d.ts +8 -1
- package/dist/components/app/Scaffold.d.ts +9 -3
- package/dist/components/index.d.ts +2 -0
- package/dist/{export-J79rMoNj.js → export-5bl2EoSs.js} +2 -2
- package/dist/{export-J79rMoNj.js.map → export-5bl2EoSs.js.map} +1 -1
- package/dist/form/components/FieldBlock.d.ts +58 -4
- package/dist/form/components/FormRail.d.ts +13 -0
- package/dist/form/components/FormSections.d.ts +15 -1
- package/dist/form/components/useRailVisible.d.ts +5 -4
- package/dist/form/form_utils.d.ts +38 -1
- package/dist/{history-D7L_JlLO.js → history-BuKKAhoY.js} +4 -4
- package/dist/{history-D7L_JlLO.js.map → history-BuKKAhoY.js.map} +1 -1
- package/dist/hooks/useEntityDisplay.d.ts +102 -0
- package/dist/hooks/useEntityDisplayTitle.d.ts +18 -9
- package/dist/{import-BvYl--U4.js → import-BhdWs34H.js} +3 -3
- package/dist/{import-BvYl--U4.js.map → import-BhdWs34H.js.map} +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +152 -57
- package/dist/index.js.map +1 -1
- package/dist/preview/compact_text.d.ts +23 -0
- package/dist/preview/components/BooleanPreview.d.ts +3 -1
- package/dist/preview/property_previews/ArrayOfStringsPreview.d.ts +1 -1
- package/dist/preview/property_previews/ArrayOneOfPreview.d.ts +1 -1
- package/dist/preview/property_previews/ArrayPropertyPreview.d.ts +1 -1
- package/dist/preview/property_previews/MapPropertyPreview.d.ts +1 -1
- package/dist/preview/property_previews/StringPropertyPreview.d.ts +1 -1
- package/dist/routes/RebaseRoute.d.ts +1 -1
- package/dist/types/components/PropertyPreviewProps.d.ts +19 -0
- package/dist/util/number_format.d.ts +14 -0
- package/dist/util/previews.d.ts +9 -2
- package/dist/{util-GiwPgxnL.js → util-DNG7iK0w.js} +3756 -2300
- package/dist/util-DNG7iK0w.js.map +1 -0
- package/package.json +9 -9
- package/src/collection_editor/serializable_types.ts +1 -0
- package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +9 -11
- package/src/components/AdditionalFieldValue.tsx +68 -0
- package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +7 -8
- package/src/components/CollectionTableBinding/PropertyTableCell.tsx +2 -2
- package/src/components/CollectionTableBinding/column_utils.tsx +15 -6
- package/src/components/CollectionTableBinding/internal/popup_field/PopupFormField.tsx +2 -2
- package/src/components/CollectionViewBinding/BoardCardBinding.tsx +26 -27
- package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +723 -348
- package/src/components/CollectionViewBinding/CollectionViewActions.tsx +1 -1
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +38 -8
- package/src/components/CollectionViewBinding/EntityCardBinding.tsx +23 -36
- package/src/components/CollectionViewBinding/SlotValue.tsx +156 -0
- package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +23 -7
- package/src/components/CollectionViewBinding/SplitListShowButton.tsx +28 -0
- package/src/components/CollectionViewBinding/SplitListView.tsx +33 -2
- package/src/components/CollectionViewBinding/useBoardDataController.tsx +12 -3
- package/src/components/CollectionViewBinding/usePreviewSlots.ts +183 -64
- package/src/components/CollectionViewBinding/utils.ts +7 -4
- package/src/components/DefaultDrawer.tsx +19 -23
- package/src/components/DetailViewBinding.tsx +93 -56
- package/src/components/DrawerNavigationGroup.tsx +16 -39
- package/src/components/DrawerNavigationItem.tsx +44 -16
- package/src/components/EditViewBinding.tsx +215 -42
- package/src/components/EntityDisplayHeader.tsx +170 -0
- package/src/components/EntityIdentityBar.tsx +163 -23
- package/src/components/EntityInspector.tsx +24 -6
- package/src/components/EntityPreviewBinding.tsx +58 -18
- package/src/components/EntityViewBinding.tsx +342 -41
- package/src/components/InlineEntityPreview.tsx +10 -10
- package/src/components/RebaseLayout.tsx +2 -2
- package/src/components/RebaseShell.tsx +2 -2
- package/src/components/ReferenceTable/SelectionTableBinding.tsx +4 -2
- package/src/components/RelationSelector.tsx +15 -4
- package/src/components/SideDialogs.tsx +15 -3
- package/src/components/SidePanelBinding.tsx +122 -66
- package/src/components/UserSelector.tsx +15 -4
- package/src/components/app/Scaffold.tsx +112 -10
- package/src/components/history/LastEditedByIndicator.tsx +7 -12
- package/src/components/index.ts +3 -0
- package/src/data_import/utils/data.ts +9 -1
- package/src/form/EntityForm.tsx +64 -52
- package/src/form/EntityFormBinding.tsx +24 -11
- package/src/form/PropertyFieldBinding.tsx +2 -2
- package/src/form/components/FieldBlock.tsx +80 -10
- package/src/form/components/FormRail.tsx +19 -2
- package/src/form/components/FormSections.tsx +32 -5
- package/src/form/components/StorageUploadProgress.tsx +9 -3
- package/src/form/components/useRailVisible.tsx +5 -4
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +2 -2
- package/src/form/field_bindings/BinaryFieldBinding.tsx +10 -5
- package/src/form/field_bindings/BlockFieldBinding.tsx +3 -3
- package/src/form/field_bindings/GeopointFieldBinding.tsx +23 -16
- package/src/form/field_bindings/MapFieldBinding.tsx +2 -2
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +13 -7
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +7 -1
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +7 -1
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -1
- package/src/form/field_bindings/RelationFieldBinding.tsx +5 -2
- package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -3
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +2 -2
- package/src/form/field_bindings/UserSelectFieldBinding.tsx +4 -1
- package/src/form/field_bindings/VectorFieldBinding.tsx +6 -4
- package/src/form/form_utils.ts +78 -0
- package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +29 -5
- package/src/hooks/useEntityDisplay.tsx +262 -0
- package/src/hooks/useEntityDisplayTitle.tsx +27 -38
- package/src/index.ts +9 -1
- package/src/preview/PropertyPreview.tsx +25 -5
- package/src/preview/compact_text.ts +68 -0
- package/src/preview/components/BooleanPreview.tsx +5 -2
- package/src/preview/components/StorageThumbnail.tsx +5 -2
- package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +6 -3
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +26 -1
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +10 -1
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +10 -1
- package/src/preview/property_previews/MapPropertyPreview.tsx +52 -1
- package/src/preview/property_previews/NumberPropertyPreview.tsx +9 -1
- package/src/preview/property_previews/StringPropertyPreview.tsx +8 -5
- package/src/routes/RebaseRoute.tsx +62 -4
- package/src/types/components/PropertyPreviewProps.tsx +21 -0
- package/src/util/number_format.ts +46 -0
- package/src/util/previews.ts +8 -55
- package/src/util/property_utils.tsx +6 -1
- package/dist/RouterCollectionsStudioView-BsLTNoqX.js.map +0 -1
- package/dist/util-GiwPgxnL.js.map +0 -1
package/dist/{RouterCollectionsStudioView-BsLTNoqX.js → RouterCollectionsStudioView-vzS848Gf.js}
RENAMED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { in as useUrlController } from "./util-
|
|
3
|
-
import { s as AIModifiedPathsProvider } from "./PropertyEditView-
|
|
4
|
-
import { t as CollectionEditor } from "./CollectionEditorDialog-
|
|
2
|
+
import { in as useUrlController } from "./util-DNG7iK0w.js";
|
|
3
|
+
import { s as AIModifiedPathsProvider } from "./PropertyEditView-DDUC7giL.js";
|
|
4
|
+
import { t as CollectionEditor } from "./CollectionEditorDialog-CUFXevVg.js";
|
|
5
5
|
import { useEffect, useState } from "react";
|
|
6
6
|
import { Button, IconButton, PlusIcon, ResizablePanels, Tooltip, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
|
|
7
7
|
import { IconForView, UnsavedChangesDialog, useUnsavedChangesDialog } from "@rebasepro/app";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { readStoredString, writeStoredString } from "@rebasepro/utils";
|
|
9
10
|
import { useLocation, useNavigate } from "react-router";
|
|
10
11
|
//#region src/collection_editor/ui/collection_editor/CollectionStudioView.tsx
|
|
11
12
|
function CollectionStudioView({ collectionId, onSave, onCancel, ...props }) {
|
|
@@ -74,17 +75,12 @@ function CollectionsStudioView({ configController, collections: collectionsProp,
|
|
|
74
75
|
};
|
|
75
76
|
const collections = collectionsProp ?? configController.collections ?? [];
|
|
76
77
|
const [sidebarSize, setSidebarSize] = useState(() => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
} catch (e) {
|
|
81
|
-
return 25;
|
|
82
|
-
}
|
|
78
|
+
const saved = readStoredString("rebase_collections_editor_sidebar_size");
|
|
79
|
+
const parsed = saved === null ? NaN : parseFloat(saved);
|
|
80
|
+
return Number.isFinite(parsed) && parsed > 0 && parsed < 100 ? parsed : 25;
|
|
83
81
|
});
|
|
84
82
|
useEffect(() => {
|
|
85
|
-
|
|
86
|
-
localStorage.setItem("rebase_collections_editor_sidebar_size", sidebarSize.toString());
|
|
87
|
-
} catch (e) {}
|
|
83
|
+
writeStoredString("rebase_collections_editor_sidebar_size", sidebarSize.toString());
|
|
88
84
|
}, [sidebarSize]);
|
|
89
85
|
return /* @__PURE__ */ jsx("div", {
|
|
90
86
|
className: "flex h-full w-full bg-surface-50 dark:bg-surface-800 overflow-hidden text-text-primary dark:text-text-primary-dark",
|
|
@@ -189,4 +185,4 @@ function RouterCollectionsStudioView(props) {
|
|
|
189
185
|
//#endregion
|
|
190
186
|
export { CollectionStudioView as i, RouterCollectionsStudioView_exports as n, CollectionsStudioView as r, RouterCollectionsStudioView as t };
|
|
191
187
|
|
|
192
|
-
//# sourceMappingURL=RouterCollectionsStudioView-
|
|
188
|
+
//# sourceMappingURL=RouterCollectionsStudioView-vzS848Gf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RouterCollectionsStudioView-vzS848Gf.js","names":[],"sources":["../src/collection_editor/ui/collection_editor/CollectionStudioView.tsx","../src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx","../src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx"],"sourcesContent":["\nimport { useUnsavedChangesDialog, UnsavedChangesDialog } from \"@rebasepro/app\";\nimport * as React from \"react\";\nimport { useState } from \"react\";\nimport { Button, PlusIcon, Typography } from \"@rebasepro/ui\";\nimport { CollectionEditorDialogProps } from \"./CollectionEditorDialog\";\nimport { AIModifiedPathsProvider } from \"./AIModifiedPathsContext\";\nimport { CollectionEditor } from \"./CollectionEditorDialog\";\nimport type { AdminCollection } from \"@rebasepro/admin-types\";\n\nexport type CollectionStudioViewProps = Omit<CollectionEditorDialogProps, \"open\" | \"isNewCollection\" | \"editedCollectionId\" | \"handleClose\" | \"handleCancel\"> & {\n collectionId?: string | \"new\";\n\n /**\n * Called after a successful save.\n * Receives the saved collection (or undefined if dismissed without saving).\n * When not provided, save completes silently.\n */\n onSave?: (collection?: AdminCollection) => void;\n\n /**\n * Called when the user cancels editing.\n * When not provided, cancel is a no-op.\n */\n onCancel?: () => void;\n};\n\nexport function CollectionStudioView({ collectionId, onSave, onCancel, ...props }: CollectionStudioViewProps) {\n\n // Form state from the editor\n const [formDirty, setFormDirty] = useState<boolean>(false);\n const [cancelRequested, setCancelRequested] = useState<boolean>(false);\n\n const { dialogProps, triggerDialog } = useUnsavedChangesDialog(\n formDirty,\n () => setFormDirty(false)\n );\n\n const activeCollectionId = collectionId;\n\n const handleCancelClick = () => {\n if (!formDirty) {\n onCancel?.();\n } else {\n setCancelRequested(true);\n triggerDialog();\n }\n };\n\n return (\n <div className=\"flex-grow flex flex-col h-full w-full bg-surface-50 dark:bg-surface-800\">\n <AIModifiedPathsProvider>\n {activeCollectionId ? (\n <CollectionEditor\n key={activeCollectionId}\n {...props}\n fullScreen={true}\n open={true}\n isNewCollection={activeCollectionId === \"new\"}\n editedCollectionId={activeCollectionId !== \"new\" ? activeCollectionId : undefined}\n handleCancel={handleCancelClick}\n handleClose={(savedCollection) => {\n setFormDirty(false);\n if (savedCollection) {\n setTimeout(() => {\n onSave?.(savedCollection);\n }, 0);\n }\n }}\n setFormDirty={setFormDirty}\n />\n ) : (\n <div className=\"flex-grow flex flex-col items-center justify-center h-full gap-4\">\n <Typography variant=\"label\">\n Select a collection or create a new one\n </Typography>\n <Button\n disabled={props.configController?.readOnly}\n onClick={() => onSave?.()}\n >\n <PlusIcon/>\n Add new collection\n </Button>\n </div>\n )}\n\n <UnsavedChangesDialog\n {...dialogProps}\n handleOk={() => {\n dialogProps.handleOk();\n if (cancelRequested) {\n onCancel?.();\n setCancelRequested(false);\n }\n }}\n handleCancel={() => {\n dialogProps.handleCancel();\n setCancelRequested(false);\n }}\n />\n </AIModifiedPathsProvider>\n </div>\n );\n}\n","\nimport { IconForView } from \"@rebasepro/app\";\nimport React, { useState, useEffect } from \"react\";\nimport {\n Button,\n cls,\n defaultBorderMixin,\n IconButton,\n iconSize,\n PlusIcon,\n ResizablePanels,\n Tooltip,\n Typography\n} from \"@rebasepro/ui\";\n\nimport { CollectionsConfigController } from \"../../types/config_controller\";\nimport { CollectionStudioView } from \"./CollectionStudioView\";\nimport type { CollectionEditorExtensionProps } from \"../../extensibility_types\";\nimport type { AdminCollection } from \"@rebasepro/admin-types\";\nimport { readStoredString, writeStoredString } from \"@rebasepro/utils\";\n\nexport interface CollectionsStudioViewProps extends CollectionEditorExtensionProps {\n configController: CollectionsConfigController;\n\n /**\n * Collections to show in the sidebar.\n * When provided, overrides the collections from `configController`.\n * Use this to control exactly which collections the editor displays.\n */\n collections?: AdminCollection[];\n\n /**\n * Controlled active collection ID.\n * When provided together with `onActiveCollectionChange`,\n * navigation is fully external — no internal state is used.\n */\n activeCollectionId?: string;\n\n /**\n * Called when the user clicks a collection, \"new\", or navigates.\n * When provided, the component is fully controlled (no internal state).\n * When not provided, the component manages selection via internal state.\n *\n * Pass `\"new\"` to create a new collection. Pass `undefined` to deselect.\n */\n onActiveCollectionChange?: (collectionId: string | undefined) => void;\n}\n\nexport function CollectionsStudioView({\n configController,\n collections: collectionsProp,\n activeCollectionId: controlledActiveId,\n onActiveCollectionChange,\n propertyTypePresets,\n hiddenPropertyTypes,\n renderExtraPropertyFields,\n renderExtraCollectionFields,\n visibleTabs,\n standalone,\n}: CollectionsStudioViewProps) {\n\n // ── Navigation state ────────────────────────────────────────────────\n // If onActiveCollectionChange is provided, the component is controlled.\n // Otherwise, use internal state.\n const [internalActiveId, setInternalActiveId] = useState<string | undefined>(undefined);\n const isControlled = onActiveCollectionChange !== undefined;\n const activeCollectionId = isControlled ? controlledActiveId : internalActiveId;\n\n const setActiveCollectionId = (id: string | undefined) => {\n if (isControlled) {\n onActiveCollectionChange?.(id);\n } else {\n setInternalActiveId(id);\n }\n };\n\n // ── Collections list ────────────────────────────────────────────────\n const collections = collectionsProp ?? configController.collections ?? [];\n\n // ── Sidebar sizing ──────────────────────────────────────────────────\n // Checked, not just parsed: `parseFloat` answers NaN for anything it cannot\n // read, and a NaN pane size lays the sidebar out to nothing — with the bad\n // value still in storage on reload. `SplitListView.getSavedPanelSize` is the\n // same read, done this way.\n const [sidebarSize, setSidebarSize] = useState(() => {\n const saved = readStoredString(\"rebase_collections_editor_sidebar_size\");\n const parsed = saved === null ? NaN : parseFloat(saved);\n return Number.isFinite(parsed) && parsed > 0 && parsed < 100 ? parsed : 25;\n });\n\n useEffect(() => {\n writeStoredString(\"rebase_collections_editor_sidebar_size\", sidebarSize.toString());\n }, [sidebarSize]);\n\n return (\n <div className=\"flex h-full w-full bg-surface-50 dark:bg-surface-800 overflow-hidden text-text-primary dark:text-text-primary-dark\">\n <ResizablePanels\n orientation=\"horizontal\"\n panelSizePercent={sidebarSize}\n onPanelSizeChange={setSidebarSize}\n minPanelSizePx={220}\n firstPanel={\n <div className={cls(\"flex flex-col h-full w-full bg-surface-50 dark:bg-surface-800 border-r\", defaultBorderMixin)}>\n <div className={cls(\"flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\", defaultBorderMixin)}>\n <Typography variant=\"caption\" className=\"font-semibold text-[11px] uppercase tracking-wider text-surface-400 dark:text-surface-400\">\n Collections\n </Typography>\n <Tooltip title={configController.readOnly ? configController.readOnlyReason || \"Read only\" : \"Add collection\"}>\n <div>\n <IconButton\n size=\"small\"\n disabled={configController.readOnly}\n onClick={() => setActiveCollectionId(\"new\")}\n className={activeCollectionId === \"new\" ? \"text-primary dark:text-primary-dark\" : \"text-text-secondary dark:text-text-secondary-dark\"}\n >\n <PlusIcon size={iconSize.smallest}/>\n </IconButton>\n </div>\n </Tooltip>\n </div>\n\n <div className=\"flex-grow overflow-y-auto w-full no-scrollbar p-2 space-y-0.5\">\n {collections.length === 0 && (\n <div className=\"p-4 text-center\">\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark italic\">\n No collections yet.\n </Typography>\n </div>\n )}\n {collections.map((collection) => {\n const collectionKey = collection.slug;\n const isSelected = activeCollectionId === collectionKey;\n return (\n <div\n key={collectionKey}\n onClick={() => setActiveCollectionId(collectionKey)}\n className={cls(\n \"flex items-center gap-2.5 px-3 h-[30px] cursor-pointer rounded-lg text-[13px] font-medium transition-colors\",\n isSelected\n ? \"bg-primary/8 text-primary dark:bg-primary/10 dark:text-primary-light font-semibold\"\n : \"hover:bg-primary/5 dark:hover:bg-primary/5 text-surface-700 dark:text-surface-300 hover:text-surface-900 dark:hover:text-white\"\n )}\n >\n <IconForView collectionOrView={collection} size={\"smallest\"} className={cls(\n isSelected\n ? \"text-primary dark:text-primary-light\"\n : \"text-surface-500 dark:text-text-secondary-dark\"\n )}/>\n <span className=\"truncate flex-1\">\n {collection.name || collection.slug}\n </span>\n </div>\n );\n })}\n </div>\n </div>\n }\n secondPanel={\n <div className=\"flex-grow flex flex-col min-w-0 h-full w-full\">\n {/* We use key to force unmount when switching collections, preventing stale state */}\n {activeCollectionId ? (\n <CollectionStudioView\n key={activeCollectionId}\n configController={configController}\n collectionId={activeCollectionId}\n onSave={(savedCollection) => {\n // After creating a new collection, switch to it\n if (activeCollectionId === \"new\" && savedCollection?.slug) {\n setActiveCollectionId(savedCollection.slug);\n }\n }}\n onCancel={() => setActiveCollectionId(undefined)}\n propertyTypePresets={propertyTypePresets}\n hiddenPropertyTypes={hiddenPropertyTypes}\n renderExtraPropertyFields={renderExtraPropertyFields}\n renderExtraCollectionFields={renderExtraCollectionFields}\n visibleTabs={visibleTabs}\n standalone={standalone}\n />\n ) : (\n <div className=\"flex flex-col items-center justify-center h-full gap-4\">\n <Typography variant=\"label\">\n Select a collection or create a new one to start editing\n </Typography>\n <Button\n disabled={configController.readOnly}\n onClick={() => setActiveCollectionId(\"new\")}\n >\n <PlusIcon/>\n Add new collection\n </Button>\n </div>\n )}\n </div>\n }\n />\n </div>\n );\n}\n","/**\n * Router-aware wrapper around CollectionsStudioView.\n *\n * This component provides the react-router-based navigation behavior\n * that Rebase Studio expects: the active collection is derived from the\n * URL, and clicking a collection navigates via react-router.\n *\n * External consumers should use `CollectionsStudioView` directly\n * (which has no react-router dependency).\n */\nimport React from \"react\";\nimport { useLocation, useNavigate } from \"react-router\";\nimport { useUrlController } from \"../../_cms_internals\";\nimport { CollectionsStudioView, CollectionsStudioViewProps } from \"./CollectionsStudioView\";\n\nexport type RouterCollectionsStudioViewProps = Omit<CollectionsStudioViewProps, \"activeCollectionId\" | \"onActiveCollectionChange\">;\n\nexport function RouterCollectionsStudioView(props: RouterCollectionsStudioViewProps) {\n const navigate = useNavigate();\n const urlController = useUrlController();\n const location = useLocation();\n\n // Determine the active collection from the URL segment after \"schema/\"\n const basePath = urlController.buildAppUrlPath(\"schema\");\n const relativePath = location.pathname.replace(basePath, \"\").replace(/^\\//, \"\");\n const activeCollectionId = relativePath.split(\"/\")[0] || undefined;\n\n return (\n <CollectionsStudioView\n {...props}\n activeCollectionId={activeCollectionId}\n onActiveCollectionChange={(id) => {\n if (id) {\n navigate(urlController.buildAppUrlPath(`schema/${id}`));\n } else {\n navigate(urlController.buildAppUrlPath(\"schema\"));\n }\n }}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -116,6 +116,7 @@ export interface SerializableAdminRelationOptions extends SerializableAdminBaseO
|
|
|
116
116
|
fixedFilter?: FilterValues<string>;
|
|
117
117
|
includeId?: boolean;
|
|
118
118
|
includeEntityLink?: boolean;
|
|
119
|
+
renderInForm?: boolean;
|
|
119
120
|
}
|
|
120
121
|
/** JSON-serializable version of `AdminArrayOptions`. */
|
|
121
122
|
export interface SerializableAdminArrayOptions extends SerializableAdminBaseOptions {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { in as useUrlController } from "./util-
|
|
2
|
-
import { n as PropertyForm, r as PropertyFormDialog } from "./PropertyEditView-
|
|
3
|
-
import { n as CollectionEditorDialog } from "./CollectionEditorDialog-
|
|
4
|
-
import { i as CollectionStudioView, r as CollectionsStudioView, t as RouterCollectionsStudioView } from "./RouterCollectionsStudioView-
|
|
1
|
+
import { in as useUrlController } from "./util-DNG7iK0w.js";
|
|
2
|
+
import { n as PropertyForm, r as PropertyFormDialog } from "./PropertyEditView-DDUC7giL.js";
|
|
3
|
+
import { n as CollectionEditorDialog } from "./CollectionEditorDialog-CUFXevVg.js";
|
|
4
|
+
import { i as CollectionStudioView, r as CollectionsStudioView, t as RouterCollectionsStudioView } from "./RouterCollectionsStudioView-vzS848Gf.js";
|
|
5
5
|
import "react";
|
|
6
6
|
import { useSnackbarController } from "@rebasepro/app";
|
|
7
7
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AdditionalFieldDelegate, RebaseContext } from "@rebasepro/admin-types";
|
|
2
|
+
import type { Entity } from "@rebasepro/types";
|
|
3
|
+
import React from "react";
|
|
4
|
+
export interface AdditionalFieldValueProps<M extends Record<string, unknown>> {
|
|
5
|
+
field: AdditionalFieldDelegate<M>;
|
|
6
|
+
entity: Entity<M>;
|
|
7
|
+
context: RebaseContext;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Renders an `additionalFields` entry that supplies a `value` rather than a
|
|
11
|
+
* `Builder`.
|
|
12
|
+
*
|
|
13
|
+
* `value` is typed `string | number | Promise<string | number>`, and the promise
|
|
14
|
+
* arm has been part of that type from the start — the CSV export awaits it
|
|
15
|
+
* correctly. Every *renderer* called `.toString()` on the result instead, so an
|
|
16
|
+
* async value drew the literal text `[object Promise]` in the cell: a shape the
|
|
17
|
+
* type promised, that worked in one of its two consumers, and failed silently in
|
|
18
|
+
* the other. That is what made the obvious port of a FireCMS column — one that
|
|
19
|
+
* fetches a title from a subcollection — look supported and not be.
|
|
20
|
+
*
|
|
21
|
+
* Three call sites had the same three lines copied between them (the table cell,
|
|
22
|
+
* the form and the read-only record), which is why the bug reached all three.
|
|
23
|
+
* Now there is one.
|
|
24
|
+
*/
|
|
25
|
+
export declare function AdditionalFieldValue<M extends Record<string, unknown>>({ field, entity, context }: AdditionalFieldValueProps<M>): React.JSX.Element;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
2
|
+
import { FilterValues } from "@rebasepro/types";
|
|
2
3
|
import React from "react";
|
|
3
4
|
/**
|
|
4
5
|
* @group Components
|
|
@@ -39,3 +40,18 @@ export type CollectionViewBindingProps<M extends Record<string, unknown>> = {
|
|
|
39
40
|
selectedTab?: string;
|
|
40
41
|
} & AdminCollection<M>;
|
|
41
42
|
export declare const CollectionViewBinding: React.FunctionComponent<CollectionViewBindingProps<any>>;
|
|
43
|
+
export declare function EntitiesCount({ path, collection, filter, sortBy, searchString, onCountChange }: {
|
|
44
|
+
path: string;
|
|
45
|
+
collection: AdminCollection;
|
|
46
|
+
filter?: FilterValues<any>;
|
|
47
|
+
sortBy?: [string, "asc" | "desc"];
|
|
48
|
+
/**
|
|
49
|
+
* Required, not optional. The term sits in the same scope as the element
|
|
50
|
+
* that mounts this and is passed to the toolbar and the empty state beside
|
|
51
|
+
* it — it was simply not passed here, so a searched list showed the count
|
|
52
|
+
* of the unsearched collection. A required prop is what stops the next
|
|
53
|
+
* caller doing the same; there is no useful "count without the search".
|
|
54
|
+
*/
|
|
55
|
+
searchString: string | undefined;
|
|
56
|
+
onCountChange?: (count: number | null | undefined) => void;
|
|
57
|
+
}): null;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { PreviewSize } from "../../types/components/PropertyPreviewProps";
|
|
3
|
+
import type { PreviewSlot } from "./usePreviewSlots";
|
|
4
|
+
export interface SlotValueProps {
|
|
5
|
+
slot: PreviewSlot | undefined;
|
|
6
|
+
size: PreviewSize;
|
|
7
|
+
/**
|
|
8
|
+
* Relations and references render as one line of linked text rather than as
|
|
9
|
+
* a card. Defaults to true: a slot is a line in a row or a card, and a
|
|
10
|
+
* relation that renders its own bordered card there — with its own id line
|
|
11
|
+
* and its own side-panel button — is a card inside a card.
|
|
12
|
+
*
|
|
13
|
+
* Pass `false` only for a slot with room for one, which in practice is
|
|
14
|
+
* none of them.
|
|
15
|
+
*/
|
|
16
|
+
textOnly?: boolean;
|
|
17
|
+
/** Images only: fill the container rather than fitting inside it. */
|
|
18
|
+
fill?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* One display slot, rendered.
|
|
22
|
+
*
|
|
23
|
+
* A slot's value arrives one of two ways and they cannot be rendered the same:
|
|
24
|
+
*
|
|
25
|
+
* - **From a property** — render it with that property's own preview, so an
|
|
26
|
+
* enum status keeps its colour, a date keeps its format and a storage path
|
|
27
|
+
* becomes a thumbnail. This is why a declared path beats a resolver whenever
|
|
28
|
+
* the value is actually on the record.
|
|
29
|
+
* - **From a resolver** — there is no property, because the value was computed
|
|
30
|
+
* or fetched. All that can be said about it is that it is text.
|
|
31
|
+
*
|
|
32
|
+
* The branch lives here, once, rather than at each of the fourteen places the
|
|
33
|
+
* list row, the card and the board card render a slot. That count is the reason:
|
|
34
|
+
* the last time a rule like this was inlined per call site, it drifted into
|
|
35
|
+
* seven versions.
|
|
36
|
+
*/
|
|
37
|
+
export declare function SlotValue({ slot, size, textOnly, fill }: SlotValueProps): React.JSX.Element | null;
|
|
38
|
+
/**
|
|
39
|
+
* The `tags` slot, as chips beside the status.
|
|
40
|
+
*
|
|
41
|
+
* Honours both arms the same way the rest of the file does. A declared **path**
|
|
42
|
+
* goes through {@link SlotValue}, so an array of enum values keeps its own
|
|
43
|
+
* colours — that is the stated advantage of naming a property. A **resolver**
|
|
44
|
+
* returns bare strings with no property to render them as, so they become plain
|
|
45
|
+
* chips; a single string is accepted as one tag, which the role's type documents.
|
|
46
|
+
*
|
|
47
|
+
* Lives here rather than in the list, the card and the board card because that is
|
|
48
|
+
* three copies of the same decision, and this file exists to hold exactly one.
|
|
49
|
+
*/
|
|
50
|
+
export declare function TagChips({ slot, max }: {
|
|
51
|
+
slot: PreviewSlot;
|
|
52
|
+
max?: number;
|
|
53
|
+
}): React.JSX.Element | null;
|
|
54
|
+
/**
|
|
55
|
+
* Whether a computed image string can be fetched as-is, or has to be resolved
|
|
56
|
+
* through storage first.
|
|
57
|
+
*
|
|
58
|
+
* Exported because it is the whole decision: rendering either branch needs a
|
|
59
|
+
* provider (a storage source, a DOM), and the thing worth pinning is which
|
|
60
|
+
* branch a given string takes. `//host/x` is protocol-relative and equally
|
|
61
|
+
* fetchable; `data:` and `blob:` are already the bytes.
|
|
62
|
+
*/
|
|
63
|
+
export declare function isFetchableImageUrl(value: string): boolean;
|
|
@@ -3,8 +3,21 @@ import React from "react";
|
|
|
3
3
|
* Closes the list of a split view, leaving the open record on its own.
|
|
4
4
|
*
|
|
5
5
|
* It leads the record's own app bar, where the breadcrumb starts — this is the
|
|
6
|
-
* entity pane's old "Open full screen" button, moved to that edge
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* entity pane's old "Open full screen" button, moved to that edge: closing the
|
|
7
|
+
* list and opening the record full screen are the same thing, so there is one
|
|
8
|
+
* control and one route (`#full`) rather than two. Full screen carries the
|
|
9
|
+
* mirrored icon to bring the list back.
|
|
10
|
+
*
|
|
11
|
+
* A pane glyph rather than the double chevron it wore first. Now that the bar
|
|
12
|
+
* ends in a ✕ that dismisses the record, an arrow pointing off the left edge of
|
|
13
|
+
* the record's own bar was the other thing on it that read as "close me" — and
|
|
14
|
+
* it is the one control here that does not act on the record at all.
|
|
9
15
|
*/
|
|
10
|
-
export declare function SplitListCloseButton(
|
|
16
|
+
export declare function SplitListCloseButton({ onBeforeHide }?: {
|
|
17
|
+
/**
|
|
18
|
+
* Run before the layout changes, while this form is still mounted: the edit
|
|
19
|
+
* view uses it to hand its edit in progress to the full-screen form that is
|
|
20
|
+
* about to show the same record.
|
|
21
|
+
*/
|
|
22
|
+
onBeforeHide?: () => void;
|
|
23
|
+
}): React.JSX.Element | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Brings the list back beside a record that was opened full screen.
|
|
4
|
+
*
|
|
5
|
+
* The mirror of {@link SplitListCloseButton}: the same edge of the same bar,
|
|
6
|
+
* the same pane glyph opening rather than closing, and the same single route —
|
|
7
|
+
* showing the list is dropping the `#full` hash. It replaces the back arrow
|
|
8
|
+
* where it applies, because the list it unfolds already holds the record.
|
|
9
|
+
*/
|
|
10
|
+
export declare function SplitListShowButton({ onClick }: {
|
|
11
|
+
onClick: () => void;
|
|
12
|
+
}): React.JSX.Element;
|
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
import type { Property, RelationProperty } from "@rebasepro/types";
|
|
2
2
|
import type { Entity } from "@rebasepro/types";
|
|
3
|
-
import type { PropertyConfig, AdminCollection } from "@rebasepro/admin-types";
|
|
3
|
+
import type { PropertyConfig, AdminCollection, EntityDisplayRole } from "@rebasepro/admin-types";
|
|
4
4
|
import type { AuthController } from "@rebasepro/admin-types";
|
|
5
5
|
import { ChipColorScheme } from "@rebasepro/ui";
|
|
6
6
|
/**
|
|
7
|
-
* A resolved "slot"
|
|
8
|
-
*
|
|
7
|
+
* A resolved "slot": what fills one display role for one record.
|
|
8
|
+
*
|
|
9
|
+
* `property` and `propertyKey` are set when the value was read off the record,
|
|
10
|
+
* and absent when a `display` resolver computed it — a computed title has no
|
|
11
|
+
* column behind it, so there is nothing to render it *as*. Renderers must go
|
|
12
|
+
* through {@link SlotValue}, which makes that choice once: a property renders
|
|
13
|
+
* with its own preview (an enum keeps its coloured chip, a date its format), a
|
|
14
|
+
* computed value renders as text.
|
|
9
15
|
*/
|
|
10
16
|
export interface PreviewSlot {
|
|
11
|
-
property
|
|
12
|
-
propertyKey
|
|
17
|
+
property?: Property;
|
|
18
|
+
propertyKey?: string;
|
|
13
19
|
value: unknown;
|
|
20
|
+
/**
|
|
21
|
+
* Which display role this slot fills.
|
|
22
|
+
*
|
|
23
|
+
* Load-bearing for the computed case. Without a property there is nothing to
|
|
24
|
+
* infer from, and "render it as text" is right for a title and wrong for an
|
|
25
|
+
* image: a resolver that returned a URL had that URL printed inside the card's
|
|
26
|
+
* image frame. The role is the one thing that distinguishes them, so it
|
|
27
|
+
* travels with the slot rather than being re-derived at each call site.
|
|
28
|
+
*/
|
|
29
|
+
role?: EntityDisplayRole;
|
|
14
30
|
}
|
|
15
31
|
/**
|
|
16
32
|
* Date slot extends the base with a pre-formatted string.
|
|
@@ -66,6 +82,19 @@ export interface EntityPreviewSlots {
|
|
|
66
82
|
status: PreviewSlot | undefined;
|
|
67
83
|
/** META — date / timestamp */
|
|
68
84
|
date: DatePreviewSlot | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* TAGS — free chips beside the status.
|
|
87
|
+
*
|
|
88
|
+
* Declared only. Every other role has an inference ladder behind it, and
|
|
89
|
+
* this one deliberately does not: there is no "first enum" or "looks like a
|
|
90
|
+
* timestamp" that means *labels*, so guessing would scatter arbitrary chips
|
|
91
|
+
* across every row of every panel that never asked for them.
|
|
92
|
+
*
|
|
93
|
+
* The role existed on `EntityDisplay` and in `useEntityDisplay` before this
|
|
94
|
+
* field did, which is why a declared `display.tags` resolved correctly and
|
|
95
|
+
* then had nowhere to go.
|
|
96
|
+
*/
|
|
97
|
+
tags: PreviewSlot | undefined;
|
|
69
98
|
/** Entity ID (always available) */
|
|
70
99
|
entityId: string | number;
|
|
71
100
|
}
|
|
@@ -82,6 +111,8 @@ export interface CollectionSlotKeys {
|
|
|
82
111
|
relationKeys: string[];
|
|
83
112
|
statusKey: string | undefined;
|
|
84
113
|
dateKey: string | undefined;
|
|
114
|
+
/** Declared only — see {@link EntityPreviewSlots.tags}. */
|
|
115
|
+
tagsKey: string | undefined;
|
|
85
116
|
}
|
|
86
117
|
/**
|
|
87
118
|
* Resolve which property key fills each slot for a given collection.
|
|
@@ -102,3 +133,22 @@ export declare function useCollectionSlotKeys(collection: AdminCollection<Record
|
|
|
102
133
|
* `React.memo` render functions or loops without violating hook rules.
|
|
103
134
|
*/
|
|
104
135
|
export declare function resolveEntitySlots(entity: Entity<Record<string, unknown>>, collection: AdminCollection<Record<string, unknown>>, slotKeys: CollectionSlotKeys): EntityPreviewSlots;
|
|
136
|
+
/**
|
|
137
|
+
* The slots for one record, with `admin.display` resolvers applied.
|
|
138
|
+
*
|
|
139
|
+
* {@link resolveEntitySlots} answers the question from values already in hand —
|
|
140
|
+
* which is everything a slot filled by a property needs, and nothing a slot
|
|
141
|
+
* filled by a *resolver* can use. A computed title may have to be fetched, so it
|
|
142
|
+
* arrives after the first render; this hook is where the two meet.
|
|
143
|
+
*
|
|
144
|
+
* Until a resolver lands, the derived slot shows. That is deliberate: a list of
|
|
145
|
+
* fifty rows must not blank its titles for a beat while fifty promises settle,
|
|
146
|
+
* and the derived answer — the best-ranked property carrying a readable value —
|
|
147
|
+
* is a reasonable thing to be looking at meanwhile. When the resolved value
|
|
148
|
+
* arrives it replaces the slot outright, property and all, because there is no
|
|
149
|
+
* property behind a computed value (see {@link PreviewSlot}).
|
|
150
|
+
*
|
|
151
|
+
* Every role is hooked unconditionally, so the hook order is fixed regardless of
|
|
152
|
+
* what a given collection declares.
|
|
153
|
+
*/
|
|
154
|
+
export declare function useEntitySlots(entity: Entity<Record<string, unknown>>, collection: AdminCollection<Record<string, unknown>>, slotKeys: CollectionSlotKeys): EntityPreviewSlots;
|
|
@@ -6,6 +6,8 @@ export type BarActionsParams = {
|
|
|
6
6
|
status: EntityStatus;
|
|
7
7
|
path: string;
|
|
8
8
|
entityId?: string | number;
|
|
9
|
+
/** Always false here — this view reads a record, it does not edit one. */
|
|
10
|
+
dirty: boolean;
|
|
9
11
|
};
|
|
10
12
|
export type OnTabChangeParams<M extends Record<string, unknown>> = {
|
|
11
13
|
path: string;
|
|
@@ -22,6 +24,13 @@ export interface DetailViewBindingProps<M extends Record<string, unknown> = Reco
|
|
|
22
24
|
parentEntityIds: string[];
|
|
23
25
|
onTabChange?: (props: OnTabChangeParams<M>) => void;
|
|
24
26
|
onEditClick?: () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Dismiss this record — the ✕ at the identity bar's trailing edge.
|
|
29
|
+
*
|
|
30
|
+
* Set by the layouts that own the record's place on screen but are not side
|
|
31
|
+
* dialogs: the split, where dismissing is navigating back to the collection.
|
|
32
|
+
*/
|
|
33
|
+
onCloseRequest?: () => void;
|
|
25
34
|
layout?: "side_panel" | "full_screen" | "split" | "dialog";
|
|
26
35
|
barActions?: (params: BarActionsParams) => React.ReactNode;
|
|
27
36
|
/**
|
|
@@ -29,5 +38,11 @@ export interface DetailViewBindingProps<M extends Record<string, unknown> = Reco
|
|
|
29
38
|
* of the breadcrumb — the side panel's close button.
|
|
30
39
|
*/
|
|
31
40
|
barActionsStart?: React.ReactNode;
|
|
41
|
+
/**
|
|
42
|
+
* Full screen only: bring the list back beside this record. Set when the
|
|
43
|
+
* record was opened out of a split collection, so folding the list away is
|
|
44
|
+
* reversible; it takes the place of the back arrow when present.
|
|
45
|
+
*/
|
|
46
|
+
onShowList?: () => void;
|
|
32
47
|
}
|
|
33
48
|
export declare function DetailViewBinding<M extends Record<string, unknown>>({ entityId, ...props }: DetailViewBindingProps<M>): React.JSX.Element;
|
|
@@ -22,11 +22,12 @@ export interface DrawerNavigationGroupProps {
|
|
|
22
22
|
*/
|
|
23
23
|
drawerOpen: boolean;
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* @deprecated No longer read. Entry tooltips are uncontrolled — they follow the
|
|
26
|
+
* pointer and focus, and are suppressed wherever the label is already visible.
|
|
26
27
|
*/
|
|
27
|
-
tooltipsOpen
|
|
28
|
+
tooltipsOpen?: boolean;
|
|
28
29
|
/**
|
|
29
|
-
*
|
|
30
|
+
* @deprecated No longer read. See {@link tooltipsOpen}.
|
|
30
31
|
*/
|
|
31
32
|
adminMenuOpen?: boolean;
|
|
32
33
|
/**
|
|
@@ -44,8 +45,10 @@ export interface DrawerNavigationGroupProps {
|
|
|
44
45
|
/**
|
|
45
46
|
* Lucide icon name for the group header, from `NavigationGroupMapping.icon`.
|
|
46
47
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
48
|
+
* It decorates the header and nothing else: the entries below are untouched and
|
|
49
|
+
* keep their own icons. An app that wants the categorised look — rows giving up
|
|
50
|
+
* their icons to indent under the group — builds it in its own drawer, by
|
|
51
|
+
* overriding `Shell.DrawerNavigationItem` and passing `indented`.
|
|
49
52
|
*/
|
|
50
53
|
icon?: string;
|
|
51
54
|
}
|
|
@@ -53,4 +56,4 @@ export interface DrawerNavigationGroupProps {
|
|
|
53
56
|
* Shared drawer navigation group component used by both DefaultDrawer and RebaseCloudDrawer.
|
|
54
57
|
* Renders a collapsible group with header and navigation items.
|
|
55
58
|
*/
|
|
56
|
-
export declare function DrawerNavigationGroup({ group, entries, collapsed, onToggleCollapsed, drawerOpen,
|
|
59
|
+
export declare function DrawerNavigationGroup({ group, entries, collapsed, onToggleCollapsed, drawerOpen, headerActions, onItemClick, hideHeader, icon }: DrawerNavigationGroupProps): React.JSX.Element;
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Props of a drawer navigation row, named so an app overriding
|
|
4
|
+
* `Shell.DrawerNavigationItem` can type its wrapper against them.
|
|
5
|
+
*/
|
|
6
|
+
export type DrawerNavigationItemProps = {
|
|
3
7
|
icon: React.ReactElement;
|
|
4
8
|
name: string;
|
|
5
|
-
|
|
9
|
+
/** @deprecated No longer read: the tooltip is uncontrolled. */
|
|
10
|
+
tooltipsOpen?: boolean;
|
|
6
11
|
drawerOpen: boolean;
|
|
12
|
+
/** @deprecated No longer read: the tooltip is uncontrolled. */
|
|
7
13
|
adminMenuOpen?: boolean;
|
|
8
14
|
url: string;
|
|
9
15
|
onClick?: () => void;
|
|
10
16
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
17
|
+
* Drop the row's icon and indent the label into the space it occupied, so the
|
|
18
|
+
* row reads as a child of the group header above it.
|
|
19
|
+
*
|
|
20
|
+
* Nothing in the framework sets this: the stock drawer always passes `false`,
|
|
21
|
+
* and an app that wants the categorised look opts into it by overriding
|
|
22
|
+
* `Shell.DrawerNavigationItem` and deciding for itself which rows get it.
|
|
14
23
|
*/
|
|
15
24
|
indented?: boolean;
|
|
16
|
-
}
|
|
25
|
+
};
|
|
26
|
+
export declare function DrawerNavigationItem({ name, icon, drawerOpen, url, onClick, indented }: DrawerNavigationItemProps): React.JSX.Element;
|
|
@@ -10,6 +10,19 @@ export type BarActionsParams = {
|
|
|
10
10
|
status: EntityStatus;
|
|
11
11
|
path: string;
|
|
12
12
|
entityId?: string | number;
|
|
13
|
+
/** Whether `values` differ from what is stored. */
|
|
14
|
+
dirty: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Hand the edit in progress to the layout this action is about to open, so
|
|
17
|
+
* the record arrives showing — and still holding — what the user typed here.
|
|
18
|
+
* Call it from any action that changes layout without leaving the record;
|
|
19
|
+
* it decides on its own whether there is anything worth carrying, so a
|
|
20
|
+
* record nobody edited still arrives clean.
|
|
21
|
+
*
|
|
22
|
+
* Only supplied where there is a form to carry: the detail view passes no
|
|
23
|
+
* such thing.
|
|
24
|
+
*/
|
|
25
|
+
carryEdit?: () => void;
|
|
13
26
|
};
|
|
14
27
|
export type OnTabChangeParams<M extends Record<string, unknown>> = {
|
|
15
28
|
path: string;
|
|
@@ -33,6 +46,16 @@ export interface EditViewBindingProps<M extends Record<string, unknown> = Record
|
|
|
33
46
|
onSaved?: (params: OnUpdateParams) => void;
|
|
34
47
|
onTabChange?: (props: OnTabChangeParams<M>) => void;
|
|
35
48
|
navigateBack?: () => void;
|
|
49
|
+
/**
|
|
50
|
+
* Dismiss this record — the ✕ at the identity bar's trailing edge, and what
|
|
51
|
+
* "save and close" reaches once the save lands.
|
|
52
|
+
*
|
|
53
|
+
* Set by the layouts that own the record's place on screen but are not side
|
|
54
|
+
* dialogs: the split, where dismissing is navigating back to the collection.
|
|
55
|
+
* The side panel and the dialog close through {@link useSideDialogContext}
|
|
56
|
+
* instead, which is what runs their unsaved-changes prompt.
|
|
57
|
+
*/
|
|
58
|
+
onCloseRequest?: () => void;
|
|
36
59
|
layout?: "side_panel" | "full_screen" | "split" | "dialog";
|
|
37
60
|
barActions?: (params: BarActionsParams) => React.ReactNode;
|
|
38
61
|
/**
|
|
@@ -40,6 +63,12 @@ export interface EditViewBindingProps<M extends Record<string, unknown> = Record
|
|
|
40
63
|
* of the breadcrumb — the side panel's close button.
|
|
41
64
|
*/
|
|
42
65
|
barActionsStart?: React.ReactNode;
|
|
66
|
+
/**
|
|
67
|
+
* Full screen only: bring the list back beside this record. Set when the
|
|
68
|
+
* record was opened out of a split collection, so folding the list away is
|
|
69
|
+
* reversible; it takes the place of the back arrow when present.
|
|
70
|
+
*/
|
|
71
|
+
onShowList?: () => void;
|
|
43
72
|
formProps?: Partial<EntityFormBindingProps<M>>;
|
|
44
73
|
/**
|
|
45
74
|
* Pre-populate the form with these values when creating a new entity.
|
|
@@ -53,7 +82,7 @@ export interface EditViewBindingProps<M extends Record<string, unknown> = Record
|
|
|
53
82
|
* a record is opened.
|
|
54
83
|
*/
|
|
55
84
|
export declare function EditViewBinding<M extends Record<string, unknown>>({ entityId, ...props }: EditViewBindingProps<M>): React.JSX.Element;
|
|
56
|
-
export declare function EditViewBindingInner<M extends Record<string, unknown>>({ path, entityId, selectedTab: selectedTabProp, collection, parentCollectionSlugs, parentEntityIds, onValuesModified, onSaved, onTabChange, navigateBack, entity, initialDirtyValues, dataLoading, layout, barActions, barActionsStart, status, setStatus, formProps, canEdit }: EditViewBindingProps<M> & {
|
|
85
|
+
export declare function EditViewBindingInner<M extends Record<string, unknown>>({ path, entityId, selectedTab: selectedTabProp, collection, parentCollectionSlugs, parentEntityIds, onValuesModified, onSaved, onTabChange, navigateBack, onCloseRequest, entity, initialDirtyValues, dataLoading, layout, barActions, barActionsStart, onShowList, status, setStatus, formProps, canEdit }: EditViewBindingProps<M> & {
|
|
57
86
|
entity?: Entity<M>;
|
|
58
87
|
initialDirtyValues?: Partial<M>;
|
|
59
88
|
dataLoading: boolean;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
2
|
+
import type { Entity } from "@rebasepro/types";
|
|
3
|
+
import React from "react";
|
|
4
|
+
/**
|
|
5
|
+
* The roles whose property the read-only view takes out of its grid.
|
|
6
|
+
*
|
|
7
|
+
* A role rendered here and left in the grid prints the value twice, so the
|
|
8
|
+
* header's own rendering and this list have to agree — but the list is *not*
|
|
9
|
+
* simply "every role the header draws". Two are deliberately absent:
|
|
10
|
+
*
|
|
11
|
+
* `title`, because the heading above a record and a labelled field are not the
|
|
12
|
+
* same thing. `Order #` is a column with a name, a description and a format that
|
|
13
|
+
* someone reads the record to find; `ORD-2026-0043` sitting in the identity bar
|
|
14
|
+
* beside the breadcrumb is a label for the page. Removing the field because the
|
|
15
|
+
* page is named after it takes a value out of the record on the grounds that its
|
|
16
|
+
* text appears elsewhere, which is not a reason — a value in the database is
|
|
17
|
+
* shown.
|
|
18
|
+
*
|
|
19
|
+
* `date`, because the rail's record block already carries the timestamps, and a
|
|
20
|
+
* collection whose `date` role points at a business date — an order date, a due
|
|
21
|
+
* date — wants that field where it declared it.
|
|
22
|
+
*/
|
|
23
|
+
export declare const HEADER_DISPLAY_ROLES: readonly ["subtitle", "image", "status", "tags"];
|
|
24
|
+
export interface EntityDisplayHeaderProps<M extends Record<string, unknown>> {
|
|
25
|
+
entity: Entity<M>;
|
|
26
|
+
collection: AdminCollection<M>;
|
|
27
|
+
/**
|
|
28
|
+
* Render the title. Off when an identity bar above already shows it — which
|
|
29
|
+
* is every full-page use — and on inside a dialog that has no bar.
|
|
30
|
+
*/
|
|
31
|
+
showTitle?: boolean;
|
|
32
|
+
className?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* What the record *is*, above the fields that say what it holds.
|
|
36
|
+
*
|
|
37
|
+
* A record's picture, name, state and labels are the things you opened the page
|
|
38
|
+
* to see; on the plain grid they were fields like any other, so an order's
|
|
39
|
+
* status sat in the second cell of the first row at the same size and weight as
|
|
40
|
+
* its shipping cost, and its tags were a wrapped list of grey text.
|
|
41
|
+
*
|
|
42
|
+
* Every role here comes from `admin.display` and nothing is derived except the
|
|
43
|
+
* title, which was already derived for the identity bar. A collection that
|
|
44
|
+
* declares no display block therefore gets no header at all rather than a
|
|
45
|
+
* heuristic guess at which of its enums is a "status" — the panel does not know
|
|
46
|
+
* what these records are, and a wrong guess here is louder than no guess.
|
|
47
|
+
*
|
|
48
|
+
* A role filled by a *property* renders through {@link PropertyPreview}, so a
|
|
49
|
+
* status keeps its enum's colour and an image stays a storage thumbnail. A role
|
|
50
|
+
* filled by a resolver has no property behind it and renders as what it
|
|
51
|
+
* returned.
|
|
52
|
+
*/
|
|
53
|
+
export declare function EntityDisplayHeader<M extends Record<string, unknown>>({ entity, collection, showTitle, className }: EntityDisplayHeaderProps<M>): React.JSX.Element | null;
|