@rebasepro/admin 0.11.1-canary.gfd39654 → 0.12.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 (124) hide show
  1. package/README.md +1 -1
  2. package/dist/{CollectionEditorDialog-o7F_zbGY.js → CollectionEditorDialog-Dpc75wIZ.js} +56 -260
  3. package/dist/CollectionEditorDialog-Dpc75wIZ.js.map +1 -0
  4. package/dist/{PropertyEditView-DgB2b22q.js → PropertyEditView-DH3XZC2x.js} +2 -2
  5. package/dist/{PropertyEditView-DgB2b22q.js.map → PropertyEditView-DH3XZC2x.js.map} +1 -1
  6. package/dist/{RouterCollectionsStudioView-DaLRgPkj.js → RouterCollectionsStudioView-BZLq4MGp.js} +4 -4
  7. package/dist/{RouterCollectionsStudioView-DaLRgPkj.js.map → RouterCollectionsStudioView-BZLq4MGp.js.map} +1 -1
  8. package/dist/collection_editor/CollectionEditorDialogsContext.d.ts +1 -1
  9. package/dist/collection_editor/_cms_internals.d.ts +1 -1
  10. package/dist/collection_editor/extensibility_types.d.ts +1 -1
  11. package/dist/collection_editor/serializable_types.d.ts +40 -12
  12. package/dist/collection_editor/serializable_utils.d.ts +1 -1
  13. package/dist/collection_editor/ui/collection_editor/CollectionRLSTab.d.ts +1 -11
  14. package/dist/collection_editor_ui.js +4 -4
  15. package/dist/components/CollectionEditorDialogs.d.ts +2 -2
  16. package/dist/components/CollectionTableBinding/column_utils.d.ts +7 -1
  17. package/dist/components/CollectionViewBinding/CollectionListViewBinding.d.ts +2 -2
  18. package/dist/components/DefaultDrawer.d.ts +3 -3
  19. package/dist/components/DrawerNavigationGroup.d.ts +8 -1
  20. package/dist/components/DrawerNavigationItem.d.ts +7 -1
  21. package/dist/components/EditViewBinding.d.ts +1 -1
  22. package/dist/components/RebaseAdmin.d.ts +1 -1
  23. package/dist/components/RebaseAuthGate.d.ts +1 -1
  24. package/dist/components/RebaseLayout.d.ts +5 -1
  25. package/dist/components/RebaseNavigation.d.ts +2 -2
  26. package/dist/components/RebaseRouteDefs.d.ts +1 -1
  27. package/dist/components/RebaseShell.d.ts +6 -1
  28. package/dist/components/RelationSelector.d.ts +14 -0
  29. package/dist/components/SelectableTable/filters/RelationFilterField.d.ts +27 -0
  30. package/dist/components/SelectableTable/filters/null_filter.d.ts +46 -0
  31. package/dist/components/SidePanelProvider.d.ts +2 -2
  32. package/dist/components/app/Scaffold.d.ts +11 -0
  33. package/dist/components/field_configs.d.ts +1 -1
  34. package/dist/{export-BvqhjdEb.js → export-BzIpUdSK.js} +3 -3
  35. package/dist/export-BzIpUdSK.js.map +1 -0
  36. package/dist/form/EntityForm.d.ts +2 -2
  37. package/dist/form/EntityFormBinding.d.ts +5 -5
  38. package/dist/{history-Dz20hH38.js → history-g9688IaK.js} +2 -2
  39. package/dist/{history-Dz20hH38.js.map → history-g9688IaK.js.map} +1 -1
  40. package/dist/hooks/index.d.ts +1 -1
  41. package/dist/hooks/useAdminContext.d.ts +29 -0
  42. package/dist/hooks/useHistory.d.ts +7 -11
  43. package/dist/{import-DINSB-Vu.js → import-BjUG-cIY.js} +2 -2
  44. package/dist/{import-DINSB-Vu.js.map → import-BjUG-cIY.js.map} +1 -1
  45. package/dist/index.js +61 -38
  46. package/dist/index.js.map +1 -1
  47. package/dist/types/components/EntityFormProps.d.ts +1 -1
  48. package/dist/types/fields.d.ts +1 -1
  49. package/dist/{util-BhhfHY1M.js → util-B5fJm1FA.js} +296 -169
  50. package/dist/util-B5fJm1FA.js.map +1 -0
  51. package/package.json +9 -9
  52. package/src/collection_editor/CollectionEditorDialogsContext.tsx +1 -1
  53. package/src/collection_editor/ConfigControllerProvider.tsx +1 -1
  54. package/src/collection_editor/_cms_internals.ts +1 -1
  55. package/src/collection_editor/extensibility_types.ts +1 -1
  56. package/src/collection_editor/index.ts +2 -2
  57. package/src/collection_editor/serializable_types.ts +44 -13
  58. package/src/collection_editor/serializable_utils.ts +68 -59
  59. package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +1 -1
  60. package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +66 -41
  61. package/src/collection_editor/ui/collection_editor/EntityActionsEditTab.tsx +1 -1
  62. package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +1 -1
  63. package/src/collection_editor/ui/collection_editor/SubcollectionsEditTab.tsx +2 -2
  64. package/src/components/CollectionEditorDialogs.tsx +2 -2
  65. package/src/components/CollectionTableBinding/CollectionRowActions.tsx +5 -4
  66. package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +8 -2
  67. package/src/components/CollectionTableBinding/column_utils.tsx +19 -5
  68. package/src/components/CollectionTableBinding/table_bindings.tsx +10 -10
  69. package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +8 -7
  70. package/src/components/CollectionViewBinding/CollectionViewActions.tsx +2 -2
  71. package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +2 -2
  72. package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +2 -2
  73. package/src/components/DefaultDrawer.tsx +19 -8
  74. package/src/components/DeleteEntityDialog.tsx +2 -2
  75. package/src/components/DetailViewBinding.tsx +21 -1
  76. package/src/components/DrawerNavigationGroup.tsx +47 -4
  77. package/src/components/DrawerNavigationItem.tsx +19 -5
  78. package/src/components/EditFormActions.tsx +4 -3
  79. package/src/components/EditViewBinding.tsx +1 -1
  80. package/src/components/HomePage/ContentHomePage.tsx +4 -4
  81. package/src/components/RebaseAdmin.tsx +3 -3
  82. package/src/components/RebaseAuthGate.tsx +1 -1
  83. package/src/components/RebaseLayout.tsx +8 -2
  84. package/src/components/RebaseNavigation.tsx +9 -9
  85. package/src/components/RebaseRouteDefs.tsx +2 -2
  86. package/src/components/RebaseShell.tsx +10 -1
  87. package/src/components/RelationSelector.tsx +18 -3
  88. package/src/components/SelectableTable/filters/FilterFieldBinding.tsx +1 -1
  89. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +36 -9
  90. package/src/components/SelectableTable/filters/RelationFilterField.tsx +93 -17
  91. package/src/components/SelectableTable/filters/null_filter.ts +63 -0
  92. package/src/components/SidePanelProvider.tsx +2 -2
  93. package/src/components/app/Scaffold.tsx +16 -1
  94. package/src/data_export/export/ExportCollectionAction.tsx +2 -2
  95. package/src/form/EntityForm.tsx +2 -2
  96. package/src/form/EntityFormActions.tsx +2 -1
  97. package/src/form/EntityFormBinding.tsx +7 -7
  98. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -3
  99. package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +16 -5
  100. package/src/form/field_bindings/ReferenceFieldBinding.tsx +3 -3
  101. package/src/form/field_bindings/RelationFieldBinding.tsx +17 -6
  102. package/src/form/field_bindings/RepeatFieldBinding.tsx +2 -2
  103. package/src/hooks/index.ts +1 -1
  104. package/src/hooks/navigation/useNavigationResolution.ts +10 -4
  105. package/src/hooks/{useCMSContext.tsx → useAdminContext.tsx} +10 -10
  106. package/src/hooks/useHistory.ts +7 -11
  107. package/src/hooks/useResolvedNavigationFrom.tsx +2 -2
  108. package/src/index.ts +4 -4
  109. package/src/preview/PropertyPreview.tsx +6 -6
  110. package/src/preview/components/RelationPreview.tsx +1 -1
  111. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
  112. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -2
  113. package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +2 -2
  114. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +2 -2
  115. package/src/types/components/EntityFormProps.tsx +2 -2
  116. package/src/types/fields.tsx +1 -1
  117. package/dist/CollectionEditorDialog-o7F_zbGY.js.map +0 -1
  118. package/dist/collection_editor/pgColumnToProperty.d.ts +0 -7
  119. package/dist/editor/extensions/Image/index.d.ts +0 -6
  120. package/dist/export-BvqhjdEb.js.map +0 -1
  121. package/dist/hooks/useCMSContext.d.ts +0 -29
  122. package/dist/util-BhhfHY1M.js.map +0 -1
  123. package/src/collection_editor/pgColumnToProperty.ts +0 -291
  124. package/src/editor/extensions/Image/index.ts +0 -133
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
2
- import { in as useUrlController } from "./util-BhhfHY1M.js";
3
- import { s as AIModifiedPathsProvider } from "./PropertyEditView-DgB2b22q.js";
4
- import { t as CollectionEditor } from "./CollectionEditorDialog-o7F_zbGY.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";
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-DaLRgPkj.js.map
192
+ //# sourceMappingURL=RouterCollectionsStudioView-BZLq4MGp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RouterCollectionsStudioView-DaLRgPkj.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-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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"}
@@ -4,7 +4,7 @@ import React from "react";
4
4
  * sets and CollectionEditorDialogs reads.
