@jini-ai/admin 0.3.9 → 0.4.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/CHANGELOG.md +58 -0
- package/README.md +71 -37
- package/dist/agent-plugins/adapters/http.d.ts +7 -0
- package/dist/agent-plugins/adapters/http.js +15 -0
- package/dist/agent-plugins/adapters/memory.d.ts +8 -0
- package/dist/agent-plugins/adapters/memory.js +25 -0
- package/dist/agent-plugins/agent-plugins.module.d.ts +51 -0
- package/dist/agent-plugins/agent-plugins.module.js +6 -0
- package/dist/agent-plugins/conformance/agent-plugins-api.conformance.d.ts +12 -0
- package/dist/agent-plugins/conformance/agent-plugins-api.conformance.js +47 -0
- package/dist/agent-plugins/controllers/agent-plugin-files.controller.d.ts +20 -0
- package/dist/agent-plugins/controllers/agent-plugin-files.controller.js +43 -0
- package/dist/agent-plugins/controllers/agent-plugins.controller.d.ts +25 -0
- package/dist/agent-plugins/controllers/agent-plugins.controller.js +91 -0
- package/dist/agent-plugins/index.d.ts +9 -0
- package/dist/agent-plugins/index.js +9 -0
- package/dist/agent-plugins/messages.en.d.ts +23 -0
- package/dist/agent-plugins/messages.en.js +14 -0
- package/dist/agent-plugins/models.d.ts +57 -0
- package/dist/agent-plugins/models.js +2 -0
- package/dist/agent-plugins/package-file-tree.d.ts +111 -0
- package/dist/agent-plugins/package-file-tree.js +188 -0
- package/dist/agent-plugins/ports.d.ts +18 -0
- package/dist/agent-plugins/ports.js +3 -0
- package/dist/agent-plugins/react/components/AgentPluginFileContent.d.ts +5 -0
- package/dist/agent-plugins/react/components/AgentPluginFileContent.js +9 -0
- package/dist/agent-plugins/react/components/AgentPluginFileTree.d.ts +9 -0
- package/dist/agent-plugins/react/components/AgentPluginFileTree.js +7 -0
- package/dist/agent-plugins/react/components/AgentPluginInspector.d.ts +3 -0
- package/dist/agent-plugins/react/components/AgentPluginInspector.js +11 -0
- package/dist/agent-plugins/react/components/AgentPluginListPanel.d.ts +6 -0
- package/dist/agent-plugins/react/components/AgentPluginListPanel.js +10 -0
- package/dist/agent-plugins/react/components/AgentPluginRow.d.ts +5 -0
- package/dist/agent-plugins/react/components/AgentPluginRow.js +8 -0
- package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.d.ts +90 -0
- package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.js +74 -0
- package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.d.ts +67 -0
- package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.js +50 -0
- package/dist/agent-plugins/react/hooks/AgentPluginRow.hooks.d.ts +55 -0
- package/dist/agent-plugins/react/hooks/AgentPluginRow.hooks.js +6 -0
- package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.d.ts +64 -0
- package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.js +39 -0
- package/dist/agent-plugins/react/hooks/AgentPluginsPorts.hooks.d.ts +7 -0
- package/dist/agent-plugins/react/hooks/AgentPluginsPorts.hooks.js +10 -0
- package/dist/agent-plugins/react/hooks/MarketplaceTab.hooks.d.ts +6 -0
- package/dist/agent-plugins/react/hooks/MarketplaceTab.hooks.js +3 -0
- package/dist/agent-plugins/react/index.d.ts +245 -0
- package/dist/agent-plugins/react/index.js +13 -0
- package/dist/agent-plugins/react/pages/AgentPluginsPage.d.ts +4 -0
- package/dist/agent-plugins/react/pages/AgentPluginsPage.js +12 -0
- package/dist/agent-plugins/react/tabs/DownloadedTab.d.ts +4 -0
- package/dist/agent-plugins/react/tabs/DownloadedTab.js +5 -0
- package/dist/agent-plugins/react/tabs/InstalledTab.d.ts +4 -0
- package/dist/agent-plugins/react/tabs/InstalledTab.js +5 -0
- package/dist/agent-plugins/react/tabs/MarketplaceTab.d.ts +4 -0
- package/dist/agent-plugins/react/tabs/MarketplaceTab.js +10 -0
- package/dist/agent-plugins/rules.d.ts +33 -0
- package/dist/agent-plugins/rules.js +51 -0
- package/dist/browser/index.d.ts +1 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/navigation.d.ts +19 -4
- package/dist/browser/navigation.js +8 -8
- package/dist/browser/shell-navigation.d.ts +12 -0
- package/dist/browser/shell-navigation.js +16 -0
- package/dist/contracts/credential-management.d.ts +8 -0
- package/dist/contracts/credential-management.js +3 -0
- package/dist/contracts/media-picker.d.ts +17 -0
- package/dist/contracts/media-picker.js +5 -0
- package/dist/contracts/playground-render-target.d.ts +18 -0
- package/dist/contracts/playground-render-target.js +3 -0
- package/dist/core/entities/rules.d.ts +138 -0
- package/dist/core/entities/rules.js +344 -0
- package/dist/core/gated/types.d.ts +8 -4
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +1 -0
- package/dist/core/manifest/rules.d.ts +24 -6
- package/dist/core/manifest/rules.js +12 -11
- package/dist/core/module/controller-store.d.ts +22 -0
- package/dist/core/module/controller-store.js +32 -0
- package/dist/core/module/create-admin.d.ts +41 -0
- package/dist/core/module/create-admin.js +187 -0
- package/dist/core/module/define-admin-module.d.ts +4 -0
- package/dist/core/module/define-admin-module.js +5 -0
- package/dist/core/module/index.d.ts +7 -0
- package/dist/core/module/index.js +4 -0
- package/dist/core/module/token.d.ts +19 -0
- package/dist/core/module/token.js +4 -0
- package/dist/core/module/types.d.ts +79 -0
- package/dist/core/module/types.js +2 -0
- package/dist/core/permissions/rules.d.ts +4 -1
- package/dist/core/permissions/rules.js +1 -1
- package/dist/core/ports/analytics.d.ts +1 -1
- package/dist/core/ports/auth.d.ts +2 -2
- package/dist/core/ports/comments.d.ts +9 -6
- package/dist/core/ports/database.d.ts +13 -10
- package/dist/core/ports/entities.d.ts +302 -0
- package/dist/core/ports/entities.js +47 -0
- package/dist/core/ports/forms.d.ts +19 -7
- package/dist/core/ports/identity.d.ts +42 -17
- package/dist/core/ports/index.d.ts +2 -0
- package/dist/core/ports/integrations.d.ts +9 -4
- package/dist/core/ports/media.d.ts +16 -7
- package/dist/core/ports/members.d.ts +7 -3
- package/dist/core/ports/menus.d.ts +43 -0
- package/dist/core/ports/menus.js +43 -0
- package/dist/core/ports/plugins.d.ts +3 -2
- package/dist/core/ports/recovery.d.ts +10 -6
- package/dist/core/ports/redirects.d.ts +17 -7
- package/dist/core/ports/seo.d.ts +12 -6
- package/dist/core/ports/shell.d.ts +41 -0
- package/dist/core/ports/shell.js +2 -0
- package/dist/core/ports/workspace.d.ts +3 -3
- package/dist/core/routing/rules.d.ts +17 -4
- package/dist/core/routing/rules.js +4 -4
- package/dist/core/transport/errors.d.ts +13 -4
- package/dist/core/transport/errors.js +4 -4
- package/dist/core/transport/http.d.ts +26 -16
- package/dist/core/transport/http.js +59 -21
- package/dist/core/transport/types.d.ts +26 -14
- package/dist/core/transport/types.js +9 -7
- package/dist/media/adapters/http.d.ts +27 -0
- package/dist/media/adapters/http.js +44 -0
- package/dist/media/adapters/memory.d.ts +11 -0
- package/dist/media/adapters/memory.js +139 -0
- package/dist/media/conformance/media-api.conformance.d.ts +7 -0
- package/dist/media/conformance/media-api.conformance.js +69 -0
- package/dist/media/controllers/edit-media.controller.d.ts +24 -0
- package/dist/media/controllers/edit-media.controller.js +69 -0
- package/dist/media/controllers/library.controller.d.ts +47 -0
- package/dist/media/controllers/library.controller.js +173 -0
- package/dist/media/controllers/providers.controller.d.ts +30 -0
- package/dist/media/controllers/providers.controller.js +76 -0
- package/dist/media/html-attributes.d.ts +82 -0
- package/dist/media/html-attributes.js +204 -0
- package/dist/media/index.d.ts +12 -0
- package/dist/media/index.js +9 -0
- package/dist/media/media.module.d.ts +83 -0
- package/dist/media/media.module.js +43 -0
- package/dist/media/messages.en.d.ts +24 -0
- package/dist/media/messages.en.js +24 -0
- package/dist/media/models.d.ts +72 -0
- package/dist/media/models.js +2 -0
- package/dist/media/ports.d.ts +75 -0
- package/dist/media/ports.js +7 -0
- package/dist/media/react/components/EditMediaPanel.d.ts +3 -0
- package/dist/media/react/components/EditMediaPanel.js +10 -0
- package/dist/media/react/components/MediaCard.d.ts +5 -0
- package/dist/media/react/components/MediaCard.js +17 -0
- package/dist/media/react/components/MediaDialog.d.ts +3 -0
- package/dist/media/react/components/MediaDialog.js +8 -0
- package/dist/media/react/components/MediaGrid.d.ts +4 -0
- package/dist/media/react/components/MediaGrid.js +8 -0
- package/dist/media/react/components/MediaIcons.d.ts +14 -0
- package/dist/media/react/components/MediaIcons.js +39 -0
- package/dist/media/react/components/MediaLightbox.d.ts +5 -0
- package/dist/media/react/components/MediaLightbox.js +12 -0
- package/dist/media/react/components/MediaPicker.d.ts +3 -0
- package/dist/media/react/components/MediaPicker.js +10 -0
- package/dist/media/react/components/MediaPurgeDialog.d.ts +4 -0
- package/dist/media/react/components/MediaPurgeDialog.js +9 -0
- package/dist/media/react/components/UploadButton.d.ts +3 -0
- package/dist/media/react/components/UploadButton.js +8 -0
- package/dist/media/react/hooks/EditMediaPanel.hooks.d.ts +84 -0
- package/dist/media/react/hooks/EditMediaPanel.hooks.js +105 -0
- package/dist/media/react/hooks/LibraryTab.hooks.d.ts +103 -0
- package/dist/media/react/hooks/LibraryTab.hooks.js +164 -0
- package/dist/media/react/hooks/MediaCard.hooks.d.ts +79 -0
- package/dist/media/react/hooks/MediaCard.hooks.js +63 -0
- package/dist/media/react/hooks/MediaDialog.hooks.d.ts +10 -0
- package/dist/media/react/hooks/MediaDialog.hooks.js +20 -0
- package/dist/media/react/hooks/MediaGrid.hooks.d.ts +28 -0
- package/dist/media/react/hooks/MediaGrid.hooks.js +6 -0
- package/dist/media/react/hooks/MediaLightbox.hooks.d.ts +27 -0
- package/dist/media/react/hooks/MediaLightbox.hooks.js +64 -0
- package/dist/media/react/hooks/MediaPage.hooks.d.ts +68 -0
- package/dist/media/react/hooks/MediaPage.hooks.js +68 -0
- package/dist/media/react/hooks/MediaPicker.hooks.d.ts +26 -0
- package/dist/media/react/hooks/MediaPicker.hooks.js +25 -0
- package/dist/media/react/hooks/MediaPorts.hooks.d.ts +9 -0
- package/dist/media/react/hooks/MediaPorts.hooks.js +10 -0
- package/dist/media/react/hooks/MediaPurgeDialog.hooks.d.ts +22 -0
- package/dist/media/react/hooks/MediaPurgeDialog.hooks.js +6 -0
- package/dist/media/react/hooks/ProvidersTab.hooks.d.ts +55 -0
- package/dist/media/react/hooks/ProvidersTab.hooks.js +124 -0
- package/dist/media/react/hooks/UploadButton.hooks.d.ts +48 -0
- package/dist/media/react/hooks/UploadButton.hooks.js +118 -0
- package/dist/media/react/index.d.ts +373 -0
- package/dist/media/react/index.js +73 -0
- package/dist/media/react/pages/MediaPage.d.ts +4 -0
- package/dist/media/react/pages/MediaPage.js +9 -0
- package/dist/media/react/tabs/LibraryTab.d.ts +4 -0
- package/dist/media/react/tabs/LibraryTab.js +14 -0
- package/dist/media/react/tabs/ProvidersTab.d.ts +4 -0
- package/dist/media/react/tabs/ProvidersTab.js +10 -0
- package/dist/media/rules.d.ts +80 -0
- package/dist/media/rules.js +127 -0
- package/dist/media/styles.d.ts +4 -0
- package/dist/media/styles.js +5 -0
- package/dist/playground/adapters/http.d.ts +8 -0
- package/dist/playground/adapters/http.js +4 -0
- package/dist/playground/adapters/memory.d.ts +10 -0
- package/dist/playground/adapters/memory.js +30 -0
- package/dist/playground/conformance/playground-api.conformance.d.ts +6 -0
- package/dist/playground/conformance/playground-api.conformance.js +37 -0
- package/dist/playground/controllers/playground.controller.d.ts +16 -0
- package/dist/playground/controllers/playground.controller.js +21 -0
- package/dist/playground/index.d.ts +7 -0
- package/dist/playground/index.js +5 -0
- package/dist/playground/messages.en.d.ts +9 -0
- package/dist/playground/messages.en.js +2 -0
- package/dist/playground/models.d.ts +4 -0
- package/dist/playground/models.js +2 -0
- package/dist/playground/playground.module.d.ts +31 -0
- package/dist/playground/playground.module.js +5 -0
- package/dist/playground/ports.d.ts +5 -0
- package/dist/playground/ports.js +2 -0
- package/dist/playground/react/components/Canvas.d.ts +3 -0
- package/dist/playground/react/components/Canvas.js +9 -0
- package/dist/playground/react/hooks/Canvas.hooks.d.ts +7 -0
- package/dist/playground/react/hooks/Canvas.hooks.js +18 -0
- package/dist/playground/react/hooks/PlaygroundPage.hooks.d.ts +8 -0
- package/dist/playground/react/hooks/PlaygroundPage.hooks.js +2 -0
- package/dist/playground/react/hooks/PlaygroundPorts.hooks.d.ts +4 -0
- package/dist/playground/react/hooks/PlaygroundPorts.hooks.js +6 -0
- package/dist/playground/react/index.d.ts +136 -0
- package/dist/playground/react/index.js +10 -0
- package/dist/playground/react/pages/PlaygroundPage.d.ts +4 -0
- package/dist/playground/react/pages/PlaygroundPage.js +11 -0
- package/dist/playground/react/tabs/CanvasTab.d.ts +4 -0
- package/dist/playground/react/tabs/CanvasTab.js +5 -0
- package/dist/playground/rules.d.ts +4 -0
- package/dist/playground/rules.js +2 -0
- package/dist/react/OverlayHost.d.ts +3 -0
- package/dist/react/OverlayHost.hooks.d.ts +9 -0
- package/dist/react/OverlayHost.hooks.js +9 -0
- package/dist/react/OverlayHost.js +7 -0
- package/dist/react/bind-react.d.ts +46 -0
- package/dist/react/bind-react.js +28 -0
- package/dist/react/components/ConfirmButton/ConfirmButton.hooks.d.ts +7 -1
- package/dist/react/components/ConfirmButton/ConfirmButton.hooks.js +2 -2
- package/dist/react/components/ConfirmButton/ConfirmButton.js +1 -1
- package/dist/react/components/ConfirmDialog/ConfirmDialog.d.ts +15 -3
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.d.ts +17 -3
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.js +3 -3
- package/dist/react/components/ConfirmDialog/ConfirmDialog.js +6 -6
- package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.d.ts +6 -2
- package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.js +7 -8
- package/dist/react/components/RowMenu/RowMenu.d.ts +5 -1
- package/dist/react/components/RowMenu/RowMenu.hooks.d.ts +8 -2
- package/dist/react/components/RowMenu/RowMenu.hooks.js +4 -4
- package/dist/react/components/RowMenu/RowMenu.js +6 -6
- package/dist/react/components/Sidebar.d.ts +1 -1
- package/dist/react/components/Sidebar.js +103 -7
- package/dist/react/entities/EntityCell.d.ts +10 -0
- package/dist/react/entities/EntityCell.js +18 -0
- package/dist/react/entities/EntityDetail.d.ts +3 -0
- package/dist/react/entities/EntityDetail.js +21 -0
- package/dist/react/entities/EntityEdit.d.ts +13 -0
- package/dist/react/entities/EntityEdit.js +45 -0
- package/dist/react/entities/EntityField.d.ts +20 -0
- package/dist/react/entities/EntityField.js +31 -0
- package/dist/react/entities/EntityIndex.d.ts +3 -0
- package/dist/react/entities/EntityIndex.js +10 -0
- package/dist/react/entities/EntityList.d.ts +7 -0
- package/dist/react/entities/EntityList.js +52 -0
- package/dist/react/entities/contribution.d.ts +26 -0
- package/dist/react/entities/contribution.js +78 -0
- package/dist/react/entities/index.d.ts +10 -0
- package/dist/react/entities/index.js +7 -0
- package/dist/react/entities/rules.d.ts +179 -0
- package/dist/react/entities/rules.js +224 -0
- package/dist/react/entities/types.d.ts +45 -0
- package/dist/react/entities/types.js +5 -0
- package/dist/react/entities/use-entity-edit.d.ts +35 -0
- package/dist/react/entities/use-entity-edit.js +98 -0
- package/dist/react/entities/use-entity-read.d.ts +22 -0
- package/dist/react/entities/use-entity-read.js +27 -0
- package/dist/react/hooks/use-nav-sections.d.ts +12 -3
- package/dist/react/hooks/use-nav-sections.js +14 -14
- package/dist/react/hooks/use-sidebar-rail.d.ts +7 -1
- package/dist/react/hooks/use-sidebar-rail.js +11 -11
- package/dist/react/index.d.ts +3 -1
- package/dist/react/index.js +5 -2
- package/dist/react/kit/Kit.hooks.d.ts +13 -0
- package/dist/react/kit/Kit.hooks.js +10 -0
- package/dist/react/kit/Native.hooks.d.ts +38 -0
- package/dist/react/kit/Native.hooks.js +65 -0
- package/dist/react/kit/index.d.ts +12 -0
- package/dist/react/kit/index.js +21 -0
- package/dist/react/kit/native.d.ts +3 -0
- package/dist/react/kit/native.js +21 -0
- package/dist/react/kit/types.d.ts +40 -0
- package/dist/react/kit/types.js +2 -0
- package/dist/react/overlay.d.ts +36 -0
- package/dist/react/overlay.js +35 -0
- package/dist/react/overlays.d.ts +38 -0
- package/dist/react/overlays.js +46 -0
- package/dist/react/shell/AdminShell.d.ts +9 -0
- package/dist/react/shell/AdminShell.hooks.d.ts +15 -0
- package/dist/react/shell/AdminShell.hooks.js +27 -0
- package/dist/react/shell/AdminShell.js +46 -0
- package/dist/react/shell/index.d.ts +7 -0
- package/dist/react/shell/index.js +5 -0
- package/dist/react/shell/model.d.ts +25 -0
- package/dist/react/shell/model.js +29 -0
- package/dist/react/shell/theme-ports.d.ts +28 -0
- package/dist/react/shell/theme-ports.js +2 -0
- package/dist/react/shell/types.d.ts +99 -0
- package/dist/react/shell/types.js +2 -0
- package/dist/react/shell/use-admin-shell-session.d.ts +13 -0
- package/dist/react/shell/use-admin-shell-session.js +81 -0
- package/dist/react/shell/use-admin-theme.d.ts +16 -0
- package/dist/react/shell/use-admin-theme.js +57 -0
- package/dist/react/types.d.ts +6 -4
- package/dist/react/types.js +2 -2
- package/dist/react/use-controller.d.ts +16 -0
- package/dist/react/use-controller.js +18 -0
- package/dist/security/adapters/http.d.ts +16 -0
- package/dist/security/adapters/http.js +94 -0
- package/dist/security/adapters/memory.d.ts +15 -0
- package/dist/security/adapters/memory.js +60 -0
- package/dist/security/conformance/security-api.conformance.d.ts +10 -0
- package/dist/security/conformance/security-api.conformance.js +48 -0
- package/dist/security/controllers/access-tokens.controller.d.ts +44 -0
- package/dist/security/controllers/access-tokens.controller.js +112 -0
- package/dist/security/controllers/other-credentials.controller.d.ts +30 -0
- package/dist/security/controllers/other-credentials.controller.js +74 -0
- package/dist/security/controllers/root-key.controller.d.ts +35 -0
- package/dist/security/controllers/root-key.controller.js +90 -0
- package/dist/security/index.d.ts +9 -0
- package/dist/security/index.js +9 -0
- package/dist/security/messages.en.d.ts +12 -0
- package/dist/security/messages.en.js +2 -0
- package/dist/security/models.d.ts +112 -0
- package/dist/security/models.js +2 -0
- package/dist/security/ports.d.ts +58 -0
- package/dist/security/ports.js +5 -0
- package/dist/security/react/components/CredentialEditor.d.ts +3 -0
- package/dist/security/react/components/CredentialEditor.js +8 -0
- package/dist/security/react/hooks/AccessTokensTab.hooks.d.ts +94 -0
- package/dist/security/react/hooks/AccessTokensTab.hooks.js +36 -0
- package/dist/security/react/hooks/CredentialEditor.hooks.d.ts +49 -0
- package/dist/security/react/hooks/CredentialEditor.hooks.js +14 -0
- package/dist/security/react/hooks/RootKeyTab.hooks.d.ts +29 -0
- package/dist/security/react/hooks/RootKeyTab.hooks.js +19 -0
- package/dist/security/react/hooks/SecurityPage.hooks.d.ts +17 -0
- package/dist/security/react/hooks/SecurityPage.hooks.js +12 -0
- package/dist/security/react/hooks/SecurityPorts.hooks.d.ts +9 -0
- package/dist/security/react/hooks/SecurityPorts.hooks.js +6 -0
- package/dist/security/react/index.d.ts +215 -0
- package/dist/security/react/index.js +10 -0
- package/dist/security/react/pages/SecurityPage.d.ts +4 -0
- package/dist/security/react/pages/SecurityPage.js +10 -0
- package/dist/security/react/tabs/AccessTokensTab.d.ts +4 -0
- package/dist/security/react/tabs/AccessTokensTab.js +10 -0
- package/dist/security/react/tabs/RootKeyTab.d.ts +4 -0
- package/dist/security/react/tabs/RootKeyTab.js +9 -0
- package/dist/security/rules.d.ts +57 -0
- package/dist/security/rules.js +63 -0
- package/dist/security/security.module.d.ts +45 -0
- package/dist/security/security.module.js +5 -0
- package/dist/source-control/adapters/http.d.ts +6 -0
- package/dist/source-control/adapters/http.js +13 -0
- package/dist/source-control/adapters/memory.d.ts +9 -0
- package/dist/source-control/adapters/memory.js +35 -0
- package/dist/source-control/conformance/source-control-api.conformance.d.ts +10 -0
- package/dist/source-control/conformance/source-control-api.conformance.js +48 -0
- package/dist/source-control/controllers/source-control.controller.d.ts +26 -0
- package/dist/source-control/controllers/source-control.controller.js +70 -0
- package/dist/source-control/index.d.ts +8 -0
- package/dist/source-control/index.js +6 -0
- package/dist/source-control/messages.en.d.ts +11 -0
- package/dist/source-control/messages.en.js +2 -0
- package/dist/source-control/models.d.ts +70 -0
- package/dist/source-control/models.js +2 -0
- package/dist/source-control/ports.d.ts +23 -0
- package/dist/source-control/ports.js +3 -0
- package/dist/source-control/react/components/SourceControlCredentialFields.d.ts +5 -0
- package/dist/source-control/react/components/SourceControlCredentialFields.js +8 -0
- package/dist/source-control/react/components/SourceControlProviderRow.d.ts +5 -0
- package/dist/source-control/react/components/SourceControlProviderRow.js +7 -0
- package/dist/source-control/react/hooks/ProvidersTab.hooks.d.ts +45 -0
- package/dist/source-control/react/hooks/ProvidersTab.hooks.js +20 -0
- package/dist/source-control/react/hooks/SourceControlCredentialFields.hooks.d.ts +43 -0
- package/dist/source-control/react/hooks/SourceControlCredentialFields.hooks.js +9 -0
- package/dist/source-control/react/hooks/SourceControlPage.hooks.d.ts +17 -0
- package/dist/source-control/react/hooks/SourceControlPage.hooks.js +5 -0
- package/dist/source-control/react/hooks/SourceControlPorts.hooks.d.ts +9 -0
- package/dist/source-control/react/hooks/SourceControlPorts.hooks.js +6 -0
- package/dist/source-control/react/index.d.ts +156 -0
- package/dist/source-control/react/index.js +10 -0
- package/dist/source-control/react/pages/SourceControlPage.d.ts +4 -0
- package/dist/source-control/react/pages/SourceControlPage.js +11 -0
- package/dist/source-control/react/tabs/ProvidersTab.d.ts +4 -0
- package/dist/source-control/react/tabs/ProvidersTab.js +10 -0
- package/dist/source-control/rules.d.ts +32 -0
- package/dist/source-control/rules.js +35 -0
- package/dist/source-control/source-control.module.d.ts +36 -0
- package/dist/source-control/source-control.module.js +6 -0
- package/package.json +207 -4
|
@@ -7,7 +7,7 @@ const VIEWPORT_MARGIN = 8;
|
|
|
7
7
|
* @param itemCount Length of the caller's item list — the only piece of `RowMenuProps` this state
|
|
8
8
|
* needs, for `ArrowUp`-to-last-item and wraparound navigation.
|
|
9
9
|
*/
|
|
10
|
-
export function useRowMenu(itemCount) {
|
|
10
|
+
export function useRowMenu({ itemCount, window, document }) {
|
|
11
11
|
const [open, setOpen] = useState(false);
|
|
12
12
|
const [position, setPosition] = useState(null);
|
|
13
13
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
@@ -59,7 +59,7 @@ export function useRowMenu(itemCount) {
|
|
|
59
59
|
window.removeEventListener('scroll', reposition, true);
|
|
60
60
|
window.removeEventListener('resize', reposition);
|
|
61
61
|
};
|
|
62
|
-
}, [open]);
|
|
62
|
+
}, [open, window, document]);
|
|
63
63
|
// Click-outside closes. Scoped to the open window only, same lifecycle discipline as
|
|
64
64
|
// `ConfirmButton`'s armed-only document listeners — a closed, idle `RowMenu` costs nothing beyond
|
|
65
65
|
// its trigger button even with many mounted per table.
|
|
@@ -75,7 +75,7 @@ export function useRowMenu(itemCount) {
|
|
|
75
75
|
document.addEventListener('mousedown', onDocMouseDown);
|
|
76
76
|
return () => document.removeEventListener('mousedown', onDocMouseDown);
|
|
77
77
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
78
|
-
}, [open]);
|
|
78
|
+
}, [open, window, document]);
|
|
79
79
|
// Keeps real DOM focus on the active item (roving focus via `tabIndex={-1}` on every item except
|
|
80
80
|
// the active one) rather than only tracking `activeIndex` in state — arrow-key navigation needs
|
|
81
81
|
// to move the browser's actual focus for a screen reader to announce it.
|
|
@@ -135,7 +135,7 @@ export function useRowMenu(itemCount) {
|
|
|
135
135
|
/** Closes (returning focus to the trigger, same as Escape) and then fires the selected item's own
|
|
136
136
|
* callback — takes the callback directly rather than a whole `RowMenuItem` so this hook stays
|
|
137
137
|
* free of any dependency on that component-level type. */
|
|
138
|
-
function selectItem(onSelect) {
|
|
138
|
+
function selectItem({ onSelect }) {
|
|
139
139
|
close(true);
|
|
140
140
|
onSelect();
|
|
141
141
|
}
|
|
@@ -15,15 +15,15 @@ import { useRowMenu } from './RowMenu.hooks.js';
|
|
|
15
15
|
* @complexity O(1).
|
|
16
16
|
*/
|
|
17
17
|
function rowMenuAgentProps(handle, options) {
|
|
18
|
-
return handle === undefined ? {} : agentHandle(handle, options);
|
|
18
|
+
return handle === undefined ? {} : agentHandle({ handle }, options);
|
|
19
19
|
}
|
|
20
20
|
export function RowMenu({ useRowMenu: useRowMenuState = useRowMenu, agentHandle: baseHandle, ...props }) {
|
|
21
|
-
const { open, position, triggerRef, menuRef, itemRefs, onTriggerClick, onTriggerKeyDown, onMenuKeyDown, selectItem, } = useRowMenuState(props.items.length);
|
|
21
|
+
const { open, position, triggerRef, menuRef, itemRefs, onTriggerClick, onTriggerKeyDown, onMenuKeyDown, selectItem, } = useRowMenuState({ itemCount: props.items.length, window, document });
|
|
22
22
|
// One base handle per item, positionally aligned with `props.items` — undefined (rather than an
|
|
23
23
|
// empty array) when the caller published no `agentHandle` at all, so the render below can tell
|
|
24
24
|
// "opted out" apart from "an empty item list" without a second flag.
|
|
25
25
|
const itemHandles = baseHandle
|
|
26
|
-
? buildAgentListHandles(`${baseHandle}-item`, props.items.map((item) => item.key))
|
|
26
|
+
? buildAgentListHandles({ prefix: `${baseHandle}-item`, ids: props.items.map((item) => item.key) })
|
|
27
27
|
: undefined;
|
|
28
28
|
return (_jsxs(_Fragment, { children: [_jsx("button", { ref: triggerRef, type: "button", className: "row-menu-trigger", "aria-label": props.triggerLabel, "aria-haspopup": "menu", "aria-expanded": open, onClick: onTriggerClick, onKeyDown: onTriggerKeyDown, ...rowMenuAgentProps(baseHandle, { role: 'button', label: props.triggerLabel }), children: _jsxs("svg", { viewBox: "0 0 18 18", fill: "currentColor", "aria-hidden": "true", children: [_jsx("circle", { cx: "9", cy: "4.5", r: "1.5" }), _jsx("circle", { cx: "9", cy: "9", r: "1.5" }), _jsx("circle", { cx: "9", cy: "13.5", r: "1.5" })] }) }), open
|
|
29
29
|
? createPortal(_jsx("div", { ref: menuRef, role: "menu", "aria-label": props.triggerLabel, className: `row-menu-popup${position?.placement === 'above' ? ' row-menu-popup-above' : ''}`, style: {
|
|
@@ -36,11 +36,11 @@ export function RowMenu({ useRowMenu: useRowMenuState = useRowMenu, agentHandle:
|
|
|
36
36
|
visibility: position ? 'visible' : 'hidden',
|
|
37
37
|
transform: position?.placement === 'above' ? 'translateY(-100%)' : undefined,
|
|
38
38
|
}, onKeyDown: onMenuKeyDown, children: props.items.map((item, index) => {
|
|
39
|
-
const toneClass = toneClassName(resolveTone(item));
|
|
39
|
+
const toneClass = toneClassName({ tone: resolveTone({}, item) });
|
|
40
40
|
return (_jsx("button", { ref: (el) => {
|
|
41
41
|
itemRefs.current[index] = el;
|
|
42
|
-
}, type: "button", role: "menuitem", tabIndex: -1, className: ['row-menu-item', toneClass].filter(Boolean).join(' '), onClick: () => selectItem(item.onSelect), ...rowMenuAgentProps(itemHandles?.[index], { role: 'button', label: item.label }), children: item.label }, item.key));
|
|
43
|
-
}) }), document.body)
|
|
42
|
+
}, type: "button", role: "menuitem", tabIndex: -1, className: ['row-menu-item', toneClass].filter(Boolean).join(' '), onClick: () => selectItem({ onSelect: item.onSelect }), ...rowMenuAgentProps(itemHandles?.[index], { role: 'button', label: item.label }), children: item.label }, item.key));
|
|
43
|
+
}) }), props.portalContainer ?? document.body)
|
|
44
44
|
: null] }));
|
|
45
45
|
}
|
|
46
46
|
//# sourceMappingURL=RowMenu.js.map
|
|
@@ -72,7 +72,7 @@ export interface SidebarMobileHeaderProps {
|
|
|
72
72
|
*/
|
|
73
73
|
declare function SidebarMobileHeader(props: SidebarMobileHeaderProps): import("react").JSX.Element;
|
|
74
74
|
export interface SidebarNavProps {
|
|
75
|
-
/** From `/core`'s `buildNav(resolvePanels(panels, context))`. */
|
|
75
|
+
/** From `/core`'s `buildNav({ panels: resolvePanels({ panels }, context) })`. */
|
|
76
76
|
groups: readonly AdminNavGroup[];
|
|
77
77
|
/** Label on a `soon` item's badge. @default "Soon" */
|
|
78
78
|
soonLabel?: ReactNode;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment, createContext, useContext, useEffect, useMemo, useRef, useState, } from 'react';
|
|
2
|
+
import { Fragment, createContext, createElement, useContext, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import { DEFAULT_ADMIN_BASE, adminHref } from '../../core/routing/rules.js';
|
|
5
5
|
import { useNavSections } from '../hooks/use-nav-sections.js';
|
|
@@ -18,8 +18,96 @@ export function useSidebar() {
|
|
|
18
18
|
}
|
|
19
19
|
return value;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Shape elements `AdminNavEntry.icon` markup may use. A host draws its own glyphs as raw inner
|
|
23
|
+
* SVG (see that field's doc comment in `core/manifest/types.ts`), so this can't be a fixed set of
|
|
24
|
+
* named icons — but it also means the string reaching {@link Icon} was written by whoever owns the
|
|
25
|
+
* nav entry, which soon enough will be a descriptor rather than code. `g` is included so multi-path
|
|
26
|
+
* icons can group and transform their pieces together.
|
|
27
|
+
*/
|
|
28
|
+
const ICON_ALLOWED_TAGS = new Set(['path', 'circle', 'rect', 'line', 'polygon', 'polyline', 'ellipse', 'g']);
|
|
29
|
+
/**
|
|
30
|
+
* Presentation attributes let through on an icon shape, mapped from their SVG/XML spelling to the
|
|
31
|
+
* camelCase prop name React expects. Anything not listed here — `style`, `href`/`xlink:href`, every
|
|
32
|
+
* `on*` handler — is dropped by {@link sanitizeIconNode} rather than passed through, so there is no
|
|
33
|
+
* allowlist gap to keep in sync with a separate denylist.
|
|
34
|
+
*/
|
|
35
|
+
const ICON_ALLOWED_ATTRS = {
|
|
36
|
+
d: 'd',
|
|
37
|
+
cx: 'cx',
|
|
38
|
+
cy: 'cy',
|
|
39
|
+
r: 'r',
|
|
40
|
+
rx: 'rx',
|
|
41
|
+
ry: 'ry',
|
|
42
|
+
x: 'x',
|
|
43
|
+
y: 'y',
|
|
44
|
+
width: 'width',
|
|
45
|
+
height: 'height',
|
|
46
|
+
x1: 'x1',
|
|
47
|
+
y1: 'y1',
|
|
48
|
+
x2: 'x2',
|
|
49
|
+
y2: 'y2',
|
|
50
|
+
points: 'points',
|
|
51
|
+
transform: 'transform',
|
|
52
|
+
fill: 'fill',
|
|
53
|
+
'fill-rule': 'fillRule',
|
|
54
|
+
opacity: 'opacity',
|
|
55
|
+
'stroke-width': 'strokeWidth',
|
|
56
|
+
'stroke-linecap': 'strokeLinecap',
|
|
57
|
+
'stroke-linejoin': 'strokeLinejoin',
|
|
58
|
+
'clip-rule': 'clipRule',
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Rebuilds one parsed element as a React element, keeping only {@link ICON_ALLOWED_TAGS} /
|
|
62
|
+
* {@link ICON_ALLOWED_ATTRS}. This is what stands in for `dangerouslySetInnerHTML`: the browser's
|
|
63
|
+
* own parser does the tokenizing (far less error-prone than a hand-rolled regex over SVG/XML), and
|
|
64
|
+
* this function decides what survives the trip into React's tree — a `<script>`, a `<foreignObject>`
|
|
65
|
+
* carrying arbitrary HTML, an `<image>`/`<use>` reference, or an `onload=` attribute all fail to
|
|
66
|
+
* match anything on either allowlist and are simply not there afterward, whatever they contained.
|
|
67
|
+
*/
|
|
68
|
+
function sanitizeIconNode(node, key) {
|
|
69
|
+
const tag = node.tagName.toLowerCase();
|
|
70
|
+
if (!ICON_ALLOWED_TAGS.has(tag))
|
|
71
|
+
return null;
|
|
72
|
+
// `key` goes straight into the props object — `createElement` pulls it out as the element's
|
|
73
|
+
// reserved key rather than forwarding it to the DOM, the same as writing `key={key}` in JSX.
|
|
74
|
+
const props = { key };
|
|
75
|
+
for (const attr of Array.from(node.attributes)) {
|
|
76
|
+
const propName = ICON_ALLOWED_ATTRS[attr.name.toLowerCase()];
|
|
77
|
+
if (propName)
|
|
78
|
+
props[propName] = attr.value;
|
|
79
|
+
}
|
|
80
|
+
const children = Array.from(node.children)
|
|
81
|
+
.map((child, i) => sanitizeIconNode(child, i))
|
|
82
|
+
.filter((child) => child !== null);
|
|
83
|
+
return createElement(tag, props, children.length ? children : undefined);
|
|
84
|
+
}
|
|
85
|
+
// Reused across parses rather than constructed per render — `DOMParser` instances are stateless
|
|
86
|
+
// and this runs once per icon render, but there is no reason to pay allocation cost for it twice.
|
|
87
|
+
let iconMarkupParser;
|
|
88
|
+
/**
|
|
89
|
+
* Parses one `AdminNavEntry.icon` string into a sanitized element list. Wrapping in a throwaway
|
|
90
|
+
* `<svg>` document (never attached to the page — this is markup parsing, not rendering) lets the
|
|
91
|
+
* browser's own XML parser validate structure for free: malformed markup surfaces as a
|
|
92
|
+
* `parsererror` node, which is treated the same as empty input — the row loses its icon, not its
|
|
93
|
+
* label or link.
|
|
94
|
+
*/
|
|
95
|
+
function parseIconMarkup(markup) {
|
|
96
|
+
if (!markup)
|
|
97
|
+
return [];
|
|
98
|
+
iconMarkupParser ??= new DOMParser();
|
|
99
|
+
const doc = iconMarkupParser.parseFromString(`<svg xmlns="http://www.w3.org/2000/svg">${markup}</svg>`, 'image/svg+xml');
|
|
100
|
+
if (doc.getElementsByTagName('parsererror').length > 0)
|
|
101
|
+
return [];
|
|
102
|
+
return Array.from(doc.documentElement.children)
|
|
103
|
+
.map((child, i) => sanitizeIconNode(child, i))
|
|
104
|
+
.filter((child) => child !== null);
|
|
105
|
+
}
|
|
21
106
|
function Icon(props) {
|
|
22
|
-
|
|
107
|
+
// Re-parsing on every keystroke of an unrelated state update would be wasted work for a string
|
|
108
|
+
// that, in practice, never changes after the nav manifest is built.
|
|
109
|
+
const children = useMemo(() => parseIconMarkup(props.markup), [props.markup]);
|
|
110
|
+
return (_jsx("svg", { viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", strokeWidth: 1.5, children: children }));
|
|
23
111
|
}
|
|
24
112
|
/**
|
|
25
113
|
* Rail-mode tooltip, portaled to `document.body`.
|
|
@@ -39,8 +127,16 @@ function RailTooltip(props) {
|
|
|
39
127
|
return null;
|
|
40
128
|
return createPortal(_jsx("div", { className: "cms-tooltip-portal", style: { top: props.target.top, left: props.target.left }, role: "presentation", children: props.target.label }), document.body);
|
|
41
129
|
}
|
|
130
|
+
// Read the browser storage property only inside the hooks' best-effort get/set calls: the
|
|
131
|
+
// property getter itself can throw in restricted embeds, before a hook can catch the failure.
|
|
132
|
+
const sidebarStorage = {
|
|
133
|
+
getItem: (key) => localStorage.getItem(key),
|
|
134
|
+
setItem: (key, value) => localStorage.setItem(key, value),
|
|
135
|
+
};
|
|
42
136
|
function SidebarRoot(props) {
|
|
43
|
-
const rail = useSidebarRail(
|
|
137
|
+
const rail = useSidebarRail({ storage: sidebarStorage, events: window }, {
|
|
138
|
+
storageKey: props.railStorageKey, defaultCollapsed: props.railDefaultCollapsed,
|
|
139
|
+
});
|
|
44
140
|
const [tooltip, setTooltip] = useState(null);
|
|
45
141
|
const open = props.open ?? false;
|
|
46
142
|
const base = props.base ?? DEFAULT_ADMIN_BASE;
|
|
@@ -102,7 +198,7 @@ function SidebarMobileHeader(props) {
|
|
|
102
198
|
}
|
|
103
199
|
function SidebarNav(props) {
|
|
104
200
|
const { activeId, base, collapsed, railTooltipProps } = useSidebar();
|
|
105
|
-
const sections = useNavSections();
|
|
201
|
+
const sections = useNavSections({ storage: sidebarStorage, events: window });
|
|
106
202
|
return (_jsx(_Fragment, { children: props.groups.map((group, gi) => {
|
|
107
203
|
/**
|
|
108
204
|
* Rail mode force-expands every section, deliberately. Collapsed to a rail the group
|
|
@@ -112,15 +208,15 @@ function SidebarNav(props) {
|
|
|
112
208
|
* returns intact when the rail expands — this only overrides what is rendered.
|
|
113
209
|
*/
|
|
114
210
|
const collapsible = Boolean(group.label) && !collapsed && (props.collapsibleGroups ?? []).includes(group.label);
|
|
115
|
-
const open = !collapsible || sections.isOpen(group.label);
|
|
211
|
+
const open = !collapsible || sections.isOpen({ groupLabel: group.label });
|
|
116
212
|
const panelId = `cms-section-${(group.label ?? `top-${gi}`).toLowerCase().replace(/[^a-z0-9]+/g, '-')}`;
|
|
117
213
|
return (_jsxs("div", { className: "cms-section", children: [group.label && collapsible ? (
|
|
118
214
|
// A real `<button>`, not a click-handled `<div>`: keyboard focus, Enter/Space
|
|
119
215
|
// activation, and the button role all come free, and `aria-expanded` only carries its
|
|
120
216
|
// meaning on an element that is actually a control.
|
|
121
|
-
_jsxs("button", { type: "button", className: `cms-group cms-group-toggle${open ? '' : ' is-closed'}`, "aria-expanded": open, "aria-controls": panelId, onClick: () => sections.toggle(group.label), children: [_jsx("span", { children: group.label }), _jsx("svg", { className: "cms-group-chevron", viewBox: "0 0 18 18", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M5 7l4 4 4-4", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) })] })) : group.label ? (_jsx("div", { className: "cms-group", children: group.label })) : null, _jsx("div", { className: "cms-section-items", id: panelId, hidden: !open, children: group.items.map((item) => {
|
|
217
|
+
_jsxs("button", { type: "button", className: `cms-group cms-group-toggle${open ? '' : ' is-closed'}`, "aria-expanded": open, "aria-controls": panelId, onClick: () => sections.toggle({ groupLabel: group.label }), children: [_jsx("span", { children: group.label }), _jsx("svg", { className: "cms-group-chevron", viewBox: "0 0 18 18", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M5 7l4 4 4-4", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round" }) })] })) : group.label ? (_jsx("div", { className: "cms-group", children: group.label })) : null, _jsx("div", { className: "cms-section-items", id: panelId, hidden: !open, children: group.items.map((item) => {
|
|
122
218
|
const active = item.id === activeId;
|
|
123
|
-
const href = adminHref(item.href, base);
|
|
219
|
+
const href = adminHref({ routePath: item.href }, { base });
|
|
124
220
|
if (props.renderItem) {
|
|
125
221
|
return _jsx(Fragment, { children: props.renderItem(item, { active, href, collapsed }) }, item.id);
|
|
126
222
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AdminEntityField, AdminEntityRowData } from '../../core/ports/entities.js';
|
|
2
|
+
import { type EntityTranslate } from './types.js';
|
|
3
|
+
import type { RelationIndex } from './rules.js';
|
|
4
|
+
export declare function EntityCell({ field, row, relations, translate }: {
|
|
5
|
+
readonly field: AdminEntityField;
|
|
6
|
+
readonly row: AdminEntityRowData;
|
|
7
|
+
readonly relations: RelationIndex;
|
|
8
|
+
readonly translate: EntityTranslate;
|
|
9
|
+
}): import("react").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=EntityCell.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { entityText } from './types.js';
|
|
3
|
+
export function EntityCell({ field, row, relations, translate }) {
|
|
4
|
+
const value = row[field.name];
|
|
5
|
+
if (value === undefined || value === null)
|
|
6
|
+
return _jsx("span", { children: translate({ key: 'Empty' }) });
|
|
7
|
+
if (field.kind === 'relation') {
|
|
8
|
+
const id = String(value);
|
|
9
|
+
const title = relations[field.target]?.titles?.[id];
|
|
10
|
+
return title !== undefined ? _jsx("span", { children: title }) : _jsxs("span", { title: entityText({ translate, key: 'Unresolved {target}: {id}', values: { target: field.target, id } }), children: [id, " \u26A0"] });
|
|
11
|
+
}
|
|
12
|
+
if (field.kind === 'json')
|
|
13
|
+
return _jsx("code", { children: JSON.stringify(value) });
|
|
14
|
+
if (field.kind === 'boolean')
|
|
15
|
+
return _jsx("span", { children: translate({ key: value === true ? 'Yes' : 'No' }) });
|
|
16
|
+
return _jsx("span", { children: String(value) });
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=EntityCell.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
|
+
import { EntityCell } from './EntityCell.js';
|
|
4
|
+
import { loadEntityRow, useEntityRead } from './use-entity-read.js';
|
|
5
|
+
import { readTitle } from './rules.js';
|
|
6
|
+
export function EntityDetail({ entityName, id, registry, translate, routes }) {
|
|
7
|
+
const port = useMemo(() => registry.getEntity({ name: entityName }), [registry, entityName]);
|
|
8
|
+
const load = useCallback(() => loadEntityRow({ registry, entityName, id }), [registry, entityName, id, port]);
|
|
9
|
+
const result = useEntityRead({ load, scope: port });
|
|
10
|
+
if (port === null)
|
|
11
|
+
return _jsx("p", { role: "alert", children: translate({ key: 'Unknown entity' }) });
|
|
12
|
+
if (result.error)
|
|
13
|
+
return _jsx("p", { role: "alert", children: translate({ key: 'Unable to load row' }) });
|
|
14
|
+
if (result.loading || !result.data)
|
|
15
|
+
return _jsx("p", { role: "status", children: translate({ key: 'Loading' }) });
|
|
16
|
+
const { row, relations } = result.data;
|
|
17
|
+
if (row === null)
|
|
18
|
+
return _jsx("p", { role: "status", children: translate({ key: 'Row not found' }) });
|
|
19
|
+
return _jsxs("div", { className: "page", children: [_jsx("a", { href: routes.href({ entity: entityName }), children: translate({ key: port.descriptor.labelPlural }) }), _jsx("h1", { children: readTitle({ row, titleField: port.descriptor.titleField }) ?? row.id }), _jsx("dl", { children: port.descriptor.fields.map((field) => _jsxs("div", { children: [_jsx("dt", { children: translate({ key: field.label ?? field.name }) }), _jsx("dd", { children: _jsx(EntityCell, { field: field, row: row, relations: relations, translate: translate }) })] }, field.name)) }), _jsx("a", { href: routes.href({ entity: entityName, id, view: 'edit' }), children: translate({ key: 'Edit' }) })] });
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=EntityDetail.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EntityEditProps } from './types.js';
|
|
2
|
+
/** Host entity editing uses the existing registry/ports and core draft validation.
|
|
3
|
+
* Required booleans default false, optional booleans remain unset, and invalid JSON blocks save
|
|
4
|
+
* while preserving textarea contents. Creates omit undefined; updates retain explicit undefined
|
|
5
|
+
* to clear fields, so the host transport must preserve that intent. Save/remove operations serialize
|
|
6
|
+
* within the mounted editor; stale reads and settlement after unmount cannot populate or navigate
|
|
7
|
+
* another screen. Relation targets load once each, bounded to 100 with truncation and raw-id fallback.
|
|
8
|
+
* Reads belong to each mount rather than a host query singleton. Navigation remount reloads data;
|
|
9
|
+
* live refresh and dirty-navigation prompts remain host responsibilities. Registry changes must
|
|
10
|
+
* replace the registry object; memoized per-entity ports can then refresh diagnostic wrappers.
|
|
11
|
+
*/
|
|
12
|
+
export declare function EntityEdit(props: EntityEditProps): import("react").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=EntityEdit.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId, useMemo } from 'react';
|
|
3
|
+
import { EntityField } from './EntityField.js';
|
|
4
|
+
import { useEntityEdit } from './use-entity-edit.js';
|
|
5
|
+
import { entityText } from './types.js';
|
|
6
|
+
import { RELATION_OPTION_LIMIT } from './rules.js';
|
|
7
|
+
function EntityEditor(props) {
|
|
8
|
+
const { port, translate, routes, entityName, id } = props;
|
|
9
|
+
const formId = useId();
|
|
10
|
+
const edit = useEntityEdit(props);
|
|
11
|
+
if (edit.read.error)
|
|
12
|
+
return _jsx("p", { role: "alert", children: translate({ key: 'Unable to load row' }) });
|
|
13
|
+
if (edit.read.loading || !edit.read.data)
|
|
14
|
+
return _jsx("p", { role: "status", children: translate({ key: 'Loading' }) });
|
|
15
|
+
if (id !== null && edit.read.data.row === null)
|
|
16
|
+
return _jsx("p", { role: "status", children: translate({ key: 'Row not found' }) });
|
|
17
|
+
if (!edit.form)
|
|
18
|
+
return _jsx("p", { role: "status", children: translate({ key: 'Loading' }) });
|
|
19
|
+
const busy = edit.pending !== null;
|
|
20
|
+
const truncated = Object.entries(edit.read.data.relations).filter(([, target]) => target.truncated).map(([name]) => name);
|
|
21
|
+
return _jsxs("form", { className: "page", onSubmit: (e) => { e.preventDefault(); void edit.save(); }, children: [_jsx("a", { href: routes.href({ entity: entityName }), children: translate({ key: port.descriptor.labelPlural }) }), _jsx("h1", { children: entityText({ translate, key: id === null ? 'New {entity}' : 'Edit {entity}', values: { entity: translate({ key: port.descriptor.labelSingular }) } }) }), edit.failed ? _jsx("p", { role: "alert", children: translate({ key: 'Operation failed' }) }) : null, port.descriptor.fields.map((field, index) => {
|
|
22
|
+
const inputId = `${formId}-${index}`;
|
|
23
|
+
const missing = edit.missing.includes(field.name);
|
|
24
|
+
const invalid = edit.invalid.some((problem) => problem.field === field.name);
|
|
25
|
+
const jsonError = edit.form?.jsonErrors[field.name] === true;
|
|
26
|
+
return _jsxs("div", { children: [_jsxs("label", { htmlFor: inputId, children: [translate({ key: field.label ?? field.name }), field.required ? ' *' : '', field.kind === 'datetime' ? ` (${translate({ key: 'UTC' })})` : ''] }), _jsx(EntityField, { inputId: inputId, field: field, value: edit.form?.draft[field.name], text: edit.form?.jsonText[field.name] ?? '', relations: edit.read.data.relations, translate: translate, disabled: busy, invalid: missing || invalid || jsonError, onChange: ({ value }) => edit.setField({ name: field.name, value }), onTextChange: ({ text }) => edit.setJsonText({ name: field.name, text }) }), missing ? _jsx("p", { children: translate({ key: 'Required' }) }) : null, invalid ? _jsx("p", { children: translate({ key: 'Invalid value' }) }) : null, jsonError ? _jsx("p", { role: "alert", children: translate({ key: 'Invalid JSON' }) }) : null] }, field.name);
|
|
27
|
+
}), truncated.length > 0 ? _jsx("p", { children: entityText({ translate, key: 'Related names limited to {limit} rows: {targets}', values: { limit: RELATION_OPTION_LIMIT, targets: truncated.join(', ') } }) }) : null, _jsx("button", { type: "submit", disabled: !edit.canSave, children: translate({ key: edit.pending === 'save' ? 'Saving' : 'Save' }) }), id !== null && port.remove ? _jsx("button", { type: "button", disabled: busy, onClick: () => { void edit.remove(); }, children: translate({ key: edit.pending === 'remove' ? 'Deleting' : 'Delete' }) }) : null] });
|
|
28
|
+
}
|
|
29
|
+
/** Host entity editing uses the existing registry/ports and core draft validation.
|
|
30
|
+
* Required booleans default false, optional booleans remain unset, and invalid JSON blocks save
|
|
31
|
+
* while preserving textarea contents. Creates omit undefined; updates retain explicit undefined
|
|
32
|
+
* to clear fields, so the host transport must preserve that intent. Save/remove operations serialize
|
|
33
|
+
* within the mounted editor; stale reads and settlement after unmount cannot populate or navigate
|
|
34
|
+
* another screen. Relation targets load once each, bounded to 100 with truncation and raw-id fallback.
|
|
35
|
+
* Reads belong to each mount rather than a host query singleton. Navigation remount reloads data;
|
|
36
|
+
* live refresh and dirty-navigation prompts remain host responsibilities. Registry changes must
|
|
37
|
+
* replace the registry object; memoized per-entity ports can then refresh diagnostic wrappers.
|
|
38
|
+
*/
|
|
39
|
+
export function EntityEdit(props) {
|
|
40
|
+
const port = useMemo(() => props.registry.getEntity({ name: props.entityName }), [props.registry, props.entityName]);
|
|
41
|
+
if (port === null)
|
|
42
|
+
return _jsx("p", { role: "alert", children: props.translate({ key: 'Unknown entity' }) });
|
|
43
|
+
return _jsx(EntityEditor, { ...props, port: port }, JSON.stringify([props.entityName, props.id]));
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=EntityEdit.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AdminEntityField } from '../../core/ports/entities.js';
|
|
2
|
+
import type { EntityTranslate } from './types.js';
|
|
3
|
+
import { type RelationIndex } from './rules.js';
|
|
4
|
+
export declare function EntityField(props: {
|
|
5
|
+
readonly inputId: string;
|
|
6
|
+
readonly field: AdminEntityField;
|
|
7
|
+
readonly value: unknown;
|
|
8
|
+
readonly text: string;
|
|
9
|
+
readonly relations: RelationIndex;
|
|
10
|
+
readonly translate: EntityTranslate;
|
|
11
|
+
readonly disabled: boolean;
|
|
12
|
+
readonly invalid: boolean;
|
|
13
|
+
readonly onChange: (args: {
|
|
14
|
+
readonly value: unknown;
|
|
15
|
+
}) => void;
|
|
16
|
+
readonly onTextChange: (args: {
|
|
17
|
+
readonly text: string;
|
|
18
|
+
}) => void;
|
|
19
|
+
}): import("react").JSX.Element;
|
|
20
|
+
//# sourceMappingURL=EntityField.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Select } from '@jini-ai/ui/admin-widgets';
|
|
3
|
+
import { fromDatetimeLocalValue, toDatetimeLocalValue } from './rules.js';
|
|
4
|
+
export function EntityField(props) {
|
|
5
|
+
const { inputId, field, value, translate, disabled, invalid, onChange } = props;
|
|
6
|
+
const common = { id: inputId, disabled, 'aria-invalid': invalid };
|
|
7
|
+
switch (field.kind) {
|
|
8
|
+
case 'json': return _jsx("textarea", { ...common, value: props.text, onChange: (e) => props.onTextChange({ text: e.target.value }) });
|
|
9
|
+
case 'relation': {
|
|
10
|
+
const options = props.relations[field.target]?.options ?? [];
|
|
11
|
+
const selectedId = typeof value === 'string' ? value : '';
|
|
12
|
+
const missing = selectedId !== '' && !options.some((option) => option.id === selectedId);
|
|
13
|
+
return _jsx(Select, { id: inputId, disabled: disabled, value: selectedId, translate: (key) => translate({ key }), "aria-label": translate({ key: field.label ?? field.name }), options: [
|
|
14
|
+
{ value: '', label: translate({ key: 'No value' }) },
|
|
15
|
+
...(missing ? [{ value: selectedId, label: `${selectedId} (${translate({ key: 'Unresolved' })})` }] : []),
|
|
16
|
+
...options.map((option) => ({ value: option.id, label: option.title })),
|
|
17
|
+
], onChange: (selected) => onChange({ value: selected === '' ? undefined : selected }) });
|
|
18
|
+
}
|
|
19
|
+
case 'boolean':
|
|
20
|
+
if (field.required)
|
|
21
|
+
return _jsx("input", { ...common, type: "checkbox", checked: value === true, onChange: (e) => onChange({ value: e.target.checked }) });
|
|
22
|
+
return _jsx(Select, { id: inputId, disabled: disabled, value: value === undefined ? '' : value === true ? 'true' : 'false', translate: (key) => translate({ key }), "aria-label": translate({ key: field.label ?? field.name }), options: [
|
|
23
|
+
{ value: '', label: translate({ key: 'No value' }) }, { value: 'true', label: translate({ key: 'Yes' }) }, { value: 'false', label: translate({ key: 'No' }) },
|
|
24
|
+
], onChange: (selected) => onChange({ value: selected === '' ? undefined : selected === 'true' }) });
|
|
25
|
+
case 'datetime': return _jsx("input", { ...common, type: "datetime-local", step: "0.001", value: toDatetimeLocalValue({ value }), onChange: (e) => onChange({ value: fromDatetimeLocalValue({ raw: e.target.value }) }) });
|
|
26
|
+
case 'integer':
|
|
27
|
+
case 'real': return _jsx("input", { ...common, type: "number", step: field.kind === 'integer' ? '1' : 'any', value: typeof value === 'number' ? value : '', onChange: (e) => onChange({ value: e.target.value === '' ? undefined : Number(e.target.value) }) });
|
|
28
|
+
case 'text': return _jsx("input", { ...common, value: typeof value === 'string' ? value : '', onChange: (e) => onChange({ value: e.target.value === '' ? undefined : e.target.value }) });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=EntityField.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DataTable } from '../components/DataTable.js';
|
|
3
|
+
export function EntityIndex({ registry, routes, translate }) {
|
|
4
|
+
return _jsxs("div", { className: "page", children: [_jsx("h1", { children: translate({ key: 'Data' }) }), _jsx(DataTable, { rows: [...registry.listEntities({})], rowKey: ({ descriptor }) => descriptor.name, empty: _jsx("p", { children: translate({ key: 'No entities registered' }) }), columns: [
|
|
5
|
+
{ key: 'name', header: translate({ key: 'Entity' }), cell: ({ descriptor }) => _jsx("a", { href: routes.href({ entity: descriptor.name }), children: translate({ key: descriptor.labelPlural }) }) },
|
|
6
|
+
{ key: 'key', header: translate({ key: 'Name' }), cell: ({ descriptor }) => _jsx("code", { children: descriptor.name }) },
|
|
7
|
+
{ key: 'fields', header: translate({ key: 'Fields' }), cell: ({ descriptor }) => descriptor.fields.length },
|
|
8
|
+
] })] });
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=EntityIndex.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type EntityListProps } from './types.js';
|
|
2
|
+
/** Cursor history records actual returned page lengths; never sort a server-paginated page locally.
|
|
3
|
+
* Host translation, registry and navigation are injected, and this list uses the existing DataTable.
|
|
4
|
+
* Relation choices are bounded to 100, expose truncation and retain raw IDs for unresolved labels.
|
|
5
|
+
*/
|
|
6
|
+
export declare function EntityList(props: EntityListProps): import("react").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=EntityList.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
3
|
+
import { DataTable } from '../components/DataTable.js';
|
|
4
|
+
import { EntityCell } from './EntityCell.js';
|
|
5
|
+
import { entityText } from './types.js';
|
|
6
|
+
import { loadRelationIndex, resolveEntityPageSize, RELATION_OPTION_LIMIT } from './rules.js';
|
|
7
|
+
import { useEntityRead } from './use-entity-read.js';
|
|
8
|
+
/** Cursor history records actual returned page lengths; never sort a server-paginated page locally.
|
|
9
|
+
* Host translation, registry and navigation are injected, and this list uses the existing DataTable.
|
|
10
|
+
* Relation choices are bounded to 100, expose truncation and retain raw IDs for unresolved labels.
|
|
11
|
+
*/
|
|
12
|
+
export function EntityList(props) {
|
|
13
|
+
const { entityName, registry, translate, routes } = props;
|
|
14
|
+
const port = useMemo(() => registry.getEntity({ name: entityName }), [registry, entityName]);
|
|
15
|
+
const limit = resolveEntityPageSize({ raw: props.limitParam });
|
|
16
|
+
const token = JSON.stringify([entityName, limit]);
|
|
17
|
+
const [walk, setWalk] = useState({ token, port, entries: [{ cursor: null, offset: 0 }] });
|
|
18
|
+
if (walk.token !== token || walk.port !== port)
|
|
19
|
+
setWalk({ token, port, entries: [{ cursor: null, offset: 0 }] });
|
|
20
|
+
const entries = walk.token === token && walk.port === port ? walk.entries : [{ cursor: null, offset: 0 }];
|
|
21
|
+
const position = entries[entries.length - 1];
|
|
22
|
+
const load = useCallback(async () => {
|
|
23
|
+
if (port === null)
|
|
24
|
+
return null;
|
|
25
|
+
const [page, relations] = await Promise.all([
|
|
26
|
+
port.list(position.cursor === null ? { limit } : { limit, cursor: position.cursor }),
|
|
27
|
+
loadRelationIndex({ descriptor: port.descriptor, registry }),
|
|
28
|
+
]);
|
|
29
|
+
return { page, relations };
|
|
30
|
+
}, [port, registry, limit, position.cursor]);
|
|
31
|
+
const result = useEntityRead({ load, scope: port });
|
|
32
|
+
if (port === null)
|
|
33
|
+
return _jsx("p", { role: "alert", children: translate({ key: 'Unknown entity' }) });
|
|
34
|
+
if (result.error)
|
|
35
|
+
return _jsx("p", { role: "alert", children: translate({ key: 'Unable to load rows' }) });
|
|
36
|
+
if (result.loading || !result.data)
|
|
37
|
+
return _jsx("p", { role: "status", children: translate({ key: 'Loading' }) });
|
|
38
|
+
const { page, relations } = result.data;
|
|
39
|
+
const descriptor = port.descriptor;
|
|
40
|
+
const truncated = Object.entries(relations).filter(([, options]) => options.truncated).map(([name]) => name);
|
|
41
|
+
return _jsxs("div", { className: "page", children: [_jsx("a", { href: routes.href({}), children: translate({ key: 'Data' }) }), _jsx("h1", { children: translate({ key: descriptor.labelPlural }) }), _jsx("a", { href: routes.href({ entity: entityName, view: 'create' }), children: entityText({ translate, key: 'New {entity}', values: { entity: translate({ key: descriptor.labelSingular }) } }) }), _jsx(DataTable, { rows: [...page.items], rowKey: (row) => row.id, empty: _jsx("p", { children: translate({ key: 'No rows' }) }), columns: descriptor.fields.map((field) => ({
|
|
42
|
+
key: field.name, header: translate({ key: field.label ?? field.name }),
|
|
43
|
+
// Sorting a server-cut page would misrepresent collection order.
|
|
44
|
+
cell: (row) => field.name === descriptor.titleField
|
|
45
|
+
? _jsx("a", { href: routes.href({ entity: entityName, id: row.id }), children: _jsx(EntityCell, { field: field, row: row, relations: relations, translate: translate }) })
|
|
46
|
+
: _jsx(EntityCell, { field: field, row: row, relations: relations, translate: translate }),
|
|
47
|
+
})) }), _jsx("p", { children: page.items.length === 0 ? translate({ key: 'No rows' }) : entityText({ translate, key: 'Showing {from}–{to}', values: { from: position.offset + 1, to: position.offset + page.items.length } }) }), _jsx("button", { type: "button", disabled: entries.length === 1, onClick: () => setWalk((current) => ({ ...current, entries: current.entries.slice(0, -1) })), children: translate({ key: 'Previous' }) }), _jsx("button", { type: "button", disabled: page.nextCursor === null, onClick: () => {
|
|
48
|
+
if (page.nextCursor !== null)
|
|
49
|
+
setWalk((current) => ({ ...current, entries: [...current.entries, { cursor: page.nextCursor, offset: position.offset + page.items.length }] }));
|
|
50
|
+
}, children: translate({ key: 'Next' }) }), truncated.length > 0 ? _jsx("p", { children: entityText({ translate, key: 'Related names limited to {limit} rows: {targets}', values: { limit: RELATION_OPTION_LIMIT, targets: truncated.join(', ') } }) }) : null] });
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=EntityList.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AdminNavEntry } from '../../core/manifest/types.js';
|
|
2
|
+
import type { AdminShellPanel } from '../shell/types.js';
|
|
3
|
+
import type { EntityRegistryPort, EntityRoutesPort, EntityTranslate } from './types.js';
|
|
4
|
+
export interface EntityRouteDependencies {
|
|
5
|
+
readonly adminBase: string;
|
|
6
|
+
readonly panelId: string;
|
|
7
|
+
readonly navigate: (args: {
|
|
8
|
+
readonly routePath: string;
|
|
9
|
+
}) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function createEntityRoutes({ adminBase, panelId, navigate }: EntityRouteDependencies): EntityRoutesPort;
|
|
12
|
+
/** Adds an existing shell contribution; no router, registry, or shell is installed by this slice.
|
|
13
|
+
* Host translate/registry/adminBase/panelId/navigation dependencies are explicit and agent access
|
|
14
|
+
* is opt-in. List/create/detail/edit routes encode names and IDs, including dot-segment protection;
|
|
15
|
+
* an ID equal to new is still a saved row, never the creation sentinel. Hosts adopting these routes
|
|
16
|
+
* own legacy bookmark migration. Select translation is adapted to its existing scalar callback ABI. */
|
|
17
|
+
export declare function createEntityPanel(args: EntityRouteDependencies & {
|
|
18
|
+
readonly registry: EntityRegistryPort;
|
|
19
|
+
readonly translate: EntityTranslate;
|
|
20
|
+
}, options?: {
|
|
21
|
+
readonly nav?: AdminNavEntry;
|
|
22
|
+
readonly agentReachable?: boolean;
|
|
23
|
+
readonly requires?: readonly string[];
|
|
24
|
+
readonly permissions?: readonly string[];
|
|
25
|
+
}): AdminShellPanel;
|
|
26
|
+
//# sourceMappingURL=contribution.d.ts.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { EntityIndex } from './EntityIndex.js';
|
|
3
|
+
import { EntityList } from './EntityList.js';
|
|
4
|
+
import { EntityDetail } from './EntityDetail.js';
|
|
5
|
+
import { EntityEdit } from './EntityEdit.js';
|
|
6
|
+
function encodeSegment({ value }) {
|
|
7
|
+
if (value === '')
|
|
8
|
+
throw new Error('Entity route segments must be non-empty');
|
|
9
|
+
const encoded = encodeURIComponent(value).replace(/~/g, '%7E');
|
|
10
|
+
// Browsers normalize even percent-encoded dot segments. A marker prevents that normalization.
|
|
11
|
+
return value === '.' || value === '..' ? `~${encoded}` : encoded;
|
|
12
|
+
}
|
|
13
|
+
function decodeSegment({ value }) {
|
|
14
|
+
return value === '~.' || value === '~..' ? value.slice(1) : decodeURIComponent(value);
|
|
15
|
+
}
|
|
16
|
+
export function createEntityRoutes({ adminBase, panelId, navigate }) {
|
|
17
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(panelId))
|
|
18
|
+
throw new Error('Entity panelId must be one URL segment');
|
|
19
|
+
if (!adminBase.startsWith('/') || adminBase.startsWith('//') || /[?#]/.test(adminBase))
|
|
20
|
+
throw new Error('Entity adminBase must be an absolute pathname');
|
|
21
|
+
const base = adminBase.replace(/\/+$/, '');
|
|
22
|
+
function routePath({ entity, id, view }) {
|
|
23
|
+
const root = `/${panelId}`;
|
|
24
|
+
if (entity === undefined)
|
|
25
|
+
return root;
|
|
26
|
+
const list = `${root}/${encodeSegment({ value: entity })}`;
|
|
27
|
+
if (view === 'create')
|
|
28
|
+
return `${list}/new`;
|
|
29
|
+
if (id === undefined)
|
|
30
|
+
return list;
|
|
31
|
+
return `${list}/row/${encodeSegment({ value: id })}${view === 'edit' ? '/edit' : ''}`;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
href: (args) => `${base}${routePath(args)}`,
|
|
35
|
+
navigate: (args) => navigate({ routePath: routePath(args) }),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Adds an existing shell contribution; no router, registry, or shell is installed by this slice.
|
|
39
|
+
* Host translate/registry/adminBase/panelId/navigation dependencies are explicit and agent access
|
|
40
|
+
* is opt-in. List/create/detail/edit routes encode names and IDs, including dot-segment protection;
|
|
41
|
+
* an ID equal to new is still a saved row, never the creation sentinel. Hosts adopting these routes
|
|
42
|
+
* own legacy bookmark migration. Select translation is adapted to its existing scalar callback ABI. */
|
|
43
|
+
export function createEntityPanel(args, options = {}) {
|
|
44
|
+
const routes = createEntityRoutes(args);
|
|
45
|
+
const dependencies = { registry: args.registry, translate: args.translate, routes };
|
|
46
|
+
return {
|
|
47
|
+
id: args.panelId,
|
|
48
|
+
nav: options.nav ?? { label: args.translate({ key: 'Data' }) },
|
|
49
|
+
agentReachable: options.agentReachable ?? false,
|
|
50
|
+
...(options.requires === undefined ? {} : { requires: options.requires }),
|
|
51
|
+
...(options.permissions === undefined ? {} : { permissions: options.permissions }),
|
|
52
|
+
routes: [
|
|
53
|
+
{ pattern: '/:entity', view: 'list' },
|
|
54
|
+
{ pattern: '/:entity/new', view: 'create' },
|
|
55
|
+
{ pattern: '/:entity/row/:id', view: 'detail' },
|
|
56
|
+
{ pattern: '/:entity/row/:id/edit', view: 'edit' },
|
|
57
|
+
],
|
|
58
|
+
render: ({ route }) => {
|
|
59
|
+
if (route.view === null)
|
|
60
|
+
return _jsx(EntityIndex, { ...dependencies });
|
|
61
|
+
let entityName;
|
|
62
|
+
let id;
|
|
63
|
+
try {
|
|
64
|
+
entityName = decodeSegment({ value: route.params.entity ?? '' });
|
|
65
|
+
id = decodeSegment({ value: route.params.id ?? '' });
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return _jsx("p", { role: "alert", children: args.translate({ key: 'Invalid route' }) });
|
|
69
|
+
}
|
|
70
|
+
if (route.view === 'list')
|
|
71
|
+
return _jsx(EntityList, { ...dependencies, entityName: entityName, limitParam: route.query.get('limit') });
|
|
72
|
+
if (route.view === 'detail')
|
|
73
|
+
return _jsx(EntityDetail, { ...dependencies, entityName: entityName, id: id });
|
|
74
|
+
return _jsx(EntityEdit, { ...dependencies, entityName: entityName, id: route.view === 'create' ? null : id });
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=contribution.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { EntityIndex } from './EntityIndex.js';
|
|
2
|
+
export { EntityList } from './EntityList.js';
|
|
3
|
+
export { EntityDetail } from './EntityDetail.js';
|
|
4
|
+
export { EntityEdit } from './EntityEdit.js';
|
|
5
|
+
export { createEntityPanel, createEntityRoutes } from './contribution.js';
|
|
6
|
+
export type { EntityRouteDependencies } from './contribution.js';
|
|
7
|
+
export type { EntityRegistryPort, EntityTranslate, EntityRoutesPort, EntityRouteTarget, EntityScreenDependencies, EntityListProps, EntityDetailProps, EntityEditProps, } from './types.js';
|
|
8
|
+
export { RELATION_OPTION_LIMIT, resolveEntityPageSize, relationFieldsOf, loadRelationIndex, readTitle, emptyDraft, draftForRow, missingRequiredFields, invalidDraftFields, toDatetimeLocalValue, fromDatetimeLocalValue, } from './rules.js';
|
|
9
|
+
export type { RelationOptions, RelationIndex, DraftFieldProblem } from './rules.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { EntityIndex } from './EntityIndex.js';
|
|
2
|
+
export { EntityList } from './EntityList.js';
|
|
3
|
+
export { EntityDetail } from './EntityDetail.js';
|
|
4
|
+
export { EntityEdit } from './EntityEdit.js';
|
|
5
|
+
export { createEntityPanel, createEntityRoutes } from './contribution.js';
|
|
6
|
+
export { RELATION_OPTION_LIMIT, resolveEntityPageSize, relationFieldsOf, loadRelationIndex, readTitle, emptyDraft, draftForRow, missingRequiredFields, invalidDraftFields, toDatetimeLocalValue, fromDatetimeLocalValue, } from './rules.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|