@rebasepro/admin 0.12.1-canary.gf5f1d39 → 0.13.0

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 (178) hide show
  1. package/README.md +1 -1
  2. package/dist/{CollectionEditorDialog-Dpc75wIZ.js → CollectionEditorDialog-CjXkWZ64.js} +75 -62
  3. package/dist/{CollectionEditorDialog-Dpc75wIZ.js.map → CollectionEditorDialog-CjXkWZ64.js.map} +1 -1
  4. package/dist/{PropertyEditView-DH3XZC2x.js → PropertyEditView-W8I3YV89.js} +36 -16
  5. package/dist/{PropertyEditView-DH3XZC2x.js.map → PropertyEditView-W8I3YV89.js.map} +1 -1
  6. package/dist/{RouterCollectionsStudioView-BZLq4MGp.js → RouterCollectionsStudioView-BsLTNoqX.js} +5 -5
  7. package/dist/{RouterCollectionsStudioView-BZLq4MGp.js.map → RouterCollectionsStudioView-BsLTNoqX.js.map} +1 -1
  8. package/dist/collection_editor/serializable_types.d.ts +4 -1
  9. package/dist/collection_editor/ui/collection_editor/CollectionRelationsTab.d.ts +1 -0
  10. package/dist/collection_editor/useLocalCollectionsConfigController.d.ts +8 -0
  11. package/dist/collection_editor_ui.js +5 -5
  12. package/dist/collection_editor_ui.js.map +1 -1
  13. package/dist/components/CollectionTableBinding/column_utils.d.ts +17 -0
  14. package/dist/components/CollectionViewBinding/CollectionViewStartActions.d.ts +7 -2
  15. package/dist/components/CollectionViewBinding/SortButton.d.ts +24 -0
  16. package/dist/components/CollectionViewBinding/SplitListCloseButton.d.ts +10 -0
  17. package/dist/components/CollectionViewBinding/SplitViewContext.d.ts +23 -0
  18. package/dist/components/CollectionViewBinding/hooks/useKanbanDragAndDrop.d.ts +26 -0
  19. package/dist/components/DetailViewBinding.d.ts +5 -0
  20. package/dist/components/EditFormActions.d.ts +12 -1
  21. package/dist/components/EditViewBinding.d.ts +6 -1
  22. package/dist/components/EntityIdentityBar.d.ts +53 -0
  23. package/dist/components/EntityInspector.d.ts +35 -0
  24. package/dist/components/EntityPreviewNesting.d.ts +25 -0
  25. package/dist/components/EntityViewBinding.d.ts +9 -0
  26. package/dist/components/InlineEntityPreview.d.ts +47 -0
  27. package/dist/components/history/EntityHistoryView.d.ts +4 -1
  28. package/dist/components/index.d.ts +2 -0
  29. package/dist/editor.js +18 -10
  30. package/dist/editor.js.map +1 -1
  31. package/dist/{export-BzIpUdSK.js → export-J79rMoNj.js} +2 -2
  32. package/dist/{export-BzIpUdSK.js.map → export-J79rMoNj.js.map} +1 -1
  33. package/dist/form/EntityForm.d.ts +1 -1
  34. package/dist/form/EntityFormActions.d.ts +11 -1
  35. package/dist/form/PropertyFieldBinding.d.ts +1 -1
  36. package/dist/form/components/FieldBlock.d.ts +30 -0
  37. package/dist/form/components/FormRail.d.ts +18 -0
  38. package/dist/form/components/FormSections.d.ts +16 -0
  39. package/dist/form/components/index.d.ts +3 -2
  40. package/dist/form/components/useRailVisible.d.ts +24 -0
  41. package/dist/form/field_bindings/BinaryFieldBinding.d.ts +1 -1
  42. package/dist/form/field_bindings/DateTimeFieldBinding.d.ts +1 -1
  43. package/dist/form/field_bindings/GeopointFieldBinding.d.ts +1 -1
  44. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  45. package/dist/form/field_bindings/MultiSelectFieldBinding.d.ts +1 -1
  46. package/dist/form/field_bindings/ReadOnlyFieldBinding.d.ts +1 -1
  47. package/dist/form/field_bindings/SelectFieldBinding.d.ts +1 -1
  48. package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +1 -1
  49. package/dist/form/field_bindings/SwitchFieldBinding.d.ts +1 -1
  50. package/dist/form/field_bindings/TextFieldBinding.d.ts +1 -1
  51. package/dist/form/field_bindings/UserSelectFieldBinding.d.ts +1 -1
  52. package/dist/form/field_bindings/VectorFieldBinding.d.ts +1 -1
  53. package/dist/{history-g9688IaK.js → history-D7L_JlLO.js} +35 -33
  54. package/dist/history-D7L_JlLO.js.map +1 -0
  55. package/dist/hooks/navigation/utils.d.ts +16 -0
  56. package/dist/hooks/useBuildSidePanel.d.ts +14 -0
  57. package/dist/hooks/useEntityDisplayTitle.d.ts +18 -0
  58. package/dist/hooks/useHistory.d.ts +7 -0
  59. package/dist/hooks/useRecordActions.d.ts +17 -0
  60. package/dist/{import-BjUG-cIY.js → import-BvYl--U4.js} +2 -2
  61. package/dist/{import-BjUG-cIY.js.map → import-BvYl--U4.js.map} +1 -1
  62. package/dist/index.js +106 -35
  63. package/dist/index.js.map +1 -1
  64. package/dist/markdown-CklalUUk.js.map +1 -1
  65. package/dist/preview/components/InlineEntityListPreview.d.ts +15 -0
  66. package/dist/preview/index.d.ts +1 -0
  67. package/dist/preview/property_previews/ArrayOfReferencesPreview.d.ts +1 -1
  68. package/dist/preview/property_previews/ArrayOfRelationsPreview.d.ts +1 -1
  69. package/dist/types/components/EntityFormActionsProps.d.ts +0 -1
  70. package/dist/types/components/EntityFormProps.d.ts +0 -5
  71. package/dist/types/fields.d.ts +24 -0
  72. package/dist/{util-B5fJm1FA.js → util-GiwPgxnL.js} +2106 -1117
  73. package/dist/util-GiwPgxnL.js.map +1 -0
  74. package/package.json +32 -34
  75. package/src/collection_editor/ConfigControllerProvider.tsx +1 -1
  76. package/src/collection_editor/serializable_types.ts +4 -3
  77. package/src/collection_editor/serializable_utils.ts +51 -17
  78. package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +65 -22
  79. package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +38 -30
  80. package/src/collection_editor/ui/collection_editor/CollectionRelationsTab.tsx +3 -1
  81. package/src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx +1 -1
  82. package/src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx +1 -1
  83. package/src/collection_editor/ui/collection_editor/properties/RelationPropertyField.tsx +16 -0
  84. package/src/collection_editor/useLocalCollectionsConfigController.tsx +127 -15
  85. package/src/collection_editor/validateCollectionJson.ts +3 -3
  86. package/src/components/AdminModeSyncer.tsx +1 -1
  87. package/src/components/CollectionTableBinding/PropertyTableCell.tsx +10 -2
  88. package/src/components/CollectionTableBinding/column_utils.tsx +59 -2
  89. package/src/components/CollectionTableBinding/table_bindings.tsx +2 -1
  90. package/src/components/CollectionViewBinding/CollectionBoardViewBinding.tsx +23 -16
  91. package/src/components/CollectionViewBinding/CollectionViewActions.tsx +7 -1
  92. package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +9 -2
  93. package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +47 -12
  94. package/src/components/CollectionViewBinding/SortButton.tsx +189 -0
  95. package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +33 -0
  96. package/src/components/CollectionViewBinding/SplitListView.tsx +18 -3
  97. package/src/components/CollectionViewBinding/SplitViewContext.tsx +34 -0
  98. package/src/components/CollectionViewBinding/hooks/useKanbanDragAndDrop.ts +48 -9
  99. package/src/components/CollectionViewBinding/useBoardDataController.tsx +80 -25
  100. package/src/components/CollectionViewBinding/usePreviewSlots.ts +18 -6
  101. package/src/components/DefaultAppBar.tsx +1 -1
  102. package/src/components/DefaultDrawer.tsx +1 -1
  103. package/src/components/DetailViewBinding.tsx +112 -148
  104. package/src/components/DrawerNavigationItem.tsx +1 -1
  105. package/src/components/EditFormActions.tsx +54 -129
  106. package/src/components/EditViewBinding.tsx +202 -99
  107. package/src/components/EntityIdentityBar.tsx +288 -0
  108. package/src/components/EntityInspector.tsx +137 -0
  109. package/src/components/EntityPreviewBinding.tsx +6 -2
  110. package/src/components/EntityPreviewNesting.tsx +50 -0
  111. package/src/components/EntityViewBinding.tsx +35 -32
  112. package/src/components/HomePage/FavouritesView.tsx +1 -1
  113. package/src/components/HomePage/NavigationCardBinding.tsx +1 -1
  114. package/src/components/HomePage/SmallNavigationCard.tsx +1 -1
  115. package/src/components/InlineEntityPreview.tsx +149 -0
  116. package/src/components/RebaseLayout.tsx +1 -1
  117. package/src/components/RebaseNavigation.tsx +45 -5
  118. package/src/components/RebaseRouteDefs.tsx +1 -1
  119. package/src/components/SelectableTable/SelectableTable.tsx +2 -2
  120. package/src/components/SidePanelBinding.tsx +19 -13
  121. package/src/components/UserSelector.tsx +12 -6
  122. package/src/components/common/default_entity_actions.tsx +4 -3
  123. package/src/components/history/EntityHistoryEntry.tsx +28 -7
  124. package/src/components/history/EntityHistoryView.tsx +29 -24
  125. package/src/components/history/LastEditedByIndicator.tsx +4 -3
  126. package/src/components/index.ts +2 -0
  127. package/src/data_import/utils/get_import_inference_type.ts +6 -0
  128. package/src/form/EntityForm.tsx +267 -179
  129. package/src/form/EntityFormActions.tsx +50 -99
  130. package/src/form/EntityFormBinding.tsx +4 -2
  131. package/src/form/PropertyFieldBinding.tsx +4 -0
  132. package/src/form/components/FieldBlock.tsx +119 -0
  133. package/src/form/components/FormRail.tsx +125 -0
  134. package/src/form/components/FormSections.tsx +118 -0
  135. package/src/form/components/index.tsx +3 -2
  136. package/src/form/components/useRailVisible.tsx +46 -0
  137. package/src/form/field_bindings/BinaryFieldBinding.tsx +3 -2
  138. package/src/form/field_bindings/DateTimeFieldBinding.tsx +3 -2
  139. package/src/form/field_bindings/GeopointFieldBinding.tsx +3 -2
  140. package/src/form/field_bindings/MapFieldBinding.tsx +1 -2
  141. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +3 -2
  142. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +2 -1
  143. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +2 -1
  144. package/src/form/field_bindings/ReferenceFieldBinding.tsx +2 -1
  145. package/src/form/field_bindings/RelationFieldBinding.tsx +6 -4
  146. package/src/form/field_bindings/RepeatFieldBinding.tsx +4 -1
  147. package/src/form/field_bindings/SelectFieldBinding.tsx +3 -2
  148. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +31 -8
  149. package/src/form/field_bindings/SwitchFieldBinding.tsx +7 -3
  150. package/src/form/field_bindings/TextFieldBinding.tsx +18 -16
  151. package/src/form/field_bindings/UserSelectFieldBinding.tsx +3 -2
  152. package/src/form/field_bindings/VectorFieldBinding.tsx +2 -1
  153. package/src/hooks/navigation/useBuildUrlController.tsx +1 -1
  154. package/src/hooks/navigation/useTopLevelNavigation.ts +18 -7
  155. package/src/hooks/navigation/utils.ts +20 -0
  156. package/src/hooks/useBuildSideDialogsController.tsx +2 -2
  157. package/src/hooks/useBuildSidePanel.tsx +22 -61
  158. package/src/hooks/useEntityDisplayTitle.tsx +60 -0
  159. package/src/hooks/useHistory.ts +20 -10
  160. package/src/hooks/useRecordActions.tsx +69 -0
  161. package/src/hooks/useResolvedUsers.ts +10 -5
  162. package/src/preview/PropertyPreview.tsx +48 -22
  163. package/src/preview/components/InlineEntityListPreview.tsx +42 -0
  164. package/src/preview/components/ReferencePreview.tsx +34 -14
  165. package/src/preview/components/RelationPreview.tsx +34 -14
  166. package/src/preview/index.ts +1 -0
  167. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +24 -1
  168. package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +25 -1
  169. package/src/routes/RebaseRoute.tsx +2 -2
  170. package/src/types/components/EntityFormActionsProps.tsx +0 -1
  171. package/src/types/components/EntityFormProps.tsx +0 -6
  172. package/src/types/fields.tsx +27 -0
  173. package/dist/form/components/FormEntry.d.ts +0 -6
  174. package/dist/form/components/FormLayout.d.ts +0 -5
  175. package/dist/history-g9688IaK.js.map +0 -1
  176. package/dist/util-B5fJm1FA.js.map +0 -1
  177. package/src/form/components/FormEntry.tsx +0 -22
  178. package/src/form/components/FormLayout.tsx +0 -16