5
5
  *
6
6
  * This indirection exists because ConfigControllerProvider is a root-scope
7
- * plugin provider that wraps <RebaseShell>, therefore living ABOVE the CMS
7
+ * plugin provider that wraps <RebaseShell>, therefore living ABOVE the admin
8
8
  * context providers (CollectionRegistryContext, NavigationStateContext,
9
9
  * UrlContext). The actual dialog components need those contexts, so they
10
10
  * must render inside the shell tree.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Internal re-exports for the collection editor module.
3
3
  * This avoids circular imports by pointing directly at source files
4
- * rather than going through the CMS barrel (index.ts).
4
+ * rather than going through the admin barrel (index.ts).
5
5
  */
6
6
  export { useCollectionRegistryController } from "../hooks/navigation/contexts/CollectionRegistryContext";
7
7
  export { useNavigationStateController } from "../hooks/navigation/contexts/NavigationStateContext";
@@ -127,7 +127,7 @@ export interface CollectionEditorExtensionProps {
127
127
  * reading from React contexts.
128
128
  *
129
129
  * Use this when embedding the collection editor outside of the Rebase
130
- * CMS, where the Rebase context providers are not available.
130
+ * admin, where the Rebase context providers are not available.
131
131
  *
132
132
  * Default: false (standard Rebase mode).
133
133
  */
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * @module
12
12
  */
13
- import type { ArrayPropertyValidationSchema, EnumValues, FileType, FilterPreset, FilterValues, ImageResize, JoinStep, NumberPropertyValidationSchema, OnAction, PropertyConditions, PropertyValidationSchema, SecurityRule } from "@rebasepro/types";
13
+ import type { ArrayPropertyValidationSchema, EnumValues, FileType, FilterPreset, FilterValues, ImageResize, JoinStep, NumberPropertyValidationSchema, OnAction, PropertyConditions, PropertyValidationSchema, SecurityRule, WhereFilterOp } from "@rebasepro/types";
14
14
  import type { CollectionSize, KanbanConfig, NavigationGroupMapping, ViewMode } from "@rebasepro/admin-types";
15
15
  /**
16
16
  * JSON-serializable version of `AdminPropertyOptions`.
@@ -23,6 +23,8 @@ export interface SerializableAdminBaseOptions {
23
23
  disabled?: boolean | SerializablePropertyDisabledConfig;
24
24
  widthPercentage?: number;
25
25
  customProps?: unknown;
26
+ /** Narrows the filter operators offered for this property. Plain data. */
27
+ filterOperators?: readonly WhereFilterOp[];
26
28
  }
