@rebasepro/admin 0.13.0 → 0.13.1-canary.g3660bd5

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 (103) hide show
  1. package/dist/{CollectionEditorDialog-CjXkWZ64.js → CollectionEditorDialog-MODRemil.js} +3 -3
  2. package/dist/{CollectionEditorDialog-CjXkWZ64.js.map → CollectionEditorDialog-MODRemil.js.map} +1 -1
  3. package/dist/{PropertyEditView-W8I3YV89.js → PropertyEditView-Bt-VM0zQ.js} +2 -2
  4. package/dist/{PropertyEditView-W8I3YV89.js.map → PropertyEditView-Bt-VM0zQ.js.map} +1 -1
  5. package/dist/{RouterCollectionsStudioView-BsLTNoqX.js → RouterCollectionsStudioView-D54P9o8O.js} +4 -4
  6. package/dist/{RouterCollectionsStudioView-BsLTNoqX.js.map → RouterCollectionsStudioView-D54P9o8O.js.map} +1 -1
  7. package/dist/collection_editor_ui.js +4 -4
  8. package/dist/components/AdditionalFieldValue.d.ts +25 -0
  9. package/dist/components/CollectionViewBinding/CollectionViewBinding.d.ts +16 -0
  10. package/dist/components/CollectionViewBinding/SlotValue.d.ts +37 -0
  11. package/dist/components/CollectionViewBinding/SplitListCloseButton.d.ts +5 -3
  12. package/dist/components/CollectionViewBinding/SplitListShowButton.d.ts +12 -0
  13. package/dist/components/CollectionViewBinding/usePreviewSlots.d.ts +29 -4
  14. package/dist/components/DetailViewBinding.d.ts +6 -0
  15. package/dist/components/DrawerNavigationGroup.d.ts +5 -4
  16. package/dist/components/DrawerNavigationItem.d.ts +4 -2
  17. package/dist/components/EditViewBinding.d.ts +7 -1
  18. package/dist/components/EntityDisplayHeader.d.ts +53 -0
  19. package/dist/components/EntityIdentityBar.d.ts +20 -1
  20. package/dist/components/EntityViewBinding.d.ts +60 -6
  21. package/dist/components/RebaseLayout.d.ts +1 -1
  22. package/dist/components/RebaseShell.d.ts +1 -1
  23. package/dist/components/app/Scaffold.d.ts +9 -3
  24. package/dist/components/index.d.ts +2 -0
  25. package/dist/{export-J79rMoNj.js → export-Bl1V7lKO.js} +2 -2
  26. package/dist/{export-J79rMoNj.js.map → export-Bl1V7lKO.js.map} +1 -1
  27. package/dist/form/components/FieldBlock.d.ts +47 -4
  28. package/dist/form/components/FormRail.d.ts +13 -0
  29. package/dist/form/components/FormSections.d.ts +15 -1
  30. package/dist/form/components/useRailVisible.d.ts +5 -4
  31. package/dist/{history-D7L_JlLO.js → history-DN_dzFAF.js} +4 -4
  32. package/dist/{history-D7L_JlLO.js.map → history-DN_dzFAF.js.map} +1 -1
  33. package/dist/hooks/useEntityDisplay.d.ts +102 -0
  34. package/dist/hooks/useEntityDisplayTitle.d.ts +18 -9
  35. package/dist/{import-BvYl--U4.js → import-BJXrHdFv.js} +3 -3
  36. package/dist/{import-BvYl--U4.js.map → import-BJXrHdFv.js.map} +1 -1
  37. package/dist/index.js +149 -51
  38. package/dist/index.js.map +1 -1
  39. package/dist/preview/compact_text.d.ts +23 -0
  40. package/dist/preview/components/BooleanPreview.d.ts +3 -1
  41. package/dist/preview/property_previews/ArrayOfStringsPreview.d.ts +1 -1
  42. package/dist/preview/property_previews/ArrayOneOfPreview.d.ts +1 -1
  43. package/dist/preview/property_previews/ArrayPropertyPreview.d.ts +1 -1
  44. package/dist/preview/property_previews/MapPropertyPreview.d.ts +1 -1
  45. package/dist/preview/property_previews/StringPropertyPreview.d.ts +1 -1
  46. package/dist/routes/RebaseRoute.d.ts +1 -1
  47. package/dist/types/components/PropertyPreviewProps.d.ts +19 -0
  48. package/dist/util/number_format.d.ts +14 -0
  49. package/dist/util/previews.d.ts +9 -2
  50. package/dist/{util-GiwPgxnL.js → util-EXfeGzbH.js} +8142 -7353
  51. package/dist/util-EXfeGzbH.js.map +1 -0
  52. package/package.json +9 -9
  53. package/src/components/AdditionalFieldValue.tsx +68 -0
  54. package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +5 -6
  55. package/src/components/CollectionViewBinding/BoardCardBinding.tsx +9 -27
  56. package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +11 -24
  57. package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +26 -6
  58. package/src/components/CollectionViewBinding/EntityCardBinding.tsx +10 -33
  59. package/src/components/CollectionViewBinding/SlotValue.tsx +60 -0
  60. package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +7 -5
  61. package/src/components/CollectionViewBinding/SplitListShowButton.tsx +28 -0
  62. package/src/components/CollectionViewBinding/SplitListView.tsx +14 -1
  63. package/src/components/CollectionViewBinding/useBoardDataController.tsx +12 -3
  64. package/src/components/CollectionViewBinding/usePreviewSlots.ts +115 -48
  65. package/src/components/DefaultDrawer.tsx +19 -23
  66. package/src/components/DetailViewBinding.tsx +74 -54
  67. package/src/components/DrawerNavigationGroup.tsx +4 -7
  68. package/src/components/DrawerNavigationItem.tsx +23 -4
  69. package/src/components/EditViewBinding.tsx +54 -33
  70. package/src/components/EntityDisplayHeader.tsx +170 -0
  71. package/src/components/EntityIdentityBar.tsx +49 -5
  72. package/src/components/EntityPreviewBinding.tsx +54 -17
  73. package/src/components/EntityViewBinding.tsx +341 -41
  74. package/src/components/InlineEntityPreview.tsx +10 -10
  75. package/src/components/RebaseLayout.tsx +2 -2
  76. package/src/components/RebaseShell.tsx +2 -2
  77. package/src/components/app/Scaffold.tsx +112 -10
  78. package/src/components/index.ts +3 -0
  79. package/src/form/EntityForm.tsx +28 -33
  80. package/src/form/components/FieldBlock.tsx +65 -8
  81. package/src/form/components/FormRail.tsx +19 -2
  82. package/src/form/components/FormSections.tsx +32 -5
  83. package/src/form/components/StorageUploadProgress.tsx +9 -3
  84. package/src/form/components/useRailVisible.tsx +5 -4
  85. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +7 -1
  86. package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +29 -5
  87. package/src/hooks/useEntityDisplay.tsx +262 -0
  88. package/src/hooks/useEntityDisplayTitle.tsx +27 -38
  89. package/src/preview/PropertyPreview.tsx +18 -2
  90. package/src/preview/compact_text.ts +68 -0
  91. package/src/preview/components/BooleanPreview.tsx +5 -2
  92. package/src/preview/components/StorageThumbnail.tsx +5 -2
  93. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +26 -1
  94. package/src/preview/property_previews/ArrayOneOfPreview.tsx +10 -1
  95. package/src/preview/property_previews/ArrayPropertyPreview.tsx +10 -1
  96. package/src/preview/property_previews/MapPropertyPreview.tsx +52 -1
  97. package/src/preview/property_previews/NumberPropertyPreview.tsx +9 -1
  98. package/src/preview/property_previews/StringPropertyPreview.tsx +8 -5
  99. package/src/routes/RebaseRoute.tsx +62 -4
  100. package/src/types/components/PropertyPreviewProps.tsx +21 -0
  101. package/src/util/number_format.ts +46 -0
  102. package/src/util/previews.ts +8 -55
  103. package/dist/util-GiwPgxnL.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { in as useUrlController } from "./util-GiwPgxnL.js";
