@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.
- package/README.md +1 -1
- package/dist/{CollectionEditorDialog-o7F_zbGY.js → CollectionEditorDialog-Dpc75wIZ.js} +56 -260
- package/dist/CollectionEditorDialog-Dpc75wIZ.js.map +1 -0
- package/dist/{PropertyEditView-DgB2b22q.js → PropertyEditView-DH3XZC2x.js} +2 -2
- package/dist/{PropertyEditView-DgB2b22q.js.map → PropertyEditView-DH3XZC2x.js.map} +1 -1
- package/dist/{RouterCollectionsStudioView-DaLRgPkj.js → RouterCollectionsStudioView-BZLq4MGp.js} +4 -4
- package/dist/{RouterCollectionsStudioView-DaLRgPkj.js.map → RouterCollectionsStudioView-BZLq4MGp.js.map} +1 -1
- package/dist/collection_editor/CollectionEditorDialogsContext.d.ts +1 -1
- package/dist/collection_editor/_cms_internals.d.ts +1 -1
- package/dist/collection_editor/extensibility_types.d.ts +1 -1
- package/dist/collection_editor/serializable_types.d.ts +40 -12
- package/dist/collection_editor/serializable_utils.d.ts +1 -1
- package/dist/collection_editor/ui/collection_editor/CollectionRLSTab.d.ts +1 -11
- package/dist/collection_editor_ui.js +4 -4
- package/dist/components/CollectionEditorDialogs.d.ts +2 -2
- package/dist/components/CollectionTableBinding/column_utils.d.ts +7 -1
- package/dist/components/CollectionViewBinding/CollectionListViewBinding.d.ts +2 -2
- package/dist/components/DefaultDrawer.d.ts +3 -3
- package/dist/components/DrawerNavigationGroup.d.ts +8 -1
- package/dist/components/DrawerNavigationItem.d.ts +7 -1
- package/dist/components/EditViewBinding.d.ts +1 -1
- package/dist/components/RebaseAdmin.d.ts +1 -1
- package/dist/components/RebaseAuthGate.d.ts +1 -1
- package/dist/components/RebaseLayout.d.ts +5 -1
- package/dist/components/RebaseNavigation.d.ts +2 -2
- package/dist/components/RebaseRouteDefs.d.ts +1 -1
- package/dist/components/RebaseShell.d.ts +6 -1
- package/dist/components/RelationSelector.d.ts +14 -0
- package/dist/components/SelectableTable/filters/RelationFilterField.d.ts +27 -0
- package/dist/components/SelectableTable/filters/null_filter.d.ts +46 -0
- package/dist/components/SidePanelProvider.d.ts +2 -2
- package/dist/components/app/Scaffold.d.ts +11 -0
- package/dist/components/field_configs.d.ts +1 -1
- package/dist/{export-BvqhjdEb.js → export-BzIpUdSK.js} +3 -3
- package/dist/export-BzIpUdSK.js.map +1 -0
- package/dist/form/EntityForm.d.ts +2 -2
- package/dist/form/EntityFormBinding.d.ts +5 -5
- package/dist/{history-Dz20hH38.js → history-g9688IaK.js} +2 -2
- package/dist/{history-Dz20hH38.js.map → history-g9688IaK.js.map} +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/useAdminContext.d.ts +29 -0
- package/dist/hooks/useHistory.d.ts +7 -11
- package/dist/{import-DINSB-Vu.js → import-BjUG-cIY.js} +2 -2
- package/dist/{import-DINSB-Vu.js.map → import-BjUG-cIY.js.map} +1 -1
- package/dist/index.js +61 -38
- package/dist/index.js.map +1 -1
- package/dist/types/components/EntityFormProps.d.ts +1 -1
- package/dist/types/fields.d.ts +1 -1
- package/dist/{util-BhhfHY1M.js → util-B5fJm1FA.js} +296 -169
- package/dist/util-B5fJm1FA.js.map +1 -0
- package/package.json +9 -9
- package/src/collection_editor/CollectionEditorDialogsContext.tsx +1 -1
- package/src/collection_editor/ConfigControllerProvider.tsx +1 -1
- package/src/collection_editor/_cms_internals.ts +1 -1
- package/src/collection_editor/extensibility_types.ts +1 -1
- package/src/collection_editor/index.ts +2 -2
- package/src/collection_editor/serializable_types.ts +44 -13
- package/src/collection_editor/serializable_utils.ts +68 -59
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +66 -41
- package/src/collection_editor/ui/collection_editor/EntityActionsEditTab.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/SubcollectionsEditTab.tsx +2 -2
- package/src/components/CollectionEditorDialogs.tsx +2 -2
- package/src/components/CollectionTableBinding/CollectionRowActions.tsx +5 -4
- package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +8 -2
- package/src/components/CollectionTableBinding/column_utils.tsx +19 -5
- package/src/components/CollectionTableBinding/table_bindings.tsx +10 -10
- package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +8 -7
- package/src/components/CollectionViewBinding/CollectionViewActions.tsx +2 -2
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +2 -2
- package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +2 -2
- package/src/components/DefaultDrawer.tsx +19 -8
- package/src/components/DeleteEntityDialog.tsx +2 -2
- package/src/components/DetailViewBinding.tsx +21 -1
- package/src/components/DrawerNavigationGroup.tsx +47 -4
- package/src/components/DrawerNavigationItem.tsx +19 -5
- package/src/components/EditFormActions.tsx +4 -3
- package/src/components/EditViewBinding.tsx +1 -1
- package/src/components/HomePage/ContentHomePage.tsx +4 -4
- package/src/components/RebaseAdmin.tsx +3 -3
- package/src/components/RebaseAuthGate.tsx +1 -1
- package/src/components/RebaseLayout.tsx +8 -2
- package/src/components/RebaseNavigation.tsx +9 -9
- package/src/components/RebaseRouteDefs.tsx +2 -2
- package/src/components/RebaseShell.tsx +10 -1
- package/src/components/RelationSelector.tsx +18 -3
- package/src/components/SelectableTable/filters/FilterFieldBinding.tsx +1 -1
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +36 -9
- package/src/components/SelectableTable/filters/RelationFilterField.tsx +93 -17
- package/src/components/SelectableTable/filters/null_filter.ts +63 -0
- package/src/components/SidePanelProvider.tsx +2 -2
- package/src/components/app/Scaffold.tsx +16 -1
- package/src/data_export/export/ExportCollectionAction.tsx +2 -2
- package/src/form/EntityForm.tsx +2 -2
- package/src/form/EntityFormActions.tsx +2 -1
- package/src/form/EntityFormBinding.tsx +7 -7
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +3 -3
- package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +16 -5
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +3 -3
- package/src/form/field_bindings/RelationFieldBinding.tsx +17 -6
- package/src/form/field_bindings/RepeatFieldBinding.tsx +2 -2
- package/src/hooks/index.ts +1 -1
- package/src/hooks/navigation/useNavigationResolution.ts +10 -4
- package/src/hooks/{useCMSContext.tsx → useAdminContext.tsx} +10 -10
- package/src/hooks/useHistory.ts +7 -11
- package/src/hooks/useResolvedNavigationFrom.tsx +2 -2
- package/src/index.ts +4 -4
- package/src/preview/PropertyPreview.tsx +6 -6
- package/src/preview/components/RelationPreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +1 -1
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +2 -2
- package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +2 -2
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +2 -2
- package/src/types/components/EntityFormProps.tsx +2 -2
- package/src/types/fields.tsx +1 -1
- package/dist/CollectionEditorDialog-o7F_zbGY.js.map +0 -1
- package/dist/collection_editor/pgColumnToProperty.d.ts +0 -7
- package/dist/editor/extensions/Image/index.d.ts +0 -6
- package/dist/export-BvqhjdEb.js.map +0 -1
- package/dist/hooks/useCMSContext.d.ts +0 -29
- package/dist/util-BhhfHY1M.js.map +0 -1
- package/src/collection_editor/pgColumnToProperty.ts +0 -291
- package/src/editor/extensions/Image/index.ts +0 -133
package/dist/{RouterCollectionsStudioView-DaLRgPkj.js → RouterCollectionsStudioView-BZLq4MGp.js}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
-
import { in as useUrlController } from "./util-
|
|
3
|
-
import { s as AIModifiedPathsProvider } from "./PropertyEditView-
|
|
4
|
-
import { t as CollectionEditor } from "./CollectionEditorDialog-
|
|
2
|
+
import { in as useUrlController } from "./util-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-
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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-
|
|
2
|
-
import { n as PropertyForm, r as PropertyFormDialog } from "./PropertyEditView-
|
|
3
|
-
import { n as CollectionEditorDialog } from "./CollectionEditorDialog-
|
|
4
|
-
import { i as CollectionStudioView, r as CollectionsStudioView, t as RouterCollectionsStudioView } from "./RouterCollectionsStudioView-
|
|
1
|
+
import { in as useUrlController } from "./util-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
|
|
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
|
|
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
|
|
45
|
-
* Designed to be the most familiar, stereotypical
|
|
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
|
|
3
|
+
* Default drawer used in the admin.
|
|
4
4
|
*
|
|
5
|
-
* When no `children` are provided, renders the full
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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;
|