27
29
  /** JSON-serializable version of `PropertyDisabledConfig`. Already serializable. */
28
30
  export interface SerializablePropertyDisabledConfig {
@@ -80,7 +82,12 @@ export interface SerializableAdminStringOptions extends SerializableAdminBaseOpt
80
82
  markdown?: boolean;
81
83
  previewAsTag?: boolean;
82
84
  clearable?: boolean;
83
- url?: boolean | "image" | "video" | "audio" | "file";
85
+ /**
86
+ * How a string holding a URL is rendered. Was mirrored here as `url`, the
87
+ * name of the *core* flag that says the string is a URL at all — so this
88
+ * option had no serializable form and the core flag had two.
89
+ */
90
+ urlPreview?: "image" | "video" | "audio" | "file";
84
91
  }
85
92
  /** JSON-serializable version of `AdminNumberOptions`. */
86
93
  export interface SerializableAdminNumberOptions extends SerializableAdminBaseOptions {
@@ -97,22 +104,31 @@ export interface SerializableAdminVectorOptions extends SerializableAdminBaseOpt
97
104
  /** JSON-serializable version of `AdminReferenceOptions`. */
98
105
  export interface SerializableAdminReferenceOptions extends SerializableAdminBaseOptions {
99
106
  previewProperties?: string[];
107
+ fixedFilter?: FilterValues<string>;
108
+ includeId?: boolean;
109
+ includeEntityLink?: boolean;
100
110
  }
101
111
  /** JSON-serializable version of `AdminRelationOptions`. */
102
112
  export interface SerializableAdminRelationOptions extends SerializableAdminBaseOptions {
103
113
  previewProperties?: string[];
104
114
  widget?: "select" | "dialog";
115
+ fixedFilter?: FilterValues<string>;
116
+ includeId?: boolean;
117
+ includeEntityLink?: boolean;
105
118
  }
106
119
  /** JSON-serializable version of `AdminArrayOptions`. */
107
120
  export interface SerializableAdminArrayOptions extends SerializableAdminBaseOptions {
108
121
  expanded?: boolean;
109
122
  minimalistView?: boolean;
123
+ sortable?: boolean;
124
+ canAddElements?: boolean;
110
125
  }
111
126
  /** JSON-serializable version of `AdminMapOptions`. */
112
127
  export interface SerializableAdminMapOptions extends SerializableAdminBaseOptions {
113
128
  expanded?: boolean;
114
129
  minimalistView?: boolean;
115
130
  spreadChildren?: boolean;
131
+ previewProperties?: string[];
116
132
  }
117
133
  /**
118
134
  * JSON-serializable version of `BaseProperty`.
@@ -125,6 +141,12 @@ export interface SerializableBaseProperty {
125
141
  description?: string;
126
142
  propertyConfig?: string;
127
143
  columnName?: string;
144
+ /**
145
+ * Server-side guarantee that the column never reaches an API response — a
146
+ * password hash, a verification token. Absent from this mirror until now,
147
+ * so editing a collection in the panel silently unset it.
148
+ */
149
+ excludeFromApi?: boolean;
128
150
  validation?: PropertyValidationSchema;
129
151
  defaultValue?: unknown;
130
152
  /**
@@ -149,6 +171,8 @@ export interface SerializableStringProperty extends SerializableBaseProperty {
149
171
  storage?: SerializableStorageConfig;
150
172
  userSelect?: boolean;
151
173
  email?: boolean;
174
+ /** The string holds a URL. Feeds the generated OpenAPI contract. */
175
+ url?: boolean;
152
176
  reference?: SerializableReferenceProperty;
153
177
  }
