@object-ui/app-shell 6.2.3 → 7.1.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 +1229 -0
- package/README.md +292 -0
- package/dist/assistant/assistantBus.d.ts +72 -0
- package/dist/assistant/assistantBus.js +133 -0
- package/dist/chrome/CommandPalette.d.ts +1 -1
- package/dist/chrome/CommandPalette.js +26 -22
- package/dist/chrome/ConditionalAuthWrapper.d.ts +1 -1
- package/dist/chrome/ConsoleToaster.d.ts +1 -1
- package/dist/chrome/ConsoleToaster.js +3 -1
- package/dist/chrome/ErrorBoundary.d.ts +1 -1
- package/dist/chrome/KeyboardShortcutsDialog.d.ts +1 -1
- package/dist/chrome/KeyboardShortcutsDialog.js +16 -5
- package/dist/chrome/LoadingScreen.d.ts +1 -1
- package/dist/chrome/LoadingScreen.js +22 -26
- package/dist/chrome/RouteFader.d.ts +1 -1
- package/dist/chrome/ThemeProvider.d.ts +1 -1
- package/dist/components/ManagedByBadge.d.ts +1 -1
- package/dist/console/AppContent.d.ts +1 -1
- package/dist/console/AppContent.js +184 -39
- package/dist/console/ConsoleShell.d.ts +7 -7
- package/dist/console/ConsoleShell.js +32 -3
- package/dist/console/ai/AiChatPage.d.ts +88 -1
- package/dist/console/ai/AiChatPage.js +747 -66
- package/dist/console/ai/ConversationsSidebar.d.ts +26 -1
- package/dist/console/ai/ConversationsSidebar.js +149 -34
- package/dist/console/ai/LiveCanvas.d.ts +28 -0
- package/dist/console/ai/LiveCanvas.js +80 -0
- package/dist/console/ai/reconcileTurn.d.ts +8 -0
- package/dist/console/ai/reconcileTurn.js +20 -0
- package/dist/console/auth/AuthPageLayout.d.ts +1 -1
- package/dist/console/auth/ForgotPasswordPage.d.ts +1 -1
- package/dist/console/auth/LoginPage.d.ts +1 -1
- package/dist/console/auth/RegisterPage.d.ts +1 -1
- package/dist/console/auth/RegisterPage.js +23 -3
- package/dist/console/cloud-connection/CloudConnectionPanel.d.ts +1 -0
- package/dist/console/cloud-connection/CloudConnectionPanel.js +169 -0
- package/dist/console/home/AppCard.d.ts +1 -1
- package/dist/console/home/AppCard.js +6 -12
- package/dist/console/home/HomeAppsStrip.d.ts +8 -0
- package/dist/console/home/HomeAppsStrip.js +61 -0
- package/dist/console/home/HomeLayout.d.ts +1 -1
- package/dist/console/home/HomeLayout.js +3 -1
- package/dist/console/home/HomePage.d.ts +1 -2
- package/dist/console/home/HomePage.js +149 -21
- package/dist/console/home/HomeRail.d.ts +22 -0
- package/dist/console/home/HomeRail.js +62 -0
- package/dist/console/home/QuickActions.d.ts +1 -1
- package/dist/console/home/QuickActions.js +3 -11
- package/dist/console/home/RecentApps.d.ts +1 -1
- package/dist/console/home/RecentApps.js +2 -2
- package/dist/console/home/StarredApps.d.ts +1 -1
- package/dist/console/home/StarredApps.js +2 -2
- package/dist/console/marketplace/InstalledListWidget.d.ts +1 -0
- package/dist/console/marketplace/InstalledListWidget.js +93 -0
- package/dist/console/marketplace/MarkdownText.d.ts +1 -1
- package/dist/console/marketplace/MarketplaceAccessDenied.d.ts +1 -1
- package/dist/console/marketplace/MarketplaceInstalledPage.d.ts +8 -14
- package/dist/console/marketplace/MarketplaceInstalledPage.js +14 -66
- package/dist/console/marketplace/MarketplacePackagePage.d.ts +1 -1
- package/dist/console/marketplace/MarketplacePackagePage.js +249 -8
- package/dist/console/marketplace/MarketplacePage.d.ts +1 -1
- package/dist/console/marketplace/MarketplacePage.js +60 -3
- package/dist/console/marketplace/PackageIcon.d.ts +1 -1
- package/dist/console/marketplace/PluginDisclosure.d.ts +14 -0
- package/dist/console/marketplace/PluginDisclosure.js +38 -0
- package/dist/console/marketplace/marketplaceApi.d.ts +123 -0
- package/dist/console/marketplace/marketplaceApi.js +254 -1
- package/dist/console/organizations/CreateWorkspaceDialog.d.ts +1 -1
- package/dist/console/organizations/OrganizationsLayout.d.ts +1 -1
- package/dist/console/organizations/OrganizationsPage.d.ts +1 -1
- package/dist/console/organizations/manage/AcceptInvitationPage.d.ts +1 -1
- package/dist/console/organizations/manage/InvitationsPage.d.ts +1 -1
- package/dist/console/organizations/manage/InviteMemberDialog.d.ts +1 -1
- package/dist/console/organizations/manage/MembersPage.d.ts +1 -1
- package/dist/console/organizations/manage/OrganizationLayout.d.ts +1 -1
- package/dist/console/organizations/manage/SettingsPage.d.ts +1 -1
- package/dist/context/CommandPaletteProvider.d.ts +44 -0
- package/dist/context/CommandPaletteProvider.js +71 -0
- package/dist/context/FavoritesProvider.d.ts +1 -1
- package/dist/context/NavigationContext.d.ts +1 -1
- package/dist/context/RecentItemsProvider.d.ts +2 -2
- package/dist/context/UserStateAdapters.d.ts +1 -1
- package/dist/context/index.d.ts +2 -0
- package/dist/context/index.js +1 -0
- package/dist/hooks/index.d.ts +5 -2
- package/dist/hooks/index.js +4 -1
- package/dist/hooks/useActionModal.d.ts +53 -0
- package/dist/hooks/useActionModal.js +111 -0
- package/dist/hooks/useChatConversation.d.ts +137 -4
- package/dist/hooks/useChatConversation.js +316 -25
- package/dist/hooks/useConsoleActionRuntime.d.ts +70 -0
- package/dist/hooks/useConsoleActionRuntime.js +564 -0
- package/dist/hooks/useConversationList.js +61 -3
- package/dist/hooks/useHomeInbox.d.ts +13 -0
- package/dist/hooks/useHomeInbox.js +142 -0
- package/dist/hooks/useNavPins.js +17 -23
- package/dist/hooks/useNavigationSync.d.ts +33 -0
- package/dist/hooks/useNavigationSync.js +98 -12
- package/dist/hooks/useReconcileOnError.d.ts +40 -0
- package/dist/hooks/useReconcileOnError.js +37 -0
- package/dist/hooks/useRecordApprovals.d.ts +18 -19
- package/dist/hooks/useRecordApprovals.js +24 -40
- package/dist/hooks/useResponsiveSidebar.js +14 -5
- package/dist/hooks/useSettleSignal.d.ts +19 -0
- package/dist/hooks/useSettleSignal.js +20 -0
- package/dist/hooks/useTrackRouteAsRecent.js +35 -0
- package/dist/hooks/useUrlOverlay.d.ts +62 -0
- package/dist/hooks/useUrlOverlay.js +88 -0
- package/dist/index.d.ts +18 -8
- package/dist/index.js +17 -5
- package/dist/layout/ActivityFeed.d.ts +1 -1
- package/dist/layout/AppHeader.d.ts +3 -2
- package/dist/layout/AppHeader.js +237 -72
- package/dist/layout/AppSidebar.d.ts +2 -1
- package/dist/layout/AppSidebar.js +26 -46
- package/dist/layout/AppSwitcher.d.ts +2 -1
- package/dist/layout/AppSwitcher.js +9 -5
- package/dist/layout/AuthPageLayout.d.ts +1 -1
- package/dist/layout/ConnectionStatus.d.ts +1 -1
- package/dist/layout/ConnectionStatus.js +9 -6
- package/dist/layout/ConsoleChatbotFab.d.ts +19 -1
- package/dist/layout/ConsoleChatbotFab.js +16 -2
- package/dist/layout/ConsoleFloatingChatbot.d.ts +34 -2
- package/dist/layout/ConsoleFloatingChatbot.js +391 -41
- package/dist/layout/ConsoleLayout.d.ts +1 -1
- package/dist/layout/ConsoleLayout.js +27 -11
- package/dist/layout/ContextSelectors.d.ts +44 -0
- package/dist/layout/ContextSelectors.js +242 -0
- package/dist/layout/InboxPopover.d.ts +6 -1
- package/dist/layout/InboxPopover.js +25 -6
- package/dist/layout/LocaleSwitcher.d.ts +1 -1
- package/dist/layout/LocalizedSidebarTrigger.d.ts +2 -0
- package/dist/layout/LocalizedSidebarTrigger.js +15 -0
- package/dist/layout/MobileViewSwitcherContext.d.ts +1 -1
- package/dist/layout/ModeToggle.d.ts +1 -1
- package/dist/layout/PageHeader.d.ts +1 -1
- package/dist/layout/UnifiedSidebar.d.ts +2 -1
- package/dist/layout/UnifiedSidebar.js +116 -15
- package/dist/layout/agentPicker.d.ts +56 -0
- package/dist/layout/agentPicker.js +40 -0
- package/dist/observability/index.d.ts +1 -0
- package/dist/observability/index.js +1 -0
- package/dist/observability/settleSignal.d.ts +64 -0
- package/dist/observability/settleSignal.js +131 -0
- package/dist/preview/CommitTimeline.d.ts +15 -0
- package/dist/preview/CommitTimeline.js +82 -0
- package/dist/preview/DraftChangesPanel.d.ts +19 -0
- package/dist/preview/DraftChangesPanel.js +114 -0
- package/dist/preview/DraftPreviewBar.d.ts +8 -0
- package/dist/preview/DraftPreviewBar.js +86 -0
- package/dist/preview/PreviewDraftEmptyState.d.ts +16 -0
- package/dist/preview/PreviewDraftEmptyState.js +47 -0
- package/dist/preview/PreviewModeContext.d.ts +57 -0
- package/dist/preview/PreviewModeContext.js +99 -0
- package/dist/preview/UnpublishedAppBar.d.ts +8 -0
- package/dist/preview/UnpublishedAppBar.js +83 -0
- package/dist/preview/commitHistory.d.ts +28 -0
- package/dist/preview/commitHistory.js +48 -0
- package/dist/preview/draftStatus.d.ts +20 -0
- package/dist/preview/draftStatus.js +27 -0
- package/dist/preview/usePublishAllDrafts.d.ts +18 -0
- package/dist/preview/usePublishAllDrafts.js +106 -0
- package/dist/providers/AdapterProvider.d.ts +1 -1
- package/dist/providers/AdapterProvider.js +6 -1
- package/dist/providers/ExpressionProvider.d.ts +1 -1
- package/dist/providers/MetadataProvider.d.ts +17 -2
- package/dist/providers/MetadataProvider.js +192 -12
- package/dist/runtime-config.d.ts +46 -2
- package/dist/runtime-config.js +39 -2
- package/dist/services/builtinComponents.js +68 -59
- package/dist/skeletons/SkeletonDashboard.d.ts +1 -1
- package/dist/skeletons/SkeletonDetail.d.ts +1 -1
- package/dist/skeletons/SkeletonGrid.d.ts +1 -1
- package/dist/utils/appRoute.d.ts +21 -0
- package/dist/utils/appRoute.js +25 -0
- package/dist/utils/deriveRelatedLists.d.ts +54 -0
- package/dist/utils/deriveRelatedLists.js +91 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/managedByEmptyState.d.ts +8 -1
- package/dist/utils/managedByEmptyState.js +13 -7
- package/dist/utils/preferLocal.d.ts +18 -0
- package/dist/utils/preferLocal.js +24 -0
- package/dist/views/ActionConfirmDialog.d.ts +1 -1
- package/dist/views/ActionConfirmDialog.js +3 -1
- package/dist/views/ActionParamDialog.d.ts +6 -1
- package/dist/views/ActionParamDialog.js +9 -3
- package/dist/views/ActionResultDialog.d.ts +13 -0
- package/dist/views/ActionResultDialog.js +134 -0
- package/dist/views/ComponentNavView.d.ts +14 -1
- package/dist/views/CreateViewDialog.d.ts +1 -1
- package/dist/views/DashboardConfigPanel.d.ts +28 -0
- package/dist/views/DashboardConfigPanel.js +81 -0
- package/dist/views/DashboardView.d.ts +4 -3
- package/dist/views/DashboardView.js +38 -239
- package/dist/views/FlowRunner.d.ts +31 -0
- package/dist/views/FlowRunner.js +121 -0
- package/dist/views/InterfaceListPage.d.ts +49 -0
- package/dist/views/InterfaceListPage.js +347 -0
- package/dist/views/MetadataInspector.d.ts +2 -2
- package/dist/views/ObjectView.d.ts +1 -1
- package/dist/views/ObjectView.js +209 -532
- package/dist/views/PageView.d.ts +8 -3
- package/dist/views/PageView.js +45 -32
- package/dist/views/RecordDetailView.d.ts +1 -1
- package/dist/views/RecordDetailView.js +363 -148
- package/dist/views/RecordFormPage.d.ts +1 -1
- package/dist/views/RecordFormPage.js +26 -1
- package/dist/views/ReportConfigPanel.d.ts +37 -0
- package/dist/views/ReportConfigPanel.js +85 -0
- package/dist/views/ReportView.d.ts +1 -1
- package/dist/views/ReportView.js +116 -7
- package/dist/views/RuntimeDraftBar.d.ts +30 -0
- package/dist/views/RuntimeDraftBar.js +112 -0
- package/dist/views/ScreenView.d.ts +70 -0
- package/dist/views/ScreenView.js +73 -0
- package/dist/views/SearchResultsPage.d.ts +1 -1
- package/dist/views/SearchResultsPage.js +8 -18
- package/dist/views/ViewConfigPanel.d.ts +24 -17
- package/dist/views/ViewConfigPanel.js +121 -77
- package/dist/views/index.d.ts +1 -1
- package/dist/views/index.js +1 -1
- package/dist/views/metadata-admin/AuditPanel.d.ts +28 -0
- package/dist/views/metadata-admin/AuditPanel.js +79 -0
- package/dist/views/metadata-admin/DiagnosticsPage.d.ts +20 -0
- package/dist/views/metadata-admin/DiagnosticsPage.js +69 -0
- package/dist/views/metadata-admin/DirectoryPage.d.ts +16 -1
- package/dist/views/metadata-admin/DirectoryPage.js +101 -24
- package/dist/views/metadata-admin/DraftReviewPanel.d.ts +33 -0
- package/dist/views/metadata-admin/DraftReviewPanel.js +77 -0
- package/dist/views/metadata-admin/EmbeddedItemEditor.d.ts +17 -1
- package/dist/views/metadata-admin/EmbeddedItemEditor.js +15 -8
- package/dist/views/metadata-admin/JsonSourceEditor.d.ts +39 -0
- package/dist/views/metadata-admin/JsonSourceEditor.js +196 -0
- package/dist/views/metadata-admin/LayeredDiff.d.ts +39 -1
- package/dist/views/metadata-admin/LayeredDiff.js +171 -5
- package/dist/views/metadata-admin/MetadataDetailDrawer.d.ts +15 -1
- package/dist/views/metadata-admin/MetadataTypeActions.d.ts +48 -0
- package/dist/views/metadata-admin/MetadataTypeActions.js +165 -0
- package/dist/views/metadata-admin/PackagesPage.d.ts +18 -0
- package/dist/views/metadata-admin/PackagesPage.js +403 -0
- package/dist/views/metadata-admin/PageShell.d.ts +1 -1
- package/dist/views/metadata-admin/PageShell.js +9 -4
- package/dist/views/metadata-admin/PermissionMatrixEditor.d.ts +35 -1
- package/dist/views/metadata-admin/QuickFind.d.ts +21 -1
- package/dist/views/metadata-admin/QuickFind.js +6 -3
- package/dist/views/metadata-admin/RelatedPanel.d.ts +24 -1
- package/dist/views/metadata-admin/RelatedPanel.js +20 -18
- package/dist/views/metadata-admin/ResourceEditPage.d.ts +40 -1
- package/dist/views/metadata-admin/ResourceEditPage.js +1250 -60
- package/dist/views/metadata-admin/ResourceHistoryPage.d.ts +39 -1
- package/dist/views/metadata-admin/ResourceHistoryPage.js +66 -16
- package/dist/views/metadata-admin/ResourceListPage.d.ts +13 -1
- package/dist/views/metadata-admin/ResourceListPage.js +258 -30
- package/dist/views/metadata-admin/ResourceRouter.d.ts +23 -1
- package/dist/views/metadata-admin/SchemaForm.d.ts +34 -1
- package/dist/views/metadata-admin/SchemaForm.js +559 -49
- package/dist/views/metadata-admin/StudioHomePage.d.ts +22 -0
- package/dist/views/metadata-admin/StudioHomePage.js +205 -0
- package/dist/views/metadata-admin/anchors.js +255 -24
- package/dist/views/metadata-admin/clientValidation.d.ts +50 -0
- package/dist/views/metadata-admin/clientValidation.js +169 -0
- package/dist/views/metadata-admin/color-variant-field.d.ts +30 -0
- package/dist/views/metadata-admin/color-variant-field.js +38 -0
- package/dist/views/metadata-admin/createDerive.d.ts +75 -0
- package/dist/views/metadata-admin/createDerive.js +179 -0
- package/dist/views/metadata-admin/dashboard-schema.d.ts +12 -0
- package/dist/views/metadata-admin/dashboard-schema.js +80 -0
- package/dist/views/metadata-admin/datasource/DatasourceResourcePage.d.ts +35 -0
- package/dist/views/metadata-admin/datasource/DatasourceResourcePage.js +327 -0
- package/dist/views/metadata-admin/datasource/register.d.ts +1 -0
- package/dist/views/metadata-admin/datasource/register.js +24 -0
- package/dist/views/metadata-admin/default-inspector-registry.d.ts +49 -0
- package/dist/views/metadata-admin/default-inspector-registry.js +8 -0
- package/dist/views/metadata-admin/default-schemas.js +115 -10
- package/dist/views/metadata-admin/external/ExternalDatasourcePanel.d.ts +27 -0
- package/dist/views/metadata-admin/external/ExternalDatasourcePanel.js +69 -0
- package/dist/views/metadata-admin/external/ImportObjectDialog.d.ts +27 -0
- package/dist/views/metadata-admin/external/ImportObjectDialog.js +77 -0
- package/dist/views/metadata-admin/external/SchemaBrowser.d.ts +16 -0
- package/dist/views/metadata-admin/external/SchemaBrowser.js +74 -0
- package/dist/views/metadata-admin/external/ValidationPanel.d.ts +16 -0
- package/dist/views/metadata-admin/external/ValidationPanel.js +68 -0
- package/dist/views/metadata-admin/external/api.d.ts +100 -0
- package/dist/views/metadata-admin/external/api.js +124 -0
- package/dist/views/metadata-admin/i18n.d.ts +1 -0
- package/dist/views/metadata-admin/i18n.js +1252 -2
- package/dist/views/metadata-admin/index.d.ts +8 -5
- package/dist/views/metadata-admin/index.js +12 -2
- package/dist/views/metadata-admin/inspector-registry.d.ts +51 -0
- package/dist/views/metadata-admin/inspector-registry.js +11 -0
- package/dist/views/metadata-admin/inspectors/ActionDefaultInspector.d.ts +30 -0
- package/dist/views/metadata-admin/inspectors/ActionDefaultInspector.js +180 -0
- package/dist/views/metadata-admin/inspectors/AppNavInspector.d.ts +16 -0
- package/dist/views/metadata-admin/inspectors/AppNavInspector.js +110 -0
- package/dist/views/metadata-admin/inspectors/ConditionBuilder.d.ts +29 -0
- package/dist/views/metadata-admin/inspectors/ConditionBuilder.js +154 -0
- package/dist/views/metadata-admin/inspectors/DashboardDefaultInspector.d.ts +28 -0
- package/dist/views/metadata-admin/inspectors/DashboardDefaultInspector.js +110 -0
- package/dist/views/metadata-admin/inspectors/DashboardWidgetInspector.d.ts +18 -0
- package/dist/views/metadata-admin/inspectors/DashboardWidgetInspector.js +139 -0
- package/dist/views/metadata-admin/inspectors/DatasetDefaultInspector.d.ts +21 -0
- package/dist/views/metadata-admin/inspectors/DatasetDefaultInspector.js +221 -0
- package/dist/views/metadata-admin/inspectors/FlowEdgeInspector.d.ts +16 -0
- package/dist/views/metadata-admin/inspectors/FlowEdgeInspector.js +126 -0
- package/dist/views/metadata-admin/inspectors/FlowInspector.d.ts +12 -0
- package/dist/views/metadata-admin/inspectors/FlowInspector.js +9 -0
- package/dist/views/metadata-admin/inspectors/FlowKeyValueField.d.ts +30 -0
- package/dist/views/metadata-admin/inspectors/FlowKeyValueField.js +125 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeConfigField.d.ts +18 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeConfigField.js +40 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeInspector.d.ts +14 -0
- package/dist/views/metadata-admin/inspectors/FlowNodeInspector.js +205 -0
- package/dist/views/metadata-admin/inspectors/FlowObjectListField.d.ts +26 -0
- package/dist/views/metadata-admin/inspectors/FlowObjectListField.js +105 -0
- package/dist/views/metadata-admin/inspectors/FlowReferenceField.d.ts +83 -0
- package/dist/views/metadata-admin/inspectors/FlowReferenceField.js +181 -0
- package/dist/views/metadata-admin/inspectors/FlowStringListField.d.ts +21 -0
- package/dist/views/metadata-admin/inspectors/FlowStringListField.js +60 -0
- package/dist/views/metadata-admin/inspectors/InspectorComboField.d.ts +40 -0
- package/dist/views/metadata-admin/inspectors/InspectorComboField.js +61 -0
- package/dist/views/metadata-admin/inspectors/ObjectDefaultInspector.d.ts +21 -0
- package/dist/views/metadata-admin/inspectors/ObjectDefaultInspector.js +55 -0
- package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.d.ts +23 -0
- package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.js +365 -0
- package/dist/views/metadata-admin/inspectors/PageBlockInspector.d.ts +48 -0
- package/dist/views/metadata-admin/inspectors/PageBlockInspector.js +332 -0
- package/dist/views/metadata-admin/inspectors/ReportDefaultInspector.d.ts +58 -0
- package/dist/views/metadata-admin/inspectors/ReportDefaultInspector.js +218 -0
- package/dist/views/metadata-admin/inspectors/ViewColumnInspector.d.ts +19 -0
- package/dist/views/metadata-admin/inspectors/ViewColumnInspector.js +144 -0
- package/dist/views/metadata-admin/inspectors/ViewInspector.d.ts +19 -0
- package/dist/views/metadata-admin/inspectors/ViewInspector.js +21 -0
- package/dist/views/metadata-admin/inspectors/ViewVariantInspector.d.ts +54 -0
- package/dist/views/metadata-admin/inspectors/ViewVariantInspector.js +191 -0
- package/dist/views/metadata-admin/inspectors/_shared.d.ts +128 -0
- package/dist/views/metadata-admin/inspectors/_shared.js +113 -0
- package/dist/views/metadata-admin/inspectors/datasetFilterCondition.d.ts +24 -0
- package/dist/views/metadata-admin/inspectors/datasetFilterCondition.js +97 -0
- package/dist/views/metadata-admin/inspectors/expression-validate.d.ts +26 -0
- package/dist/views/metadata-admin/inspectors/expression-validate.js +66 -0
- package/dist/views/metadata-admin/inspectors/flow-node-config.d.ts +143 -0
- package/dist/views/metadata-admin/inspectors/flow-node-config.js +506 -0
- package/dist/views/metadata-admin/inspectors/index.d.ts +1 -0
- package/dist/views/metadata-admin/inspectors/index.js +45 -0
- package/dist/views/metadata-admin/inspectors/json-schema-to-fields.d.ts +40 -0
- package/dist/views/metadata-admin/inspectors/json-schema-to-fields.js +227 -0
- package/dist/views/metadata-admin/inspectors/useDatasetFields.d.ts +72 -0
- package/dist/views/metadata-admin/inspectors/useDatasetFields.js +0 -0
- package/dist/views/metadata-admin/issuePath.d.ts +22 -0
- package/dist/views/metadata-admin/issuePath.js +65 -0
- package/dist/views/metadata-admin/mergeServerFields.d.ts +65 -0
- package/dist/views/metadata-admin/mergeServerFields.js +56 -0
- package/dist/views/metadata-admin/package-scope.d.ts +26 -0
- package/dist/views/metadata-admin/package-scope.js +43 -0
- package/dist/views/metadata-admin/preview-registry.d.ts +55 -0
- package/dist/views/metadata-admin/previews/ActionPreview.d.ts +25 -0
- package/dist/views/metadata-admin/previews/ActionPreview.js +238 -0
- package/dist/views/metadata-admin/previews/AddWidgetPicker.d.ts +12 -0
- package/dist/views/metadata-admin/previews/AddWidgetPicker.js +56 -0
- package/dist/views/metadata-admin/previews/AgentPreview.d.ts +24 -0
- package/dist/views/metadata-admin/previews/AgentPreview.js +100 -0
- package/dist/views/metadata-admin/previews/AppNavCanvas.d.ts +31 -0
- package/dist/views/metadata-admin/previews/AppNavCanvas.js +260 -0
- package/dist/views/metadata-admin/previews/AppPreview.d.ts +16 -1
- package/dist/views/metadata-admin/previews/AppPreview.js +23 -14
- package/dist/views/metadata-admin/previews/BookPreview.d.ts +20 -0
- package/dist/views/metadata-admin/previews/BookPreview.js +132 -0
- package/dist/views/metadata-admin/previews/DashboardPreview.d.ts +16 -1
- package/dist/views/metadata-admin/previews/DashboardPreview.js +110 -8
- package/dist/views/metadata-admin/previews/DatasetPreview.d.ts +18 -0
- package/dist/views/metadata-admin/previews/DatasetPreview.js +105 -0
- package/dist/views/metadata-admin/previews/DatasourcePreview.d.ts +23 -0
- package/dist/views/metadata-admin/previews/DatasourcePreview.js +68 -0
- package/dist/views/metadata-admin/previews/EmailTemplatePreview.d.ts +14 -1
- package/dist/views/metadata-admin/previews/FieldStub.d.ts +30 -0
- package/dist/views/metadata-admin/previews/FieldStub.js +104 -0
- package/dist/views/metadata-admin/previews/FieldsListEditor.d.ts +50 -0
- package/dist/views/metadata-admin/previews/FieldsListEditor.js +97 -0
- package/dist/views/metadata-admin/previews/FlowCanvas.d.ts +49 -0
- package/dist/views/metadata-admin/previews/FlowCanvas.js +416 -0
- package/dist/views/metadata-admin/previews/FlowPreview.d.ts +20 -0
- package/dist/views/metadata-admin/previews/FlowPreview.js +120 -0
- package/dist/views/metadata-admin/previews/FlowRunsPanel.d.ts +46 -0
- package/dist/views/metadata-admin/previews/FlowRunsPanel.js +97 -0
- package/dist/views/metadata-admin/previews/FlowSimulatorPanel.d.ts +25 -0
- package/dist/views/metadata-admin/previews/FlowSimulatorPanel.js +204 -0
- package/dist/views/metadata-admin/previews/JobPreview.d.ts +28 -0
- package/dist/views/metadata-admin/previews/JobPreview.js +290 -0
- package/dist/views/metadata-admin/previews/ObjectFormCanvas.d.ts +30 -0
- package/dist/views/metadata-admin/previews/ObjectFormCanvas.js +547 -0
- package/dist/views/metadata-admin/previews/ObjectPreview.d.ts +14 -1
- package/dist/views/metadata-admin/previews/ObjectPreview.js +5 -30
- package/dist/views/metadata-admin/previews/OutlineStrip.d.ts +32 -0
- package/dist/views/metadata-admin/previews/OutlineStrip.js +8 -0
- package/dist/views/metadata-admin/previews/PageBlockCanvas.d.ts +49 -0
- package/dist/views/metadata-admin/previews/PageBlockCanvas.js +510 -0
- package/dist/views/metadata-admin/previews/PagePreview.d.ts +10 -1
- package/dist/views/metadata-admin/previews/PagePreview.js +200 -5
- package/dist/views/metadata-admin/previews/PermissionPreview.d.ts +27 -0
- package/dist/views/metadata-admin/previews/PermissionPreview.js +115 -0
- package/dist/views/metadata-admin/previews/PreviewShell.d.ts +29 -6
- package/dist/views/metadata-admin/previews/PreviewShell.js +16 -3
- package/dist/views/metadata-admin/previews/ReportPreview.d.ts +18 -1
- package/dist/views/metadata-admin/previews/ReportPreview.js +23 -15
- package/dist/views/metadata-admin/previews/RolePreview.d.ts +19 -0
- package/dist/views/metadata-admin/previews/RolePreview.js +14 -0
- package/dist/views/metadata-admin/previews/ScreenPreview.d.ts +38 -0
- package/dist/views/metadata-admin/previews/ScreenPreview.js +61 -0
- package/dist/views/metadata-admin/previews/SkillPreview.d.ts +22 -0
- package/dist/views/metadata-admin/previews/SkillPreview.js +34 -0
- package/dist/views/metadata-admin/previews/ToolPreview.d.ts +25 -0
- package/dist/views/metadata-admin/previews/ToolPreview.js +122 -0
- package/dist/views/metadata-admin/previews/TranslationPreview.d.ts +25 -0
- package/dist/views/metadata-admin/previews/TranslationPreview.js +52 -0
- package/dist/views/metadata-admin/previews/ValidationPreview.d.ts +27 -0
- package/dist/views/metadata-admin/previews/ValidationPreview.js +110 -0
- package/dist/views/metadata-admin/previews/ViewColumnPanes.d.ts +62 -0
- package/dist/views/metadata-admin/previews/ViewColumnPanes.js +140 -0
- package/dist/views/metadata-admin/previews/ViewPreview.d.ts +23 -1
- package/dist/views/metadata-admin/previews/ViewPreview.js +101 -73
- package/dist/views/metadata-admin/previews/block-config.d.ts +82 -0
- package/dist/views/metadata-admin/previews/block-config.js +324 -0
- package/dist/views/metadata-admin/previews/block-types.d.ts +40 -0
- package/dist/views/metadata-admin/previews/block-types.js +110 -0
- package/dist/views/metadata-admin/previews/field-types.d.ts +53 -0
- package/dist/views/metadata-admin/previews/field-types.js +97 -0
- package/dist/views/metadata-admin/previews/flow-canvas-layout.d.ts +102 -0
- package/dist/views/metadata-admin/previews/flow-canvas-layout.js +227 -0
- package/dist/views/metadata-admin/previews/flow-canvas-parts.d.ts +96 -0
- package/dist/views/metadata-admin/previews/flow-canvas-parts.js +373 -0
- package/dist/views/metadata-admin/previews/form-preview.d.ts +24 -0
- package/dist/views/metadata-admin/previews/form-preview.js +29 -0
- package/dist/views/metadata-admin/previews/index.js +43 -0
- package/dist/views/metadata-admin/previews/object-fields-bridge.d.ts +66 -0
- package/dist/views/metadata-admin/previews/object-fields-bridge.js +171 -0
- package/dist/views/metadata-admin/previews/object-fields-io.d.ts +130 -0
- package/dist/views/metadata-admin/previews/object-fields-io.js +243 -0
- package/dist/views/metadata-admin/previews/screen-spec.d.ts +43 -0
- package/dist/views/metadata-admin/previews/screen-spec.js +108 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-types.d.ts +102 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-types.js +2 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-validate.d.ts +15 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-validate.js +185 -0
- package/dist/views/metadata-admin/previews/simulator/flow-simulator.d.ts +73 -0
- package/dist/views/metadata-admin/previews/simulator/flow-simulator.js +426 -0
- package/dist/views/metadata-admin/previews/useDatasetCatalog.d.ts +47 -0
- package/dist/views/metadata-admin/previews/useDatasetCatalog.js +133 -0
- package/dist/views/metadata-admin/previews/useFlowNodePalette.d.ts +44 -0
- package/dist/views/metadata-admin/previews/useFlowNodePalette.js +124 -0
- package/dist/views/metadata-admin/previews/useMetaOptions.d.ts +8 -0
- package/dist/views/metadata-admin/previews/useMetaOptions.js +50 -0
- package/dist/views/metadata-admin/previews/useObjectFields.d.ts +23 -0
- package/dist/views/metadata-admin/previews/useObjectFields.js +79 -0
- package/dist/views/metadata-admin/previews/useObjectOptions.d.ts +8 -0
- package/dist/views/metadata-admin/previews/useObjectOptions.js +43 -0
- package/dist/views/metadata-admin/previews/view-column-io.d.ts +42 -0
- package/dist/views/metadata-admin/previews/view-column-io.js +73 -0
- package/dist/views/metadata-admin/previews/widget-types.d.ts +24 -0
- package/dist/views/metadata-admin/previews/widget-types.js +40 -0
- package/dist/views/metadata-admin/registry.d.ts +140 -19
- package/dist/views/metadata-admin/report-schema.d.ts +26 -0
- package/dist/views/metadata-admin/report-schema.js +121 -0
- package/dist/views/metadata-admin/useMetadata.d.ts +100 -2
- package/dist/views/metadata-admin/useMetadata.js +155 -4
- package/dist/views/metadata-admin/view-item-normalize.d.ts +20 -0
- package/dist/views/metadata-admin/view-item-normalize.js +68 -0
- package/dist/views/metadata-admin/view-schema.d.ts +16 -0
- package/dist/views/metadata-admin/view-schema.js +107 -0
- package/dist/views/metadata-admin/view-variant-model.d.ts +23 -0
- package/dist/views/metadata-admin/view-variant-model.js +64 -0
- package/dist/views/metadata-admin/widgets.d.ts +89 -1
- package/dist/views/metadata-admin/widgets.js +491 -17
- package/dist/views/runtime-metadata-persistence.d.ts +78 -0
- package/dist/views/runtime-metadata-persistence.js +89 -0
- package/dist/views/useOpenRecordList.d.ts +18 -0
- package/dist/views/useOpenRecordList.js +36 -0
- package/dist/views/userFilterUrlState.d.ts +15 -0
- package/dist/views/userFilterUrlState.js +53 -0
- package/dist/views/view-config-adapter.d.ts +38 -0
- package/dist/views/view-config-adapter.js +80 -0
- package/package.json +52 -34
- package/dist/views/DesignDrawer.d.ts +0 -28
- package/dist/views/DesignDrawer.js +0 -51
- package/dist/views/metadata-admin/DesignerEditorWrapper.d.ts +0 -68
- package/dist/views/metadata-admin/DesignerEditorWrapper.js +0 -158
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
3
3
|
/**
|
|
4
4
|
* Built-in widget renderers for SchemaForm.
|
|
@@ -19,28 +19,35 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
19
19
|
* SchemaForm.tsx and prefetch it in ResourceEditPage.
|
|
20
20
|
*/
|
|
21
21
|
import * as React from 'react';
|
|
22
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Input, Button, } from '@object-ui/components';
|
|
23
|
-
import { Plus, Trash2 } from 'lucide-react';
|
|
22
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Input, Button, Label, Switch, LazyIcon, toKebabIconName, Popover, PopoverTrigger, PopoverContent, FilterBuilder, Dialog, DialogContent, DialogHeader, DialogTitle, } from '@object-ui/components';
|
|
23
|
+
import { ChevronDown, ChevronsUpDown, ChevronUp, Eye, EyeOff, Plus, Search, Trash2 } from 'lucide-react';
|
|
24
|
+
// @ts-ignore - lucide-react has no `exports` field; subpath types live alongside dynamic.mjs
|
|
25
|
+
import { iconNames } from 'lucide-react/dynamic.mjs';
|
|
26
|
+
import { detectLocale, t, tFormat } from './i18n';
|
|
27
|
+
import { ColorVariantPicker } from './color-variant-field';
|
|
28
|
+
import { ConditionBuilder } from './inspectors/ConditionBuilder';
|
|
24
29
|
/* -------------------------------------------------------------------------- */
|
|
25
30
|
/* ref:object — pick an object by name */
|
|
26
31
|
/* -------------------------------------------------------------------------- */
|
|
27
32
|
function RefObjectWidget({ id, value, onChange, readOnly, context, }) {
|
|
33
|
+
const locale = detectLocale();
|
|
28
34
|
const names = context?.objectNames ?? [];
|
|
29
35
|
const v = value == null ? '' : String(value);
|
|
30
36
|
if (context?.objectsLoading) {
|
|
31
|
-
return (_jsx(Input, { id: id, value: v, disabled: true, placeholder:
|
|
37
|
+
return (_jsx(Input, { id: id, value: v, disabled: true, placeholder: t('engine.form.loadingObjects', locale) }));
|
|
32
38
|
}
|
|
33
39
|
// If list is empty (e.g. no objects defined yet), fall back to a
|
|
34
40
|
// freeform text input so the user can still type a value.
|
|
35
41
|
if (names.length === 0) {
|
|
36
|
-
return (_jsx(Input, { id: id, value: v, disabled: readOnly, onChange: (e) => onChange(e.target.value || undefined), placeholder:
|
|
42
|
+
return (_jsx(Input, { id: id, value: v, disabled: readOnly, onChange: (e) => onChange(e.target.value || undefined), placeholder: t('engine.form.noObjects', locale) }));
|
|
37
43
|
}
|
|
38
|
-
return (_jsxs(Select, { value: v, onValueChange: (next) => onChange(next || undefined), disabled: readOnly, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder:
|
|
44
|
+
return (_jsxs(Select, { value: v, onValueChange: (next) => onChange(next || undefined), disabled: readOnly, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: t('engine.form.selectObject', locale) }) }), _jsx(SelectContent, { children: names.map((n) => (_jsx(SelectItem, { value: n, children: n }, n))) })] }));
|
|
39
45
|
}
|
|
40
46
|
/* -------------------------------------------------------------------------- */
|
|
41
47
|
/* object-selector — multi-select object picker */
|
|
42
48
|
/* -------------------------------------------------------------------------- */
|
|
43
49
|
function ObjectSelectorWidget({ id, value, onChange, readOnly, context, fieldSpec, }) {
|
|
50
|
+
const locale = detectLocale();
|
|
44
51
|
const names = context?.objectNames ?? [];
|
|
45
52
|
const multiple = fieldSpec?.multiple ?? false;
|
|
46
53
|
// Parse value: string[], string (comma-separated), or empty
|
|
@@ -70,14 +77,15 @@ function ObjectSelectorWidget({ id, value, onChange, readOnly, context, fieldSpe
|
|
|
70
77
|
onChange(multiple ? newSelection : '');
|
|
71
78
|
};
|
|
72
79
|
if (context?.objectsLoading) {
|
|
73
|
-
return _jsx(Input, { id: id, value:
|
|
80
|
+
return _jsx(Input, { id: id, value: t('engine.form.loadingObjects', locale), readOnly: true, disabled: true });
|
|
74
81
|
}
|
|
75
|
-
return (_jsxs("div", { className: "space-y-2", children: [selectedValues.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2", children: selectedValues.map(obj => (_jsxs("div", { className: "inline-flex items-center gap-1 rounded bg-secondary px-2 py-1 text-sm", children: [_jsx("span", { children: obj }), !readOnly && (_jsx("button", { type: "button", onClick: () => handleRemove(obj), className: "text-muted-foreground hover:text-foreground", children: "\u00D7" }))] }, obj))) })), _jsxs(Select, { value: "", onValueChange: handleToggle, disabled: readOnly || names.length === 0, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: multiple ?
|
|
82
|
+
return (_jsxs("div", { className: "space-y-2", children: [selectedValues.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2", children: selectedValues.map(obj => (_jsxs("div", { className: "inline-flex items-center gap-1 rounded bg-secondary px-2 py-1 text-sm", children: [_jsx("span", { children: obj }), !readOnly && (_jsx("button", { type: "button", onClick: () => handleRemove(obj), className: "text-muted-foreground hover:text-foreground", children: "\u00D7" }))] }, obj))) })), _jsxs(Select, { value: "", onValueChange: handleToggle, disabled: readOnly || names.length === 0, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: multiple ? t('engine.form.addObjects', locale) : t('engine.form.selectObjectDots', locale) }) }), _jsx(SelectContent, { children: names.map(name => (_jsxs(SelectItem, { value: name, disabled: !multiple && selectedValues.includes(name), children: [name, selectedValues.includes(name) && ' ✓'] }, name))) })] })] }));
|
|
76
83
|
}
|
|
77
84
|
/* -------------------------------------------------------------------------- */
|
|
78
85
|
/* field-selector — smart field picker (depends on selected object) */
|
|
79
86
|
/* -------------------------------------------------------------------------- */
|
|
80
87
|
function FieldSelectorWidget({ id, value, onChange, readOnly, fieldSpec, formData, }) {
|
|
88
|
+
const locale = detectLocale();
|
|
81
89
|
const [fields, setFields] = React.useState([]);
|
|
82
90
|
const [loading, setLoading] = React.useState(false);
|
|
83
91
|
// Resolve dependency: fieldSpec.dependsOn or fieldSpec.reference or 'objectName'
|
|
@@ -131,20 +139,21 @@ function FieldSelectorWidget({ id, value, onChange, readOnly, fieldSpec, formDat
|
|
|
131
139
|
onChange(multiple ? newSelection : '');
|
|
132
140
|
};
|
|
133
141
|
if (!objectName) {
|
|
134
|
-
return _jsx(Input, { id: id, value:
|
|
142
|
+
return _jsx(Input, { id: id, value: t('engine.form.selectObjectFirst', locale), readOnly: true, disabled: true });
|
|
135
143
|
}
|
|
136
144
|
if (loading) {
|
|
137
|
-
return _jsx(Input, { id: id, value:
|
|
145
|
+
return _jsx(Input, { id: id, value: t('engine.form.loadingFields', locale), readOnly: true, disabled: true });
|
|
138
146
|
}
|
|
139
147
|
return (_jsxs("div", { className: "space-y-2", children: [selectedValues.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2", children: selectedValues.map(field => {
|
|
140
148
|
const fieldMeta = fields.find(f => f.name === field);
|
|
141
149
|
return (_jsxs("div", { className: "inline-flex items-center gap-1 rounded bg-secondary px-2 py-1 text-sm", children: [_jsx("span", { children: fieldMeta?.label || field }), _jsx("code", { className: "text-xs text-muted-foreground", children: fieldMeta?.type }), !readOnly && (_jsx("button", { type: "button", onClick: () => handleRemove(field), className: "text-muted-foreground hover:text-foreground", children: "\u00D7" }))] }, field));
|
|
142
|
-
}) })), _jsxs(Select, { value: "", onValueChange: handleToggle, disabled: readOnly || fields.length === 0, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: multiple ?
|
|
150
|
+
}) })), _jsxs(Select, { value: "", onValueChange: handleToggle, disabled: readOnly || fields.length === 0, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: multiple ? t('engine.form.addFields', locale) : t('engine.form.selectFieldDots', locale) }) }), _jsx(SelectContent, { children: fields.map(f => (_jsx(SelectItem, { value: f.name, disabled: !multiple && selectedValues.includes(f.name), children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { children: f.label || f.name }), _jsx("code", { className: "text-xs text-muted-foreground", children: f.type }), selectedValues.includes(f.name) && ' ✓'] }) }, f.name))) })] })] }));
|
|
143
151
|
}
|
|
144
152
|
/* -------------------------------------------------------------------------- */
|
|
145
153
|
/* master-detail — inline row editor for array-of-object fields */
|
|
146
154
|
/* -------------------------------------------------------------------------- */
|
|
147
155
|
function MasterDetailWidget({ schema, value, onChange, readOnly, context, }) {
|
|
156
|
+
const locale = detectLocale();
|
|
148
157
|
// Unwrap anyOf/oneOf: pick the first array-of-object branch.
|
|
149
158
|
let resolved = schema;
|
|
150
159
|
if (resolved?.anyOf || resolved?.oneOf) {
|
|
@@ -160,7 +169,7 @@ function MasterDetailWidget({ schema, value, onChange, readOnly, context, }) {
|
|
|
160
169
|
const cols = Object.keys(itemProps);
|
|
161
170
|
if (cols.length === 0) {
|
|
162
171
|
// Falls back to JSON if the array items aren't a typed object.
|
|
163
|
-
return (
|
|
172
|
+
return (_jsx("div", { className: "rounded border border-dashed border-amber-500/40 bg-amber-500/5 p-2 text-xs text-amber-700 dark:text-amber-300", children: t('engine.form.masterDetailSchemaError', locale) }));
|
|
164
173
|
}
|
|
165
174
|
function updateRow(idx, patch) {
|
|
166
175
|
const next = rows.slice();
|
|
@@ -175,7 +184,7 @@ function MasterDetailWidget({ schema, value, onChange, readOnly, context, }) {
|
|
|
175
184
|
next.splice(idx, 1);
|
|
176
185
|
onChange(next);
|
|
177
186
|
}
|
|
178
|
-
return (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "overflow-x-auto rounded border border-border/40", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/40", children: _jsxs("tr", { children: [cols.map((c) => (_jsxs("th", { className: "px-2 py-1.5 text-left text-xs font-medium text-muted-foreground", children: [itemProps[c]?.title ?? c, required.has(c) && (_jsx("span", { className: "text-destructive ml-0.5", children: "*" }))] }, c))), _jsx("th", { className: "w-8" })] }) }), _jsxs("tbody", { children: [rows.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: cols.length + 1, className: "px-2 py-3 text-center text-xs text-muted-foreground", children:
|
|
187
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "overflow-x-auto rounded border border-border/40", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { className: "bg-muted/40", children: _jsxs("tr", { children: [cols.map((c) => (_jsxs("th", { className: "px-2 py-1.5 text-left text-xs font-medium text-muted-foreground", children: [itemProps[c]?.title ?? c, required.has(c) && (_jsx("span", { className: "text-destructive ml-0.5", children: "*" }))] }, c))), _jsx("th", { className: "w-8" })] }) }), _jsxs("tbody", { children: [rows.length === 0 && (_jsx("tr", { children: _jsx("td", { colSpan: cols.length + 1, className: "px-2 py-3 text-center text-xs text-muted-foreground", children: t('engine.form.noRows', locale) }) })), rows.map((row, idx) => (_jsxs("tr", { className: "border-t border-border/30", children: [cols.map((c) => (_jsx("td", { className: "p-1", children: _jsx(RowCell, { schema: itemProps[c], value: (row ?? {})[c], readOnly: readOnly, context: context, onChange: (v) => updateRow(idx, { [c]: v }) }) }, c))), _jsx("td", { className: "p-1 text-right", children: _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeRow(idx), disabled: readOnly, className: "h-7 w-7 p-0", "aria-label": t('engine.form.removeRow', locale), children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }) })] }, idx)))] })] }) }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addRow, disabled: readOnly, children: [_jsx(Plus, { className: "h-3.5 w-3.5 mr-1" }), " ", t('engine.form.addRow', locale)] })] }));
|
|
179
188
|
}
|
|
180
189
|
function RowCell({ schema, value, readOnly, context, onChange, }) {
|
|
181
190
|
// ref:object hint inside a row cell
|
|
@@ -205,6 +214,7 @@ function RowCell({ schema, value, readOnly, context, onChange, }) {
|
|
|
205
214
|
/* string-tags — chip input for string[] (e.g. searchableFields) */
|
|
206
215
|
/* -------------------------------------------------------------------------- */
|
|
207
216
|
function StringTagsWidget({ id, value, onChange, readOnly, }) {
|
|
217
|
+
const locale = detectLocale();
|
|
208
218
|
const tags = Array.isArray(value) ? value : [];
|
|
209
219
|
const [draft, setDraft] = React.useState('');
|
|
210
220
|
function add(raw) {
|
|
@@ -226,7 +236,7 @@ function StringTagsWidget({ id, value, onChange, readOnly, }) {
|
|
|
226
236
|
next.splice(idx, 1);
|
|
227
237
|
onChange(next);
|
|
228
238
|
}
|
|
229
|
-
return (_jsx("div", { className: "rounded border border-input bg-background p-1.5", children: _jsxs("div", { className: "flex flex-wrap items-center gap-1", children: [tags.map((t, i) => (_jsxs("span", { className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-xs", children: [_jsx("span", { className: "font-mono", children: t }), !readOnly && (_jsx("button", { type: "button", "aria-label": `Remove ${t}`, onClick: () => remove(i), className: "text-muted-foreground hover:text-destructive", children: "\u00D7" }))] }, i))), _jsx("input", { id: id, type: "text", value: draft, disabled: readOnly, placeholder: tags.length === 0 ? '
|
|
239
|
+
return (_jsx("div", { className: "rounded border border-input bg-background p-1.5", children: _jsxs("div", { className: "flex flex-wrap items-center gap-1", children: [tags.map((t, i) => (_jsxs("span", { className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-xs", children: [_jsx("span", { className: "font-mono", children: t }), !readOnly && (_jsx("button", { type: "button", "aria-label": `Remove ${t}`, onClick: () => remove(i), className: "text-muted-foreground hover:text-destructive", children: "\u00D7" }))] }, i))), _jsx("input", { id: id, type: "text", value: draft, disabled: readOnly, placeholder: tags.length === 0 ? t('engine.form.tagsPlaceholder', locale) : '', onChange: (e) => setDraft(e.target.value), onKeyDown: (e) => {
|
|
230
240
|
if (e.key === 'Enter' || e.key === ',') {
|
|
231
241
|
e.preventDefault();
|
|
232
242
|
add(draft);
|
|
@@ -237,17 +247,481 @@ function StringTagsWidget({ id, value, onChange, readOnly, }) {
|
|
|
237
247
|
}, onBlur: () => draft && add(draft), className: "min-w-[8rem] flex-1 bg-transparent text-sm outline-none" })] }) }));
|
|
238
248
|
}
|
|
239
249
|
/* -------------------------------------------------------------------------- */
|
|
240
|
-
/*
|
|
250
|
+
/* multiselect — pick from a fixed option set (array of enum) */
|
|
241
251
|
/* -------------------------------------------------------------------------- */
|
|
252
|
+
/** "grid" → "Grid", "start_date" → "Start Date". */
|
|
253
|
+
function humanizeOption(v) {
|
|
254
|
+
return v
|
|
255
|
+
.replace(/[_-]+/g, ' ')
|
|
256
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Toggleable option set for `array<enum>` fields (e.g.
|
|
260
|
+
* `appearance.allowedVisualizations`). Options come from the JSON Schema's
|
|
261
|
+
* `items.enum` (or `fieldSpec.options`); the value is the selected subset
|
|
262
|
+
* as a `string[]`, preserving the enum's declared order. Replaces the
|
|
263
|
+
* free-text tag input the generic array renderer fell back to — the author
|
|
264
|
+
* picks from the real allowed values instead of typing (and mistyping) them.
|
|
265
|
+
*/
|
|
266
|
+
function MultiSelectWidget({ value, onChange, readOnly, schema, fieldSpec }) {
|
|
267
|
+
// Prefer explicit form options; else the JSON Schema enum on the items.
|
|
268
|
+
const options = React.useMemo(() => {
|
|
269
|
+
if (Array.isArray(fieldSpec?.options) && fieldSpec.options.length) {
|
|
270
|
+
return fieldSpec.options.map((o) => ({ label: o.label, value: o.value }));
|
|
271
|
+
}
|
|
272
|
+
const enumVals = schema?.items?.enum ?? schema?.enum ?? [];
|
|
273
|
+
return (Array.isArray(enumVals) ? enumVals : [])
|
|
274
|
+
.filter((v) => typeof v === 'string')
|
|
275
|
+
.map((v) => ({ label: humanizeOption(v), value: v }));
|
|
276
|
+
}, [fieldSpec, schema]);
|
|
277
|
+
const selected = React.useMemo(() => (Array.isArray(value) ? value.filter((v) => typeof v === 'string') : []), [value]);
|
|
278
|
+
function toggle(opt) {
|
|
279
|
+
if (readOnly)
|
|
280
|
+
return;
|
|
281
|
+
// Keep selection ordered by the option list so behaviour is stable
|
|
282
|
+
// (e.g. allowedVisualizations[0] = the default/initial visualization).
|
|
283
|
+
const set = new Set(selected);
|
|
284
|
+
if (set.has(opt))
|
|
285
|
+
set.delete(opt);
|
|
286
|
+
else
|
|
287
|
+
set.add(opt);
|
|
288
|
+
const next = options.map((o) => o.value).filter((v) => set.has(v));
|
|
289
|
+
onChange(next.length ? next : undefined);
|
|
290
|
+
}
|
|
291
|
+
if (options.length === 0) {
|
|
292
|
+
// No known option set — degrade to the comma-tag editor so the field
|
|
293
|
+
// is still editable rather than rendering an empty box.
|
|
294
|
+
return _jsx(StringTagsWidget, { value: value, onChange: onChange, readOnly: readOnly, schema: schema, fieldSpec: fieldSpec });
|
|
295
|
+
}
|
|
296
|
+
return (_jsx("div", { className: "flex flex-wrap gap-1.5", role: "group", children: options.map((o) => {
|
|
297
|
+
const on = selected.includes(o.value);
|
|
298
|
+
return (_jsxs("button", { type: "button", role: "checkbox", "aria-checked": on, disabled: readOnly, onClick: () => toggle(o.value), className: 'inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1 text-xs font-medium transition-colors ' +
|
|
299
|
+
(on
|
|
300
|
+
? 'border-primary bg-primary/10 text-primary'
|
|
301
|
+
: 'border-input bg-background text-muted-foreground hover:text-foreground hover:bg-muted') +
|
|
302
|
+
(readOnly ? ' opacity-60 cursor-not-allowed' : ''), children: [_jsx("span", { "aria-hidden": true, className: 'flex h-3.5 w-3.5 items-center justify-center rounded-[3px] border text-[9px] leading-none ' +
|
|
303
|
+
(on ? 'border-primary bg-primary text-primary-foreground' : 'border-muted-foreground/40'), children: on ? '✓' : '' }), o.label] }, o.value));
|
|
304
|
+
}) }));
|
|
305
|
+
}
|
|
306
|
+
/* -------------------------------------------------------------------------- */
|
|
307
|
+
/* field-ref / field-multi — pick object field(s) from the bound object */
|
|
308
|
+
/* -------------------------------------------------------------------------- */
|
|
309
|
+
const NO_FIELD = '__none__';
|
|
310
|
+
/**
|
|
311
|
+
* Single object-field picker. Used for View config props that reference one
|
|
312
|
+
* field by name (titleField, groupByField, startDateField, colorField,
|
|
313
|
+
* xAxisField, …). Field list comes from `context.objectFields`; a value not
|
|
314
|
+
* present in the catalog is still shown so stale/custom values survive.
|
|
315
|
+
*/
|
|
316
|
+
function FieldRefWidget({ id, value, onChange, readOnly, context }) {
|
|
317
|
+
const locale = detectLocale();
|
|
318
|
+
const fields = context?.objectFields ?? [];
|
|
319
|
+
const current = value == null ? '' : String(value);
|
|
320
|
+
const inCatalog = !current || fields.some((f) => f.name === current);
|
|
321
|
+
return (_jsxs(Select, { value: current || NO_FIELD, onValueChange: (v) => onChange(v === NO_FIELD ? '' : v), disabled: readOnly, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: fields.length ? t('engine.form.selectField', locale) : t('engine.form.noObjectBound', locale) }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: NO_FIELD, children: _jsx("span", { className: "text-muted-foreground", children: t('engine.form.none', locale) }) }), !inCatalog && current && (_jsxs(SelectItem, { value: current, children: [_jsx("span", { className: "font-mono", children: current }), _jsx("span", { className: "ml-2 text-xs text-muted-foreground", children: t('engine.form.notInObject', locale) })] })), fields.map((f) => (_jsx(SelectItem, { value: f.name, children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { children: f.label || f.name }), _jsx("code", { className: "text-xs text-muted-foreground", children: f.name })] }) }, f.name)))] })] }));
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Resolve a stored `sourceView` value against a source object's view catalog,
|
|
325
|
+
* mirroring the runtime resolver (InterfaceListPage.resolveSourceView): a value
|
|
326
|
+
* resolves if it's an exact view name, a bare name matching a view's
|
|
327
|
+
* `<object>.<name>` suffix, or the special `default`/`list` (→ object default
|
|
328
|
+
* view). `showStored` is true when the stored value needs a synthesized option
|
|
329
|
+
* (i.e. it isn't already an exact catalog entry). Exported for unit tests.
|
|
330
|
+
*/
|
|
331
|
+
export function resolveStoredViewRef(views, current) {
|
|
332
|
+
const exact = current ? views.find((v) => v.name === current) : undefined;
|
|
333
|
+
const suffixMatch = current && !exact ? views.find((v) => v.name.endsWith(`.${current}`)) : undefined;
|
|
334
|
+
const isSpecial = current === 'default' || current === 'list';
|
|
335
|
+
return { exact, suffixMatch, isSpecial, resolves: !!exact || !!suffixMatch || isSpecial, showStored: !!current && !exact };
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Single view picker for `interfaceConfig.sourceView`. Views come from
|
|
339
|
+
* `context.objectViews` (the source object's views, loaded from the object
|
|
340
|
+
* named by the sibling `source` field). A value not present in the catalog is
|
|
341
|
+
* still shown so stale/custom names survive; clearing to "None" omits the
|
|
342
|
+
* field, which the protocol treats as the object's default view. Replaces the
|
|
343
|
+
* free-text input where an author could type a non-existent view name.
|
|
344
|
+
*/
|
|
345
|
+
function ViewRefWidget({ id, value, onChange, readOnly, context }) {
|
|
346
|
+
const locale = detectLocale();
|
|
347
|
+
const views = context?.objectViews ?? [];
|
|
348
|
+
const current = value == null ? '' : String(value);
|
|
349
|
+
// Mirror the runtime resolver (InterfaceListPage.resolveSourceView): a stored
|
|
350
|
+
// value resolves if it's an exact view name, OR a bare name matching a view's
|
|
351
|
+
// `<object>.<name>` suffix, OR the special `default`/`list` (→ object default
|
|
352
|
+
// view). Only a value that resolves to NOTHING gets the "(not in object)" tag —
|
|
353
|
+
// so a working bare value like `default` is no longer mislabelled.
|
|
354
|
+
const { suffixMatch, resolves, showStored } = resolveStoredViewRef(views, current);
|
|
355
|
+
return (_jsxs(Select, { value: current || NO_FIELD, onValueChange: (v) => onChange(v === NO_FIELD ? undefined : v), disabled: readOnly, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: views.length ? t('engine.form.selectEllipsis', locale) : t('engine.form.noObjectBound', locale) }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: NO_FIELD, children: _jsx("span", { className: "text-muted-foreground", children: t('engine.form.none', locale) }) }), showStored && (_jsx(SelectItem, { value: current, children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { children: suffixMatch?.label || current }), _jsx("code", { className: "text-xs text-muted-foreground", children: suffixMatch ? `\u2192 ${suffixMatch.name}` : current }), !resolves && (_jsx("span", { className: "ml-1 text-xs text-muted-foreground", children: t('engine.form.notInObject', locale) }))] }) })), views.map((v) => (_jsx(SelectItem, { value: v.name, children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { children: v.label || v.name }), _jsx("code", { className: "text-xs text-muted-foreground", children: v.name })] }) }, v.name)))] })] }));
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Ordered multi object-field picker. Used for props that reference a list of
|
|
359
|
+
* fields (kanban card `columns`, gallery `visibleFields`, chart
|
|
360
|
+
* `yAxisFields`, `searchableFields`, …). Preserves order; supports reorder
|
|
361
|
+
* and removal; values outside the catalog are retained.
|
|
362
|
+
*/
|
|
363
|
+
function FieldRefMultiWidget({ id, value, onChange, readOnly, context }) {
|
|
364
|
+
const locale = detectLocale();
|
|
365
|
+
const fields = context?.objectFields ?? [];
|
|
366
|
+
const selected = Array.isArray(value)
|
|
367
|
+
? value.map(String)
|
|
368
|
+
: typeof value === 'string' && value
|
|
369
|
+
? value.split(',').map((s) => s.trim()).filter(Boolean)
|
|
370
|
+
: [];
|
|
371
|
+
const labelFor = (name) => fields.find((f) => f.name === name)?.label || name;
|
|
372
|
+
const remaining = fields.filter((f) => !selected.includes(f.name));
|
|
373
|
+
const add = (name) => {
|
|
374
|
+
if (!selected.includes(name))
|
|
375
|
+
onChange([...selected, name]);
|
|
376
|
+
};
|
|
377
|
+
const removeAt = (i) => {
|
|
378
|
+
const next = selected.slice();
|
|
379
|
+
next.splice(i, 1);
|
|
380
|
+
onChange(next);
|
|
381
|
+
};
|
|
382
|
+
const move = (i, dir) => {
|
|
383
|
+
const j = i + dir;
|
|
384
|
+
if (j < 0 || j >= selected.length)
|
|
385
|
+
return;
|
|
386
|
+
const next = selected.slice();
|
|
387
|
+
[next[i], next[j]] = [next[j], next[i]];
|
|
388
|
+
onChange(next);
|
|
389
|
+
};
|
|
390
|
+
return (_jsxs("div", { className: "space-y-2", children: [selected.length > 0 && (_jsx("div", { className: "space-y-1", children: selected.map((name, i) => (_jsxs("div", { className: "flex items-center gap-1 rounded border border-input bg-background px-2 py-1 text-sm", children: [_jsxs("span", { className: "flex-1 truncate", children: [labelFor(name), _jsx("code", { className: "ml-2 text-xs text-muted-foreground", children: name })] }), !readOnly && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "aria-label": "Move up", disabled: i === 0, onClick: () => move(i, -1), className: "px-1 text-muted-foreground hover:text-foreground disabled:opacity-30", children: "\u2191" }), _jsx("button", { type: "button", "aria-label": "Move down", disabled: i === selected.length - 1, onClick: () => move(i, 1), className: "px-1 text-muted-foreground hover:text-foreground disabled:opacity-30", children: "\u2193" }), _jsx("button", { type: "button", "aria-label": `Remove ${name}`, onClick: () => removeAt(i), className: "px-1 text-muted-foreground hover:text-destructive", children: "\u00D7" })] }))] }, name))) })), !readOnly && (_jsxs(Select, { value: "", onValueChange: add, disabled: remaining.length === 0, children: [_jsx(SelectTrigger, { id: id, children: _jsx(SelectValue, { placeholder: fields.length
|
|
391
|
+
? remaining.length
|
|
392
|
+
? t('engine.form.addField', locale)
|
|
393
|
+
: t('engine.form.allFieldsAdded', locale)
|
|
394
|
+
: t('engine.form.noObjectBound', locale) }) }), _jsx(SelectContent, { children: remaining.map((f) => (_jsx(SelectItem, { value: f.name, children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { children: f.label || f.name }), _jsx("code", { className: "text-xs text-muted-foreground", children: f.name })] }) }, f.name))) })] }))] }));
|
|
395
|
+
}
|
|
396
|
+
/* -------------------------------------------------------------------------- */
|
|
397
|
+
/* icon — searchable Lucide icon picker */
|
|
398
|
+
/* -------------------------------------------------------------------------- */
|
|
399
|
+
// Lucide ships ~1500+ kebab-case icon names; freeze once for O(1) reuse.
|
|
400
|
+
const LUCIDE_ICON_NAMES = iconNames;
|
|
401
|
+
const LUCIDE_ICON_SET = new Set(LUCIDE_ICON_NAMES);
|
|
402
|
+
// Cap the rendered grid — each cell mounts a lazily-loaded icon, so showing all
|
|
403
|
+
// ~1500 at once would fire a flood of chunk requests. The search box narrows it.
|
|
404
|
+
const ICON_RESULT_LIMIT = 120;
|
|
405
|
+
/**
|
|
406
|
+
* Searchable icon picker for `widget: 'icon'` string fields (page/app/object
|
|
407
|
+
* `icon`). Replaces the raw text input where an author had to know and type a
|
|
408
|
+
* Lucide name. The trigger shows a live preview of the current icon; opening it
|
|
409
|
+
* reveals a search box + a grid of matching icons (preview + name). Selecting
|
|
410
|
+
* writes the kebab-case name string.
|
|
411
|
+
*
|
|
412
|
+
* Out-of-catalog values survive: a name that isn't a Lucide icon (e.g. one from
|
|
413
|
+
* another library, or a typo to be fixed later) is still shown on the trigger as
|
|
414
|
+
* plain text (LazyIcon degrades to a fallback glyph) and is offered as the first
|
|
415
|
+
* "keep" option so re-opening the picker never silently drops it.
|
|
416
|
+
*
|
|
417
|
+
* Built inline (no Radix portal) so the search + grid render eagerly — the same
|
|
418
|
+
* jsdom-friendly choice the other pickers' tests rely on.
|
|
419
|
+
*/
|
|
420
|
+
export function IconPickerWidget({ id, value, onChange, readOnly }) {
|
|
421
|
+
const locale = detectLocale();
|
|
422
|
+
const current = value == null ? '' : String(value);
|
|
423
|
+
const [open, setOpen] = React.useState(false);
|
|
424
|
+
const [query, setQuery] = React.useState('');
|
|
425
|
+
const currentKebab = current ? toKebabIconName(current) : '';
|
|
426
|
+
const inCatalog = !current || LUCIDE_ICON_SET.has(currentKebab);
|
|
427
|
+
const q = toKebabIconName(query.trim());
|
|
428
|
+
const allMatches = React.useMemo(() => (q ? LUCIDE_ICON_NAMES.filter((n) => n.includes(q)) : LUCIDE_ICON_NAMES), [q]);
|
|
429
|
+
const results = allMatches.slice(0, ICON_RESULT_LIMIT);
|
|
430
|
+
const truncated = allMatches.length > results.length;
|
|
431
|
+
const select = (name) => {
|
|
432
|
+
onChange(name || undefined);
|
|
433
|
+
setOpen(false);
|
|
434
|
+
setQuery('');
|
|
435
|
+
};
|
|
436
|
+
return (_jsxs(_Fragment, { children: [_jsxs("button", { id: id, type: "button", role: "combobox", "aria-haspopup": "dialog", "aria-expanded": open, disabled: readOnly, onClick: () => !readOnly && setOpen(true), className: "flex w-full items-center gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm text-left disabled:cursor-not-allowed disabled:opacity-60", children: [_jsx(LazyIcon, { name: inCatalog ? current : undefined, className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: 'flex-1 truncate ' + (current ? 'font-mono' : 'text-muted-foreground'), children: current || t('engine.form.selectEllipsis', locale) }), !inCatalog && current && (_jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: t('engine.form.notInObject', locale) })), _jsx(ChevronsUpDown, { "aria-hidden": true, className: "h-3.5 w-3.5 shrink-0 opacity-50" })] }), _jsx(Dialog, { open: open, onOpenChange: (o) => { setOpen(o); if (!o)
|
|
437
|
+
setQuery(''); }, children: _jsxs(DialogContent, { className: "max-w-2xl", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: t('engine.form.chooseIcon', locale) }) }), _jsxs("div", { className: "flex items-center gap-2 rounded-md border border-input bg-background px-3 py-2", children: [_jsx(Search, { "aria-hidden": true, className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsx("input", { type: "text", autoFocus: true, value: query, "aria-label": t('engine.form.searchIcons', locale), placeholder: t('engine.form.searchIcons', locale), onChange: (e) => setQuery(e.target.value), className: "w-full bg-transparent text-sm outline-none" })] }), _jsxs("div", { id: id ? `${id}-listbox` : undefined, role: "listbox", className: "grid max-h-[52vh] grid-cols-6 gap-1.5 overflow-y-auto pr-1 sm:grid-cols-8", children: [current && (_jsxs("button", { type: "button", role: "option", "aria-selected": false, title: t('engine.form.none', locale), onClick: () => select(undefined), className: "flex aspect-square flex-col items-center justify-center gap-1 rounded border border-dashed border-border p-1 text-muted-foreground hover:bg-accent hover:text-accent-foreground", children: [_jsx(Trash2, { className: "h-4 w-4" }), _jsx("span", { className: "w-full truncate text-center text-[9px] leading-tight", children: "none" })] })), !inCatalog && current && (_jsxs("button", { type: "button", role: "option", "aria-selected": true, title: current, onClick: () => select(current), className: "flex aspect-square flex-col items-center justify-center gap-1 rounded bg-accent p-1 text-accent-foreground ring-1 ring-primary", children: [_jsx(LazyIcon, { name: undefined, className: "h-4 w-4" }), _jsx("span", { className: "w-full truncate text-center text-[9px] leading-tight", children: current })] })), results.map((name) => {
|
|
438
|
+
const selected = name === currentKebab;
|
|
439
|
+
return (_jsxs("button", { type: "button", role: "option", "aria-selected": selected, title: name, onClick: () => select(name), className: 'flex aspect-square flex-col items-center justify-center gap-1 rounded p-1 text-muted-foreground hover:bg-accent hover:text-accent-foreground ' +
|
|
440
|
+
(selected ? 'bg-accent text-accent-foreground ring-1 ring-primary' : ''), children: [_jsx(LazyIcon, { name: name, className: "h-5 w-5" }), _jsx("span", { className: "w-full truncate text-center text-[9px] leading-tight", children: name })] }, name));
|
|
441
|
+
}), results.length === 0 && (_jsx("p", { className: "col-span-full px-2 py-6 text-center text-sm text-muted-foreground", children: t('engine.form.noMatchingIcons', locale) }))] }), truncated && (_jsx("p", { className: "text-center text-xs text-muted-foreground", children: tFormat('engine.form.iconsTruncated', locale, { shown: results.length, total: allMatches.length }) }))] }) })] }));
|
|
442
|
+
}
|
|
443
|
+
const FILTER_MODES = [
|
|
444
|
+
{ key: 'none', label: 'None' },
|
|
445
|
+
{ key: 'tabs', label: 'Tabs' },
|
|
446
|
+
{ key: 'dropdown', label: 'Dropdown' },
|
|
447
|
+
];
|
|
448
|
+
const slugifyTabName = (s) => (s || '').trim().toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'tab';
|
|
449
|
+
function FilterModeWidget({ value, onChange, readOnly, context }) {
|
|
450
|
+
const uf = (value && typeof value === 'object' ? value : undefined);
|
|
451
|
+
const mode = uf?.element ?? (uf ? 'dropdown' : 'none');
|
|
452
|
+
const objectFields = context?.objectFields ?? [];
|
|
453
|
+
const setMode = (next) => {
|
|
454
|
+
if (readOnly)
|
|
455
|
+
return;
|
|
456
|
+
if (next === 'none') {
|
|
457
|
+
onChange(undefined);
|
|
458
|
+
return;
|
|
459
|
+
} // omit-is-none
|
|
460
|
+
onChange({ ...(uf ?? {}), element: next });
|
|
461
|
+
};
|
|
462
|
+
const fields = Array.isArray(uf?.fields) ? uf.fields : [];
|
|
463
|
+
const patchFields = (nextFields) => onChange({ ...(uf ?? {}), element: mode === 'none' ? 'dropdown' : mode, fields: nextFields });
|
|
464
|
+
const selected = new Set(fields.map((f) => f.field));
|
|
465
|
+
const remaining = objectFields.filter((f) => !selected.has(f.name));
|
|
466
|
+
const labelFor = (name) => objectFields.find((f) => f.name === name)?.label || name;
|
|
467
|
+
// A deprecated `element: 'toggle'` config still lands here — render its
|
|
468
|
+
// field picker too so it stays editable, even though Toggle isn't offered
|
|
469
|
+
// as a new authoring choice.
|
|
470
|
+
const isFieldMode = mode === 'dropdown' || mode === 'toggle';
|
|
471
|
+
// ── Tabs preset editing (ADR-0053) ──────────────────────────────────────
|
|
472
|
+
// Read any shape (canonical {name,filter} or legacy {id,filters}) into the
|
|
473
|
+
// canonical editing model; writes always emit the canonical form so authoring
|
|
474
|
+
// converges on one schema.
|
|
475
|
+
const readTabs = () => {
|
|
476
|
+
const raw = Array.isArray(uf?.tabs) ? uf.tabs : [];
|
|
477
|
+
return raw.map((t) => ({
|
|
478
|
+
...t,
|
|
479
|
+
name: t?.name ?? t?.id ?? '',
|
|
480
|
+
label: typeof t?.label === 'string' ? t.label : (t?.name ?? t?.id ?? ''),
|
|
481
|
+
filter: Array.isArray(t?.filter)
|
|
482
|
+
? t.filter
|
|
483
|
+
: Array.isArray(t?.filters)
|
|
484
|
+
? t.filters
|
|
485
|
+
.filter((r) => Array.isArray(r) && r.length >= 2)
|
|
486
|
+
.map((r) => ({ field: String(r[0]), operator: String(r[1]), value: r[2] }))
|
|
487
|
+
: [],
|
|
488
|
+
isDefault: t?.isDefault ?? t?.default,
|
|
489
|
+
}));
|
|
490
|
+
};
|
|
491
|
+
const tabs = readTabs();
|
|
492
|
+
const canonicalTab = (t) => {
|
|
493
|
+
const out = { name: slugifyTabName(t.label || t.name), label: t.label || t.name || 'Tab' };
|
|
494
|
+
if (t.icon)
|
|
495
|
+
out.icon = t.icon;
|
|
496
|
+
out.filter = Array.isArray(t.filter) ? t.filter : [];
|
|
497
|
+
if (t.isDefault)
|
|
498
|
+
out.isDefault = true;
|
|
499
|
+
return out;
|
|
500
|
+
};
|
|
501
|
+
const writeTabs = (next) => {
|
|
502
|
+
const seen = {};
|
|
503
|
+
const canon = next.map(canonicalTab).map((o) => {
|
|
504
|
+
let n = o.name;
|
|
505
|
+
if (seen[n]) {
|
|
506
|
+
let k = 2;
|
|
507
|
+
while (seen[`${o.name}_${k}`])
|
|
508
|
+
k++;
|
|
509
|
+
n = `${o.name}_${k}`;
|
|
510
|
+
}
|
|
511
|
+
seen[n] = true;
|
|
512
|
+
return { ...o, name: n };
|
|
513
|
+
});
|
|
514
|
+
onChange({ ...(uf ?? {}), element: 'tabs', tabs: canon });
|
|
515
|
+
};
|
|
516
|
+
const addTab = () => writeTabs([...tabs, { name: '', label: `Tab ${tabs.length + 1}`, filter: [] }]);
|
|
517
|
+
const removeTab = (ti) => writeTabs(tabs.filter((_, i) => i !== ti));
|
|
518
|
+
const moveTab = (ti, dir) => {
|
|
519
|
+
const next = [...tabs];
|
|
520
|
+
const j = ti + dir;
|
|
521
|
+
if (j < 0 || j >= next.length)
|
|
522
|
+
return;
|
|
523
|
+
[next[ti], next[j]] = [next[j], next[ti]];
|
|
524
|
+
writeTabs(next);
|
|
525
|
+
};
|
|
526
|
+
const patchTab = (ti, patch) => writeTabs(tabs.map((t, i) => (i === ti ? { ...t, ...patch } : t)));
|
|
527
|
+
const setShowAllRecords = (c) => onChange({ ...(uf ?? {}), element: 'tabs', showAllRecords: c });
|
|
528
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "inline-flex rounded-md border border-input bg-background p-0.5", role: "radiogroup", "aria-label": "Filter element", children: FILTER_MODES.map((m) => {
|
|
529
|
+
const active = mode === m.key;
|
|
530
|
+
return (_jsx("button", { type: "button", role: "radio", "aria-checked": active, "data-testid": `filter-mode-${m.key}`, disabled: readOnly, onClick: () => setMode(m.key), className: 'px-3 py-1 text-xs font-medium rounded transition-colors ' +
|
|
531
|
+
(active
|
|
532
|
+
? 'bg-primary text-primary-foreground shadow-sm'
|
|
533
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-muted'), children: m.label }, m.key));
|
|
534
|
+
}) }), isFieldMode && (_jsxs("div", { className: "space-y-2", "data-testid": "filter-mode-fields", children: [fields.length > 0 && (_jsx("div", { className: "space-y-1", children: fields.map((f, i) => (_jsxs("div", { className: "flex items-center gap-2 rounded border border-input bg-background px-2 py-1 text-sm", children: [_jsxs("span", { className: "flex-1 truncate", children: [labelFor(f.field), _jsx("code", { className: "ml-2 text-xs text-muted-foreground", children: f.field })] }), _jsxs("label", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(Switch, { checked: !!f.showCount, disabled: readOnly, onCheckedChange: (c) => patchFields(fields.map((x, j) => (j === i ? { ...x, showCount: c } : x))) }), "count"] }), !readOnly && (_jsx("button", { type: "button", "aria-label": "Remove field", onClick: () => patchFields(fields.filter((_, j) => j !== i)), className: "px-1 text-muted-foreground hover:text-destructive", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }, f.field))) })), !readOnly && remaining.length > 0 && (_jsxs(Select, { onValueChange: (name) => patchFields([...fields, { field: name }]), children: [_jsx(SelectTrigger, { className: "h-8 text-xs", "data-testid": "filter-mode-add-field", children: _jsx(SelectValue, { placeholder: "+ Add filter field\u2026" }) }), _jsx(SelectContent, { children: remaining.map((f) => (_jsx(SelectItem, { value: f.name, children: f.label || f.name }, f.name))) })] })), objectFields.length === 0 && (_jsx("p", { className: "text-xs text-muted-foreground", children: "Bind a source object to pick filter fields." }))] })), mode === 'tabs' && (_jsxs("div", { className: "space-y-2", "data-testid": "filter-mode-tabs-editor", children: [tabs.map((tab, ti) => (_jsxs("div", { className: "rounded-md border border-input bg-background p-2 space-y-2", "data-testid": `tab-preset-${ti}`, children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Input, { value: tab.label, placeholder: "Tab label", disabled: readOnly, className: "h-8 text-sm flex-1", "data-testid": `tab-label-${ti}`, onChange: (e) => patchTab(ti, { label: e.target.value }) }), _jsx("code", { className: "text-[10px] text-muted-foreground shrink-0 max-w-[6rem] truncate", title: tab.name, children: tab.name }), !readOnly && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "aria-label": "Move tab up", disabled: ti === 0, onClick: () => moveTab(ti, -1), className: "px-1 text-muted-foreground hover:text-foreground disabled:opacity-30", children: _jsx(ChevronUp, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", "aria-label": "Move tab down", disabled: ti === tabs.length - 1, onClick: () => moveTab(ti, 1), className: "px-1 text-muted-foreground hover:text-foreground disabled:opacity-30", children: _jsx(ChevronDown, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", "aria-label": "Remove tab", onClick: () => removeTab(ti), className: "px-1 text-muted-foreground hover:text-destructive", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }))] }), _jsx("div", { className: "pl-1", "data-testid": `tab-${ti}-filter`, children: _jsx(FilterBuilderField, { value: tab.filter, onChange: (f) => patchTab(ti, { filter: f }), fields: objectFields, readOnly: readOnly }) })] }, ti))), !readOnly && (_jsxs("button", { type: "button", "data-testid": "filter-mode-add-tab", onClick: addTab, className: "inline-flex items-center text-xs font-medium text-primary hover:underline", children: [_jsx(Plus, { className: "h-3.5 w-3.5 mr-1" }), " Add tab"] })), _jsxs("label", { className: "flex items-center gap-2 text-xs text-muted-foreground pt-1", children: [_jsx(Switch, { checked: uf?.showAllRecords !== false, disabled: readOnly, "data-testid": "filter-mode-show-all", onCheckedChange: setShowAllRecords }), "Show \u201CAll records\u201D tab"] }), objectFields.length === 0 && (_jsx("p", { className: "text-xs text-muted-foreground", children: "Bind a source object to build tab filter rules." }))] }))] }));
|
|
535
|
+
}
|
|
536
|
+
/* -------------------------------------------------------------------------- */
|
|
537
|
+
/* action-multi — pick toolbar buttons from the source object's actions */
|
|
538
|
+
/* */
|
|
539
|
+
/* Interface-page `buttons` are object Actions (ActionSchema), not free text. */
|
|
540
|
+
/* This makes "buttons = object actions" correct-by-construction (the picker */
|
|
541
|
+
/* only offers actions the object actually defines). */
|
|
542
|
+
/* -------------------------------------------------------------------------- */
|
|
543
|
+
function ActionMultiWidget({ id, value, onChange, readOnly, context }) {
|
|
544
|
+
const actions = context?.objectActions ?? [];
|
|
545
|
+
const selected = Array.isArray(value)
|
|
546
|
+
? value.map(String)
|
|
547
|
+
: typeof value === 'string' && value
|
|
548
|
+
? value.split(',').map((s) => s.trim()).filter(Boolean)
|
|
549
|
+
: [];
|
|
550
|
+
const labelFor = (name) => actions.find((a) => a.name === name)?.label || name;
|
|
551
|
+
const remaining = actions.filter((a) => !selected.includes(a.name));
|
|
552
|
+
const add = (name) => { if (!selected.includes(name))
|
|
553
|
+
onChange([...selected, name]); };
|
|
554
|
+
const removeAt = (i) => { const next = selected.slice(); next.splice(i, 1); onChange(next); };
|
|
555
|
+
const move = (i, dir) => {
|
|
556
|
+
const j = i + dir;
|
|
557
|
+
if (j < 0 || j >= selected.length)
|
|
558
|
+
return;
|
|
559
|
+
const next = selected.slice();
|
|
560
|
+
[next[i], next[j]] = [next[j], next[i]];
|
|
561
|
+
onChange(next);
|
|
562
|
+
};
|
|
563
|
+
return (_jsxs("div", { className: "space-y-2", "data-testid": "action-multi", children: [selected.length > 0 && (_jsx("div", { className: "space-y-1", children: selected.map((name, i) => (_jsxs("div", { className: "flex items-center gap-1 rounded border border-input bg-background px-2 py-1 text-sm", children: [_jsxs("span", { className: "flex-1 truncate", children: [labelFor(name), _jsx("code", { className: "ml-2 text-xs text-muted-foreground", children: name })] }), !readOnly && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "aria-label": "Move up", disabled: i === 0, onClick: () => move(i, -1), className: "px-1 text-muted-foreground hover:text-foreground disabled:opacity-30", children: "\u2191" }), _jsx("button", { type: "button", "aria-label": "Move down", disabled: i === selected.length - 1, onClick: () => move(i, 1), className: "px-1 text-muted-foreground hover:text-foreground disabled:opacity-30", children: "\u2193" }), _jsx("button", { type: "button", "aria-label": `Remove ${name}`, onClick: () => removeAt(i), className: "px-1 text-muted-foreground hover:text-destructive", children: "\u00D7" })] }))] }, name))) })), !readOnly && (_jsxs(Select, { value: "", onValueChange: add, disabled: remaining.length === 0, children: [_jsx(SelectTrigger, { id: id, "data-testid": "action-multi-add", children: _jsx(SelectValue, { placeholder: actions.length ? (remaining.length ? '+ Add action button…' : 'All actions added') : 'Bind a source object to pick actions' }) }), _jsx(SelectContent, { children: remaining.map((a) => (_jsx(SelectItem, { value: a.name, children: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx("span", { children: a.label || a.name }), _jsx("code", { className: "text-xs text-muted-foreground", children: a.name })] }) }, a.name))) })] }))] }));
|
|
564
|
+
}
|
|
565
|
+
/* -------------------------------------------------------------------------- */
|
|
566
|
+
/* filter-builder — the SAME runtime FilterBuilder used by the list toolbar, */
|
|
567
|
+
/* reused in Studio for tab presets and the page base filter (unified UX). */
|
|
568
|
+
/* Stored format stays spec ViewFilterRule[] ({field,operator,value}); the */
|
|
569
|
+
/* builder's camelCase operators are mapped at the boundary so the runtime */
|
|
570
|
+
/* (specOperatorToAst) keeps working unchanged. */
|
|
571
|
+
/* -------------------------------------------------------------------------- */
|
|
572
|
+
const FB_TO_SPEC = {
|
|
573
|
+
equals: 'equals', notEquals: 'not_equals', contains: 'contains', notContains: 'not_contains',
|
|
574
|
+
isEmpty: 'is_empty', isNotEmpty: 'is_not_empty', greaterThan: 'gt', lessThan: 'lt',
|
|
575
|
+
greaterOrEqual: 'gte', lessOrEqual: 'lte', before: 'lt', after: 'gt', between: 'between',
|
|
576
|
+
in: 'in', notIn: 'not_in',
|
|
577
|
+
};
|
|
578
|
+
const SPEC_TO_FB = {
|
|
579
|
+
equals: 'equals', eq: 'equals', not_equals: 'notEquals', ne: 'notEquals', neq: 'notEquals',
|
|
580
|
+
contains: 'contains', not_contains: 'notContains', is_empty: 'isEmpty', is_not_empty: 'isNotEmpty',
|
|
581
|
+
gt: 'greaterThan', greater_than: 'greaterThan', lt: 'lessThan', less_than: 'lessThan',
|
|
582
|
+
gte: 'greaterOrEqual', lte: 'lessOrEqual', in: 'in', not_in: 'notIn', nin: 'notIn',
|
|
583
|
+
};
|
|
584
|
+
function FilterBuilderField({ value, onChange, fields, readOnly }) {
|
|
585
|
+
const rules = Array.isArray(value) ? value : [];
|
|
586
|
+
const group = {
|
|
587
|
+
id: 'g',
|
|
588
|
+
logic: 'and',
|
|
589
|
+
conditions: rules.map((r, i) => ({
|
|
590
|
+
id: `c${i}`,
|
|
591
|
+
field: r.field,
|
|
592
|
+
operator: SPEC_TO_FB[r.operator] ?? r.operator ?? 'equals',
|
|
593
|
+
value: r.value ?? '',
|
|
594
|
+
})),
|
|
595
|
+
};
|
|
596
|
+
const fbFields = fields.map((f) => ({ value: f.name, label: f.label || f.name, type: f.type }));
|
|
597
|
+
const summary = rules.length
|
|
598
|
+
? rules.map((r) => `${fields.find((f) => f.name === r.field)?.label || r.field}`).filter(Boolean).join(', ')
|
|
599
|
+
: '';
|
|
600
|
+
const handle = (g) => {
|
|
601
|
+
const next = (g?.conditions ?? [])
|
|
602
|
+
.filter((c) => c?.field)
|
|
603
|
+
.map((c) => ({ field: c.field, operator: FB_TO_SPEC[c.operator] ?? c.operator, value: c.value }));
|
|
604
|
+
onChange(next);
|
|
605
|
+
};
|
|
606
|
+
return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", disabled: readOnly, className: "h-8 w-full justify-between text-xs font-normal", "data-testid": "filter-builder-trigger", children: [_jsx("span", { className: "truncate text-left", children: summary || _jsx("span", { className: "text-muted-foreground", children: "+ Add filter\u2026" }) }), _jsx(ChevronDown, { className: "h-3.5 w-3.5 opacity-60 shrink-0" })] }) }), _jsx(PopoverContent, { align: "start", className: "w-[440px] max-w-[90vw] p-3", children: fields.length === 0 ? (_jsx("p", { className: "text-xs text-muted-foreground", children: "Bind a source object to add filter conditions." })) : (_jsx(FilterBuilder, { fields: fbFields, value: group, onChange: handle })) })] }));
|
|
607
|
+
}
|
|
608
|
+
function FilterBuilderWidget({ value, onChange, readOnly, context }) {
|
|
609
|
+
return (_jsx(FilterBuilderField, { value: value, onChange: (rules) => onChange(rules.length ? rules : undefined), fields: context?.objectFields ?? [], readOnly: readOnly }));
|
|
610
|
+
}
|
|
611
|
+
/* -------------------------------------------------------------------------- */
|
|
612
|
+
/* color-picker — semantic swatch row (enum) or native hex input (free color) */
|
|
613
|
+
/* -------------------------------------------------------------------------- */
|
|
614
|
+
function ColorPickerWidget({ value, onChange, readOnly, schema, fieldSpec }) {
|
|
615
|
+
const enumOpts = Array.isArray(fieldSpec?.options) && fieldSpec.options.length
|
|
616
|
+
? fieldSpec.options.map((o) => ({ value: o.value, label: o.label }))
|
|
617
|
+
: Array.isArray(schema?.enum)
|
|
618
|
+
? schema.enum.filter((v) => typeof v === 'string').map((v) => ({ value: v }))
|
|
619
|
+
: null;
|
|
620
|
+
if (enumOpts && enumOpts.length) {
|
|
621
|
+
return (_jsx(ColorVariantPicker, { value: value == null ? undefined : String(value), onChange: (v) => onChange(v), disabled: readOnly, options: enumOpts }));
|
|
622
|
+
}
|
|
623
|
+
// Free color → native picker + hex text.
|
|
624
|
+
const v = value == null ? '' : String(value);
|
|
625
|
+
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "color", value: /^#([0-9a-f]{6})$/i.test(v) ? v : '#000000', disabled: readOnly, onChange: (e) => onChange(e.target.value), className: "h-8 w-10 shrink-0 rounded border border-input bg-background p-0.5 disabled:opacity-60", "aria-label": "Color" }), _jsx(Input, { value: v, placeholder: "#RRGGBB", disabled: readOnly, onChange: (e) => onChange(e.target.value || undefined), className: "h-8 text-sm font-mono" })] }));
|
|
626
|
+
}
|
|
627
|
+
/* -------------------------------------------------------------------------- */
|
|
628
|
+
/* condition — no-code CEL predicate builder (visible / hidden / disabled / …) */
|
|
629
|
+
/* -------------------------------------------------------------------------- */
|
|
630
|
+
function ConditionWidget({ value, onChange, readOnly, context }) {
|
|
631
|
+
return (_jsx(ConditionBuilder, { value: value == null ? '' : String(value), onCommit: (cel) => onChange(cel || undefined), fields: context?.objectFields, disabled: readOnly }));
|
|
632
|
+
}
|
|
633
|
+
/* -------------------------------------------------------------------------- */
|
|
634
|
+
/* secret — write-only / masked credential input (encrypt-on-write fields) */
|
|
635
|
+
/* -------------------------------------------------------------------------- */
|
|
636
|
+
/** Mirrors objectql\'s SECRET_MASK: a stored secret reads back as this, never plaintext. */
|
|
637
|
+
export const SECRET_MASK = '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022';
|
|
638
|
+
/**
|
|
639
|
+
* `secret` widget — a write-only credential input for `secret` field types (and
|
|
640
|
+
* `format: 'password'` / `writeOnly` props). A stored secret reads back masked,
|
|
641
|
+
* so the box starts empty with a "leave blank to keep" hint; typing replaces it,
|
|
642
|
+
* a reveal toggle shows what you type, and Clear removes it. Emits the typed
|
|
643
|
+
* value (new secret), `SECRET_MASK` (blank + existing = keep, a no-op on write),
|
|
644
|
+
* `null` (Clear), or `undefined` (blank + none).
|
|
645
|
+
*/
|
|
646
|
+
function SecretWidget({ value, onChange, readOnly, schema, id }) {
|
|
647
|
+
const stored = value === SECRET_MASK;
|
|
648
|
+
const [reveal, setReveal] = React.useState(false);
|
|
649
|
+
const [draft, setDraft] = React.useState(stored || value == null ? '' : String(value));
|
|
650
|
+
const update = (v) => {
|
|
651
|
+
setDraft(v);
|
|
652
|
+
if (v === '')
|
|
653
|
+
onChange(stored ? SECRET_MASK : undefined); // blank: keep if stored, else unset
|
|
654
|
+
else
|
|
655
|
+
onChange(v);
|
|
656
|
+
};
|
|
657
|
+
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { id: id, type: reveal ? 'text' : 'password', value: draft, disabled: readOnly, autoComplete: "off", placeholder: stored ? (schema?.description ? '•••••••• set — type to replace' : '•••••••• set — leave blank to keep') : (typeof schema?.description === 'string' ? '' : 'Enter a value'), onChange: (e) => update(e.target.value), className: "h-8 text-sm font-mono", "aria-label": "Secret value" }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-8 w-8 shrink-0", disabled: readOnly, "aria-label": reveal ? 'Hide value' : 'Reveal value', onClick: () => setReveal((r) => !r), children: reveal ? _jsx(EyeOff, { className: "h-4 w-4" }) : _jsx(Eye, { className: "h-4 w-4" }) }), stored && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-8 shrink-0 text-destructive hover:text-destructive", disabled: readOnly, onClick: () => { setDraft(''); onChange(null); }, children: "Clear" }))] }));
|
|
658
|
+
}
|
|
659
|
+
/* -------------------------------------------------------------------------- */
|
|
660
|
+
/* dynamic-config — sub-form whose schema is chosen by a sibling field value */
|
|
661
|
+
/* -------------------------------------------------------------------------- */
|
|
662
|
+
/**
|
|
663
|
+
* `dynamic-config` widget — renders an object field whose shape depends on a
|
|
664
|
+
* SIBLING field\'s value. `fieldSpec.dependsOn` names the parent field (e.g.
|
|
665
|
+
* `driver`); `context.dynamicSchemas[<parentValue>]` supplies the JSON-Schema
|
|
666
|
+
* for the config object. The canonical case is a datasource\'s `config` field
|
|
667
|
+
* that changes fields per driver, but it is generic: any "pick X → these
|
|
668
|
+
* settings" pattern can reuse it by populating `dynamicSchemas`.
|
|
669
|
+
*/
|
|
670
|
+
function DynamicConfigWidget({ value, onChange, readOnly, fieldSpec, formData, context }) {
|
|
671
|
+
const dep = Array.isArray(fieldSpec?.dependsOn) ? fieldSpec.dependsOn[0] : fieldSpec?.dependsOn;
|
|
672
|
+
const depVal = dep ? formData?.[dep] : undefined;
|
|
673
|
+
const sub = depVal != null ? context?.dynamicSchemas?.[String(depVal)] : undefined;
|
|
674
|
+
const props = (sub?.properties ?? {});
|
|
675
|
+
const required = new Set(sub?.required ?? []);
|
|
676
|
+
const cfg = (value && typeof value === 'object' ? value : {});
|
|
677
|
+
const setKey = (k, v) => {
|
|
678
|
+
const next = { ...cfg };
|
|
679
|
+
if (v === undefined || v === '')
|
|
680
|
+
delete next[k];
|
|
681
|
+
else
|
|
682
|
+
next[k] = v;
|
|
683
|
+
onChange(next);
|
|
684
|
+
};
|
|
685
|
+
if (!depVal) {
|
|
686
|
+
return _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Select ", dep ?? 'an option', " to configure."] });
|
|
687
|
+
}
|
|
688
|
+
if (!sub || Object.keys(props).length === 0) {
|
|
689
|
+
return _jsxs("p", { className: "text-xs text-muted-foreground", children: ["No configuration needed for \"", String(depVal), "\"."] });
|
|
690
|
+
}
|
|
691
|
+
return (_jsx("div", { className: "space-y-2 rounded-md border border-border/60 p-3", children: Object.entries(props).map(([key, p]) => {
|
|
692
|
+
const label = p?.title || key;
|
|
693
|
+
const cur = cfg[key];
|
|
694
|
+
const isReq = required.has(key);
|
|
695
|
+
if (p?.type === 'boolean') {
|
|
696
|
+
return (_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Label, { className: "text-xs", children: label }), _jsx(Switch, { checked: Boolean(cur), disabled: readOnly, onCheckedChange: (c) => setKey(key, c) })] }, key));
|
|
697
|
+
}
|
|
698
|
+
if (Array.isArray(p?.enum)) {
|
|
699
|
+
return (_jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { className: "text-xs", children: [label, isReq ? ' *' : ''] }), _jsxs(Select, { value: cur != null ? String(cur) : '', onValueChange: (v) => setKey(key, v), disabled: readOnly, children: [_jsx(SelectTrigger, { className: "h-8", "aria-label": label, children: _jsx(SelectValue, { placeholder: "Select\u2026" }) }), _jsx(SelectContent, { children: p.enum.map((o) => _jsx(SelectItem, { value: String(o), children: String(o) }, String(o))) })] })] }, key));
|
|
700
|
+
}
|
|
701
|
+
const isSecret = p?.format === 'password' || p?.writeOnly === true;
|
|
702
|
+
const isNum = p?.type === 'number' || p?.type === 'integer';
|
|
703
|
+
return (_jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { className: "text-xs", children: [label, isReq ? ' *' : ''] }), isSecret ? (_jsx(SecretWidget, { value: cur, onChange: (v) => setKey(key, v), readOnly: readOnly, schema: p })) : (_jsx(Input, { type: isNum ? 'number' : 'text', value: cur != null ? String(cur) : '', disabled: readOnly, "aria-label": label, onChange: (e) => setKey(key, isNum ? (e.target.value === '' ? undefined : Number(e.target.value)) : e.target.value), className: "h-8 text-sm" })), p?.description && _jsx("p", { className: "text-[11px] text-muted-foreground", children: p.description })] }, key));
|
|
704
|
+
}) }));
|
|
705
|
+
}
|
|
242
706
|
export const WIDGETS = {
|
|
243
707
|
'ref:object': RefObjectWidget,
|
|
708
|
+
'filter-mode': FilterModeWidget,
|
|
244
709
|
'object-selector': ObjectSelectorWidget,
|
|
245
710
|
'field-selector': FieldSelectorWidget,
|
|
711
|
+
'field-ref': FieldRefWidget,
|
|
712
|
+
'field-multi': FieldRefMultiWidget,
|
|
713
|
+
'action-multi': ActionMultiWidget,
|
|
714
|
+
'filter-builder': FilterBuilderWidget,
|
|
715
|
+
'view-ref': ViewRefWidget,
|
|
716
|
+
'icon': IconPickerWidget,
|
|
717
|
+
'color-picker': ColorPickerWidget,
|
|
718
|
+
'condition': ConditionWidget,
|
|
246
719
|
'master-detail': MasterDetailWidget,
|
|
247
720
|
'string-tags': StringTagsWidget,
|
|
721
|
+
'multiselect': MultiSelectWidget,
|
|
248
722
|
'code': CodeWidget,
|
|
249
|
-
|
|
250
|
-
'
|
|
723
|
+
'secret': SecretWidget,
|
|
724
|
+
'dynamic-config': DynamicConfigWidget,
|
|
251
725
|
};
|
|
252
726
|
/* -------------------------------------------------------------------------- */
|
|
253
727
|
/* CodeWidget — Monaco editor for `type: 'code'` fields */
|