@@ -1,12 +1,12 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { in as useUrlController } from "./util-B5fJm1FA.js";
3
- import { s as AIModifiedPathsProvider } from "./PropertyEditView-DH3XZC2x.js";
4
- import { t as CollectionEditor } from "./CollectionEditorDialog-Dpc75wIZ.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";
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 { useLocation, useNavigate } from "react-router-dom";
9
+ import { useLocation, useNavigate } from "react-router";
10
10
  //#region src/collection_editor/ui/collection_editor/CollectionStudioView.tsx
11
11
  function CollectionStudioView({ collectionId, onSave, onCancel, ...props }) {
12
12
  const [formDirty, setFormDirty] = useState(false);
@@ -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-BZLq4MGp.js.map
192
+ //# sourceMappingURL=RouterCollectionsStudioView-BsLTNoqX.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RouterCollectionsStudioView-BZLq4MGp.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-dom\";\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-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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"}
@@ -21,7 +21,8 @@ export interface SerializableAdminBaseOptions {
21
21
  hideFromCollection?: boolean;
22
22
  readOnly?: boolean;
23
23
  disabled?: boolean | SerializablePropertyDisabledConfig;
24
- widthPercentage?: number;
24
+ span?: 1 | 2 | 3 | 4;
25
+ /** Opaque props handed to a custom `Field` / `Preview`. Round-tripped as-is. */
25
26
  customProps?: unknown;
26
27
  /** Narrows the filter operators offered for this property. Plain data. */
27
28
  filterOperators?: readonly WhereFilterOp[];
@@ -245,6 +246,7 @@ export type SerializableRelation = {
245
246
  target?: string;
246
247
  relationName?: string;
247
248
  foreignKeyOnTarget?: string;
249
+ sourceKey?: string;
248
250
  onUpdate?: OnAction;
249
251
  onDelete?: OnAction;
250
252
  } | {
@@ -252,6 +254,7 @@ export type SerializableRelation = {
252
254
  target?: string;
253
255
  relationName?: string;
254
256
  foreignKeyOnTarget?: string;
257
+ sourceKey?: string;
255
258
  onUpdate?: OnAction;
256
259
  onDelete?: OnAction;
257
260
  } | {
@@ -14,6 +14,7 @@ export type RelationDraft = {
14
14
  target?: string;
15
15
  localKey?: string;
16
16
  foreignKeyOnTarget?: string;
17
+ sourceKey?: string;
17
18
  through?: {
18
19
  table?: string;
19
20
  sourceColumn?: string;
@@ -3,4 +3,12 @@ import type { AdminCollection } from "@rebasepro/admin-types";
3
3
  export declare function useLocalCollectionsConfigController(clientOrUrl: any, baseCollections?: AdminCollection[], options?: {
4
4
  readOnly?: boolean;
5
5
  getAuthToken?: () => Promise<string | null>;
6
+ /**
7
+ * Identity of the signed-in user, if any.
8
+ *
9
+ * Only used to re-ask the backend when it changes: whether the editor
10
+ * will accept a write depends on who is asking, and the answer to an
11
+ * anonymous probe does not survive a sign-in.
12
+ */
13
+ authKey?: string | null;
6
14
  }): CollectionsConfigController;
@@ -1,11 +1,11 @@
1
- import { in as useUrlController } from "./util-B5fJm1FA.js";
2
- import { n as PropertyForm, r as PropertyFormDialog } from "./PropertyEditView-DH3XZC2x.js";
3
- import { n as CollectionEditorDialog } from "./CollectionEditorDialog-Dpc75wIZ.js";
4
- import { i as CollectionStudioView, r as CollectionsStudioView, t as RouterCollectionsStudioView } from "./RouterCollectionsStudioView-BZLq4MGp.js";
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";
5
5
  import "react";
6
6
  import { useSnackbarController } from "@rebasepro/app";
7
7
  import { jsx } from "react/jsx-runtime";
8
- import { useNavigate } from "react-router-dom";
8
+ import { useNavigate } from "react-router";
9
9
  //#region src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx
10
10
  function RouterCollectionStudioView(props) {
11
11
  const navigate = useNavigate();
@@ -1 +1 @@
1
- {"version":3,"file":"collection_editor_ui.js","names":[],"sources":["../src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx"],"sourcesContent":["/**\n * Router-aware wrapper around CollectionStudioView.\n *\n * This component provides the react-router-based navigation behavior\n * that Rebase Studio expects: cancel navigates to the root path,\n * save navigates to the saved collection's slug, and a snackbar\n * toast is shown on success.\n *\n * External consumers should use `CollectionStudioView` directly\n * (which has no react-router dependency).\n */\nimport React from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useUrlController } from \"../../_cms_internals\";\nimport { useSnackbarController } from \"@rebasepro/app\";\nimport { CollectionStudioView, CollectionStudioViewProps } from \"./CollectionStudioView\";\n\nexport type RouterCollectionStudioViewProps = Omit<CollectionStudioViewProps, \"onSave\" | \"onCancel\">;\n\nexport function RouterCollectionStudioView(props: RouterCollectionStudioViewProps) {\n const navigate = useNavigate();\n const urlController = useUrlController();\n const snackbarController = useSnackbarController();\n\n return (\n <CollectionStudioView\n {...props}\n onCancel={() => {\n navigate(urlController.buildAppUrlPath(\"/\"));\n }}\n onSave={(savedCollection) => {\n if (savedCollection) {\n snackbarController.open({\n type: \"success\",\n message: `Collection ${savedCollection.name || savedCollection.slug} saved`\n });\n if (props.collectionId === \"new\") {\n navigate(urlController.buildAppUrlPath(`s/schema/${savedCollection.slug}`));\n }\n }\n }}\n />\n );\n}\n"],"mappings":";;;;;;;;;AAmBA,SAAgB,2BAA2B,OAAwC;CAC/E,MAAM,WAAW,YAAY;CAC7B,MAAM,gBAAgB,iBAAiB;CACvC,MAAM,qBAAqB,sBAAsB;CAEjD,OACI,oBAAC,sBAAD;EACI,GAAI;EACJ,gBAAgB;GACZ,SAAS,cAAc,gBAAgB,GAAG,CAAC;EAC/C;EACA,SAAS,oBAAoB;GACzB,IAAI,iBAAiB;IACjB,mBAAmB,KAAK;KACpB,MAAM;KACN,SAAS,cAAc,gBAAgB,QAAQ,gBAAgB,KAAK;IACxE,CAAC;IACD,IAAI,MAAM,iBAAiB,OACvB,SAAS,cAAc,gBAAgB,YAAY,gBAAgB,MAAM,CAAC;GAElF;EACJ;CACH,CAAA;AAET"}
1
+ {"version":3,"file":"collection_editor_ui.js","names":[],"sources":["../src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx"],"sourcesContent":["/**\n * Router-aware wrapper around CollectionStudioView.\n *\n * This component provides the react-router-based navigation behavior\n * that Rebase Studio expects: cancel navigates to the root path,\n * save navigates to the saved collection's slug, and a snackbar\n * toast is shown on success.\n *\n * External consumers should use `CollectionStudioView` directly\n * (which has no react-router dependency).\n */\nimport React from \"react\";\nimport { useNavigate } from \"react-router\";\nimport { useUrlController } from \"../../_cms_internals\";\nimport { useSnackbarController } from \"@rebasepro/app\";\nimport { CollectionStudioView, CollectionStudioViewProps } from \"./CollectionStudioView\";\n\nexport type RouterCollectionStudioViewProps = Omit<CollectionStudioViewProps, \"onSave\" | \"onCancel\">;\n\nexport function RouterCollectionStudioView(props: RouterCollectionStudioViewProps) {\n const navigate = useNavigate();\n const urlController = useUrlController();\n const snackbarController = useSnackbarController();\n\n return (\n <CollectionStudioView\n {...props}\n onCancel={() => {\n navigate(urlController.buildAppUrlPath(\"/\"));\n }}\n onSave={(savedCollection) => {\n if (savedCollection) {\n snackbarController.open({\n type: \"success\",\n message: `Collection ${savedCollection.name || savedCollection.slug} saved`\n });\n if (props.collectionId === \"new\") {\n navigate(urlController.buildAppUrlPath(`s/schema/${savedCollection.slug}`));\n }\n }\n }}\n />\n );\n}\n"],"mappings":";;;;;;;;;AAmBA,SAAgB,2BAA2B,OAAwC;CAC/E,MAAM,WAAW,YAAY;CAC7B,MAAM,gBAAgB,iBAAiB;CACvC,MAAM,qBAAqB,sBAAsB;CAEjD,OACI,oBAAC,sBAAD;EACI,GAAI;EACJ,gBAAgB;GACZ,SAAS,cAAc,gBAAgB,GAAG,CAAC;EAC/C;EACA,SAAS,oBAAoB;GACzB,IAAI,iBAAiB;IACjB,mBAAmB,KAAK;KACpB,MAAM;KACN,SAAS,cAAc,gBAAgB,QAAQ,gBAAgB,KAAK;IACxE,CAAC;IACD,IAAI,MAAM,iBAAiB,OACvB,SAAS,cAAc,gBAAgB,YAAY,gBAAgB,MAAM,CAAC;GAElF;EACJ;CACH,CAAA;AAET"}
@@ -20,3 +20,20 @@ export interface PropertiesToColumnsParams<M extends Record<string, unknown>> {
20
20
  }>;
21
21
  }
22
22
  export declare function propertiesToColumns<M extends Record<string, unknown>>({ properties, sortable, fixedFilter, engine, AdditionalHeaderWidget }: PropertiesToColumnsParams<M>): VirtualTableColumn[];
23
+ export type SortablePropertyOption = {
24
+ key: string;
25
+ title: string;
26
+ property: Property;
27
+ };
28
+ /**
29
+ * The properties a sort can be applied to, as keys and titles.
30
+ *
31
+ * Only the table renders headers, so the list, card and split views had no way
32
+ * to order anything — the controller has carried `sortBy` all along, with no
33
+ * control bound to it outside the table. This is the same walk
34
+ * `propertiesToColumns` makes, under the same keys and the same
35
+ * `sortableProperty` authority, so a sort picked from the toolbar means exactly
36
+ * what clicking the header would have meant, and the two cannot drift into
37
+ * offering different columns.
38
+ */
39
+ export declare function getSortablePropertyOptions(properties: Properties): SortablePropertyOption[];
@@ -1,6 +1,6 @@
1
1
  import type { Properties } from "@rebasepro/types";
2
2
  import React from "react";
3
- import { EntityTableController, SelectionController, AdminCollection } from "@rebasepro/admin-types";
3
+ import { EntityTableController, SelectionController, AdminCollection, ViewMode } from "@rebasepro/admin-types";
4
4
  export type CollectionViewStartActionsProps<M extends Record<string, unknown>> = {
5
5
  collection: AdminCollection<M>;
6
6
  path: string;
@@ -14,7 +14,12 @@ export type CollectionViewStartActionsProps<M extends Record<string, unknown>> =
14
14
  * Resolved properties from the collection for the filters dialog
15
15
  */
16
16
  resolvedProperties?: Properties;
17
+ /**
18
+ * Which view is being displayed. Decides whether the sort control is
19
+ * offered — see `sortButton`.
20
+ */
21
+ viewMode?: ViewMode;
17
22
  compact?: boolean;
18
23
  openNewDocument: (defaultValues?: Record<string, unknown>) => void;
19
24
  };
20
- export declare function CollectionViewStartActions<M extends Record<string, unknown>>({ collection, relativePath, parentCollectionSlugs, parentEntityIds, path, selectionController, tableController, collectionEntitiesCount, resolvedProperties, compact, openNewDocument }: CollectionViewStartActionsProps<M>): React.JSX.Element;
25
+ export declare function CollectionViewStartActions<M extends Record<string, unknown>>({ collection, relativePath, parentCollectionSlugs, parentEntityIds, path, selectionController, tableController, collectionEntitiesCount, resolvedProperties, viewMode, compact, openNewDocument }: CollectionViewStartActionsProps<M>): React.JSX.Element;
@@ -0,0 +1,24 @@
1
+ import type { Properties } from "@rebasepro/types";
2
+ import React from "react";
3
+ import { EntityTableController } from "@rebasepro/admin-types";
4
+ export type SortButtonProps<M extends Record<string, unknown>> = {
5
+ tableController: EntityTableController<M>;
6
+ /**
7
+ * Resolved properties of the collection. The sortable ones among them are
8
+ * the options offered.
9
+ */
10
+ properties: Properties;
11
+ };
12
+ /**
13
+ * Toolbar control that orders the collection by one property.
14
+ *
15
+ * The table orders by clicking a column header, which leaves every other view
16
+ * mode — list, cards, and either of them inside a split view — with no way to
17
+ * order at all, even though the controller has always carried `sortBy` and the
18
+ * query has always honoured it. This binds a control to it.
19
+ *
20
+ * Clicking the active property flips its direction, so ascending → descending
21
+ * is one click rather than a trip through a direction selector; clicking a
22
+ * different one starts it ascending. The popover stays open through both.
23
+ */
24
+ export declare function SortButton<M extends Record<string, unknown>>({ tableController, properties }: SortButtonProps<M>): React.JSX.Element | null;
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ /**
3
+ * Closes the list of a split view, leaving the open record on its own.
4
+ *
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.
9
+ */
10
+ export declare function SplitListCloseButton(): React.JSX.Element | null;
@@ -0,0 +1,23 @@
1
+ import React from "react";
2
+ /**
3
+ * The enclosing master-detail split view, exposed to the list panel so it can
4
+ * carry the control that closes it.
5
+ */
6
+ export type SplitViewController = {
7
+ /** Whether a record is open in the detail pane. */
8
+ detailOpen: boolean;
9
+ /**
10
+ * Close the list and leave the open record on its own, full screen. This is
11
+ * the existing full-screen entity route (`#full`), not a second mechanism.
12
+ */
13
+ openFullScreen: () => void;
14
+ };
15
+ /**
16
+ * The enclosing split view, or `undefined` when the component is not rendered
17
+ * inside one (a side panel, a dialog, a full-screen route).
18
+ */
19
+ export declare function useSplitView(): SplitViewController | undefined;
20
+ export declare function SplitViewProvider({ value, children }: {
21
+ value: SplitViewController;
22
+ children: React.ReactNode;
23
+ }): React.JSX.Element;
@@ -2,6 +2,32 @@ import { RebaseData, Entity } from "@rebasepro/types";
2
2
  import { RebaseContext, AnalyticsController, AdminCollection } from "@rebasepro/admin-types";
3
3
  import { BoardItem } from "@rebasepro/ui";
4
4
  import { BoardDataController } from "../useBoardDataController";
5
+ /**
6
+ * The digits a board order key is built from: base36, lower case only.
7
+ *
8
+ * The library's default alphabet is base62, and its keys only sort correctly
9
+ * under byte ordering. The column is sorted by the *database*, and Postgres'
10
+ * default collation is not byte ordering — under `en_US.UTF-8`, `"aa"` sorts
11
+ * before `"aC"`, so as soon as a board had been dragged around enough to reach
12
+ * the upper-case digits its order stopped matching the keys. One case has no
13
+ * such ambiguity: digits before letters is true of every collation in use.
14
+ *
15
+ * A key written with the old alphabet no longer validates, which is what makes
16
+ * the board offer to initialise the column — one click, and the order is
17
+ * rewritten in a form the database can sort.
18
+ */
19
+ export declare const ORDER_KEY_DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz";
20
+ /**
21
+ * Whether a stored order value is a key `fractional-indexing` can interpolate
22
+ * against.
23
+ *
24
+ * Anything else — a plain `"12"` from a seed or a migration, an empty string —
25
+ * makes `generateKeyBetween` throw, and the fallback below then hands out the
26
+ * same first key to every card, so each drag lands at the bottom of the column
27
+ * on top of the last one that did. Asking the library itself is the only
28
+ * definition of "valid" worth trusting.
29
+ */
30
+ export declare function isValidOrderKey(value: unknown): value is string;
5
31
  export interface UseKanbanDragAndDropParams<M extends Record<string, unknown>> {
6
32
  collection: AdminCollection<M>;
7
33
  fullPath: string;
@@ -24,5 +24,10 @@ export interface DetailViewBindingProps<M extends Record<string, unknown> = Reco
24
24
  onEditClick?: () => void;
25
25
  layout?: "side_panel" | "full_screen" | "split" | "dialog";
26
26
  barActions?: (params: BarActionsParams) => React.ReactNode;
27
+ /**
28
+ * Controls the owning panel puts at the identity bar's leading edge, ahead
29
+ * of the breadcrumb — the side panel's close button.
30
+ */
31
+ barActionsStart?: React.ReactNode;
27
32
  }
28
33
  export declare function DetailViewBinding<M extends Record<string, unknown>>({ entityId, ...props }: DetailViewBindingProps<M>): React.JSX.Element;
@@ -1,3 +1,14 @@
1
1
  import React from "react";
2
2
  import type { EntityFormActionsProps } from "../types/components/EntityFormActionsProps";
3
- export declare function EditFormActions({ collection, path, entity, layout, savingError, formex, disabled, status, pluginActions, openEntityMode, showDefaultActions, navigateBack, formContext }: EntityFormActionsProps): React.JSX.Element;
3
+ /**
4
+ * The entity form's footer.
5
+ *
6
+ * It used to render in two shapes: this footer, and a `w-80 2xl:w-96` sticky
7
+ * side rail carrying the same Save and Discard above several hundred pixels of
8
+ * nothing. The rail is gone, and Save/Discard have moved to the identity bar
9
+ * where they stay visible while the form scrolls — so with
10
+ * `showDefaultActions={false}` this renders only what the bar cannot host:
11
+ * record actions (copy, delete, custom), plugin actions, the saving error, and
12
+ * the side-panel-only "save and close".
13
+ */
14
+ export declare function EditFormActions({ collection, path, entity, savingError, formex, disabled, status, pluginActions, openEntityMode, showDefaultActions, navigateBack, formContext }: EntityFormActionsProps): React.JSX.Element | null;
@@ -35,6 +35,11 @@ export interface EditViewBindingProps<M extends Record<string, unknown> = Record
35
35
  navigateBack?: () => void;
36
36
  layout?: "side_panel" | "full_screen" | "split" | "dialog";
37
37
  barActions?: (params: BarActionsParams) => React.ReactNode;
38
+ /**
39
+ * Controls the owning panel puts at the identity bar's leading edge, ahead
40
+ * of the breadcrumb — the side panel's close button.
41
+ */
42
+ barActionsStart?: React.ReactNode;
38
43
  formProps?: Partial<EntityFormBindingProps<M>>;
39
44
  /**
40
45
  * Pre-populate the form with these values when creating a new entity.
@@ -48,7 +53,7 @@ export interface EditViewBindingProps<M extends Record<string, unknown> = Record
48
53
  * a record is opened.
49
54
  */
50
55
  export declare function EditViewBinding<M extends Record<string, unknown>>({ entityId, ...props }: EditViewBindingProps<M>): React.JSX.Element;
51
- 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, status, setStatus, formProps, canEdit }: EditViewBindingProps<M> & {
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> & {
52
57
  entity?: Entity<M>;
53
58
  initialDirtyValues?: Partial<M>;
54
59
  dataLoading: boolean;
@@ -0,0 +1,53 @@
1
+ import type { AdminCollection } from "@rebasepro/admin-types";
2
+ import type { EntityStatus } from "@rebasepro/types";
3
+ import React from "react";
4
+ export interface EntityIdentityBarProps {
5
+ /** Plural collection name, shown as the breadcrumb ahead of the title. */
6
+ collection: AdminCollection;
7
+ /** Resolved record title, already guarded against showing a field default. */
8
+ title: string;
9
+ entityId?: string | number;
10
+ status: EntityStatus;
11
+ dirty: boolean;
12
+ saving: boolean;
13
+ /** Undefined hides the Save button — read-only views, or a custom form. */
14
+ onSave?: () => void;
15
+ onDiscard?: () => void;
16
+ saveDisabled?: boolean;
17
+ hasErrors?: boolean;
18
+ /**
19
+ * Save and dismiss, for the side panel and the dialog where the dominant
20
+ * action is "done with this record" rather than "keep editing".
21
+ */
22
+ onSaveAndClose?: () => void;
23
+ onBack?: () => void;
24
+ onInspect?: () => void;
25
+ /** Opens the inspector straight on its history tab. */
26
+ onViewHistory?: () => void;
27
+ /**
28
+ * Actions performed *on* the record — copy, delete, whatever the collection
29
+ * adds — as menu items under the overflow button. They live here rather
30
+ * than in a footer, which is what let the footer go away entirely.
31
+ */
32
+ recordActions?: React.ReactNode;
33
+ /** Plugin-contributed actions, rendered inline before Save. */
34
+ pluginActions?: React.ReactNode;
35
+ /** Close / full-screen buttons supplied by the panel that owns this view. */
36
+ trailing?: React.ReactNode;
37
+ /**
38
+ * Controls the owning panel puts at the bar's leading edge, ahead of the
39
+ * breadcrumb — where the side panel's close button lives.
40
+ */
41
+ leading?: React.ReactNode;
42
+ }
43
+ /**
44
+ * The persistent header of an entity view: who this record is, and what you can
45
+ * do to it.
46
+ *
47
+ * The title and the id used to live inside the scrolling form, above 72px of
48
+ * empty space — 219px of chrome that carried nothing and scrolled away the
49
+ * moment you touched the wheel, leaving no indication of which record you were
50
+ * editing. They live here instead, and the left half of a bar that was
51
+ * previously empty now does the work.
52
+ */
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;
@@ -0,0 +1,35 @@
1
+ import type { AdminCollection } from "@rebasepro/admin-types";
2
+ import type { Entity } from "@rebasepro/types";
3
+ import type { FormContext } from "../types/fields";
4
+ import React from "react";
5
+ export type InspectorTab = "json" | "history";
6
+ export interface EntityInspectorProps {
7
+ /** The open tab, or null when the panel is closed. */
8
+ tab: InspectorTab | null;
9
+ onTabChange: (tab: InspectorTab) => void;
10
+ onClose: () => void;
11
+ collection: AdminCollection;
12
+ entity?: Entity<Record<string, unknown>>;
13
+ formContext?: FormContext<Record<string, unknown>>;
14
+ values?: Record<string, unknown>;
15
+ /** History is only offered when the collection records it. */
16
+ includeHistory: boolean;
17
+ /**
18
+ * Bumped once per save by the view that owns the form. The panel stays open
19
+ * across a save — the bar's Save button sits above it — so without this the
20
+ * history list would still be the one fetched when the panel opened.
21
+ */
22
+ refreshToken?: number;
23
+ }
24
+ /**
25
+ * Raw values and revision history, as an inspector rather than as tabs.
26
+ *
27
+ * These were the first two entries in the entity tab strip — two icon-only tabs
28
+ * with no label and no tooltip, sitting *before* the record you opened the page
29
+ * to edit. They are developer tools, not places the record lives, so they belong
30
+ * behind one affordance in the bar; a content editor should never meet `<>`.
31
+ *
32
+ * The panel sits below the identity bar and beside the record deliberately: you
33
+ * keep seeing which record you are inspecting, and keep being able to edit it.
34
+ */
35
+ export declare function EntityInspector({ tab, onTabChange, onClose, collection, entity, formContext, values, includeHistory, refreshToken }: EntityInspectorProps): React.JSX.Element | null;
@@ -0,0 +1,25 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Current entity preview nesting depth. 0 means "not inside another preview".
4
+ */
5
+ export declare function useEntityPreviewDepth(): number;
6
+ /**
7
+ * True when the preview being rendered lives inside another entity preview,
8
+ * and should therefore render its compact variant.
9
+ */
10
+ export declare function useIsNestedEntityPreview(): boolean;
11
+ /**
12
+ * Marks its children as being rendered inside an entity preview, so any
13
+ * reference or relation below collapses to a chip.
14
+ */
15
+ export declare function NestedEntityPreviewBoundary({ children }: {
16
+ children: React.ReactNode;
17
+ }): React.JSX.Element;
18
+ /**
19
+ * Resets the nesting depth back to 0. Used by surfaces that open a fresh
20
+ * rendering context for an entity — a side panel, a dialog — from within a
21
+ * preview, where the children are no longer visually nested.
22
+ */
23
+ export declare function RootEntityPreviewBoundary({ children }: {
24
+ children: React.ReactNode;
25
+ }): React.JSX.Element;
@@ -10,4 +10,13 @@ export interface EntityViewBindingProps<M extends Record<string, unknown>> {
10
10
  path: string;
11
11
  className?: string;
12
12
  }
13
+ /**
14
+ * The read-only rendering of a record's values.
15
+ *
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.
21
+ */
13
22
  export declare function EntityViewBinding<M extends Record<string, unknown>>({ entity, collection, path, className }: EntityViewBindingProps<M>): React.JSX.Element;
@@ -0,0 +1,47 @@
1
+ import * as React from "react";
2
+ import { Entity } from "@rebasepro/types";
3
+ import type { AdminCollection } from "@rebasepro/admin-types";
4
+ export type InlineEntityPreviewProps = {
5
+ entity: Entity<any>;
6
+ collection?: AdminCollection;
7
+ /** Overrides the resolved title. */
8
+ label?: string;
9
+ disabled?: boolean;
10
+ /** When set, takes over the click. Otherwise the row opens the side panel. */
11
+ onClick?: (e: React.SyntheticEvent) => void;
12
+ /** Set to false to render text only, with no click target. */
13
+ includeEntityLink?: boolean;
14
+ /** Icon size in px. Defaults to a hair under the surrounding text. */
15
+ iconSize?: number;
16
+ className?: string;
17
+ };
18
+ /**
19
+ * An entity rendered as one line of text: a small link glyph that marks it as
20
+ * pointing elsewhere, then the target's title in whatever typography surrounds
21
+ * it.
22
+ *
23
+ * This is what a reference or relation renders as when it is *not* the subject
24
+ * of its slot — nested inside another entity preview, or filling a title slot.
25
+ * The card treatment (image, id, preview properties, side-panel button) only
26
+ * makes sense at the top level; repeating it one level down produces a bordered
27
+ * box inside a bordered box, each with its own action buttons.
28
+ *
29
+ * @group Preview components
30
+ */
31
+ export declare function InlineEntityPreview({ entity, collection: collectionProp, label, disabled, onClick, includeEntityLink, iconSize, className }: InlineEntityPreviewProps): React.JSX.Element;
32
+ /**
33
+ * Placeholder shown while the target of an inline reference is being fetched.
34
+ */
35
+ export declare function InlineEntityPreviewSkeleton({ className }: {
36
+ className?: string;
37
+ }): React.JSX.Element;
38
+ /**
39
+ * Shown when the target of an inline reference cannot be resolved — a deleted
40
+ * row, or a collection that is not registered. Keeps the line readable instead
41
+ * of dropping an error box into the middle of a card.
42
+ */
43
+ export declare function InlineEntityPreviewMissing({ label, tooltip, className }: {
44
+ label: string;
45
+ tooltip?: string;
46
+ className?: string;
47
+ }): React.JSX.Element;
@@ -3,4 +3,7 @@ import type { EntityCustomViewParams } from "@rebasepro/admin-types";
3
3
  * Entity history tab view. Shows a paginated list of entity revisions
4
4
  * fetched from the backend API. Supports infinite scroll and revert.
5
5
  */
6
- export declare function EntityHistoryView<M extends Record<string, unknown>>({ entity, collection, formContext }: EntityCustomViewParams<M>): import("react").JSX.Element;
6
+ export declare function EntityHistoryView<M extends Record<string, unknown>>({ entity, collection, formContext, refreshToken }: EntityCustomViewParams<M> & {
7
+ /** Bumped once per save, so the list picks up the revision it just made. */
8
+ refreshToken?: number;
9
+ }): import("react").JSX.Element;
@@ -19,6 +19,8 @@ export * from "./ReferenceWidget";
19
19
  export * from "./SearchIconsView";
20
20
  export * from "./FieldCaption";
21
21
  export * from "./EntityPreviewBinding";
22
+ export * from "./EntityPreviewNesting";
23
+ export * from "./InlineEntityPreview";
22
24
  export * from "./common";
23
25
  export * from "./field_configs";
24
26
  export * from "./SidePanelProvider";