154
178
  /** JSON-serializable `NumberProperty`. */
@@ -203,9 +227,6 @@ export interface SerializableReferenceProperty extends SerializableBaseProperty
203
227
  type: "reference";
204
228
  isId?: boolean;
205
229
  path?: string;
206
- fixedFilter?: FilterValues<string>;
207
- includeId?: boolean;
208
- includeEntityLink?: boolean;
209
230
  }
210
231
  /**
211
232
  * A `Relation` as JSON: the same tagged union, with `target` narrowed to a
@@ -271,10 +292,6 @@ export interface SerializableRelationProperty extends SerializableBaseProperty {
271
292
  * went on describing a shape nothing produced, and the cast hid it.
272
293
  */
273
294
  relation?: SerializableRelation;
274
- fixedFilter?: FilterValues<string>;
275
- includeId?: boolean;
276
- includeEntityLink?: boolean;
277
- widget?: "select" | "dialog";
278
295
  }
279
296
  /** JSON-serializable `ArrayProperty`. */
280
297
  export interface SerializableArrayProperty extends SerializableBaseProperty {
@@ -289,8 +306,6 @@ export interface SerializableArrayProperty extends SerializableBaseProperty {
289
306
  valueField?: string;
290
307
  };
291
308
  validation?: ArrayPropertyValidationSchema;
292
- sortable?: boolean;
293
- canAddElements?: boolean;
294
309
  }
295
310
  /** JSON-serializable `MapProperty`. */