3
- import { s as AIModifiedPathsProvider } from "./PropertyEditView-W8I3YV89.js";
4
- import { t as CollectionEditor } from "./CollectionEditorDialog-CjXkWZ64.js";
2
+ import { in as useUrlController } from "./util-EXfeGzbH.js";
3
+ import { s as AIModifiedPathsProvider } from "./PropertyEditView-Bt-VM0zQ.js";
4
+ import { t as CollectionEditor } from "./CollectionEditorDialog-MODRemil.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";
@@ -189,4 +189,4 @@ function RouterCollectionsStudioView(props) {
189
189
  //#endregion
190
190
  export { CollectionStudioView as i, RouterCollectionsStudioView_exports as n, CollectionsStudioView as r, RouterCollectionsStudioView as t };
191
191
 
192
- //# sourceMappingURL=RouterCollectionsStudioView-BsLTNoqX.js.map
192
+ //# sourceMappingURL=RouterCollectionsStudioView-D54P9o8O.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RouterCollectionsStudioView-BsLTNoqX.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\";\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 const [sidebarSize, setSidebarSize] = useState(() => {\n try {\n const saved = localStorage.getItem(\"rebase_collections_editor_sidebar_size\");\n return saved !== null ? parseFloat(saved) : 25;\n } catch (e) {\n return 25;\n }\n });\n\n useEffect(() => {\n try {\n localStorage.setItem(\"rebase_collections_editor_sidebar_size\", sidebarSize.toString());\n } catch (e) {\n // ignore local storage error\n }\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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"}
1
+ {"version":3,"file":"RouterCollectionsStudioView-D54P9o8O.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\";\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 const [sidebarSize, setSidebarSize] = useState(() => {\n try {\n const saved = localStorage.getItem(\"rebase_collections_editor_sidebar_size\");\n return saved !== null ? parseFloat(saved) : 25;\n } catch (e) {\n return 25;\n }\n });\n\n useEffect(() => {\n try {\n localStorage.setItem(\"rebase_collections_editor_sidebar_size\", sidebarSize.toString());\n } catch (e) {\n // ignore local storage error\n }\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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"}
@@ -1,7 +1,7 @@
1
- import { in as useUrlController } from "./util-GiwPgxnL.js";
2
- import { n as PropertyForm, r as PropertyFormDialog } from "./PropertyEditView-W8I3YV89.js";
3
- import { n as CollectionEditorDialog } from "./CollectionEditorDialog-CjXkWZ64.js";
4
- import { i as CollectionStudioView, r as CollectionsStudioView, t as RouterCollectionsStudioView } from "./RouterCollectionsStudioView-BsLTNoqX.js";
1
+ import { in as useUrlController } from "./util-EXfeGzbH.js";
2
+ import { n as PropertyForm, r as PropertyFormDialog } from "./PropertyEditView-Bt-VM0zQ.js";
3
+ import { n as CollectionEditorDialog } from "./CollectionEditorDialog-MODRemil.js";
4
+ import { i as CollectionStudioView, r as CollectionsStudioView, t as RouterCollectionsStudioView } from "./RouterCollectionsStudioView-D54P9o8O.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,37 @@
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;
@@ -3,8 +3,10 @@ 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 and wearing an
7
- * X: closing the list and opening the record full screen are the same thing, so
8
- * there is one control and one route (`#full`) rather than two.
6
+ * entity pane's old "Open full screen" button, moved to that edge and wearing a
7
+ * double chevron pointing at the list it folds away: closing the list and
8
+ * opening the record full screen are the same thing, so there is one control
9
+ * and one route (`#full`) rather than two. Full screen carries the mirrored
10
+ * chevron to bring the list back.
9
11
  */
10
12
  export declare function SplitListCloseButton(): 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 double chevron pointing the other way, 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;
@@ -4,12 +4,18 @@ import type { PropertyConfig, AdminCollection } 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" containing the property definition, key, and the
8
- * concrete value extracted from a entity.
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: Property;
12
- propertyKey: string;
17
+ property?: Property;
18
+ propertyKey?: string;
13
19
  value: unknown;
14
20
  }
