@pilotiq/pilotiq 0.7.2 → 0.8.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/.turbo/turbo-build.log +2 -2
- package/CHANGELOG.md +142 -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/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 +152 -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/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 +233 -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
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, it } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { Pilotiq } from '../Pilotiq.js'
|
|
4
|
+
import { isNavItemActive } from './component-slots.js'
|
|
5
|
+
|
|
6
|
+
describe('Pilotiq.components({ nav, header, footer })', () => {
|
|
7
|
+
it('starts empty', () => {
|
|
8
|
+
const panel = Pilotiq.make('Admin')
|
|
9
|
+
assert.deepEqual(panel.getComponentSlots(), {})
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('stores a registered nav slot', () => {
|
|
13
|
+
const Nav = () => null
|
|
14
|
+
const panel = Pilotiq.make('Admin').components({ nav: Nav })
|
|
15
|
+
const slots = panel.getComponentSlots()
|
|
16
|
+
assert.equal(slots.nav, Nav)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('stores a registered header slot', () => {
|
|
20
|
+
const Header = () => null
|
|
21
|
+
const panel = Pilotiq.make('Admin').components({ header: Header })
|
|
22
|
+
assert.equal(panel.getComponentSlots().header, Header)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('stores a registered footer slot', () => {
|
|
26
|
+
const Footer = () => null
|
|
27
|
+
const panel = Pilotiq.make('Admin').components({ footer: Footer })
|
|
28
|
+
assert.equal(panel.getComponentSlots().footer, Footer)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('merges across multiple calls — later wins per slot, unset keys preserved', () => {
|
|
32
|
+
const A = () => null
|
|
33
|
+
const B = () => null
|
|
34
|
+
const panel = Pilotiq.make('Admin')
|
|
35
|
+
.components({ nav: A })
|
|
36
|
+
.components({}) // empty object keeps existing
|
|
37
|
+
.components({ nav: B }) // overrides
|
|
38
|
+
assert.equal(panel.getComponentSlots().nav, B)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('preserves unset slots when overriding only one slot', () => {
|
|
42
|
+
const Nav = () => null
|
|
43
|
+
const Header = () => null
|
|
44
|
+
const Footer = () => null
|
|
45
|
+
const Nav2 = () => null
|
|
46
|
+
const panel = Pilotiq.make('Admin')
|
|
47
|
+
.components({ nav: Nav, header: Header, footer: Footer })
|
|
48
|
+
.components({ nav: Nav2 }) // only nav changes
|
|
49
|
+
const slots = panel.getComponentSlots()
|
|
50
|
+
assert.equal(slots.nav, Nav2)
|
|
51
|
+
assert.equal(slots.header, Header)
|
|
52
|
+
assert.equal(slots.footer, Footer)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('allows registering all three slots together', () => {
|
|
56
|
+
const Nav = () => null
|
|
57
|
+
const Header = () => null
|
|
58
|
+
const Footer = () => null
|
|
59
|
+
const panel = Pilotiq.make('Admin')
|
|
60
|
+
.components({ nav: Nav, header: Header, footer: Footer })
|
|
61
|
+
const slots = panel.getComponentSlots()
|
|
62
|
+
assert.equal(slots.nav, Nav)
|
|
63
|
+
assert.equal(slots.header, Header)
|
|
64
|
+
assert.equal(slots.footer, Footer)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('survives plugin registration without interference', () => {
|
|
68
|
+
const Nav = () => null
|
|
69
|
+
const panel = Pilotiq.make('Admin')
|
|
70
|
+
.components({ nav: Nav })
|
|
71
|
+
.use({ name: 'noop', register() { /* no-op */ } })
|
|
72
|
+
assert.equal(panel.getComponentSlots().nav, Nav)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe('isNavItemActive', () => {
|
|
77
|
+
const basePath = '/admin'
|
|
78
|
+
|
|
79
|
+
it('returns false when currentPath is undefined', () => {
|
|
80
|
+
assert.equal(isNavItemActive('/admin/users', undefined, basePath), false)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('matches the dashboard URL only on exact equality', () => {
|
|
84
|
+
assert.equal(isNavItemActive(basePath, basePath, basePath), true)
|
|
85
|
+
// Dashboard URL is a prefix of every panel URL; it must NOT light up
|
|
86
|
+
// for non-root pages — otherwise it'd always read as active.
|
|
87
|
+
assert.equal(isNavItemActive(basePath, '/admin/users', basePath), false)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('matches non-dashboard URLs on exact equality', () => {
|
|
91
|
+
assert.equal(isNavItemActive('/admin/users', '/admin/users', basePath), true)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('matches non-dashboard URLs as a prefix followed by `/`', () => {
|
|
95
|
+
assert.equal(isNavItemActive('/admin/users', '/admin/users/42', basePath), true)
|
|
96
|
+
assert.equal(isNavItemActive('/admin/users', '/admin/users/42/edit', basePath), true)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('does NOT match a sibling URL that shares a prefix without `/`', () => {
|
|
100
|
+
// `/admin/users` must not activate when on `/admin/user` (singular).
|
|
101
|
+
assert.equal(isNavItemActive('/admin/user', '/admin/users', basePath), false)
|
|
102
|
+
})
|
|
103
|
+
})
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type * as React from 'react'
|
|
2
|
+
import type { NavItem } from '../pageData.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Props passed to a component registered at `Pilotiq.components({ nav })`.
|
|
6
|
+
*
|
|
7
|
+
* The component owns the entire nav region — for `SidebarLayout` that
|
|
8
|
+
* means the `<SidebarContent>` body (the panel chrome's header / footer
|
|
9
|
+
* still render); for `TopbarLayout` that means the `<nav>` element
|
|
10
|
+
* between the brand cluster and the right-side controls.
|
|
11
|
+
*
|
|
12
|
+
* `navigation` is the pre-grouped, pre-sorted tree built by
|
|
13
|
+
* `panelInfo()` — same shape the default renderers consume — so a
|
|
14
|
+
* custom nav can opt into the framework's `navigationGroup` / `sort` /
|
|
15
|
+
* badge metadata for free, or ignore it and walk a custom tree.
|
|
16
|
+
*/
|
|
17
|
+
export interface NavComponentProps {
|
|
18
|
+
/**
|
|
19
|
+
* Pre-grouped navigation items. May be empty when nothing the user
|
|
20
|
+
* can access is registered. Items with `children` represent nested
|
|
21
|
+
* sub-navigation (the default sidebar renders these under
|
|
22
|
+
* `SidebarMenuSub`).
|
|
23
|
+
*/
|
|
24
|
+
navigation: NavItem[]
|
|
25
|
+
/** Panel base path (e.g. `/admin`). */
|
|
26
|
+
basePath: string
|
|
27
|
+
/** Current request pathname — undefined when not in a request scope
|
|
28
|
+
* (e.g. unit-testing the layout standalone). */
|
|
29
|
+
currentPath?: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Props passed to a component registered at `Pilotiq.components({ header })`.
|
|
34
|
+
*
|
|
35
|
+
* The component owns the entire `<header>` chrome bar — in `SidebarLayout`
|
|
36
|
+
* that's the top bar above the main content (sidebar trigger, search,
|
|
37
|
+
* theme toggle, bell, user menu); in `TopbarLayout` that's the *whole*
|
|
38
|
+
* topbar including the brand cluster and the nav region. Setting `header`
|
|
39
|
+
* on `TopbarLayout` makes the `nav` slot irrelevant for that layout —
|
|
40
|
+
* the consumer's header owns everything between page edges.
|
|
41
|
+
*
|
|
42
|
+
* Render hooks that splice INSIDE the default header
|
|
43
|
+
* (`panels::topbar.start`, `panels::topbar.end`, `panels::user-menu.before`,
|
|
44
|
+
* `panels::user-menu.after`) don't fire when the header is replaced —
|
|
45
|
+
* the surrounding container is gone, so there's nowhere to splice into.
|
|
46
|
+
* Hooks rooted outside the header (`panels::sidebar.start` / `.footer`,
|
|
47
|
+
* `panels::sidebar.nav.start` / `.end`, `panels::footer`) keep firing.
|
|
48
|
+
*
|
|
49
|
+
* Shape mirrors `NavComponentProps` so a header that wants to render
|
|
50
|
+
* the nav inline (the TopbarLayout case) can do so without juggling a
|
|
51
|
+
* second slot.
|
|
52
|
+
*/
|
|
53
|
+
export interface HeaderComponentProps {
|
|
54
|
+
/**
|
|
55
|
+
* Pre-grouped navigation items — same tree the `nav` slot receives.
|
|
56
|
+
* Header consumers that want to mount the topbar nav inline read
|
|
57
|
+
* this; sidebar-layout consumers can ignore it.
|
|
58
|
+
*/
|
|
59
|
+
navigation: NavItem[]
|
|
60
|
+
/** Panel base path (e.g. `/admin`). */
|
|
61
|
+
basePath: string
|
|
62
|
+
/** Current request pathname — undefined when not in a request scope. */
|
|
63
|
+
currentPath?: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Props passed to a component registered at `Pilotiq.components({ footer })`.
|
|
68
|
+
*
|
|
69
|
+
* The component mounts as a `<footer>` element BELOW the main content
|
|
70
|
+
* area — outside the scrolling region in both layouts. It is a separate
|
|
71
|
+
* surface from the `panels::footer` render hook, which continues to
|
|
72
|
+
* fire INSIDE the main content area (use the hook to append per-page
|
|
73
|
+
* trailing chrome; use this slot for site-chrome that frames every
|
|
74
|
+
* page like a status bar or copyright row).
|
|
75
|
+
*/
|
|
76
|
+
export interface FooterComponentProps {
|
|
77
|
+
/** Panel base path (e.g. `/admin`). */
|
|
78
|
+
basePath: string
|
|
79
|
+
/** Current request pathname — undefined when not in a request scope. */
|
|
80
|
+
currentPath?: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Registry of build-time component overrides registered through
|
|
85
|
+
* `Pilotiq.components({ nav, header, footer, … })`. The Vite plugin
|
|
86
|
+
* harvests the actual React refs into `pages/(pilotiq)/_components.ts`
|
|
87
|
+
* and forwards them to `<AppShell>`; component refs never travel over
|
|
88
|
+
* the wire.
|
|
89
|
+
*
|
|
90
|
+
* The shape is open-ended so additional slots can land without a
|
|
91
|
+
* breaking change when a concrete consumer asks for them.
|
|
92
|
+
*/
|
|
93
|
+
export interface ComponentSlotRegistry {
|
|
94
|
+
nav?: React.ComponentType<NavComponentProps>
|
|
95
|
+
header?: React.ComponentType<HeaderComponentProps>
|
|
96
|
+
footer?: React.ComponentType<FooterComponentProps>
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Active-link match identical to the default sidebar's. Exported so
|
|
101
|
+
* custom nav components can reuse the framework's longest-prefix
|
|
102
|
+
* semantics: the dashboard URL (`basePath`) only matches on exact
|
|
103
|
+
* equality — otherwise it would light up on every panel page — and
|
|
104
|
+
* non-dashboard URLs match as a prefix followed by `/` or end-of-string
|
|
105
|
+
* so `/admin/users` doesn't activate `/admin/user`.
|
|
106
|
+
*/
|
|
107
|
+
export function isNavItemActive(
|
|
108
|
+
url: string,
|
|
109
|
+
currentPath: string | undefined,
|
|
110
|
+
basePath: string,
|
|
111
|
+
): boolean {
|
|
112
|
+
if (!currentPath) return false
|
|
113
|
+
if (url === basePath) return currentPath === basePath
|
|
114
|
+
if (url === currentPath) return true
|
|
115
|
+
return currentPath.startsWith(url + '/')
|
|
116
|
+
}
|
|
@@ -20,6 +20,18 @@ import {
|
|
|
20
20
|
DEFAULT_CLONE,
|
|
21
21
|
DEFAULT_DELETE,
|
|
22
22
|
} from './rowChromeButton.js'
|
|
23
|
+
import {
|
|
24
|
+
generateRowId, makeAccordionStorage, makeCollapsedStorage,
|
|
25
|
+
} from './rowState.js'
|
|
26
|
+
import { useRowReorderDnd } from './useRowReorderDnd.js'
|
|
27
|
+
|
|
28
|
+
const collapsedStorage = makeCollapsedStorage('builder')
|
|
29
|
+
const accordionStorage = makeAccordionStorage('builder')
|
|
30
|
+
const initSeedCollapsed = collapsedStorage.seed
|
|
31
|
+
const writeCollapsedToStorage = collapsedStorage.write
|
|
32
|
+
const deleteCollapsedFromStorage = collapsedStorage.remove
|
|
33
|
+
const readAccordionFromStorage = accordionStorage.read
|
|
34
|
+
const writeAccordionToStorage = accordionStorage.write
|
|
23
35
|
|
|
24
36
|
interface BlockShape {
|
|
25
37
|
name: string
|
|
@@ -288,42 +300,23 @@ export function BuilderInput({
|
|
|
288
300
|
}
|
|
289
301
|
|
|
290
302
|
// ── DnD state (skipped when buttonsOnly) ────────────────
|
|
291
|
-
const
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
setDropAt(aboveHalf ? idx : idx + 1)
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const onRowDrop = (e: React.DragEvent<HTMLDivElement>): void => {
|
|
312
|
-
if (!dndEnabled || disabled || dragId === null || dropAt === null) {
|
|
313
|
-
setDragId(null); setDropAt(null); return
|
|
314
|
-
}
|
|
315
|
-
e.preventDefault()
|
|
316
|
-
setRows(prev => {
|
|
317
|
-
const fromIdx = prev.findIndex(r => r.id === dragId)
|
|
318
|
-
if (fromIdx < 0) return prev
|
|
319
|
-
return reorderRows(prev, fromIdx, dropAt)
|
|
320
|
-
})
|
|
321
|
-
setDragId(null); setDropAt(null)
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const onRowDragEnd = (): void => {
|
|
325
|
-
setDragId(null); setDropAt(null)
|
|
326
|
-
}
|
|
303
|
+
const dndEnabled = reorderable && !buttonsOnly && !disabled
|
|
304
|
+
const {
|
|
305
|
+
dragId, dropAt,
|
|
306
|
+
onDragStart: onRowDragStart,
|
|
307
|
+
onDragOver: onRowDragOver,
|
|
308
|
+
onDrop: onRowDrop,
|
|
309
|
+
onDragEnd: onRowDragEnd,
|
|
310
|
+
} = useRowReorderDnd({
|
|
311
|
+
enabled: dndEnabled,
|
|
312
|
+
onDrop: (fromId, at) => {
|
|
313
|
+
setRows(prev => {
|
|
314
|
+
const fromIdx = prev.findIndex(r => r.id === fromId)
|
|
315
|
+
if (fromIdx < 0) return prev
|
|
316
|
+
return reorderRows(prev, fromIdx, at)
|
|
317
|
+
})
|
|
318
|
+
},
|
|
319
|
+
})
|
|
327
320
|
|
|
328
321
|
// ── Inner-field live re-resolve (mirrors RepeaterInput) ─
|
|
329
322
|
const formState = useFormState()
|
|
@@ -949,84 +942,3 @@ function prefixFieldNames(el: ElementMeta, prefix: string): ElementMeta {
|
|
|
949
942
|
return el
|
|
950
943
|
}
|
|
951
944
|
|
|
952
|
-
let _rowSeqFallback = 0
|
|
953
|
-
function generateRowId(): string {
|
|
954
|
-
type CryptoLike = { randomUUID?: () => string }
|
|
955
|
-
const c = (globalThis as { crypto?: CryptoLike }).crypto
|
|
956
|
-
if (c?.randomUUID) return c.randomUUID()
|
|
957
|
-
return `row-${Date.now()}-${++_rowSeqFallback}`
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
function collapsedStorageKey(formId: string, name: string, rowId: string): string {
|
|
961
|
-
return `pilotiq.builder.${formId}.${name}.${rowId}`
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
function initSeedCollapsed(
|
|
965
|
-
rows: RowState[],
|
|
966
|
-
formId: string,
|
|
967
|
-
name: string,
|
|
968
|
-
defaultValue: boolean,
|
|
969
|
-
collapsible: boolean,
|
|
970
|
-
): Record<string, boolean> {
|
|
971
|
-
if (!collapsible) return {}
|
|
972
|
-
const out: Record<string, boolean> = {}
|
|
973
|
-
for (const row of rows) {
|
|
974
|
-
out[row.id] = readCollapsedFromStorage(formId, name, row.id, defaultValue)
|
|
975
|
-
}
|
|
976
|
-
return out
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
function readCollapsedFromStorage(
|
|
980
|
-
formId: string,
|
|
981
|
-
name: string,
|
|
982
|
-
rowId: string,
|
|
983
|
-
defaultValue: boolean,
|
|
984
|
-
): boolean {
|
|
985
|
-
if (typeof window === 'undefined') return defaultValue
|
|
986
|
-
try {
|
|
987
|
-
const raw = window.localStorage.getItem(collapsedStorageKey(formId, name, rowId))
|
|
988
|
-
if (raw === null) return defaultValue
|
|
989
|
-
return raw === 'true'
|
|
990
|
-
} catch { return defaultValue }
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
function writeCollapsedToStorage(
|
|
994
|
-
formId: string,
|
|
995
|
-
name: string,
|
|
996
|
-
rowId: string,
|
|
997
|
-
value: boolean,
|
|
998
|
-
): void {
|
|
999
|
-
if (typeof window === 'undefined') return
|
|
1000
|
-
try {
|
|
1001
|
-
window.localStorage.setItem(collapsedStorageKey(formId, name, rowId), String(value))
|
|
1002
|
-
} catch { /* quota exceeded; silent */ }
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
function deleteCollapsedFromStorage(formId: string, name: string, rowId: string): void {
|
|
1006
|
-
if (typeof window === 'undefined') return
|
|
1007
|
-
try { window.localStorage.removeItem(collapsedStorageKey(formId, name, rowId)) } catch { /* */ }
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
function accordionStorageKey(formId: string, name: string): string {
|
|
1011
|
-
return `pilotiq.builder.${formId}.${name}.accordion`
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
/**
|
|
1015
|
-
* Read the persisted accordion-open row id. Mirror of
|
|
1016
|
-
* `RepeaterInput.readAccordionFromStorage` — `undefined` = no value
|
|
1017
|
-
* stored, `''` = explicitly all-collapsed, anything else = open row id.
|
|
1018
|
-
*/
|
|
1019
|
-
function readAccordionFromStorage(formId: string, name: string): string | undefined {
|
|
1020
|
-
if (typeof window === 'undefined') return undefined
|
|
1021
|
-
try {
|
|
1022
|
-
const raw = window.localStorage.getItem(accordionStorageKey(formId, name))
|
|
1023
|
-
return raw === null ? undefined : raw
|
|
1024
|
-
} catch { return undefined }
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
function writeAccordionToStorage(formId: string, name: string, openId: string | null): void {
|
|
1028
|
-
if (typeof window === 'undefined') return
|
|
1029
|
-
try {
|
|
1030
|
-
window.localStorage.setItem(accordionStorageKey(formId, name), openId ?? '')
|
|
1031
|
-
} catch { /* quota exceeded — fall back to in-memory only */ }
|
|
1032
|
-
}
|
|
@@ -296,7 +296,6 @@ export function MarkdownInput({
|
|
|
296
296
|
<div
|
|
297
297
|
className="prose prose-sm dark:prose-invert max-w-none px-3 py-2"
|
|
298
298
|
style={taStyle}
|
|
299
|
-
// eslint-disable-next-line react/no-danger
|
|
300
299
|
dangerouslySetInnerHTML={{ __html: previewHtml || '<p class="text-muted-foreground italic">Nothing to preview</p>' }}
|
|
301
300
|
/>
|
|
302
301
|
</>
|
|
@@ -20,6 +20,18 @@ import {
|
|
|
20
20
|
DEFAULT_DELETE,
|
|
21
21
|
} from './rowChromeButton.js'
|
|
22
22
|
import { syncRowGates } from './syncRowGates.js'
|
|
23
|
+
import {
|
|
24
|
+
generateRowId, makeAccordionStorage, makeCollapsedStorage,
|
|
25
|
+
} from './rowState.js'
|
|
26
|
+
import { useRowReorderDnd } from './useRowReorderDnd.js'
|
|
27
|
+
|
|
28
|
+
const collapsedStorage = makeCollapsedStorage('repeater')
|
|
29
|
+
const accordionStorage = makeAccordionStorage('repeater')
|
|
30
|
+
const initSeedCollapsed = collapsedStorage.seed
|
|
31
|
+
const writeCollapsedToStorage = collapsedStorage.write
|
|
32
|
+
const deleteCollapsedFromStorage = collapsedStorage.remove
|
|
33
|
+
const readAccordionFromStorage = accordionStorage.read
|
|
34
|
+
const writeAccordionToStorage = accordionStorage.write
|
|
23
35
|
|
|
24
36
|
/**
|
|
25
37
|
* Pure reorder helper — used by both the HTML5 DnD path and the
|
|
@@ -324,49 +336,22 @@ export function RepeaterInput({
|
|
|
324
336
|
}
|
|
325
337
|
|
|
326
338
|
// ── DnD state ───────────────────────────────────────────
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const onRowDragOver = (idx: number) => (e: React.DragEvent<HTMLElement>): void => {
|
|
345
|
-
if (!reorderable || disabled || dragId === null) return
|
|
346
|
-
e.preventDefault()
|
|
347
|
-
e.dataTransfer.dropEffect = 'move'
|
|
348
|
-
// Drop above this row when cursor is in its top half, below when in its bottom half.
|
|
349
|
-
const rect = e.currentTarget.getBoundingClientRect()
|
|
350
|
-
const aboveHalf = e.clientY < rect.top + rect.height / 2
|
|
351
|
-
setDropAt(aboveHalf ? idx : idx + 1)
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const onRowDrop = (e: React.DragEvent<HTMLElement>): void => {
|
|
355
|
-
if (!reorderable || disabled || dragId === null || dropAt === null) {
|
|
356
|
-
setDragId(null); setDropAt(null); return
|
|
357
|
-
}
|
|
358
|
-
e.preventDefault()
|
|
359
|
-
setRows(prev => {
|
|
360
|
-
const fromIdx = prev.findIndex(r => r.id === dragId)
|
|
361
|
-
if (fromIdx < 0) return prev
|
|
362
|
-
return reorderRows(prev, fromIdx, dropAt)
|
|
363
|
-
})
|
|
364
|
-
setDragId(null); setDropAt(null)
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
const onRowDragEnd = (): void => {
|
|
368
|
-
setDragId(null); setDropAt(null)
|
|
369
|
-
}
|
|
339
|
+
const {
|
|
340
|
+
dragId, dropAt,
|
|
341
|
+
onDragStart: onRowDragStart,
|
|
342
|
+
onDragOver: onRowDragOver,
|
|
343
|
+
onDrop: onRowDrop,
|
|
344
|
+
onDragEnd: onRowDragEnd,
|
|
345
|
+
} = useRowReorderDnd({
|
|
346
|
+
enabled: reorderable && !disabled,
|
|
347
|
+
onDrop: (fromId, at) => {
|
|
348
|
+
setRows(prev => {
|
|
349
|
+
const fromIdx = prev.findIndex(r => r.id === fromId)
|
|
350
|
+
if (fromIdx < 0) return prev
|
|
351
|
+
return reorderRows(prev, fromIdx, at)
|
|
352
|
+
})
|
|
353
|
+
},
|
|
354
|
+
})
|
|
370
355
|
|
|
371
356
|
// ── Inner-field live re-resolve (Plan #14 v1.1) ─────────────
|
|
372
357
|
// Inner Repeater inputs are uncontrolled (so reorder/clone preserves
|
|
@@ -487,7 +472,6 @@ export function RepeaterInput({
|
|
|
487
472
|
<RepeaterTableLayout
|
|
488
473
|
rows={rows}
|
|
489
474
|
name={name}
|
|
490
|
-
formId={formId}
|
|
491
475
|
disabled={disabled}
|
|
492
476
|
columns={tableColumns}
|
|
493
477
|
addLabel={addLabel}
|
|
@@ -939,7 +923,7 @@ function DropIndicator(): React.ReactElement {
|
|
|
939
923
|
* stays stable across rows even when individual buttons disable.
|
|
940
924
|
*/
|
|
941
925
|
function RepeaterTableLayout({
|
|
942
|
-
rows, name,
|
|
926
|
+
rows, name, disabled, columns, addLabel, buttons, atMin, atMax,
|
|
943
927
|
reorderable, cloneable,
|
|
944
928
|
firstVisibleIdx, lastVisibleIdx, hasVisibleRow,
|
|
945
929
|
dragId,
|
|
@@ -949,7 +933,6 @@ function RepeaterTableLayout({
|
|
|
949
933
|
}: {
|
|
950
934
|
rows: RowState[]
|
|
951
935
|
name: string
|
|
952
|
-
formId: string
|
|
953
936
|
disabled: boolean
|
|
954
937
|
columns: TableColumnShape[]
|
|
955
938
|
addLabel: string
|
|
@@ -1265,88 +1248,4 @@ function prefixFieldNames(el: ElementMeta, prefix: string): ElementMeta {
|
|
|
1265
1248
|
return el
|
|
1266
1249
|
}
|
|
1267
1250
|
|
|
1268
|
-
let _rowSeqFallback = 0
|
|
1269
|
-
function generateRowId(): string {
|
|
1270
|
-
type CryptoLike = { randomUUID?: () => string }
|
|
1271
|
-
const c = (globalThis as { crypto?: CryptoLike }).crypto
|
|
1272
|
-
if (c?.randomUUID) return c.randomUUID()
|
|
1273
|
-
return `row-${Date.now()}-${++_rowSeqFallback}`
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
function collapsedStorageKey(formId: string, name: string, rowId: string): string {
|
|
1277
|
-
return `pilotiq.repeater.${formId}.${name}.${rowId}`
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
function initSeedCollapsed(
|
|
1281
|
-
rows: RowState[],
|
|
1282
|
-
formId: string,
|
|
1283
|
-
name: string,
|
|
1284
|
-
defaultValue: boolean,
|
|
1285
|
-
collapsible: boolean,
|
|
1286
|
-
): Record<string, boolean> {
|
|
1287
|
-
if (!collapsible) return {}
|
|
1288
|
-
const out: Record<string, boolean> = {}
|
|
1289
|
-
for (const row of rows) {
|
|
1290
|
-
out[row.id] = readCollapsedFromStorage(formId, name, row.id, defaultValue)
|
|
1291
|
-
}
|
|
1292
|
-
return out
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
function readCollapsedFromStorage(
|
|
1296
|
-
formId: string,
|
|
1297
|
-
name: string,
|
|
1298
|
-
rowId: string,
|
|
1299
|
-
defaultValue: boolean,
|
|
1300
|
-
): boolean {
|
|
1301
|
-
if (typeof window === 'undefined') return defaultValue
|
|
1302
|
-
try {
|
|
1303
|
-
const raw = window.localStorage.getItem(collapsedStorageKey(formId, name, rowId))
|
|
1304
|
-
if (raw === null) return defaultValue
|
|
1305
|
-
return raw === 'true'
|
|
1306
|
-
} catch { return defaultValue }
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
function writeCollapsedToStorage(
|
|
1310
|
-
formId: string,
|
|
1311
|
-
name: string,
|
|
1312
|
-
rowId: string,
|
|
1313
|
-
value: boolean,
|
|
1314
|
-
): void {
|
|
1315
|
-
if (typeof window === 'undefined') return
|
|
1316
|
-
try {
|
|
1317
|
-
window.localStorage.setItem(collapsedStorageKey(formId, name, rowId), String(value))
|
|
1318
|
-
} catch { /* quota exceeded — fall back to in-memory only */ }
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
function deleteCollapsedFromStorage(formId: string, name: string, rowId: string): void {
|
|
1322
|
-
if (typeof window === 'undefined') return
|
|
1323
|
-
try {
|
|
1324
|
-
window.localStorage.removeItem(collapsedStorageKey(formId, name, rowId))
|
|
1325
|
-
} catch { /* ignore */ }
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
function accordionStorageKey(formId: string, name: string): string {
|
|
1329
|
-
return `pilotiq.repeater.${formId}.${name}.accordion`
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
/**
|
|
1333
|
-
* Read the persisted accordion-open row id. Returns `undefined` when no
|
|
1334
|
-
* value is stored (so the caller can fall back to the default-open
|
|
1335
|
-
* heuristic). Returns `''` when the user explicitly closed every row —
|
|
1336
|
-
* the caller maps that to `null` openId.
|
|
1337
|
-
*/
|
|
1338
|
-
function readAccordionFromStorage(formId: string, name: string): string | undefined {
|
|
1339
|
-
if (typeof window === 'undefined') return undefined
|
|
1340
|
-
try {
|
|
1341
|
-
const raw = window.localStorage.getItem(accordionStorageKey(formId, name))
|
|
1342
|
-
return raw === null ? undefined : raw
|
|
1343
|
-
} catch { return undefined }
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
function writeAccordionToStorage(formId: string, name: string, openId: string | null): void {
|
|
1347
|
-
if (typeof window === 'undefined') return
|
|
1348
|
-
try {
|
|
1349
|
-
window.localStorage.setItem(accordionStorageKey(formId, name), openId ?? '')
|
|
1350
|
-
} catch { /* quota exceeded — fall back to in-memory only */ }
|
|
1351
|
-
}
|
|
1352
1251
|
|