296
311
  export interface SerializableMapProperty extends SerializableBaseProperty {
@@ -300,7 +315,6 @@ export interface SerializableMapProperty extends SerializableBaseProperty {
300
315
  properties?: SerializableProperties;
301
316
  propertiesOrder?: string[];
302
317
  validation?: PropertyValidationSchema;
303
- previewProperties?: string[];
304
318
  keyValue?: boolean;
305
319
  }
306
320
  /**
@@ -378,7 +392,21 @@ export interface SerializableCollectionConfig {
378
392
  disableDefaultActions?: ("edit" | "copy" | "delete")[];
379
393
  table?: string;
380
394
  schema?: string;
395
+ /**
396
+ * Collection-level relations, `target` as a slug — a thunk does not
397
+ * serialize, same as on a relation property.
398
+ *
399
+ * Absent from this mirror until now, and `buildCollectionFromTableMetadata`
400
+ * is the reason it matters: importing a table detects its foreign keys and
401
+ * junctions, puts them on the form, and the save then dropped every one of
402
+ * them. The import looked like it worked.
403
+ */
404
+ relations?: SerializableRelation[];
381
405
  securityRules?: readonly SecurityRule[];
406
+ /** Removes the framework's injected baseline RLS policies. */
407
+ disableDefaultPolicies?: boolean;
408
+ /** Whether a write naming an undeclared field is rejected with a 400. */
409
+ strictWrites?: boolean;
382
410
  ownerId?: string;
383
411
  /**
384
412
  * Arbitrary key-value metadata for external consumers.
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @module
9
9
  */
10
- import type { Properties, Property } from "@rebasepro/types";
10
+ import { type Properties, type Property } from "@rebasepro/types";
11
11
  import type { AdminCollection } from "@rebasepro/admin-types";
12
12
  import type { SerializableCollectionConfig, SerializableProperties, SerializableProperty } from "./serializable_types";
13
13
  /**
@@ -1,13 +1,3 @@
1
1
  import React from "react";
2
- /** Postgres RLS policy shape defined inline to avoid depending on @rebasepro/studio */
3
- export interface PostgresPolicy {
4
- policyname: string;
5
- tablename: string;
6
- permissive: "PERMISSIVE" | "RESTRICTIVE";
7
- roles: string[];
8
- cmd: "SELECT" | "INSERT" | "UPDATE" | "DELETE" | "ALL";
9
- qual: string | null;
10
- with_check: string | null;
11
- status?: "live" | "code_only" | "both";
12
- }
2
+ export type { PostgresPolicy } from "@rebasepro/types";
13
3
  export declare function CollectionRLSTab(): React.JSX.Element;
@@ -1,7 +1,7 @@
1
- import { in as useUrlController } from "./util-BhhfHY1M.js";
2
- import { n as PropertyForm, r as PropertyFormDialog } from "./PropertyEditView-DgB2b22q.js";
3
- import { n as CollectionEditorDialog } from "./CollectionEditorDialog-o7F_zbGY.js";
4
- import { i as CollectionStudioView, r as CollectionsStudioView, t as RouterCollectionsStudioView } from "./RouterCollectionsStudioView-DaLRgPkj.js";
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";
5
5
  import "react";
6
6
  import { useSnackbarController } from "@rebasepro/app";
7
7
  import { jsx } from "react/jsx-runtime";
@@ -1,12 +1,12 @@
1
1
  import React from "react";
2
2
  /**
3
3
  * Renders the CollectionEditorDialog and PropertyFormDialog inside
4
- * the RebaseShell tree where CMS-internal contexts
4
+ * the RebaseShell tree where admin-internal contexts
5
5
  * (CollectionRegistryContext, NavigationStateContext, UrlContext)
6
6
  * are available.
7
7
  *
8
8
  * Both dialog components are loaded dynamically via React.lazy so that
9
- * production CMS bundles that never open the collection editor don't pay
9
+ * production admin bundles that never open the collection editor don't pay
10
10
  * the download/parse cost up-front.
11
11
  *
12
12
  * The dialog state (open/close, props) is managed by ConfigControllerProvider
@@ -7,10 +7,16 @@ export interface PropertiesToColumnsParams<M extends Record<string, unknown>> {
7
7
  properties: Properties;
8
8
  sortable?: boolean;
9
9
  fixedFilter?: FilterValues<keyof M extends string ? keyof M : never>;
10
+ /**
11
+ * The engine backing the collection (`collection.engine`). Decides which
12
+ * relation kinds can be filtered — see `isFilterableRelation`. Omitted
13
+ * falls back to the default engine's capabilities.
14
+ */
15
+ engine?: string;
10
16
  AdditionalHeaderWidget?: React.ComponentType<{
11
17
  property: Property;
12
18
  propertyKey: string;
13
19
  onHover: boolean;
14
20
  }>;
15
21
  }