15
21
  /**
@@ -102,3 +108,22 @@ export declare function useCollectionSlotKeys(collection: AdminCollection<Record
102
108
  * `React.memo` render functions or loops without violating hook rules.
103
109
  */
104
110
  export declare function resolveEntitySlots(entity: Entity<Record<string, unknown>>, collection: AdminCollection<Record<string, unknown>>, slotKeys: CollectionSlotKeys): EntityPreviewSlots;
111
+ /**
112
+ * The slots for one record, with `admin.display` resolvers applied.
113
+ *
114
+ * {@link resolveEntitySlots} answers the question from values already in hand —
115
+ * which is everything a slot filled by a property needs, and nothing a slot
116
+ * filled by a *resolver* can use. A computed title may have to be fetched, so it
117
+ * arrives after the first render; this hook is where the two meet.
118
+ *
119
+ * Until a resolver lands, the derived slot shows. That is deliberate: a list of
120
+ * fifty rows must not blank its titles for a beat while fifty promises settle,
121
+ * and the derived answer — the best-ranked property carrying a readable value —
122
+ * is a reasonable thing to be looking at meanwhile. When the resolved value
123
+ * arrives it replaces the slot outright, property and all, because there is no
124
+ * property behind a computed value (see {@link PreviewSlot}).
125
+ *
126
+ * Every role is hooked unconditionally, so the hook order is fixed regardless of
127
+ * what a given collection declares.
128
+ */
129
+ export declare function useEntitySlots(entity: Entity<Record<string, unknown>>, collection: AdminCollection<Record<string, unknown>>, slotKeys: CollectionSlotKeys): EntityPreviewSlots;
@@ -29,5 +29,11 @@ export interface DetailViewBindingProps<M extends Record<string, unknown> = Reco
29
29
  * of the breadcrumb — the side panel's close button.
