@pilotiq/pilotiq 0.7.2 → 0.8.1
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/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +208 -0
- package/CLAUDE.md +59 -3
- package/dist/Pilotiq.d.ts +83 -0
- package/dist/Pilotiq.d.ts.map +1 -1
- package/dist/Pilotiq.js +39 -0
- package/dist/Pilotiq.js.map +1 -1
- package/dist/actions/Action.d.ts +27 -99
- package/dist/actions/Action.d.ts.map +1 -1
- package/dist/actions/Action.js +52 -754
- package/dist/actions/Action.js.map +1 -1
- package/dist/actions/bulkFactories.d.ts +46 -0
- package/dist/actions/bulkFactories.d.ts.map +1 -0
- package/dist/actions/bulkFactories.js +144 -0
- package/dist/actions/bulkFactories.js.map +1 -0
- package/dist/actions/crudFactories.d.ts +94 -0
- package/dist/actions/crudFactories.d.ts.map +1 -0
- package/dist/actions/crudFactories.js +209 -0
- package/dist/actions/crudFactories.js.map +1 -0
- package/dist/actions/factoryHelpers.d.ts +108 -0
- package/dist/actions/factoryHelpers.d.ts.map +1 -0
- package/dist/actions/factoryHelpers.js +138 -0
- package/dist/actions/factoryHelpers.js.map +1 -0
- package/dist/actions/m2mFactories.d.ts +47 -0
- package/dist/actions/m2mFactories.d.ts.map +1 -0
- package/dist/actions/m2mFactories.js +173 -0
- package/dist/actions/m2mFactories.js.map +1 -0
- package/dist/actions/relationFactories.d.ts +93 -0
- package/dist/actions/relationFactories.d.ts.map +1 -0
- package/dist/actions/relationFactories.js +321 -0
- package/dist/actions/relationFactories.js.map +1 -0
- package/dist/elements/dispatchForm.js +1 -1
- package/dist/elements/dispatchForm.js.map +1 -1
- package/dist/elements/dispatchTable.js +1 -1
- package/dist/elements/dispatchTable.js.map +1 -1
- package/dist/fields/Field.d.ts +31 -0
- package/dist/fields/Field.d.ts.map +1 -1
- package/dist/fields/Field.js +25 -0
- package/dist/fields/Field.js.map +1 -1
- package/dist/pageData/breadcrumbs.d.ts +42 -0
- package/dist/pageData/breadcrumbs.d.ts.map +1 -0
- package/dist/pageData/breadcrumbs.js +172 -0
- package/dist/pageData/breadcrumbs.js.map +1 -0
- package/dist/pageData/forms.d.ts +137 -0
- package/dist/pageData/forms.d.ts.map +1 -0
- package/dist/pageData/forms.js +427 -0
- package/dist/pageData/forms.js.map +1 -0
- package/dist/pageData/helpers.d.ts +239 -0
- package/dist/pageData/helpers.d.ts.map +1 -0
- package/dist/pageData/helpers.js +703 -0
- package/dist/pageData/helpers.js.map +1 -0
- package/dist/pageData/misc.d.ts +76 -0
- package/dist/pageData/misc.d.ts.map +1 -0
- package/dist/pageData/misc.js +263 -0
- package/dist/pageData/misc.js.map +1 -0
- package/dist/pageData/navigation.d.ts +292 -0
- package/dist/pageData/navigation.d.ts.map +1 -0
- package/dist/pageData/navigation.js +591 -0
- package/dist/pageData/navigation.js.map +1 -0
- package/dist/pageData/relationPages.d.ts +172 -0
- package/dist/pageData/relationPages.d.ts.map +1 -0
- package/dist/pageData/relationPages.js +867 -0
- package/dist/pageData/relationPages.js.map +1 -0
- package/dist/pageData/relationTabs.d.ts +65 -0
- package/dist/pageData/relationTabs.d.ts.map +1 -0
- package/dist/pageData/relationTabs.js +258 -0
- package/dist/pageData/relationTabs.js.map +1 -0
- package/dist/pageData/resourcePages.d.ts +48 -0
- package/dist/pageData/resourcePages.d.ts.map +1 -0
- package/dist/pageData/resourcePages.js +504 -0
- package/dist/pageData/resourcePages.js.map +1 -0
- package/dist/pageData.d.ts +12 -792
- package/dist/pageData.d.ts.map +1 -1
- package/dist/pageData.js +24 -3797
- package/dist/pageData.js.map +1 -1
- package/dist/react/AppShell.d.ts +8 -0
- package/dist/react/AppShell.d.ts.map +1 -1
- package/dist/react/AppShell.js +11 -1
- package/dist/react/AppShell.js.map +1 -1
- package/dist/react/CollabExtensionFactoryRegistry.d.ts +47 -0
- package/dist/react/CollabExtensionFactoryRegistry.d.ts.map +1 -0
- package/dist/react/CollabExtensionFactoryRegistry.js +14 -0
- package/dist/react/CollabExtensionFactoryRegistry.js.map +1 -0
- package/dist/react/CollabRoomContext.d.ts +37 -0
- package/dist/react/CollabRoomContext.d.ts.map +1 -0
- package/dist/react/CollabRoomContext.js +12 -0
- package/dist/react/CollabRoomContext.js.map +1 -0
- package/dist/react/FormCollabBindingRegistry.d.ts +62 -0
- package/dist/react/FormCollabBindingRegistry.d.ts.map +1 -0
- package/dist/react/FormCollabBindingRegistry.js +14 -0
- package/dist/react/FormCollabBindingRegistry.js.map +1 -0
- package/dist/react/FormStateContext.d.ts.map +1 -1
- package/dist/react/FormStateContext.js +87 -0
- package/dist/react/FormStateContext.js.map +1 -1
- package/dist/react/RecordWrapperGate.d.ts +25 -0
- package/dist/react/RecordWrapperGate.d.ts.map +1 -0
- package/dist/react/RecordWrapperGate.js +30 -0
- package/dist/react/RecordWrapperGate.js.map +1 -0
- package/dist/react/RecordWrapperRegistry.d.ts +31 -0
- package/dist/react/RecordWrapperRegistry.d.ts.map +1 -0
- package/dist/react/RecordWrapperRegistry.js +15 -0
- package/dist/react/RecordWrapperRegistry.js.map +1 -0
- package/dist/react/SchemaRenderer.d.ts +17 -23
- package/dist/react/SchemaRenderer.d.ts.map +1 -1
- package/dist/react/SchemaRenderer.js +71 -3647
- package/dist/react/SchemaRenderer.js.map +1 -1
- package/dist/react/component-slots.d.ts +103 -0
- package/dist/react/component-slots.d.ts.map +1 -0
- package/dist/react/component-slots.js +18 -0
- package/dist/react/component-slots.js.map +1 -0
- package/dist/react/fields/BuilderInput.d.ts.map +1 -1
- package/dist/react/fields/BuilderInput.js +21 -117
- package/dist/react/fields/BuilderInput.js.map +1 -1
- package/dist/react/fields/MarkdownInput.d.ts.map +1 -1
- package/dist/react/fields/MarkdownInput.js +1 -3
- package/dist/react/fields/MarkdownInput.js.map +1 -1
- package/dist/react/fields/RepeaterInput.d.ts.map +1 -1
- package/dist/react/fields/RepeaterInput.js +22 -127
- package/dist/react/fields/RepeaterInput.js.map +1 -1
- package/dist/react/fields/rowState.d.ts +40 -0
- package/dist/react/fields/rowState.d.ts.map +1 -0
- package/dist/react/fields/rowState.js +60 -0
- package/dist/react/fields/rowState.js.map +1 -0
- package/dist/react/fields/useRowReorderDnd.d.ts +28 -0
- package/dist/react/fields/useRowReorderDnd.d.ts.map +1 -0
- package/dist/react/fields/useRowReorderDnd.js +51 -0
- package/dist/react/fields/useRowReorderDnd.js.map +1 -0
- package/dist/react/index.d.ts +9 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +8 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/layouts/SidebarLayout.d.ts +1 -1
- package/dist/react/layouts/SidebarLayout.d.ts.map +1 -1
- package/dist/react/layouts/SidebarLayout.js +10 -2
- package/dist/react/layouts/SidebarLayout.js.map +1 -1
- package/dist/react/layouts/TopbarLayout.d.ts +1 -1
- package/dist/react/layouts/TopbarLayout.d.ts.map +1 -1
- package/dist/react/layouts/TopbarLayout.js +19 -11
- package/dist/react/layouts/TopbarLayout.js.map +1 -1
- package/dist/react/parseRecordEditUrl.d.ts +29 -0
- package/dist/react/parseRecordEditUrl.d.ts.map +1 -0
- package/dist/react/parseRecordEditUrl.js +25 -0
- package/dist/react/parseRecordEditUrl.js.map +1 -0
- package/dist/react/persistedState.d.ts +19 -0
- package/dist/react/persistedState.d.ts.map +1 -0
- package/dist/react/persistedState.js +51 -0
- package/dist/react/persistedState.js.map +1 -0
- package/dist/react/schemaRenderer/AlertRenderer.d.ts +12 -0
- package/dist/react/schemaRenderer/AlertRenderer.d.ts.map +1 -0
- package/dist/react/schemaRenderer/AlertRenderer.js +61 -0
- package/dist/react/schemaRenderer/AlertRenderer.js.map +1 -0
- package/dist/react/schemaRenderer/EntryRenderer.d.ts +13 -0
- package/dist/react/schemaRenderer/EntryRenderer.d.ts.map +1 -0
- package/dist/react/schemaRenderer/EntryRenderer.js +277 -0
- package/dist/react/schemaRenderer/EntryRenderer.js.map +1 -0
- package/dist/react/schemaRenderer/SectionRenderer.d.ts +16 -0
- package/dist/react/schemaRenderer/SectionRenderer.d.ts.map +1 -0
- package/dist/react/schemaRenderer/SectionRenderer.js +62 -0
- package/dist/react/schemaRenderer/SectionRenderer.js.map +1 -0
- package/dist/react/schemaRenderer/SimpleElements.d.ts +25 -0
- package/dist/react/schemaRenderer/SimpleElements.d.ts.map +1 -0
- package/dist/react/schemaRenderer/SimpleElements.js +147 -0
- package/dist/react/schemaRenderer/SimpleElements.js.map +1 -0
- package/dist/react/schemaRenderer/TabsRenderer.d.ts +17 -0
- package/dist/react/schemaRenderer/TabsRenderer.d.ts.map +1 -0
- package/dist/react/schemaRenderer/TabsRenderer.js +31 -0
- package/dist/react/schemaRenderer/TabsRenderer.js.map +1 -0
- package/dist/react/schemaRenderer/WizardRenderer.d.ts +34 -0
- package/dist/react/schemaRenderer/WizardRenderer.d.ts.map +1 -0
- package/dist/react/schemaRenderer/WizardRenderer.js +208 -0
- package/dist/react/schemaRenderer/WizardRenderer.js.map +1 -0
- package/dist/react/schemaRenderer/action/ActionGroupTrigger.d.ts +21 -0
- package/dist/react/schemaRenderer/action/ActionGroupTrigger.d.ts.map +1 -0
- package/dist/react/schemaRenderer/action/ActionGroupTrigger.js +82 -0
- package/dist/react/schemaRenderer/action/ActionGroupTrigger.js.map +1 -0
- package/dist/react/schemaRenderer/action/ActionModalDialog.d.ts +30 -0
- package/dist/react/schemaRenderer/action/ActionModalDialog.d.ts.map +1 -0
- package/dist/react/schemaRenderer/action/ActionModalDialog.js +182 -0
- package/dist/react/schemaRenderer/action/ActionModalDialog.js.map +1 -0
- package/dist/react/schemaRenderer/action/ConfirmActionDialog.d.ts +17 -0
- package/dist/react/schemaRenderer/action/ConfirmActionDialog.d.ts.map +1 -0
- package/dist/react/schemaRenderer/action/ConfirmActionDialog.js +19 -0
- package/dist/react/schemaRenderer/action/ConfirmActionDialog.js.map +1 -0
- package/dist/react/schemaRenderer/action/HandlerActionButton.d.ts +16 -0
- package/dist/react/schemaRenderer/action/HandlerActionButton.d.ts.map +1 -0
- package/dist/react/schemaRenderer/action/HandlerActionButton.js +16 -0
- package/dist/react/schemaRenderer/action/HandlerActionButton.js.map +1 -0
- package/dist/react/schemaRenderer/action/MethodActionButton.d.ts +22 -0
- package/dist/react/schemaRenderer/action/MethodActionButton.d.ts.map +1 -0
- package/dist/react/schemaRenderer/action/MethodActionButton.js +26 -0
- package/dist/react/schemaRenderer/action/MethodActionButton.js.map +1 -0
- package/dist/react/schemaRenderer/action/buttons.d.ts +18 -0
- package/dist/react/schemaRenderer/action/buttons.d.ts.map +1 -0
- package/dist/react/schemaRenderer/action/buttons.js +74 -0
- package/dist/react/schemaRenderer/action/buttons.js.map +1 -0
- package/dist/react/schemaRenderer/action/helpers.d.ts +26 -0
- package/dist/react/schemaRenderer/action/helpers.d.ts.map +1 -0
- package/dist/react/schemaRenderer/action/helpers.js +126 -0
- package/dist/react/schemaRenderer/action/helpers.js.map +1 -0
- package/dist/react/schemaRenderer/action/renderAction.d.ts +21 -0
- package/dist/react/schemaRenderer/action/renderAction.d.ts.map +1 -0
- package/dist/react/schemaRenderer/action/renderAction.js +102 -0
- package/dist/react/schemaRenderer/action/renderAction.js.map +1 -0
- package/dist/react/schemaRenderer/columnFormat.d.ts +10 -0
- package/dist/react/schemaRenderer/columnFormat.d.ts.map +1 -0
- package/dist/react/schemaRenderer/columnFormat.js +76 -0
- package/dist/react/schemaRenderer/columnFormat.js.map +1 -0
- package/dist/react/schemaRenderer/constants.d.ts +8 -0
- package/dist/react/schemaRenderer/constants.d.ts.map +1 -0
- package/dist/react/schemaRenderer/constants.js +45 -0
- package/dist/react/schemaRenderer/constants.js.map +1 -0
- package/dist/react/schemaRenderer/form/FormRenderer.d.ts +29 -0
- package/dist/react/schemaRenderer/form/FormRenderer.d.ts.map +1 -0
- package/dist/react/schemaRenderer/form/FormRenderer.js +163 -0
- package/dist/react/schemaRenderer/form/FormRenderer.js.map +1 -0
- package/dist/react/schemaRenderer/form/renderField.d.ts +6 -0
- package/dist/react/schemaRenderer/form/renderField.d.ts.map +1 -0
- package/dist/react/schemaRenderer/form/renderField.js +239 -0
- package/dist/react/schemaRenderer/form/renderField.js.map +1 -0
- package/dist/react/schemaRenderer/helpers.d.ts +32 -0
- package/dist/react/schemaRenderer/helpers.d.ts.map +1 -0
- package/dist/react/schemaRenderer/helpers.js +52 -0
- package/dist/react/schemaRenderer/helpers.js.map +1 -0
- package/dist/react/schemaRenderer/table/CardsLayoutBody.d.ts +60 -0
- package/dist/react/schemaRenderer/table/CardsLayoutBody.d.ts.map +1 -0
- package/dist/react/schemaRenderer/table/CardsLayoutBody.js +189 -0
- package/dist/react/schemaRenderer/table/CardsLayoutBody.js.map +1 -0
- package/dist/react/schemaRenderer/table/TableRenderer.d.ts +29 -0
- package/dist/react/schemaRenderer/table/TableRenderer.d.ts.map +1 -0
- package/dist/react/schemaRenderer/table/TableRenderer.js +85 -0
- package/dist/react/schemaRenderer/table/TableRenderer.js.map +1 -0
- package/dist/react/schemaRenderer/table/TableRendererBody.d.ts +18 -0
- package/dist/react/schemaRenderer/table/TableRendererBody.d.ts.map +1 -0
- package/dist/react/schemaRenderer/table/TableRendererBody.js +555 -0
- package/dist/react/schemaRenderer/table/TableRendererBody.js.map +1 -0
- package/dist/react/schemaRenderer/table/filters.d.ts +263 -0
- package/dist/react/schemaRenderer/table/filters.d.ts.map +1 -0
- package/dist/react/schemaRenderer/table/filters.js +497 -0
- package/dist/react/schemaRenderer/table/filters.js.map +1 -0
- package/dist/react/schemaRenderer/table/formatCell.d.ts +11 -0
- package/dist/react/schemaRenderer/table/formatCell.d.ts.map +1 -0
- package/dist/react/schemaRenderer/table/formatCell.js +172 -0
- package/dist/react/schemaRenderer/table/formatCell.js.map +1 -0
- package/dist/react/schemaRenderer/table/links.d.ts +42 -0
- package/dist/react/schemaRenderer/table/links.d.ts.map +1 -0
- package/dist/react/schemaRenderer/table/links.js +55 -0
- package/dist/react/schemaRenderer/table/links.js.map +1 -0
- package/dist/react/schemaRenderer/table/renderRowActions.d.ts +13 -0
- package/dist/react/schemaRenderer/table/renderRowActions.d.ts.map +1 -0
- package/dist/react/schemaRenderer/table/renderRowActions.js +25 -0
- package/dist/react/schemaRenderer/table/renderRowActions.js.map +1 -0
- package/dist/react/schemaRenderer/table/url.d.ts +41 -0
- package/dist/react/schemaRenderer/table/url.d.ts.map +1 -0
- package/dist/react/schemaRenderer/table/url.js +114 -0
- package/dist/react/schemaRenderer/table/url.js.map +1 -0
- package/dist/routes/globals.d.ts +13 -0
- package/dist/routes/globals.d.ts.map +1 -0
- package/dist/routes/globals.js +131 -0
- package/dist/routes/globals.js.map +1 -0
- package/dist/routes/helpers.d.ts +217 -0
- package/dist/routes/helpers.d.ts.map +1 -0
- package/dist/routes/helpers.js +498 -0
- package/dist/routes/helpers.js.map +1 -0
- package/dist/routes/pages.d.ts +15 -0
- package/dist/routes/pages.d.ts.map +1 -0
- package/dist/routes/pages.js +145 -0
- package/dist/routes/pages.js.map +1 -0
- package/dist/routes/panel.d.ts +19 -0
- package/dist/routes/panel.d.ts.map +1 -0
- package/dist/routes/panel.js +191 -0
- package/dist/routes/panel.js.map +1 -0
- package/dist/routes/relations.d.ts +21 -0
- package/dist/routes/relations.d.ts.map +1 -0
- package/dist/routes/relations.js +1239 -0
- package/dist/routes/relations.js.map +1 -0
- package/dist/routes/resources.d.ts +28 -0
- package/dist/routes/resources.d.ts.map +1 -0
- package/dist/routes/resources.js +741 -0
- package/dist/routes/resources.js.map +1 -0
- package/dist/routes/theme.d.ts +12 -0
- package/dist/routes/theme.d.ts.map +1 -0
- package/dist/routes/theme.js +82 -0
- package/dist/routes/theme.js.map +1 -0
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +64 -3078
- package/dist/routes.js.map +1 -1
- package/dist/vite.d.ts +1 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +26 -5
- package/dist/vite.js.map +1 -1
- package/package.json +2 -1
- package/src/Pilotiq.ts +95 -0
- package/src/actions/Action.ts +79 -723
- package/src/actions/bulkFactories.ts +168 -0
- package/src/actions/crudFactories.ts +220 -0
- package/src/actions/factoryHelpers.ts +177 -0
- package/src/actions/m2mFactories.ts +193 -0
- package/src/actions/relationFactories.ts +372 -0
- package/src/elements/dispatchForm.ts +1 -1
- package/src/elements/dispatchTable.ts +1 -1
- package/src/fields/Field.ts +39 -0
- package/src/pageData/breadcrumbs.ts +288 -0
- package/src/pageData/forms.ts +578 -0
- package/src/pageData/helpers.ts +764 -0
- package/src/pageData/misc.ts +347 -0
- package/src/pageData/navigation.ts +779 -0
- package/src/pageData/relationPages.ts +1246 -0
- package/src/pageData/relationTabs.ts +286 -0
- package/src/pageData/resourcePages.ts +593 -0
- package/src/pageData.ts +122 -4731
- package/src/react/AppShell.tsx +27 -1
- package/src/react/CollabExtensionFactoryRegistry.ts +55 -0
- package/src/react/CollabRoomContext.ts +42 -0
- package/src/react/FormCollabBindingRegistry.ts +72 -0
- package/src/react/FormStateContext.tsx +91 -0
- package/src/react/RecordWrapperGate.tsx +40 -0
- package/src/react/RecordWrapperRegistry.ts +39 -0
- package/src/react/SchemaRenderer.tsx +230 -6479
- package/src/react/component-slots.test.ts +103 -0
- package/src/react/component-slots.ts +116 -0
- package/src/react/fields/BuilderInput.tsx +29 -117
- package/src/react/fields/MarkdownInput.tsx +0 -1
- package/src/react/fields/RepeaterInput.tsx +29 -130
- package/src/react/fields/rowState.ts +106 -0
- package/src/react/fields/useRowReorderDnd.ts +78 -0
- package/src/react/index.ts +38 -0
- package/src/react/layouts/SidebarLayout.tsx +39 -28
- package/src/react/layouts/TopbarLayout.tsx +70 -57
- package/src/react/parseRecordEditUrl.test.ts +75 -0
- package/src/react/parseRecordEditUrl.ts +55 -0
- package/src/react/persistedState.ts +40 -0
- package/src/react/schemaRenderer/AlertRenderer.tsx +112 -0
- package/src/react/schemaRenderer/EntryRenderer.tsx +501 -0
- package/src/react/schemaRenderer/SectionRenderer.tsx +120 -0
- package/src/react/schemaRenderer/SimpleElements.tsx +306 -0
- package/src/react/schemaRenderer/TabsRenderer.tsx +62 -0
- package/src/react/schemaRenderer/WizardRenderer.tsx +338 -0
- package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +177 -0
- package/src/react/schemaRenderer/action/ActionModalDialog.tsx +273 -0
- package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +61 -0
- package/src/react/schemaRenderer/action/HandlerActionButton.tsx +43 -0
- package/src/react/schemaRenderer/action/MethodActionButton.tsx +64 -0
- package/src/react/schemaRenderer/action/buttons.tsx +99 -0
- package/src/react/schemaRenderer/action/helpers.ts +140 -0
- package/src/react/schemaRenderer/action/renderAction.tsx +245 -0
- package/src/react/schemaRenderer/columnFormat.ts +65 -0
- package/src/react/schemaRenderer/constants.ts +50 -0
- package/src/react/schemaRenderer/form/FormRenderer.tsx +245 -0
- package/src/react/schemaRenderer/form/renderField.tsx +511 -0
- package/src/react/schemaRenderer/helpers.tsx +81 -0
- package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +308 -0
- package/src/react/schemaRenderer/table/TableRenderer.tsx +123 -0
- package/src/react/schemaRenderer/table/TableRendererBody.tsx +974 -0
- package/src/react/schemaRenderer/table/filters.tsx +1233 -0
- package/src/react/schemaRenderer/table/formatCell.tsx +264 -0
- package/src/react/schemaRenderer/table/links.tsx +112 -0
- package/src/react/schemaRenderer/table/renderRowActions.tsx +52 -0
- package/src/react/schemaRenderer/table/url.tsx +143 -0
- package/src/routes/globals.ts +154 -0
- package/src/routes/helpers.ts +668 -0
- package/src/routes/pages.ts +173 -0
- package/src/routes/panel.ts +204 -0
- package/src/routes/relations.ts +1219 -0
- package/src/routes/resources.ts +786 -0
- package/src/routes/theme.ts +109 -0
- package/src/routes.test.ts +1 -1
- package/src/routes.ts +64 -3176
- package/src/schema/TableWidget.test.ts +2 -2
- package/src/theme/migrate.test.ts +178 -0
- package/src/vite.test.ts +184 -0
- package/src/vite.ts +26 -4
package/src/react/AppShell.tsx
CHANGED
|
@@ -10,10 +10,12 @@ import type { RightPanelRegistry } from './right-panel-registry.js'
|
|
|
10
10
|
import { RightPanelRegistryProvider } from './right-panel-registry.js'
|
|
11
11
|
import { RightSidebarProvider, useRightSidebarOptional } from './RightSidebarContext.js'
|
|
12
12
|
import { RightSidebar } from './RightSidebar.js'
|
|
13
|
+
import { RecordWrapperGate } from './RecordWrapperGate.js'
|
|
13
14
|
import { useIsMobile } from './hooks/use-mobile.js'
|
|
14
15
|
import type { NavItem, UserMenuMeta, DatabaseNotificationsMeta, RightSidebarMeta } from '../pageData.js'
|
|
15
16
|
import type { RenderHookMap } from '../RenderHook.js'
|
|
16
17
|
import { RenderHookSlot } from './RenderHookSlot.js'
|
|
18
|
+
import type { ComponentSlotRegistry } from './component-slots.js'
|
|
17
19
|
|
|
18
20
|
export interface AppShellProps {
|
|
19
21
|
panel: {
|
|
@@ -73,6 +75,13 @@ export interface AppShellProps {
|
|
|
73
75
|
* default — chrome renders without any extra wrapping.
|
|
74
76
|
*/
|
|
75
77
|
layoutProviderRegistry?: ReadonlyArray<React.ComponentType<{ children: React.ReactNode; basePath?: string }>>
|
|
78
|
+
/**
|
|
79
|
+
* Build-time chrome-slot overrides from the Vite plugin. Populated
|
|
80
|
+
* when the panel module calls `Pilotiq.components({ nav, … })`.
|
|
81
|
+
* Sparse `{}` is the no-op default. Currently only `nav` is honored;
|
|
82
|
+
* additional slots will land as concrete consumers ask.
|
|
83
|
+
*/
|
|
84
|
+
componentSlotRegistry?: ComponentSlotRegistry
|
|
76
85
|
children: React.ReactNode
|
|
77
86
|
}
|
|
78
87
|
|
|
@@ -110,12 +119,29 @@ export function AppShell({ layout = 'sidebar', notifications, componentRegistry,
|
|
|
110
119
|
const hooks = props.panel.renderHooks
|
|
111
120
|
const rightSidebarMeta = props.panel.rightSidebar
|
|
112
121
|
|
|
122
|
+
// Record-scoped wrapper (collab room, audit trail, …) — pass-through
|
|
123
|
+
// when no plugin registered a wrapper or when the URL isn't a
|
|
124
|
+
// record-edit page. Wrapping `children` before forwarding to `Layout`
|
|
125
|
+
// mounts the wrapper around the page content area only, leaving the
|
|
126
|
+
// sidebar / topbar chrome outside.
|
|
127
|
+
const layoutProps = {
|
|
128
|
+
...props,
|
|
129
|
+
children: (
|
|
130
|
+
<RecordWrapperGate
|
|
131
|
+
basePath={props.basePath}
|
|
132
|
+
{...(props.currentPath !== undefined ? { currentPath: props.currentPath } : {})}
|
|
133
|
+
>
|
|
134
|
+
{props.children}
|
|
135
|
+
</RecordWrapperGate>
|
|
136
|
+
),
|
|
137
|
+
}
|
|
138
|
+
|
|
113
139
|
const inner = (
|
|
114
140
|
<ToasterProvider {...toasterProps}>
|
|
115
141
|
<CommandPaletteProvider setOpen={setPaletteOpen}>
|
|
116
142
|
<RenderHookSlot name="panels::body.start" hooks={hooks} />
|
|
117
143
|
<RightSidebarLayoutFrame>
|
|
118
|
-
<Layout {...
|
|
144
|
+
<Layout {...layoutProps} />
|
|
119
145
|
</RightSidebarLayoutFrame>
|
|
120
146
|
<RenderHookSlot name="panels::body.end" hooks={hooks} />
|
|
121
147
|
<CommandPalette {...paletteProps} />
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level registry slot for the Tiptap extension factory that turns
|
|
3
|
+
* a collab room into editor-attachable extensions (`Collaboration` +
|
|
4
|
+
* `CollaborationCursor`).
|
|
5
|
+
*
|
|
6
|
+
* Wiring posture (mirrors `PendingSuggestionOverlayRegistry`):
|
|
7
|
+
* - `@pilotiq-pro/collab`'s plugin calls `registerCollabExtensions(...)`
|
|
8
|
+
* once at boot from inside `register(panel)`. The factory closes over
|
|
9
|
+
* the `@tiptap/extension-collaboration` + `-cursor` imports, so pilotiq
|
|
10
|
+
* core (and `@pilotiq/tiptap`) never carry those as peer deps.
|
|
11
|
+
* - `@pilotiq/tiptap`'s `TiptapEditor` calls `getCollabExtensions()` at
|
|
12
|
+
* mount; if non-null AND a `useCollabRoom()` value is present, it calls
|
|
13
|
+
* the factory and spreads the returned array into the editor's
|
|
14
|
+
* `extensions` slot. If either is missing, plain Tiptap + History runs.
|
|
15
|
+
*
|
|
16
|
+
* `unknown[]` return type is deliberate — pilotiq core has zero `@tiptap/*`
|
|
17
|
+
* imports and treats the returned values as opaque editor-extension refs.
|
|
18
|
+
* The Tiptap host trusts them and spreads them in.
|
|
19
|
+
*/
|
|
20
|
+
export interface CollabExtensionFactoryArgs {
|
|
21
|
+
/** `Y.Doc` for the surrounding record. Opaque to pilotiq core. */
|
|
22
|
+
ydoc: unknown
|
|
23
|
+
/** `WebsocketProvider` for the same room. Opaque to pilotiq core. */
|
|
24
|
+
provider: unknown
|
|
25
|
+
/**
|
|
26
|
+
* Field name — becomes the `Y.XmlFragment` selector
|
|
27
|
+
* (`Collaboration.configure({ field: fieldName })`) so multiple
|
|
28
|
+
* collab editors on the same record write to distinct fragments
|
|
29
|
+
* inside one shared ydoc.
|
|
30
|
+
*/
|
|
31
|
+
fieldName: string
|
|
32
|
+
/** Presence info forwarded to `CollaborationCursor`. Sparse. */
|
|
33
|
+
user?: {
|
|
34
|
+
name?: string
|
|
35
|
+
color?: string
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type CollabExtensionFactory = (args: CollabExtensionFactoryArgs) => unknown[]
|
|
40
|
+
|
|
41
|
+
let _factory: CollabExtensionFactory | null = null
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Register the factory that builds collab extensions for one field +
|
|
45
|
+
* room. Called once at boot by `@pilotiq-pro/collab`'s plugin. No-op when
|
|
46
|
+
* no plugin registers — Tiptap renderers fall back to plain editing.
|
|
47
|
+
*/
|
|
48
|
+
export function registerCollabExtensions(factory: CollabExtensionFactory): void {
|
|
49
|
+
_factory = factory
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Returns the registered factory, or `null`. */
|
|
53
|
+
export function getCollabExtensions(): CollabExtensionFactory | null {
|
|
54
|
+
return _factory
|
|
55
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Per-record realtime-collab room — a Y.Doc plus its WebsocketProvider,
|
|
5
|
+
* shared across every collaborative field in the same form. Mirrors the
|
|
6
|
+
* Tiptap "Collaborative Fields" pattern: one ydoc per record, each editor
|
|
7
|
+
* scopes itself to its own `Y.XmlFragment` via the field's `name`.
|
|
8
|
+
*
|
|
9
|
+
* Pilotiq core does NOT instantiate the room — implementation lives in
|
|
10
|
+
* `@pilotiq-pro/collab`, which mounts the context with real Yjs values
|
|
11
|
+
* via `<RecordCollabRoom>`. Core just owns the shape so any field
|
|
12
|
+
* renderer (Tiptap, in-house, third-party) can subscribe through one
|
|
13
|
+
* open-core seam without taking a hard peer dep on Yjs.
|
|
14
|
+
*
|
|
15
|
+
* `ydoc` and `provider` are typed `unknown` deliberately — the consumer
|
|
16
|
+
* (typically `@pilotiq/tiptap`) hands them straight to a registered
|
|
17
|
+
* `CollabExtensionFactory` and never touches them directly, so pilotiq
|
|
18
|
+
* core stays Yjs-free.
|
|
19
|
+
*/
|
|
20
|
+
export interface CollabRoom {
|
|
21
|
+
/** `Y.Doc` instance. Opaque to pilotiq core. */
|
|
22
|
+
ydoc: unknown
|
|
23
|
+
/** `WebsocketProvider` instance. Opaque to pilotiq core. */
|
|
24
|
+
provider: unknown
|
|
25
|
+
/** Presence info for cursors / avatars. Forwarded to the extension factory. */
|
|
26
|
+
user?: {
|
|
27
|
+
name?: string
|
|
28
|
+
color?: string
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `null` default — fields read the context, and a null result means
|
|
34
|
+
* "no collab room mounted, fall back to local-only editing." This is
|
|
35
|
+
* the 99% case (collab plugin not installed).
|
|
36
|
+
*/
|
|
37
|
+
export const CollabRoomContext = createContext<CollabRoom | null>(null)
|
|
38
|
+
|
|
39
|
+
/** Read the active collab room for the surrounding record, or `null`. */
|
|
40
|
+
export function useCollabRoom(): CollabRoom | null {
|
|
41
|
+
return useContext(CollabRoomContext)
|
|
42
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { CollabRoom } from './CollabRoomContext.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Binding contract that a collab plugin returns from
|
|
5
|
+
* `registerFormCollabBinding` — wraps a single form's value map in a
|
|
6
|
+
* shared CRDT type (typically a `Y.Map` on the surrounding record's
|
|
7
|
+
* `Y.Doc`) so every field on the form syncs across clients.
|
|
8
|
+
*
|
|
9
|
+
* Pilotiq's `FormStateProvider` calls into this contract when a
|
|
10
|
+
* `<RecordCollabRoom>` is mounted up-tree:
|
|
11
|
+
*
|
|
12
|
+
* - `get()` is read once on mount to overlay any already-synced
|
|
13
|
+
* state on top of the form's SSR-rendered defaults.
|
|
14
|
+
* - `set(name, v)` is called from `setValue` after the local React
|
|
15
|
+
* state update — every controlled field's edit lands in the CRDT.
|
|
16
|
+
* - `subscribe(fn)` registers a listener that fires when REMOTE
|
|
17
|
+
* changes land; `fn(snapshot)` receives the full updated map.
|
|
18
|
+
* The provider re-applies this snapshot onto its React state.
|
|
19
|
+
* - `destroy()` is called on unmount — gives the plugin a chance to
|
|
20
|
+
* remove its CRDT observer.
|
|
21
|
+
*
|
|
22
|
+
* `unknown` payloads keep pilotiq core Yjs-free; the binding owns its
|
|
23
|
+
* own type knowledge. Same posture as `CollabExtensionFactory`.
|
|
24
|
+
*/
|
|
25
|
+
export interface FormCollabBinding {
|
|
26
|
+
/** Snapshot of the current synced values. Called once on mount. */
|
|
27
|
+
get(): Record<string, unknown>
|
|
28
|
+
/** Write the local edit to the CRDT. Triggers a broadcast to peers. */
|
|
29
|
+
set(name: string, value: unknown): void
|
|
30
|
+
/** Subscribe to remote changes. Returns an unsubscribe function. */
|
|
31
|
+
subscribe(fn: (snapshot: Record<string, unknown>) => void): () => void
|
|
32
|
+
/** Cleanup hook called when the form unmounts. */
|
|
33
|
+
destroy(): void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface FormCollabBindingFactoryArgs {
|
|
37
|
+
/** Active collab room — provides `ydoc`, `provider`, `user`. Opaque to pilotiq core. */
|
|
38
|
+
room: CollabRoom
|
|
39
|
+
/**
|
|
40
|
+
* Form identifier — used by the binding to partition CRDT state when
|
|
41
|
+
* multiple forms render against the same room (e.g. a record page that
|
|
42
|
+
* shows a primary form plus a side-mounted action form). Most pages
|
|
43
|
+
* only have one form, so most bindings use this as the `Y.Map` name.
|
|
44
|
+
*/
|
|
45
|
+
formId: string
|
|
46
|
+
/**
|
|
47
|
+
* Initial values from pilotiq's normal SSR resolution. The binding
|
|
48
|
+
* uses these to perform the idempotent "first-load seed" (`!ymap.has(k)`
|
|
49
|
+
* gated write) so the first client to open a record populates the
|
|
50
|
+
* Y.Map with the DB-derived defaults; subsequent clients find the
|
|
51
|
+
* map already populated and skip.
|
|
52
|
+
*/
|
|
53
|
+
initial: Record<string, unknown>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type FormCollabBindingFactory = (args: FormCollabBindingFactoryArgs) => FormCollabBinding
|
|
57
|
+
|
|
58
|
+
let _factory: FormCollabBindingFactory | null = null
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Register the form-level CRDT binding factory. Called once at boot by
|
|
62
|
+
* `@pilotiq-pro/collab`'s plugin. No-op when no plugin registers —
|
|
63
|
+
* `FormStateProvider` falls back to its plain local-state path.
|
|
64
|
+
*/
|
|
65
|
+
export function registerFormCollabBinding(factory: FormCollabBindingFactory): void {
|
|
66
|
+
_factory = factory
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Returns the registered binding factory, or `null`. */
|
|
70
|
+
export function getFormCollabBinding(): FormCollabBindingFactory | null {
|
|
71
|
+
return _factory
|
|
72
|
+
}
|
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
} from './formStateHelpers.js'
|
|
18
18
|
import { runJsHandler } from './fieldJsHandler.js'
|
|
19
19
|
import { useToast } from './Toaster.js'
|
|
20
|
+
import { useCollabRoom } from './CollabRoomContext.js'
|
|
21
|
+
import { getFormCollabBinding, type FormCollabBinding } from './FormCollabBindingRegistry.js'
|
|
20
22
|
|
|
21
23
|
export type FieldStatus = 'idle' | 'pending'
|
|
22
24
|
|
|
@@ -58,6 +60,19 @@ export function useFormState(): FormStateApi | null {
|
|
|
58
60
|
*/
|
|
59
61
|
export const FormIdContext = createContext<string>('')
|
|
60
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Phase F2 — returns `true` iff the named field has explicitly opted out
|
|
65
|
+
* of realtime collab via `Field.collab(false)`. Sparse meta — absent =
|
|
66
|
+
* inherit the panel default (collab on). Walks the form meta tree the
|
|
67
|
+
* same way `findFieldMeta` does; cheap because it only runs on the
|
|
68
|
+
* per-write path (already a hot path, but every check is one map
|
|
69
|
+
* lookup + one boolean compare).
|
|
70
|
+
*/
|
|
71
|
+
function fieldOptsOutOfCollab(formMeta: ElementMeta, name: string): boolean {
|
|
72
|
+
const meta = findFieldMeta(formMeta, name) as { collab?: boolean } | undefined
|
|
73
|
+
return meta?.collab === false
|
|
74
|
+
}
|
|
75
|
+
|
|
61
76
|
export interface UseFieldStateResult {
|
|
62
77
|
/** True when the field is inside a controlled form (live fields enabled).
|
|
63
78
|
* Renderers should fall back to their `defaultValue` path when false.
|
|
@@ -184,6 +199,62 @@ export function FormStateProvider({
|
|
|
184
199
|
useEffect(() => { formMetaRef.current = formMeta }, [formMeta])
|
|
185
200
|
|
|
186
201
|
const stateUrl = (formMeta as { stateUrl?: string })['stateUrl']
|
|
202
|
+
const formId = (formMeta as { formId?: string })['formId'] ?? ''
|
|
203
|
+
|
|
204
|
+
// Phase F2 — collab binding. When `<RecordCollabRoom>` is mounted up-tree
|
|
205
|
+
// AND `@pilotiq-pro/collab` registered a `FormCollabBinding` factory, we
|
|
206
|
+
// construct a binding for this form, lift any already-synced state on
|
|
207
|
+
// top of the SSR-rendered defaults, and proxy every local write through
|
|
208
|
+
// it. Remote writes flow back via `subscribe`. Outside a room (or with
|
|
209
|
+
// no factory registered), `bindingRef` stays null and the plain
|
|
210
|
+
// local-state path runs unchanged.
|
|
211
|
+
const collabRoom = useCollabRoom()
|
|
212
|
+
const bindingFactory = getFormCollabBinding()
|
|
213
|
+
const bindingRef = useRef<FormCollabBinding | null>(null)
|
|
214
|
+
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
if (!collabRoom || !bindingFactory || !formId) return
|
|
217
|
+
|
|
218
|
+
const binding = bindingFactory({ room: collabRoom, formId, initial: valuesRef.current })
|
|
219
|
+
bindingRef.current = binding
|
|
220
|
+
|
|
221
|
+
// Lift any state already in the room (subsequent joiners — first
|
|
222
|
+
// mover sees an empty snapshot here and the local SSR defaults
|
|
223
|
+
// stay authoritative). Shallow merge so fields the binding doesn't
|
|
224
|
+
// know about (defaults the seed skipped, dotted-path Repeater rows
|
|
225
|
+
// we don't sync in F2) survive.
|
|
226
|
+
const synced = binding.get()
|
|
227
|
+
if (Object.keys(synced).length > 0) {
|
|
228
|
+
setValuesState((prev) => ({ ...prev, ...synced }))
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Subscribe to remote changes. Local writes ALSO trigger this
|
|
232
|
+
// (Yjs observers fire on local transactions too) — the per-key
|
|
233
|
+
// Object.is short-circuit below collapses them into no-op renders.
|
|
234
|
+
const unsubscribe = binding.subscribe((snapshot) => {
|
|
235
|
+
setValuesState((prev) => {
|
|
236
|
+
let changed = false
|
|
237
|
+
const next: Record<string, unknown> = { ...prev }
|
|
238
|
+
for (const [k, v] of Object.entries(snapshot)) {
|
|
239
|
+
if (!Object.is(prev[k], v)) {
|
|
240
|
+
next[k] = v
|
|
241
|
+
changed = true
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return changed ? next : prev
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
return () => {
|
|
249
|
+
unsubscribe()
|
|
250
|
+
binding.destroy()
|
|
251
|
+
bindingRef.current = null
|
|
252
|
+
}
|
|
253
|
+
// `valuesRef.current` is intentionally read once at mount — initial
|
|
254
|
+
// values seed the binding; subsequent edits flow through `setValue`
|
|
255
|
+
// and remote changes flow through `subscribe`.
|
|
256
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
257
|
+
}, [collabRoom, bindingFactory, formId])
|
|
187
258
|
|
|
188
259
|
/**
|
|
189
260
|
* Tier-2 follow-up to Plan #5 — fire `Field.afterStateUpdatedJs(body)`
|
|
@@ -238,6 +309,14 @@ export function FormStateProvider({
|
|
|
238
309
|
if (Object.is(prev[name], value)) return prev
|
|
239
310
|
return { ...prev, [name]: value }
|
|
240
311
|
})
|
|
312
|
+
// Phase F2 — proxy the write through the collab binding when active
|
|
313
|
+
// AND the field hasn't opted out via `.collab(false)`. Dotted-path
|
|
314
|
+
// names (Repeater / Builder row leaves) stay local-only in v1; their
|
|
315
|
+
// syncing belongs to Phase F.5 (`Y.Array<Y.Map>` row identity).
|
|
316
|
+
const binding = bindingRef.current
|
|
317
|
+
if (binding && !name.includes('.') && !fieldOptsOutOfCollab(formMetaRef.current, name)) {
|
|
318
|
+
binding.set(name, value)
|
|
319
|
+
}
|
|
241
320
|
// Fire the client-side JS hook synchronously after the state write.
|
|
242
321
|
// Dotted-name fields don't go through here (their setter is a no-op
|
|
243
322
|
// in `useFieldState`); they fire JS via `triggerLive` instead so we
|
|
@@ -311,6 +390,18 @@ export function FormStateProvider({
|
|
|
311
390
|
const serverValues = (data.form as { values?: Record<string, unknown> }).values
|
|
312
391
|
if (serverValues) {
|
|
313
392
|
setValuesState((prev) => ({ ...prev, ...serverValues }))
|
|
393
|
+
// Phase F2 (Q2) — derived fields propagate to peers via the
|
|
394
|
+
// collab binding so every client sees the auto-`slug` / etc.
|
|
395
|
+
// without each peer roundtripping the server. Skip dotted-path
|
|
396
|
+
// names + fields that opted out.
|
|
397
|
+
const binding = bindingRef.current
|
|
398
|
+
if (binding) {
|
|
399
|
+
for (const [k, v] of Object.entries(serverValues)) {
|
|
400
|
+
if (k.includes('.')) continue
|
|
401
|
+
if (fieldOptsOutOfCollab(data.form, k)) continue
|
|
402
|
+
binding.set(k, v)
|
|
403
|
+
}
|
|
404
|
+
}
|
|
314
405
|
}
|
|
315
406
|
setErrors({})
|
|
316
407
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type ReactNode } from 'react'
|
|
2
|
+
import { getRecordWrapper } from './RecordWrapperRegistry.js'
|
|
3
|
+
import { parseRecordEditUrl } from './parseRecordEditUrl.js'
|
|
4
|
+
|
|
5
|
+
export interface RecordWrapperGateProps {
|
|
6
|
+
currentPath?: string
|
|
7
|
+
basePath: string
|
|
8
|
+
children: ReactNode
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Conditionally wraps the page tree with the plugin-registered
|
|
13
|
+
* `RecordWrapper` when the current URL resolves to a record-bound edit
|
|
14
|
+
* page. Pass-through in every other case:
|
|
15
|
+
*
|
|
16
|
+
* - no plugin registered a wrapper (`getRecordWrapper() === null`)
|
|
17
|
+
* - the URL isn't a record-edit URL (list / create / view / dashboard / …)
|
|
18
|
+
* - `currentPath` not yet known on the very first SSR render
|
|
19
|
+
*
|
|
20
|
+
* Mounted once inside `AppShell` around the page content area so
|
|
21
|
+
* record-scoped plugins (collab room, audit trail, …) get one
|
|
22
|
+
* lifetimed mount per record-view-or-edit without each plugin having
|
|
23
|
+
* to thread URL parsing into its own provider.
|
|
24
|
+
*
|
|
25
|
+
* Scope limited to `/edit` URLs in v1; view-page support (read-only
|
|
26
|
+
* collab cursors on `ViewPage`) is a follow-up.
|
|
27
|
+
*/
|
|
28
|
+
export function RecordWrapperGate({ currentPath, basePath, children }: RecordWrapperGateProps) {
|
|
29
|
+
const Wrapper = getRecordWrapper()
|
|
30
|
+
if (!Wrapper || !currentPath) return <>{children}</>
|
|
31
|
+
|
|
32
|
+
const identity = parseRecordEditUrl(currentPath, basePath)
|
|
33
|
+
if (!identity) return <>{children}</>
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Wrapper resourceSlug={identity.resourceSlug} recordId={identity.recordId}>
|
|
37
|
+
{children}
|
|
38
|
+
</Wrapper>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Props the page-record wrapper receives from `RecordWrapperGate` when
|
|
5
|
+
* the current URL resolves to a record-bound edit page (`${base}/.../:id/edit`).
|
|
6
|
+
*
|
|
7
|
+
* The wrapper is responsible for whatever record-scoped context the
|
|
8
|
+
* plugin owns — `@pilotiq-pro/collab` mounts a `<RecordCollabRoom>`
|
|
9
|
+
* here so every collab field inside the page shares one Y.Doc + WS
|
|
10
|
+
* connection. Other plugins could mount per-record presence, audit
|
|
11
|
+
* logging, or anything else that's record-bound rather than panel-bound.
|
|
12
|
+
*
|
|
13
|
+
* `resourceSlug` is the slash-joined slug-path (cluster-prefixed for
|
|
14
|
+
* clustered resources, parent-prefixed for nested-relation edits) so
|
|
15
|
+
* two URLs that target different records always produce distinct
|
|
16
|
+
* wrapper keys / room names.
|
|
17
|
+
*/
|
|
18
|
+
export interface RecordWrapperProps {
|
|
19
|
+
resourceSlug: string
|
|
20
|
+
recordId: string
|
|
21
|
+
children: ReactNode
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let _component: ComponentType<RecordWrapperProps> | null = null
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Register a component that wraps the page tree on every record-edit
|
|
28
|
+
* route. Called once at boot by a plugin (e.g. `@pilotiq-pro/collab`).
|
|
29
|
+
* No-op when no plugin registers — `RecordWrapperGate` passes through
|
|
30
|
+
* unchanged.
|
|
31
|
+
*/
|
|
32
|
+
export function registerRecordWrapper(C: ComponentType<RecordWrapperProps>): void {
|
|
33
|
+
_component = C
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Returns the registered wrapper component, or `null`. */
|
|
37
|
+
export function getRecordWrapper(): ComponentType<RecordWrapperProps> | null {
|
|
38
|
+
return _component
|
|
39
|
+
}
|