16
- export declare function propertiesToColumns<M extends Record<string, unknown>>({ properties, sortable, fixedFilter, AdditionalHeaderWidget }: PropertiesToColumnsParams<M>): VirtualTableColumn[];
22
+ export declare function propertiesToColumns<M extends Record<string, unknown>>({ properties, sortable, fixedFilter, engine, AdditionalHeaderWidget }: PropertiesToColumnsParams<M>): VirtualTableColumn[];
@@ -41,8 +41,8 @@ export type CollectionListViewBindingProps<M extends Record<string, unknown> = R
41
41
  openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
42
42
  };
43
43
  /**
44
- * Classic CMS list view for displaying entities.
45
- * Designed to be the most familiar, stereotypical CMS content management view:
44
+ * Classic admin list view for displaying entities.
45
+ * Designed to be the most familiar, stereotypical admin content management view:
46
46
  * - Clean rows with checkbox, icon/avatar, title, metadata, and actions
47
47
  * - Column-sortable headers
48
48
  * - Infinite scroll
@@ -1,8 +1,8 @@
1
1
  import React from "react";
2
2
  /**
3
- * Default drawer used in the CMS.
3
+ * Default drawer used in the admin.
4
4
  *
5
- * When no `children` are provided, renders the full CMS navigation
5
+ * When no `children` are provided, renders the full admin navigation
6
6
  * (collection groups, mode switch, plugin slots).
7
7
  *
8
8
  * When `children` **are** provided, renders the shared drawer shell
@@ -19,7 +19,7 @@ export declare function DefaultDrawer({ title, logo, logoDestination, children,
19
19
  logoDestination?: string;
20
20
  /**
21
21
  * Custom navigation content for the drawer.
22
- * When provided, replaces the default CMS navigation (collection groups,
22
+ * When provided, replaces the default admin navigation (collection groups,
23
23
  * mode switch, slots). The shared shell (logo, scroll area, footer
24
24
  * actions, collapse toggle) is still rendered around it.
25
25
  */
@@ -41,9 +41,16 @@ export interface DrawerNavigationGroupProps {
41
41
  * Hide the group header (title and expandable panel)
42
42
  */
43
43
  hideHeader?: boolean;
44
+ /**
45
+ * Lucide icon name for the group header, from `NavigationGroupMapping.icon`.
46
+ *
47
+ * Supplying one moves the visual anchor from the rows to the group: the header
48
+ * gets the icon, and the entries below trade theirs for an indent.
49
+ */
50
+ icon?: string;
44
51
  }
45
52
  /**
46
53
  * Shared drawer navigation group component used by both DefaultDrawer and RebaseCloudDrawer.
47
54
  * Renders a collapsible group with header and navigation items.
48
55
  */