30
30
  */
31
31
  barActionsStart?: React.ReactNode;
32
+ /**
33
+ * Full screen only: bring the list back beside this record. Set when the
34
+ * record was opened out of a split collection, so folding the list away is
35
+ * reversible; it takes the place of the back arrow when present.
36
+ */
37
+ onShowList?: () => void;
32
38
  }
33
39
  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
- * Whether tooltips should be shown (drawer closed + hovered)
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: boolean;
28
+ tooltipsOpen?: boolean;
28
29
  /**
29
- * Whether admin menu is open (used to control tooltip visibility)
30
+ * @deprecated No longer read. See {@link tooltipsOpen}.
30
31
  */
31
32
  adminMenuOpen?: boolean;
32
33
  /**
@@ -53,4 +54,4 @@ export interface DrawerNavigationGroupProps {
53
54
  * Shared drawer navigation group component used by both DefaultDrawer and RebaseCloudDrawer.
54
55
  * Renders a collapsible group with header and navigation items.
55
56
  */
56
- export declare function DrawerNavigationGroup({ group, entries, collapsed, onToggleCollapsed, drawerOpen, tooltipsOpen, adminMenuOpen, headerActions, onItemClick, hideHeader, icon }: DrawerNavigationGroupProps): React.JSX.Element;
57
+ export declare function DrawerNavigationGroup({ group, entries, collapsed, onToggleCollapsed, drawerOpen, headerActions, onItemClick, hideHeader, icon }: DrawerNavigationGroupProps): React.JSX.Element;
@@ -1,9 +1,11 @@
1
1
  import React from "react";
2
- export declare function DrawerNavigationItem({ name, icon, drawerOpen, adminMenuOpen, tooltipsOpen, url, onClick, indented }: {
2
+ export declare function DrawerNavigationItem({ name, icon, drawerOpen, url, onClick, indented }: {
3
3
  icon: React.ReactElement;
4
4
  name: string;
5
- tooltipsOpen: boolean;
5
+ /** @deprecated No longer read: the tooltip is uncontrolled. */
6
+ tooltipsOpen?: boolean;
6
7
  drawerOpen: boolean;
8
+ /** @deprecated No longer read: the tooltip is uncontrolled. */
7
9
  adminMenuOpen?: boolean;
8
10
  url: string;
9
11
  onClick?: () => void;
@@ -40,6 +40,12 @@ export interface EditViewBindingProps<M extends Record<string, unknown> = Record
40
40
  * of the breadcrumb — the side panel's close button.
41
41
  */
42
42
  barActionsStart?: React.ReactNode;
43
+ /**
44
+ * Full screen only: bring the list back beside this record. Set when the
45
+ * record was opened out of a split collection, so folding the list away is
46
+ * reversible; it takes the place of the back arrow when present.
47
+ */
48
+ onShowList?: () => void;
43
49
  formProps?: Partial<EntityFormBindingProps<M>>;
44
50
  /**
45
51
  * Pre-populate the form with these values when creating a new entity.
@@ -53,7 +59,7 @@ export interface EditViewBindingProps<M extends Record<string, unknown> = Record
53
59
  * a record is opened.
54
60
  */
55
61
  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> & {
62
+ 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, onShowList, status, setStatus, formProps, canEdit }: EditViewBindingProps<M> & {
57
63
  entity?: Entity<M>;
58
64
  initialDirtyValues?: Partial<M>;
59
65
  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;
@@ -4,6 +4,15 @@ import React from "react";
4
4
  export interface EntityIdentityBarProps {
5
5
  /** Plural collection name, shown as the breadcrumb ahead of the title. */
6
6
  collection: AdminCollection;
7
+ /**
8
+ * Where the breadcrumb points. Without it the collection name is inert text
9
+ * that still reads as a breadcrumb — worse than no breadcrumb at all, and
10
+ * the only way back once a layout drops its back arrow.
11
+ *
12
+ * Left unset by the overlays (side panel, dialog), where the collection is
13
+ * already underneath and navigating would dismiss the record instead.
14
+ */
15
+ collectionUrl?: string;
7
16
  /** Resolved record title, already guarded against showing a field default. */
8
17
  title: string;
9
18
  entityId?: string | number;
@@ -24,6 +33,16 @@ export interface EntityIdentityBarProps {
24
33
  onInspect?: () => void;
25
34
  /** Opens the inspector straight on its history tab. */
26
35
  onViewHistory?: () => void;
36
+ /**
37
+ * Where this record appears on the live site, from `entityLinkBuilder`.
38
+ *
39
+ * Here rather than above the record, where it used to be a single unlabelled
40
+ * icon alone on its own right-aligned row — the only thing between the bar
41
+ * and the first field, and the only control in the view that did not say
42
+ * what it was. It is a thing you do *to* a record, so it belongs with the
43
+ * others.
44
+ */
45
+ externalLink?: string;
27
46
  /**
28
47
  * Actions performed *on* the record — copy, delete, whatever the collection
29
48
  * adds — as menu items under the overflow button. They live here rather
@@ -50,4 +69,4 @@ export interface EntityIdentityBarProps {
50
69
  * editing. They live here instead, and the left half of a bar that was
51
70
  * previously empty now does the work.
52
71
  */
53
- export declare function EntityIdentityBar({ collection, title, entityId, status, dirty, saving, onSave, onDiscard, saveDisabled, hasErrors, onSaveAndClose, onBack, onInspect, onViewHistory, recordActions, pluginActions, trailing, leading }: EntityIdentityBarProps): React.JSX.Element;
72
+ export declare function EntityIdentityBar({ collection, collectionUrl, title, entityId, status, dirty, saving, onSave, onDiscard, saveDisabled, hasErrors, onSaveAndClose, onBack, onInspect, onViewHistory, externalLink, recordActions, pluginActions, trailing, leading }: EntityIdentityBarProps): React.JSX.Element;
@@ -1,6 +1,30 @@
1
1
  import type { AdminCollection } from "@rebasepro/admin-types";
2
+ import type { FormContext } from "../types/fields";
2
3
  import React from "react";
3
4
  import { Entity } from "@rebasepro/types";
5
+ /**
6
+ * The fields the read-only view lays out, in render order.
7
+ *
8
+ * The same set the form uses, minus two groups. Both are dropped *before* the
9
+ * layout resolves rather than skipped while rendering, because the resolver has
10
+ * by then given each one a place on the grid — skipping later leaves that place
11
+ * as a hole.
12
+ *
13
+ * - The `additionalFields` when there is no form context to hand them: those
14
+ * entries are arbitrary components, not values, and one rendered against no
15
+ * context is nothing at all.
16
+ * - `headerKeys` — the properties {@link EntityDisplayHeader} has already
17
+ * rendered as the record's picture, subtitle, state and labels. Leaving one in
18
+ * prints its value twice on one screen.
19
+ *
20
+ * The record's **title property is not among them**, deliberately. It names the
21
+ * page in the identity bar, but a heading and a labelled field are different
22
+ * things: `Order #` is a column with a name and a description that someone opens
23
+ * the record to read, and dropping it because its text also appears in the
24
+ * breadcrumb removes a value from the record on the grounds that it is legible
25
+ * somewhere else. See {@link HEADER_DISPLAY_ROLES}.
26
+ */
27
+ export declare function readOnlyFieldKeys(collection: AdminCollection, hasFormContext: boolean, headerKeys?: ReadonlySet<string>): string[];
4
28
  /**
5
29
  * @group Components
6
30
  */
@@ -9,14 +33,44 @@ export interface EntityViewBindingProps<M extends Record<string, unknown>> {
9
33
  collection: AdminCollection<M>;
10
34
  path: string;
11
35
  className?: string;
36
+ /**
37
+ * Lay the record out as a page: a centred, scrolling column with the
38
+ * metadata rail beside it, exactly as the form does. Off for the inline
39
+ * uses — the delete dialog renders the record inside a box it already owns.
40
+ */
41
+ asPage?: boolean;
42
+ /** Padding preset, matching the form's. Only read when `asPage`. */
43
+ openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
44
+ /**
45
+ * Needed only to render `additionalFields`, which are arbitrary components
46
+ * that take a form context. Without one those entries are left out rather
47
+ * than rendered against a fake context.
48
+ */
49
+ formContext?: FormContext<M>;
12
50
  }
13
51
  /**
14
52
  * The read-only rendering of a record's values.
15
53
  *
16
- * This used to prepend a synthetic `Id` row of its own. Between that row, the
17
- * collection's own id property, and the `path/id` chip above it, the same UUID
18
- * appeared three times — and in the ~340px split pane each copy wrapped over
19
- * four lines, so most of the first screen was one id repeated. The id is now a
20
- * copyable chip in the identity bar, once.
54
+ * This is the same layout as {@link EntityForm}: the sections, the grid spans,
55
+ * the labels and the rail all come from the one resolver, so the record does
56
+ * not rearrange itself when you press Edit. Only the controls differ — each
57
+ * field renders its {@link PropertyPreview} instead of an input.
58
+ *
59
+ * It used to be a flat two-column table of every value in the record: no
60
+ * grouping, no widths, a label column sized for short names and a value column
61
+ * that wrapped emails over two lines while the pane beside it stayed empty. A
62
+ * boolean and a markdown body were given the same room.
63
+ *
64
+ * The synthetic `Id` row it used to prepend is gone with the same reasoning the
65
+ * form applies: between that row, the collection's own id property and the
66
+ * `path/id` chip above them, one UUID appeared three times. The id is a
67
+ * copyable chip in the identity bar, and the rail's record block, once each.
68
+ *
69
+ * The labels are the form's labels, icon and all: a field is the same field
70
+ * whether or not you are editing it. What `mode: "read"` drops is the per-field
71
+ * *description*, which is input help rather than part of the label — and what it
72
+ * adds is room. A control is a bordered box that separates itself from the next
73
+ * one; a value is bare text, so the rows and sections are opened up rather than
74
+ * inheriting spacing sized for inputs.
21
75
  */
22
- export declare function EntityViewBinding<M extends Record<string, unknown>>({ entity, collection, path, className }: EntityViewBindingProps<M>): React.JSX.Element;
76
+ export declare function EntityViewBinding<M extends Record<string, unknown>>({ entity, collection, className, asPage, openEntityMode, formContext }: EntityViewBindingProps<M>): React.JSX.Element;
@@ -7,7 +7,7 @@ export interface RebaseLayoutProps {
7
7
  appBar?: React.ReactNode;
8
8
  /** Custom Drawer to override the default. */
9
9
  drawer?: React.ReactNode;
10
- /** Expand the drawer while the pointer is over it. Not a load-time default — that is `defaultDrawerOpen`. */
10
+ /** Expand the drawer while the pointer is over it. On by default; pass `false` to opt out. Not a load-time default — that is `defaultDrawerOpen`. */
11
11
  autoOpenDrawer?: boolean;
12
12
  /** Start with the drawer expanded instead of collapsed to icons. Ignored on small layouts. */
13
13
  defaultDrawerOpen?: boolean;
@@ -4,7 +4,7 @@ export interface RebaseShellProps {
4
4
  /** Top bar. Only rendered if provided — pass `<AppBar/>` for the default one. */
5
5
  appBar?: React.ReactNode;
6
6
  drawer?: React.ReactNode;
7
- /** Expand the drawer while the pointer is over it. Not a load-time default — that is `defaultDrawerOpen`. */
7
+ /** Expand the drawer while the pointer is over it. On by default; pass `false` to opt out. Not a load-time default — that is `defaultDrawerOpen`. */
8
8
  autoOpenDrawer?: boolean;
9
9
  /** Start with the drawer expanded instead of collapsed to icons. Ignored on small layouts. */
10
10
  defaultDrawerOpen?: boolean;
@@ -1,19 +1,25 @@
1
1
  import React from "react";
2
2
  export declare const DRAWER_WIDTH = 280;
3
+ /**
4
+ * Namespaced by base path: two admins served from one origin are two different
5
+ * navigations, and a single key would have them overwrite each other's drawer.
6
+ */
7
+ export declare function drawerOpenStorageKey(basePath: string | undefined): string;
3
8
  /**
4
9
  * @group Core
5
10
  */
6
11
  export interface ScaffoldProps {
7
12
  /**
8
- * Open the drawer on hover
13
+ * Expand the collapsed drawer while the pointer is over it. On by default —
14
+ * pass `false` to keep the rail a rail until the user clicks the toggle.
9
15
  */
10
16
  autoOpenDrawer?: boolean;
11
17
  /**
12
18
  * Start with the drawer expanded rather than collapsed to icons.
13
19
  *
14
20
  * Distinct from {@link autoOpenDrawer}, which is a hover behaviour: this one
15
- * only seeds the initial state, so the user's own toggle still wins for the
16
- * rest of the session.
21
+ * only seeds the very first visit. Once the user toggles the drawer, that
22
+ * choice is stored in `localStorage` and wins over this prop on every load.
17
23
  *
18
24
  * Ignored on small layouts, where an expanded drawer covers the content it is
19
25
  * meant to navigate.
@@ -1,5 +1,7 @@
1
1
  export type { EntityViewBindingProps } from "./EntityViewBinding";
2
2
  export { EntityViewBinding } from "./EntityViewBinding";
3
+ export type { EntityDisplayHeaderProps } from "./EntityDisplayHeader";
4
+ export { EntityDisplayHeader, HEADER_DISPLAY_ROLES } from "./EntityDisplayHeader";
3
5
  export * from "./DetailViewBinding";
4
6
  export type { SelectionProps } from "./ReferenceTable/SelectionTableBinding";
5
7
  export { SelectionTableBinding } from "./ReferenceTable/SelectionTableBinding";
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { nn as useAdminContext } from "./util-GiwPgxnL.js";
2
+ import { nn as useAdminContext } from "./util-EXfeGzbH.js";
3
3
  import React, { useCallback } from "react";
4
4
  import { Alert, BooleanSwitchWithLabel, Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle, DownloadIcon, IconButton, Label, RadioGroup, RadioGroupItem, Tooltip, iconSize } from "@rebasepro/ui";
5
5
  import { getDefaultValuesFor } from "@rebasepro/common";
@@ -515,4 +515,4 @@ var export_exports = /* @__PURE__ */ __exportAll({
515
515
  //#endregion
516
516
  export { downloadDataAsCsv as a, getEntityJsonExportableData as c, downloadBlob as i, ExportCollectionAction as n, downloadEntitiesExport as o, BasicExportAction as r, getEntityCSVExportableData as s, export_exports as t };
517
517
 
518
- //# sourceMappingURL=export-J79rMoNj.js.map
518
+ //# sourceMappingURL=export-Bl1V7lKO.js.map