49
- export declare function DrawerNavigationGroup({ group, entries, collapsed, onToggleCollapsed, drawerOpen, tooltipsOpen, adminMenuOpen, headerActions, onItemClick, hideHeader }: DrawerNavigationGroupProps): React.JSX.Element;
56
+ export declare function DrawerNavigationGroup({ group, entries, collapsed, onToggleCollapsed, drawerOpen, tooltipsOpen, adminMenuOpen, headerActions, onItemClick, hideHeader, icon }: DrawerNavigationGroupProps): React.JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- export declare function DrawerNavigationItem({ name, icon, drawerOpen, adminMenuOpen, tooltipsOpen, url, onClick }: {
2
+ export declare function DrawerNavigationItem({ name, icon, drawerOpen, adminMenuOpen, tooltipsOpen, url, onClick, indented }: {
3
3
  icon: React.ReactElement;
4
4
  name: string;
5
5
  tooltipsOpen: boolean;
@@ -7,4 +7,10 @@ export declare function DrawerNavigationItem({ name, icon, drawerOpen, adminMenu
7
7
  adminMenuOpen?: boolean;
8
8
  url: string;
9
9
  onClick?: () => void;
10
+ /**
11
+ * Render as a child of an icon-bearing group: no icon of its own, indented to
12
+ * sit under the group's label. Set by {@link DrawerNavigationGroup} only when
13
+ * the group actually has an icon and the drawer is expanded.
14
+ */
15
+ indented?: boolean;
10
16
  }): React.JSX.Element;
@@ -19,7 +19,7 @@ export type OnTabChangeParams<M extends Record<string, unknown>> = {
19
19
  };
20
20
  export interface EditViewBindingProps<M extends Record<string, unknown> = Record<string, unknown>> {
21
21
  /**
22
- * The CMS path of the entity, e.g. "users" or "products".
22
+ * The admin path of the entity, e.g. "users" or "products".
23
23
  */
24
24
  path: string;
25
25
  collection: AdminCollection<M>;
@@ -1,6 +1,6 @@
1
1
  import type { RebaseAdminConfig } from "@rebasepro/admin-types";
2
2
  /**
3
- * Declarative component to configure the CMS in Rebase.
3
+ * Declarative component to configure the admin in Rebase.
4
4
  * Renders nothing — purely registers config into the RebaseRegistry.
5
5
  *
6
6
  * When `collectionEditor` is provided, the built-in visual schema editor
@@ -7,7 +7,7 @@ import React from "react";
7
7
  * - Renders `children` when a user is authenticated.
8
8
  *
9
9
  * **Independently usable**: Use this alone when you want auth gating
10
- * without the full CMS layout or navigation.
10
+ * without the full admin layout or navigation.
11
11
  *
12
12
  * @example
13
13
  * ```tsx
@@ -7,8 +7,12 @@ export interface RebaseLayoutProps {
7
7
  appBar?: React.ReactNode;
8
8
  /** Custom Drawer to override the default. */
9
9
  drawer?: React.ReactNode;
10
- /** Whether to auto-open the drawer on load. */
10
+ /** Expand the drawer while the pointer is over it. Not a load-time default — that is `defaultDrawerOpen`. */
11
11
  autoOpenDrawer?: boolean;
12
+ /** Start with the drawer expanded instead of collapsed to icons. Ignored on small layouts. */
13
+ defaultDrawerOpen?: boolean;
14
+ /** Path to the logo shown in the drawer and top bar. Falls back to the Rebase mark. */
15
+ logo?: string;
12
16
  /** Dev views passed to AdminModeSyncer (resolved from RebaseNavigation). */
13
17
  devViews?: AppView[];
14
18
  }
@@ -3,13 +3,13 @@ export interface RebaseNavigationProps {
3
3
  children: React.ReactNode;
4
4
  }
5
5
  /**
6
- * Navigation layer — builds and provides all CMS navigation controllers:
6
+ * Navigation layer — builds and provides all admin navigation controllers:
7
7
  * collection registry, URL controller, navigation state, side entity,
8
8
  * and the self-assembling Studio bridge.
9
9
  *
10
10
  * Also handles the collection editor config controller when enabled.
11
11
  *
12
- * **Independently usable**: Use this when you need CMS navigation
12
+ * **Independently usable**: Use this when you need admin navigation
13
13
  * (entity tables, side panels) in a custom layout.
14
14
  *
15
15
  * @example
@@ -10,7 +10,7 @@ export interface RebaseRouteDefsProps {
10
10
  layout?: React.ReactElement;
11
11
  }
12
12
  /**
13
- * Route definitions for the CMS.
13
+ * Route definitions for the admin.
14
14
  *
15
15
  * Defines all standard routes: home, studio home, collection view,
16
16
  * settings, debug, custom views, and a catch-all 404.
@@ -4,11 +4,16 @@ 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
8
  autoOpenDrawer?: boolean;
9
+ /** Start with the drawer expanded instead of collapsed to icons. Ignored on small layouts. */
10
+ defaultDrawerOpen?: boolean;
11
+ /** Path to the logo shown in the drawer and top bar. Falls back to the Rebase mark. */
12
+ logo?: string;
8
13
  children?: React.ReactNode;
9
14
  }
10
15
  /**
11
- * Convenience component that composes all four CMS layers:
16
+ * Convenience component that composes all four admin layers:
12
17
  *
13
18
  * ```
14
19
  * <RebaseAuthGate>
@@ -22,6 +22,20 @@ export interface RelationSelectorProps {
22
22
  padding?: boolean;
23
23
  invisible?: boolean;
24
24
  relation: Relation;
25
+ /**
26
+ * Whether the selector picks many rows or one.
27
+ *
28
+ * Defaults to what the relation implies — a `hasMany` or `manyToMany`
29
+ * picks many — which is right wherever the selector *edits* the relation,
30
+ * because there the cardinality is the shape of the stored value.
31
+ *
32
+ * A filter is the case where it is not. `tags == <one tag>` asks a single
33
+ * question about a to-many link, and the operator is what decides how many
34
+ * values it takes. Deriving multiplicity from the relation there meant a
35
+ * to-many relation could only ever emit a list, so the filter field had to
36
+ * hide every single-value operator to avoid handing `==` an array.
37
+ */
38
+ multiple?: boolean;
25
39
  fixedFilter?: FilterValues<string>;
26
40
  pageSize?: number;
27
41
  emptyPlaceholder?: string;
@@ -15,5 +15,32 @@ interface RelationFilterFieldProps {
15
15
  */
16
16
  operators?: readonly VirtualTableWhereFilterOp[];
17
17
  }
18
+ declare const operationLabels: {
19
+ "==": string;
20
+ "!=": string;
21
+ ">": string;
22
+ "<": string;
23
+ ">=": string;
24
+ "<=": string;
25
+ in: string;
26
+ "not-in": string;
27
+ "array-contains": string;
28
+ "array-contains-any": string;
29
+ "is-null": string;
30
+ "is-not-null": string;
31
+ };
32
+ /**
33
+ * The operators this field will actually put on screen.
34
+ *
35
+ * Exported because the failure mode is silence: when the intersection of what
36
+ * the field can render and what the engine can run comes out empty, the field
37
+ * returns null and the filter control the table header offered opens onto
38
+ * nothing. That is a rendering outcome no unit test of either side alone
39
+ * catches, so the intersection itself is the thing worth pinning.
40
+ */
41
+ export declare function renderableRelationOperators(relation: {
42
+ kind?: string;
43
+ cardinality?: string;
44
+ } | undefined, operators?: readonly VirtualTableWhereFilterOp[]): (keyof typeof operationLabels)[];
18
45
  export declare function RelationFilterField({ value, setValue, relation, name: _name, hidden: _hidden, setHidden: _setHidden, operators }: RelationFilterFieldProps): React.JSX.Element | null;
19
46
  export {};
@@ -0,0 +1,46 @@
1
+ import { WhereFilterOp } from "@rebasepro/types";
2
+ /**
3
+ * The "filter for null values" checkbox, expressed portably.
4
+ *
5
+ * The checkbox used to emit `[whichever operator is selected, null]` and leave
6
+ * the driver to work out that a null value means a null check. That reads as
7
+ * an engine-neutral convention and is not one:
8
+ *
9
+ * - Postgres took `["==", null]` as `IS NULL`, but dropped `["in", null]`
10
+ * entirely — a dropped condition widens, so ticking the box returned every
11
+ * row instead of the ones with no value.
12
+ * - MongoDB maps `in`/`not-in` straight onto `$in`/`$nin`, and Mongo rejects
13
+ * those with a non-array: `$in needs an array`. The read fails outright.
14
+ * - A Firestore driver is supplied by the developer, and the Firestore SDK
15
+ * likewise refuses `in` without a non-empty array.
16
+ *
17
+ * Only `==`/`!=` ever worked, and only because two engines independently chose
18
+ * to special-case a null operand. Nothing in the filter contract promises that,
19
+ * and a filter field is shared by every engine the admin can talk to.
20
+ *
21
+ * `is-null`/`is-not-null` are the operators that mean this, they are in
22
+ * {@link ALL_WHERE_FILTER_OPS}, and every driver already handles them
23
+ * explicitly rather than by inference. So the checkbox emits those, and the
24
+ * question of what a null operand means to a given engine stops being asked.
25
+ */
26
+ export declare const NULL_FILTER_OPERATORS: readonly WhereFilterOp[];
27
+ /** Whether `op` asks about absence rather than about a value. */
28
+ export declare function isNullFilterOperator(op: WhereFilterOp | undefined): boolean;
29
+ /**
30
+ * The null check carrying the same sense as `op`.
31
+ *
32
+ * The checkbox is a toggle, not an operator, so the operator it is ticked
33
+ * alongside is what says which way round it means: `==`/`in` are asking for
34
+ * a match, so their null form is "has nothing"; `!=`/`not-in` are asking for
35
+ * the absence of one, so theirs is "has something".
36
+ */
37
+ export declare function nullFilterOperatorFor(op: WhereFilterOp | undefined): WhereFilterOp;
38
+ /**
39
+ * The value operator to return to when the checkbox is unticked.
40
+ *
41
+ * `is-null` could have been reached from `==` or from `in` — the field cannot
42
+ * know which, and it does not matter. What matters is landing on an operator
43
+ * the dropdown actually offers, in the same sense, so the control does not
44
+ * come back showing something it cannot render.
45
+ */
46
+ export declare function valueOperatorFor(op: WhereFilterOp | undefined, offered: readonly WhereFilterOp[]): WhereFilterOp | undefined;