@object-ui/app-shell 6.2.3 → 7.0.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 +948 -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 +170 -37
- 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 +743 -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 +22 -0
- package/dist/console/ai/LiveCanvas.js +78 -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 +107 -4
- package/dist/hooks/useChatConversation.js +253 -25
- package/dist/hooks/useConsoleActionRuntime.d.ts +70 -0
- package/dist/hooks/useConsoleActionRuntime.js +560 -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 +16 -7
- package/dist/index.js +12 -4
- 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 +32 -2
- package/dist/layout/ConsoleFloatingChatbot.js +374 -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 +218 -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/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/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 +79 -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 +183 -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 +59 -0
- package/dist/views/FlowRunner.js +153 -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/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 +113 -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 +37 -0
- package/dist/views/metadata-admin/JsonSourceEditor.js +178 -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 +395 -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 +1223 -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 +266 -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 +213 -0
- package/dist/views/metadata-admin/anchors.js +237 -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 +1166 -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 +107 -0
- package/dist/views/metadata-admin/inspectors/FlowEdgeInspector.d.ts +16 -0
- package/dist/views/metadata-admin/inspectors/FlowEdgeInspector.js +45 -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 +140 -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 +54 -0
- package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.d.ts +23 -0
- package/dist/views/metadata-admin/inspectors/ObjectFieldInspector.js +330 -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 +160 -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 +124 -0
- package/dist/views/metadata-admin/inspectors/_shared.js +113 -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 +461 -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/mergeServerFields.d.ts +65 -0
- package/dist/views/metadata-admin/mergeServerFields.js +56 -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 +89 -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 +43 -0
- package/dist/views/metadata-admin/previews/FlowCanvas.js +328 -0
- package/dist/views/metadata-admin/previews/FlowPreview.d.ts +20 -0
- package/dist/views/metadata-admin/previews/FlowPreview.js +92 -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 +170 -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 +90 -4
- 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/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 +88 -0
- package/dist/views/metadata-admin/previews/flow-canvas-layout.js +190 -0
- package/dist/views/metadata-admin/previews/flow-canvas-parts.d.ts +88 -0
- package/dist/views/metadata-admin/previews/flow-canvas-parts.js +358 -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 +109 -0
- package/dist/views/metadata-admin/previews/object-fields-io.js +208 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-types.d.ts +91 -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 +8 -0
- package/dist/views/metadata-admin/previews/simulator/flow-sim-validate.js +113 -0
- package/dist/views/metadata-admin/previews/simulator/flow-simulator.d.ts +44 -0
- package/dist/views/metadata-admin/previews/simulator/flow-simulator.js +316 -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
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
3
|
+
/**
|
|
4
|
+
* ConditionBuilder — a no-code editor for a CEL predicate (ServiceNow-style),
|
|
5
|
+
* compiling rows of [subject][operator][value] joined by AND/OR into a CEL
|
|
6
|
+
* string emitted via onCommit (empty ⇒ '' ⇒ caller should unset).
|
|
7
|
+
*
|
|
8
|
+
* Stateful by design: rows live in local state so an in-progress row (no
|
|
9
|
+
* subject yet) stays on screen instead of vanishing the moment it compiles to
|
|
10
|
+
* an empty string. The emitted CEL is recomputed from rows on every edit.
|
|
11
|
+
*
|
|
12
|
+
* Safety: on (re)load the builder only adopts an existing expression when it
|
|
13
|
+
* parses AND round-trips byte-for-byte (whitespace-normalised). Anything it
|
|
14
|
+
* can't round-trip cleanly opens in a raw expression textarea, so hand-authored
|
|
15
|
+
* complex CEL is never silently rewritten.
|
|
16
|
+
*/
|
|
17
|
+
import * as React from 'react';
|
|
18
|
+
import { Button, Input, Label, Select, SelectTrigger, SelectContent, SelectItem, SelectValue, } from '@object-ui/components';
|
|
19
|
+
import { Plus, X, Code2, ListFilter } from 'lucide-react';
|
|
20
|
+
import { useObjectFields } from '../previews/useObjectFields';
|
|
21
|
+
const COMPARE_OPS = [
|
|
22
|
+
{ value: '==', label: 'equals' },
|
|
23
|
+
{ value: '!=', label: 'not equals' },
|
|
24
|
+
{ value: '>', label: 'greater than' },
|
|
25
|
+
{ value: '<', label: 'less than' },
|
|
26
|
+
{ value: '>=', label: '≥' },
|
|
27
|
+
{ value: '<=', label: '≤' },
|
|
28
|
+
{ value: 'truthy', label: 'is set / true' },
|
|
29
|
+
{ value: 'falsy', label: 'is empty / false' },
|
|
30
|
+
];
|
|
31
|
+
const CONTEXT_SUBJECTS = [
|
|
32
|
+
{ value: 'record.id', label: 'record.id' },
|
|
33
|
+
{ value: 'user.id', label: 'user.id' },
|
|
34
|
+
{ value: 'user.email', label: 'user.email' },
|
|
35
|
+
{ value: 'user.role', label: 'user.role' },
|
|
36
|
+
{ value: 'user.isAdmin', label: 'user.isAdmin' },
|
|
37
|
+
{ value: 'org.id', label: 'org.id' },
|
|
38
|
+
];
|
|
39
|
+
const norm = (s) => s.replace(/\s+/g, ' ').trim();
|
|
40
|
+
/** Quote a raw value for CEL unless it is a number / boolean / null. */
|
|
41
|
+
function fmtValue(v) {
|
|
42
|
+
const t = v.trim();
|
|
43
|
+
if (t === 'true' || t === 'false' || t === 'null')
|
|
44
|
+
return t;
|
|
45
|
+
if (t !== '' && !Number.isNaN(Number(t)))
|
|
46
|
+
return t;
|
|
47
|
+
return `'${t.replace(/'/g, "\\'")}'`;
|
|
48
|
+
}
|
|
49
|
+
/** Inverse of fmtValue for display in the value input. */
|
|
50
|
+
function unfmtValue(raw) {
|
|
51
|
+
const t = raw.trim();
|
|
52
|
+
const m = /^'(.*)'$/.exec(t);
|
|
53
|
+
if (m)
|
|
54
|
+
return m[1].replace(/\\'/g, "'");
|
|
55
|
+
return t;
|
|
56
|
+
}
|
|
57
|
+
/** Compile rows → CEL. Rows without a subject are skipped (in-progress). */
|
|
58
|
+
function compile(rows, join) {
|
|
59
|
+
return rows
|
|
60
|
+
.filter((r) => r.subject)
|
|
61
|
+
.map((r) => {
|
|
62
|
+
if (r.op === 'truthy')
|
|
63
|
+
return r.subject;
|
|
64
|
+
if (r.op === 'falsy')
|
|
65
|
+
return `!${r.subject}`;
|
|
66
|
+
return `${r.subject} ${r.op} ${fmtValue(r.value)}`;
|
|
67
|
+
})
|
|
68
|
+
.join(` ${join} `);
|
|
69
|
+
}
|
|
70
|
+
/** Parse a simple AND/OR predicate. Returns null if it isn't the simple shape. */
|
|
71
|
+
function parse(expr) {
|
|
72
|
+
const s = norm(expr);
|
|
73
|
+
if (!s)
|
|
74
|
+
return { rows: [], join: '&&' };
|
|
75
|
+
const hasAnd = s.includes('&&');
|
|
76
|
+
const hasOr = s.includes('||');
|
|
77
|
+
if (hasAnd && hasOr)
|
|
78
|
+
return null; // mixed joins → too complex
|
|
79
|
+
const join = hasOr ? '||' : '&&';
|
|
80
|
+
const parts = s.split(hasOr ? '||' : '&&').map((p) => p.trim());
|
|
81
|
+
const rows = [];
|
|
82
|
+
for (const p of parts) {
|
|
83
|
+
const cmp = /^([a-zA-Z_][\w.]*)\s*(==|!=|>=|<=|>|<)\s*(.+)$/.exec(p);
|
|
84
|
+
if (cmp) {
|
|
85
|
+
rows.push({ subject: cmp[1], op: cmp[2], value: unfmtValue(cmp[3]) });
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const neg = /^!\s*([a-zA-Z_][\w.]*)$/.exec(p);
|
|
89
|
+
if (neg) {
|
|
90
|
+
rows.push({ subject: neg[1], op: 'falsy', value: '' });
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const truthy = /^([a-zA-Z_][\w.]*)$/.exec(p);
|
|
94
|
+
if (truthy) {
|
|
95
|
+
rows.push({ subject: truthy[1], op: 'truthy', value: '' });
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
return null; // unrecognised term
|
|
99
|
+
}
|
|
100
|
+
return { rows, join };
|
|
101
|
+
}
|
|
102
|
+
function initFrom(value) {
|
|
103
|
+
const p = parse(value || '');
|
|
104
|
+
if (p && norm(compile(p.rows, p.join)) === norm(value || '')) {
|
|
105
|
+
return { rows: p.rows, join: p.join, raw: false };
|
|
106
|
+
}
|
|
107
|
+
return { rows: [], join: '&&', raw: !!value };
|
|
108
|
+
}
|
|
109
|
+
export function ConditionBuilder({ label, value, onCommit, objectName, fields: fieldsProp, disabled }) {
|
|
110
|
+
const { fields: hookFields } = useObjectFields(objectName);
|
|
111
|
+
const fields = fieldsProp ?? hookFields;
|
|
112
|
+
const subjectOptions = React.useMemo(() => {
|
|
113
|
+
const fieldOpts = fields
|
|
114
|
+
.filter((f) => !f.hidden)
|
|
115
|
+
.map((f) => ({ value: `record.${f.name}`, label: `record.${f.name}` }));
|
|
116
|
+
return [...fieldOpts, ...CONTEXT_SUBJECTS];
|
|
117
|
+
}, [fields]);
|
|
118
|
+
const init = React.useMemo(() => initFrom(value), []); // first mount only
|
|
119
|
+
const [rows, setRowsState] = React.useState(init.rows);
|
|
120
|
+
const [join, setJoin] = React.useState(init.join);
|
|
121
|
+
const [raw, setRaw] = React.useState(init.raw);
|
|
122
|
+
// Adopt an externally-changed value (e.g. switching records, or a raw edit
|
|
123
|
+
// from elsewhere) when it isn't the CEL we just emitted.
|
|
124
|
+
const lastEmitted = React.useRef(value || '');
|
|
125
|
+
React.useEffect(() => {
|
|
126
|
+
const v = value || '';
|
|
127
|
+
if (v === lastEmitted.current)
|
|
128
|
+
return;
|
|
129
|
+
lastEmitted.current = v;
|
|
130
|
+
const next = initFrom(v);
|
|
131
|
+
setRowsState(next.rows);
|
|
132
|
+
setJoin(next.join);
|
|
133
|
+
setRaw(next.raw);
|
|
134
|
+
}, [value]);
|
|
135
|
+
const emit = (nextRows, nextJoin) => {
|
|
136
|
+
const cel = compile(nextRows, nextJoin);
|
|
137
|
+
lastEmitted.current = cel;
|
|
138
|
+
onCommit(cel);
|
|
139
|
+
};
|
|
140
|
+
const update = (nextRows, nextJoin = join) => {
|
|
141
|
+
setRowsState(nextRows);
|
|
142
|
+
setJoin(nextJoin);
|
|
143
|
+
emit(nextRows, nextJoin);
|
|
144
|
+
};
|
|
145
|
+
const compiled = compile(rows, join);
|
|
146
|
+
if (raw) {
|
|
147
|
+
return (_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center justify-between", children: [label ? _jsx(Label, { className: "text-xs text-muted-foreground", children: label }) : _jsx("span", {}), _jsxs("button", { type: "button", disabled: disabled, onClick: () => { const n = initFrom(value); if (!value || !n.raw) {
|
|
148
|
+
setRowsState(n.rows);
|
|
149
|
+
setJoin(n.join);
|
|
150
|
+
setRaw(false);
|
|
151
|
+
} }, className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-50", children: [_jsx(ListFilter, { className: "h-3 w-3" }), " Builder"] })] }), _jsx("textarea", { value: value, onChange: (e) => { lastEmitted.current = e.target.value; onCommit(e.target.value); }, disabled: disabled, spellCheck: false, rows: 2, placeholder: "CEL expression, e.g. record.status != 'done' && user.isAdmin", className: "w-full rounded border border-input bg-background px-2 py-1.5 text-xs font-mono outline-none focus:ring-1 focus:ring-primary resize-y disabled:opacity-60" }), value && !parse(value) && (_jsx("div", { className: "text-[10px] text-muted-foreground/70", children: "Advanced expression \u2014 Builder only supports simple AND/OR conditions." }))] }));
|
|
152
|
+
}
|
|
153
|
+
return (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [label ? _jsx(Label, { className: "text-xs text-muted-foreground", children: label }) : _jsx("span", {}), _jsxs("button", { type: "button", disabled: disabled, onClick: () => setRaw(true), className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground disabled:opacity-50", children: [_jsx(Code2, { className: "h-3 w-3" }), " Expression"] })] }), rows.length === 0 ? (_jsx("p", { className: "rounded-md border border-dashed bg-muted/30 px-3 py-2 text-center text-[11px] text-muted-foreground", children: "Always \u2014 no condition." })) : (_jsx("div", { className: "space-y-1.5", children: rows.map((r, i) => (_jsxs("div", { className: "space-y-1 rounded-md border border-border p-1.5", children: [i > 0 && (_jsx("div", { className: "flex justify-center", children: _jsxs(Select, { value: join, onValueChange: (v) => update(rows, v), disabled: disabled, children: [_jsx(SelectTrigger, { className: "h-6 w-16 text-[10px]", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "&&", children: "AND" }), _jsx(SelectItem, { value: "||", children: "OR" })] })] }) })), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "min-w-0 flex-1", children: _jsxs(Select, { value: r.subject, onValueChange: (v) => update(rows.map((x, j) => j === i ? { ...x, subject: v } : x)), disabled: disabled, children: [_jsx(SelectTrigger, { className: "h-7 text-xs", children: _jsx(SelectValue, { placeholder: "field / context" }) }), _jsxs(SelectContent, { children: [subjectOptions.map((o) => _jsx(SelectItem, { value: o.value, children: o.label }, o.value)), r.subject && !subjectOptions.some((o) => o.value === r.subject) && (_jsx(SelectItem, { value: r.subject, children: r.subject }))] })] }) }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-7 w-7 shrink-0", disabled: disabled, "aria-label": "Remove condition", onClick: () => update(rows.filter((_, j) => j !== i)), children: _jsx(X, { className: "h-3.5 w-3.5" }) })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("div", { className: "w-28 shrink-0", children: _jsxs(Select, { value: r.op, onValueChange: (v) => update(rows.map((x, j) => j === i ? { ...x, op: v } : x)), disabled: disabled, children: [_jsx(SelectTrigger, { className: "h-7 text-xs", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: COMPARE_OPS.map((o) => _jsx(SelectItem, { value: o.value, children: o.label }, o.value)) })] }) }), r.op !== 'truthy' && r.op !== 'falsy' && (_jsx(Input, { className: "h-7 flex-1 text-xs", value: r.value, placeholder: "value", disabled: disabled, onChange: (e) => update(rows.map((x, j) => j === i ? { ...x, value: e.target.value } : x)) }))] })] }, i))) })), !disabled && (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => update([...rows, { subject: '', op: 'truthy', value: '' }]), children: [_jsx(Plus, { className: "mr-1 h-3.5 w-3.5" }), " Add condition"] })), compiled && (_jsx("div", { className: "rounded bg-muted/40 px-2 py-1 text-[10px] font-mono text-muted-foreground break-all", children: compiled }))] }));
|
|
154
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DashboardDefaultInspector — the curated "home" panel for a Dashboard.
|
|
3
|
+
*
|
|
4
|
+
* Shown as the DEFAULT right panel (no selection) for a dashboard. Mirrors
|
|
5
|
+
* {@link ReportDefaultInspector} but for the flat Dashboard document.
|
|
6
|
+
*
|
|
7
|
+
* SPEC-DRIVEN: the layout / filter / performance config fields are NOT
|
|
8
|
+
* hardcoded. They are rendered by feeding the spec's canonical authoring
|
|
9
|
+
* form (`dashboardForm`) and the spec-derived Dashboard JSONSchema into the
|
|
10
|
+
* generic {@link SchemaForm}. Adding a new dashboard prop to
|
|
11
|
+
* `@objectstack/spec` flows through with zero code changes here.
|
|
12
|
+
*
|
|
13
|
+
* The inspector keeps a thin curated layer for the cross-cutting concerns the
|
|
14
|
+
* spec form can't express well on its own:
|
|
15
|
+
* 1. the LABEL / DESCRIPTION basics, and
|
|
16
|
+
* 2. the WIDGETS list — add / remove / reorder / select drills into the
|
|
17
|
+
* scoped {@link DashboardWidgetInspector}. Those fields are therefore
|
|
18
|
+
* pruned from the spec form to avoid double-editing.
|
|
19
|
+
*
|
|
20
|
+
* Unlike a View (a nested document with a variant BODY), a Dashboard is FLAT:
|
|
21
|
+
* label / description / widgets / layout all live at the draft top level, so
|
|
22
|
+
* every write is a plain shallow `onPatch`. Widgets are addressed by their
|
|
23
|
+
* own `id` (matching {@link DashboardWidgetInspector} and {@link
|
|
24
|
+
* DashboardPreview}), not by an index path.
|
|
25
|
+
*/
|
|
26
|
+
import * as React from 'react';
|
|
27
|
+
import type { MetadataDefaultInspectorProps } from '../default-inspector-registry';
|
|
28
|
+
export declare function DashboardDefaultInspector({ draft, onPatch, readOnly, locale, onSelectionChange, serverSchema, }: MetadataDefaultInspectorProps): React.JSX.Element;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
3
|
+
/**
|
|
4
|
+
* DashboardDefaultInspector — the curated "home" panel for a Dashboard.
|
|
5
|
+
*
|
|
6
|
+
* Shown as the DEFAULT right panel (no selection) for a dashboard. Mirrors
|
|
7
|
+
* {@link ReportDefaultInspector} but for the flat Dashboard document.
|
|
8
|
+
*
|
|
9
|
+
* SPEC-DRIVEN: the layout / filter / performance config fields are NOT
|
|
10
|
+
* hardcoded. They are rendered by feeding the spec's canonical authoring
|
|
11
|
+
* form (`dashboardForm`) and the spec-derived Dashboard JSONSchema into the
|
|
12
|
+
* generic {@link SchemaForm}. Adding a new dashboard prop to
|
|
13
|
+
* `@objectstack/spec` flows through with zero code changes here.
|
|
14
|
+
*
|
|
15
|
+
* The inspector keeps a thin curated layer for the cross-cutting concerns the
|
|
16
|
+
* spec form can't express well on its own:
|
|
17
|
+
* 1. the LABEL / DESCRIPTION basics, and
|
|
18
|
+
* 2. the WIDGETS list — add / remove / reorder / select drills into the
|
|
19
|
+
* scoped {@link DashboardWidgetInspector}. Those fields are therefore
|
|
20
|
+
* pruned from the spec form to avoid double-editing.
|
|
21
|
+
*
|
|
22
|
+
* Unlike a View (a nested document with a variant BODY), a Dashboard is FLAT:
|
|
23
|
+
* label / description / widgets / layout all live at the draft top level, so
|
|
24
|
+
* every write is a plain shallow `onPatch`. Widgets are addressed by their
|
|
25
|
+
* own `id` (matching {@link DashboardWidgetInspector} and {@link
|
|
26
|
+
* DashboardPreview}), not by an index path.
|
|
27
|
+
*/
|
|
28
|
+
import * as React from 'react';
|
|
29
|
+
import { GripVertical, X } from 'lucide-react';
|
|
30
|
+
import { Badge, Button, Label } from '@object-ui/components';
|
|
31
|
+
import { InspectorShell, InspectorTextField, appendArray, moveArray, spliceArray, uniqueId, } from './_shared';
|
|
32
|
+
import { AddWidgetPicker } from '../previews/AddWidgetPicker';
|
|
33
|
+
import { WIDGET_TYPE_META, UnknownWidgetIcon } from '../previews/widget-types';
|
|
34
|
+
import { SchemaForm } from '../SchemaForm';
|
|
35
|
+
import { getDashboardForm, getDashboardSchema } from '../dashboard-schema';
|
|
36
|
+
import { mergeServerFields } from '../mergeServerFields';
|
|
37
|
+
import { t } from '../i18n';
|
|
38
|
+
/**
|
|
39
|
+
* Top-level dashboard fields rendered by this inspector's own controls (or by
|
|
40
|
+
* the dedicated widgets list), pruned from the spec-form graft so they are not
|
|
41
|
+
* double-rendered. Mirrors the `hiddenFields` passed to SchemaForm.
|
|
42
|
+
*/
|
|
43
|
+
const DASHBOARD_CURATED_FIELDS = new Set([
|
|
44
|
+
'name',
|
|
45
|
+
'label',
|
|
46
|
+
'description',
|
|
47
|
+
'widgets',
|
|
48
|
+
]);
|
|
49
|
+
export function DashboardDefaultInspector({ draft, onPatch, readOnly, locale, onSelectionChange, serverSchema, }) {
|
|
50
|
+
const tr = React.useCallback((key) => t(key, locale), [locale]);
|
|
51
|
+
const labelValue = typeof draft.label === 'string' ? draft.label : '';
|
|
52
|
+
const descriptionValue = typeof draft.description === 'string' ? draft.description : '';
|
|
53
|
+
const widgets = React.useMemo(() => Array.isArray(draft.widgets) ? draft.widgets : [], [draft.widgets]);
|
|
54
|
+
/* ─────────────── Widget read / write / reorder / select ─────────────── */
|
|
55
|
+
const addWidget = (type) => {
|
|
56
|
+
const existingIds = widgets.map((w) => w?.id).filter(Boolean);
|
|
57
|
+
const id = uniqueId('widget', existingIds);
|
|
58
|
+
const meta = WIDGET_TYPE_META[type];
|
|
59
|
+
const title = meta ? `New ${meta.label.toLowerCase()}` : 'New widget';
|
|
60
|
+
const widget = { id, type, title, ...(meta?.defaults ?? {}) };
|
|
61
|
+
onPatch({ widgets: appendArray(widgets, widget) });
|
|
62
|
+
onSelectionChange?.({ kind: 'widget', id, label: title });
|
|
63
|
+
};
|
|
64
|
+
const removeWidget = (index) => {
|
|
65
|
+
onPatch({ widgets: spliceArray(widgets, index, null) });
|
|
66
|
+
};
|
|
67
|
+
const moveWidget = (from, to) => {
|
|
68
|
+
if (from === to)
|
|
69
|
+
return;
|
|
70
|
+
onPatch({ widgets: moveArray(widgets, from, to) });
|
|
71
|
+
};
|
|
72
|
+
const selectWidget = (widget, index) => {
|
|
73
|
+
if (!widget?.id)
|
|
74
|
+
return;
|
|
75
|
+
onSelectionChange?.({
|
|
76
|
+
kind: 'widget',
|
|
77
|
+
id: widget.id,
|
|
78
|
+
label: widget.title || widget.id || `Widget ${index + 1}`,
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
const [dragIndex, setDragIndex] = React.useState(null);
|
|
82
|
+
const [overIndex, setOverIndex] = React.useState(null);
|
|
83
|
+
// Graft any server-only top-level dashboard fields onto the bundled-spec
|
|
84
|
+
// form so they are editable even when the bundled spec lags the server.
|
|
85
|
+
const { schema, form } = React.useMemo(() => mergeServerFields({
|
|
86
|
+
bundledSchema: getDashboardSchema(),
|
|
87
|
+
bundledForm: getDashboardForm(),
|
|
88
|
+
serverSchema,
|
|
89
|
+
excludeFields: DASHBOARD_CURATED_FIELDS,
|
|
90
|
+
sectionTitle: t('engine.inspector.moreFields', locale),
|
|
91
|
+
}), [serverSchema, locale]);
|
|
92
|
+
return (_jsxs(InspectorShell, { kindLabel: tr('engine.inspector.dashboard.kind'), title: String(labelValue || draft.name || tr('engine.inspector.dashboard.kind')), onClose: () => { }, closeLabel: tr('engine.inspector.dashboard.close'), hideClose: true, children: [_jsx(InspectorTextField, { label: tr('engine.inspector.dashboard.label'), value: labelValue, onCommit: (v) => onPatch({ label: v }), placeholder: tr('engine.inspector.dashboard.labelPlaceholder'), disabled: readOnly }), _jsx(InspectorTextField, { label: tr('engine.inspector.dashboard.description'), value: descriptionValue, onCommit: (v) => onPatch({ description: v }), placeholder: tr('engine.inspector.dashboard.descriptionPlaceholder'), disabled: readOnly }), _jsxs("div", { className: "border-t pt-3 space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { className: "text-xs text-muted-foreground", children: tr('engine.inspector.dashboard.widgets') }), _jsx(Badge, { variant: "outline", className: "text-[10px]", children: widgets.length })] }), widgets.length === 0 ? (_jsx("p", { className: "rounded-md border border-dashed bg-muted/30 px-3 py-3 text-center text-[11px] text-muted-foreground", children: tr('engine.inspector.dashboard.widgetsEmpty') })) : (_jsx("div", { className: "space-y-1", children: widgets.map((w, i) => {
|
|
93
|
+
const meta = WIDGET_TYPE_META[w?.type];
|
|
94
|
+
const Icon = meta?.icon ?? UnknownWidgetIcon;
|
|
95
|
+
const showDropLine = overIndex === i && dragIndex !== null && dragIndex !== i;
|
|
96
|
+
return (_jsxs("div", { draggable: !readOnly, onDragStart: () => setDragIndex(i), onDragEnd: () => {
|
|
97
|
+
setDragIndex(null);
|
|
98
|
+
setOverIndex(null);
|
|
99
|
+
}, onDragOver: (e) => {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
setOverIndex(i);
|
|
102
|
+
}, onDrop: () => {
|
|
103
|
+
if (dragIndex != null && dragIndex !== i)
|
|
104
|
+
moveWidget(dragIndex, i);
|
|
105
|
+
setDragIndex(null);
|
|
106
|
+
setOverIndex(null);
|
|
107
|
+
}, className: 'group flex items-center gap-2 rounded-md border px-2 py-1.5 text-xs hover:bg-accent' +
|
|
108
|
+
(showDropLine ? ' border-primary' : ''), children: [!readOnly && (_jsx(GripVertical, { className: "h-3.5 w-3.5 shrink-0 cursor-grab text-muted-foreground" })), _jsx(Icon, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("button", { type: "button", className: "min-w-0 flex-1 truncate text-left font-medium", onClick: () => selectWidget(w, i), title: w?.title || w?.id, children: w?.title || w?.id || `Widget ${i + 1}` }), _jsx("code", { className: "text-[10px] text-muted-foreground", children: w?.type }), !readOnly && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-6 w-6 shrink-0 p-0 opacity-0 group-hover:opacity-100", onClick: () => removeWidget(i), title: tr('engine.inspector.dashboard.removeWidget'), "aria-label": tr('engine.inspector.dashboard.removeWidget'), children: _jsx(X, { className: "h-3.5 w-3.5" }) }))] }, w?.id ?? i));
|
|
109
|
+
}) })), !readOnly && (_jsx(AddWidgetPicker, { onAdd: addWidget, label: tr('engine.inspector.add.widget') }))] }), _jsx("div", { className: "border-t pt-3", children: schema ? (_jsx(SchemaForm, { schema: schema, form: form, value: draft, hiddenFields: ['name', 'label', 'description', 'widgets'], readOnly: readOnly, onChange: (next) => onPatch(next) })) : (_jsx("p", { className: "text-[11px] text-muted-foreground", children: tr('engine.inspector.dashboard.noSchema') })) })] }));
|
|
110
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DashboardWidgetInspector — scoped property panel for the widget
|
|
3
|
+
* selected inside <DashboardPreview>.
|
|
4
|
+
*
|
|
5
|
+
* Renders the per-widget form (title / type / data source / KPI value
|
|
6
|
+
* / aggregate / color / layout size) for the widget identified by
|
|
7
|
+
* `selection.id`. Patches are written back into `draft.widgets[i]`
|
|
8
|
+
* (immutably) and emitted via `onPatch`, so live preview updates
|
|
9
|
+
* instantly on the left side.
|
|
10
|
+
*
|
|
11
|
+
* The shape mirrors the WidgetPropertyPanel that ships in
|
|
12
|
+
* @object-ui/plugin-designer's DashboardEditor — same fields, same
|
|
13
|
+
* defaults, same enums — so users familiar with the standalone
|
|
14
|
+
* designer feel at home here.
|
|
15
|
+
*/
|
|
16
|
+
import * as React from 'react';
|
|
17
|
+
import type { MetadataInspectorProps } from '../inspector-registry';
|
|
18
|
+
export declare function DashboardWidgetInspector({ draft, selection, onPatch, onClearSelection, onSelectionChange, readOnly, locale, }: MetadataInspectorProps): React.JSX.Element;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
3
|
+
/**
|
|
4
|
+
* DashboardWidgetInspector — scoped property panel for the widget
|
|
5
|
+
* selected inside <DashboardPreview>.
|
|
6
|
+
*
|
|
7
|
+
* Renders the per-widget form (title / type / data source / KPI value
|
|
8
|
+
* / aggregate / color / layout size) for the widget identified by
|
|
9
|
+
* `selection.id`. Patches are written back into `draft.widgets[i]`
|
|
10
|
+
* (immutably) and emitted via `onPatch`, so live preview updates
|
|
11
|
+
* instantly on the left side.
|
|
12
|
+
*
|
|
13
|
+
* The shape mirrors the WidgetPropertyPanel that ships in
|
|
14
|
+
* @object-ui/plugin-designer's DashboardEditor — same fields, same
|
|
15
|
+
* defaults, same enums — so users familiar with the standalone
|
|
16
|
+
* designer feel at home here.
|
|
17
|
+
*/
|
|
18
|
+
import * as React from 'react';
|
|
19
|
+
import { ColorVariantPicker } from '../color-variant-field';
|
|
20
|
+
import { X } from 'lucide-react';
|
|
21
|
+
import { Button, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@object-ui/components';
|
|
22
|
+
import { t } from '../i18n';
|
|
23
|
+
import { InspectorReorderButtons, moveArray } from './_shared';
|
|
24
|
+
import { InspectorComboField } from './InspectorComboField';
|
|
25
|
+
import { DatasetNamesEditor } from './ReportDefaultInspector';
|
|
26
|
+
import { useDatasetCatalog, useDatasetSemantics } from '../previews/useDatasetCatalog';
|
|
27
|
+
import { useObjectFields } from '../previews/useObjectFields';
|
|
28
|
+
import { useObjectOptions } from './useDatasetFields';
|
|
29
|
+
const WIDGET_TYPES = [
|
|
30
|
+
{ value: 'metric', label: 'KPI Metric' },
|
|
31
|
+
{ value: 'bar', label: 'Bar Chart' },
|
|
32
|
+
{ value: 'line', label: 'Line Chart' },
|
|
33
|
+
{ value: 'pie', label: 'Pie Chart' },
|
|
34
|
+
{ value: 'table', label: 'Table' },
|
|
35
|
+
{ value: 'grid', label: 'Grid' },
|
|
36
|
+
];
|
|
37
|
+
const AGGREGATES = ['count', 'sum', 'avg', 'min', 'max'];
|
|
38
|
+
const COLORS = [
|
|
39
|
+
'default',
|
|
40
|
+
'blue',
|
|
41
|
+
'teal',
|
|
42
|
+
'orange',
|
|
43
|
+
'purple',
|
|
44
|
+
'success',
|
|
45
|
+
'warning',
|
|
46
|
+
'danger',
|
|
47
|
+
];
|
|
48
|
+
function findWidget(draft, id) {
|
|
49
|
+
const widgets = Array.isArray(draft.widgets)
|
|
50
|
+
? draft.widgets
|
|
51
|
+
: [];
|
|
52
|
+
const index = widgets.findIndex((w) => w?.id === id);
|
|
53
|
+
if (index < 0)
|
|
54
|
+
return null;
|
|
55
|
+
return { widget: widgets[index], index };
|
|
56
|
+
}
|
|
57
|
+
export function DashboardWidgetInspector({ draft, selection, onPatch, onClearSelection, onSelectionChange, readOnly, locale, }) {
|
|
58
|
+
const widgetsAll = Array.isArray(draft.widgets)
|
|
59
|
+
? draft.widgets
|
|
60
|
+
: [];
|
|
61
|
+
const selId = selection.kind === 'widget' ? selection.id : undefined;
|
|
62
|
+
const hit = selId ? findWidget(draft, selId) : null;
|
|
63
|
+
// ── Dataset binding (ADR-0021) ──────────────────────────────────────────
|
|
64
|
+
// Field access goes through `as any`: the bundled `@object-ui/types`
|
|
65
|
+
// `DashboardWidgetSchema` only gains `dataset`/`dimensions`/`values` once
|
|
66
|
+
// objectui bumps `@objectstack/spec`. Same accessor pattern as DatasetWidget.
|
|
67
|
+
const w = (hit?.widget ?? {});
|
|
68
|
+
const datasetName = typeof w.dataset === 'string' ? w.dataset : '';
|
|
69
|
+
const objectName = typeof w.object === 'string' ? w.object : '';
|
|
70
|
+
const dimensions = Array.isArray(w.dimensions)
|
|
71
|
+
? w.dimensions.filter((x) => typeof x === 'string')
|
|
72
|
+
: [];
|
|
73
|
+
const values = Array.isArray(w.values)
|
|
74
|
+
? w.values.filter((x) => typeof x === 'string')
|
|
75
|
+
: [];
|
|
76
|
+
// Catalogs — called unconditionally (stable hook order) BEFORE any early
|
|
77
|
+
// return, so the dataset/object/field pickers below bind to the live schema
|
|
78
|
+
// instead of free-text the author has to recall.
|
|
79
|
+
const catalog = useDatasetCatalog();
|
|
80
|
+
const semantics = useDatasetSemantics(datasetName || undefined, catalog);
|
|
81
|
+
const { options: objectOptions, loading: objectsLoading } = useObjectOptions();
|
|
82
|
+
const { fields: objectFields } = useObjectFields(objectName || undefined);
|
|
83
|
+
const datasetComboOptions = React.useMemo(() => {
|
|
84
|
+
const opts = catalog.datasets.map((d) => ({
|
|
85
|
+
value: d.name,
|
|
86
|
+
label: d.label && d.label !== d.name ? `${d.label} (${d.name})` : d.name,
|
|
87
|
+
}));
|
|
88
|
+
if (datasetName && !opts.some((o) => o.value === datasetName)) {
|
|
89
|
+
opts.push({ value: datasetName, label: datasetName });
|
|
90
|
+
}
|
|
91
|
+
return opts;
|
|
92
|
+
}, [catalog.datasets, datasetName]);
|
|
93
|
+
const objectComboOptions = React.useMemo(() => objectOptions.map((o) => ({ value: o.name, label: o.label })), [objectOptions]);
|
|
94
|
+
const fieldComboOptions = React.useMemo(() => objectFields.map((f) => ({ value: f.name, label: f.label, hint: f.type })), [objectFields]);
|
|
95
|
+
const measureOptions = React.useMemo(() => semantics.measures.map((m) => ({ name: m.name, label: m.aggregate ? `${m.name} · ${m.aggregate}` : m.name, type: 'number', hidden: false })), [semantics.measures]);
|
|
96
|
+
const dimensionOptions = React.useMemo(() => semantics.dimensions.map((d) => ({ name: d.name, label: d.name, type: d.type ?? 'text', hidden: false })), [semantics.dimensions]);
|
|
97
|
+
if (selection.kind !== 'widget') {
|
|
98
|
+
return (_jsx(InspectorEmpty, { message: `Unsupported selection kind: ${selection.kind}`, onClose: onClearSelection, locale: locale }));
|
|
99
|
+
}
|
|
100
|
+
if (!hit) {
|
|
101
|
+
return (_jsx(InspectorEmpty, { message: "The selected widget was removed from the draft.", onClose: onClearSelection, locale: locale }));
|
|
102
|
+
}
|
|
103
|
+
const { widget, index } = hit;
|
|
104
|
+
function patchWidget(updates) {
|
|
105
|
+
const widgets = [...widgetsAll];
|
|
106
|
+
widgets[index] = { ...widgets[index], ...updates };
|
|
107
|
+
onPatch({ widgets });
|
|
108
|
+
}
|
|
109
|
+
function moveWidget(to) {
|
|
110
|
+
onPatch({ widgets: moveArray(widgetsAll, index, to) });
|
|
111
|
+
if (widget.id) {
|
|
112
|
+
onSelectionChange?.({ kind: 'widget', id: widget.id, label: widget.title ?? undefined });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "text-[10px] uppercase tracking-wider text-muted-foreground", children: t('engine.inspector.widget.kind', locale) }), _jsx("div", { className: "truncate text-sm font-semibold", children: widget.title || selection.label || `Widget ${index + 1}` })] }), _jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [_jsx(InspectorReorderButtons, { index: index, total: widgetsAll.length, onMove: moveWidget, upLabel: t('engine.inspector.reorder.up', locale), downLabel: t('engine.inspector.reorder.down', locale), disabled: readOnly }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: onClearSelection, title: t('engine.inspector.widget.close', locale), "aria-label": t('engine.inspector.widget.close', locale), children: _jsx(X, { className: "h-3.5 w-3.5" }) })] })] }), _jsx(Field, { id: "widget-title", label: t('engine.inspector.widget.title', locale), children: _jsx(Input, { id: "widget-title", value: widget.title ?? '', onChange: (e) => patchWidget({ title: e.target.value }), disabled: readOnly }) }), _jsx(Field, { id: "widget-type", label: t('engine.inspector.widget.type', locale), children: _jsxs(Select, { value: widget.type ?? 'metric', onValueChange: (v) => patchWidget({ type: v }), disabled: readOnly, children: [_jsx(SelectTrigger, { id: "widget-type", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: WIDGET_TYPES.map((wt) => (_jsx(SelectItem, { value: wt.value, children: wt.label }, wt.value))) })] }) }), _jsxs("div", { className: "space-y-3 rounded-md border border-dashed border-primary/40 bg-primary/5 p-3", children: [_jsx("div", { className: "text-[10px] font-semibold uppercase tracking-wider text-primary/80", children: t('engine.inspector.widget.datasetSection', locale) }), _jsxs(Field, { id: "widget-dataset", label: t('engine.inspector.widget.dataset', locale), children: [_jsx(InspectorComboField, { value: datasetName, onCommit: (v) => patchWidget({ dataset: v || undefined }), options: datasetComboOptions, loading: catalog.loading, placeholder: t('engine.inspector.widget.datasetPlaceholder', locale), searchPlaceholder: t('engine.inspector.widget.datasetPlaceholder', locale), disabled: readOnly, mono: true }), _jsx("p", { className: "text-[10px] leading-snug text-muted-foreground", children: t('engine.inspector.widget.datasetHint', locale) })] }), datasetName && (_jsxs(_Fragment, { children: [_jsx(DatasetNamesEditor, { label: t('engine.inspector.widget.dimensions', locale), emptyText: t('engine.inspector.widget.dimensionsHint', locale), names: dimensions, options: dimensionOptions, loading: semantics.loading, error: semantics.error, readOnly: readOnly, onCommit: (next) => patchWidget({ dimensions: next }) }), _jsx(DatasetNamesEditor, { label: t('engine.inspector.widget.values', locale), emptyText: t('engine.inspector.widget.valuesHint', locale), names: values, options: measureOptions, loading: semantics.loading, error: semantics.error, readOnly: readOnly, onCommit: (next) => patchWidget({ values: next }) })] }))] }), _jsx(Field, { id: "widget-object", label: t('engine.inspector.widget.object', locale), children: _jsx(InspectorComboField, { value: widget.object ?? '', onCommit: (v) => patchWidget({ object: v || undefined }), options: objectComboOptions, loading: objectsLoading, placeholder: "Select an object\u2026", searchPlaceholder: "Search objects\u2026", disabled: readOnly, mono: true }) }), _jsx(Field, { id: "widget-value-field", label: t('engine.inspector.widget.valueField', locale), children: _jsx(InspectorComboField, { value: widget.valueField ?? '', onCommit: (v) => patchWidget({ valueField: v || undefined }), options: fieldComboOptions, placeholder: widget.object ? 'Select a field…' : 'e.g. amount', searchPlaceholder: "Search fields\u2026", disabled: readOnly, mono: true }) }), _jsx(Field, { id: "widget-category-field", label: t('engine.inspector.widget.categoryField', locale), children: _jsx(InspectorComboField, { value: widget.categoryField ?? '', onCommit: (v) => patchWidget({ categoryField: v || undefined }), options: fieldComboOptions, placeholder: widget.object ? 'Select a field…' : 'e.g. status', searchPlaceholder: "Search fields\u2026", disabled: readOnly, mono: true }) }), _jsx(Field, { id: "widget-aggregate", label: t('engine.inspector.widget.aggregate', locale), children: _jsxs(Select, { value: widget.aggregate ?? 'count', onValueChange: (v) => patchWidget({ aggregate: v }), disabled: readOnly, children: [_jsx(SelectTrigger, { id: "widget-aggregate", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: AGGREGATES.map((a) => (_jsx(SelectItem, { value: a, children: a }, a))) })] }) }), _jsx(Field, { id: "widget-color", label: t('engine.inspector.widget.color', locale), children: _jsx(ColorVariantPicker, { value: widget.colorVariant ?? 'default', onChange: (v) => patchWidget({ colorVariant: v }), disabled: readOnly, options: COLORS.map((c) => ({ value: c })) }) }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsx(Field, { id: "widget-w", label: t('engine.inspector.widget.width', locale), children: _jsx(Input, { id: "widget-w", type: "number", min: 1, value: widget.layout?.w ?? 1, onChange: (e) => patchWidget({
|
|
116
|
+
layout: {
|
|
117
|
+
...(widget.layout ?? {}),
|
|
118
|
+
w: Number(e.target.value) || 1,
|
|
119
|
+
},
|
|
120
|
+
}), disabled: readOnly }) }), _jsx(Field, { id: "widget-h", label: t('engine.inspector.widget.height', locale), children: _jsx(Input, { id: "widget-h", type: "number", min: 1, value: widget.layout?.h ?? 1, onChange: (e) => patchWidget({
|
|
121
|
+
layout: {
|
|
122
|
+
...(widget.layout ?? {}),
|
|
123
|
+
h: Number(e.target.value) || 1,
|
|
124
|
+
},
|
|
125
|
+
}), disabled: readOnly }) })] }), !readOnly && (_jsx("div", { className: "pt-2 border-t", children: _jsx(Button, { type: "button", variant: "outline", size: "sm", className: "w-full text-destructive hover:text-destructive", onClick: () => {
|
|
126
|
+
const widgets = Array.isArray(draft.widgets)
|
|
127
|
+
? [...draft.widgets]
|
|
128
|
+
: [];
|
|
129
|
+
widgets.splice(index, 1);
|
|
130
|
+
onPatch({ widgets });
|
|
131
|
+
onClearSelection();
|
|
132
|
+
}, children: t('engine.inspector.widget.remove', locale) }) }))] }));
|
|
133
|
+
}
|
|
134
|
+
function Field({ id, label, children, }) {
|
|
135
|
+
return (_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: id, className: "text-xs font-medium text-muted-foreground", children: label }), children] }));
|
|
136
|
+
}
|
|
137
|
+
function InspectorEmpty({ message, onClose, locale, }) {
|
|
138
|
+
return (_jsxs("div", { className: "space-y-3 rounded-md border border-dashed p-4 text-sm text-muted-foreground", children: [_jsx("p", { children: message }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: onClose, children: t('engine.inspector.widget.close', locale) })] }));
|
|
139
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DatasetDefaultInspector — the curated designer for an analytics `dataset`
|
|
3
|
+
* (ADR-0021). Replaces the generic whole-draft JSON SchemaForm with structured,
|
|
4
|
+
* fool-proof editors for the dataset's parts:
|
|
5
|
+
*
|
|
6
|
+
* - base `object`,
|
|
7
|
+
* - `include` relationships (the join allowlist — D-C),
|
|
8
|
+
* - `dimensions` (name + field/`relationship.field` + type + granularity), and
|
|
9
|
+
* - `measures` (name + aggregate + field + certified + format/currency/derived).
|
|
10
|
+
*
|
|
11
|
+
* The base object, the included relationships, and every `field` are picked
|
|
12
|
+
* from the live object graph (a searchable combo over {@link useDatasetFieldCatalog})
|
|
13
|
+
* — not recalled by hand — so authoring matches mainstream low-code dataset
|
|
14
|
+
* builders. The aggregate / type / granularity are closed dropdowns so an
|
|
15
|
+
* author can't type an unsupported value. Each combo still allows a custom
|
|
16
|
+
* value as an escape hatch (offline catalog, computed path). Edits flow through
|
|
17
|
+
* `onPatch`; the DatasetPreview on the canvas re-runs live as the draft changes.
|
|
18
|
+
*/
|
|
19
|
+
import * as React from 'react';
|
|
20
|
+
import type { MetadataDefaultInspectorProps } from '../default-inspector-registry';
|
|
21
|
+
export declare function DatasetDefaultInspector({ draft, onPatch, readOnly }: MetadataDefaultInspectorProps): React.JSX.Element;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
|
|
3
|
+
/**
|
|
4
|
+
* DatasetDefaultInspector — the curated designer for an analytics `dataset`
|
|
5
|
+
* (ADR-0021). Replaces the generic whole-draft JSON SchemaForm with structured,
|
|
6
|
+
* fool-proof editors for the dataset's parts:
|
|
7
|
+
*
|
|
8
|
+
* - base `object`,
|
|
9
|
+
* - `include` relationships (the join allowlist — D-C),
|
|
10
|
+
* - `dimensions` (name + field/`relationship.field` + type + granularity), and
|
|
11
|
+
* - `measures` (name + aggregate + field + certified + format/currency/derived).
|
|
12
|
+
*
|
|
13
|
+
* The base object, the included relationships, and every `field` are picked
|
|
14
|
+
* from the live object graph (a searchable combo over {@link useDatasetFieldCatalog})
|
|
15
|
+
* — not recalled by hand — so authoring matches mainstream low-code dataset
|
|
16
|
+
* builders. The aggregate / type / granularity are closed dropdowns so an
|
|
17
|
+
* author can't type an unsupported value. Each combo still allows a custom
|
|
18
|
+
* value as an escape hatch (offline catalog, computed path). Edits flow through
|
|
19
|
+
* `onPatch`; the DatasetPreview on the canvas re-runs live as the draft changes.
|
|
20
|
+
*/
|
|
21
|
+
import * as React from 'react';
|
|
22
|
+
import { ArrowRight, Plus, Trash2, X } from 'lucide-react';
|
|
23
|
+
import { Badge, Button, Label } from '@object-ui/components';
|
|
24
|
+
import { InspectorShell, InspectorTextField, InspectorSelectField, InspectorCheckboxField, appendArray, spliceArray, } from './_shared';
|
|
25
|
+
import { InspectorComboField } from './InspectorComboField';
|
|
26
|
+
import { useObjectOptions, useDatasetFieldCatalog, useDatasetUsage, fieldTypeToDimensionType, } from './useDatasetFields';
|
|
27
|
+
// Closed to what the dataset compiler supports (no array_agg/string_agg in v1).
|
|
28
|
+
const AGGREGATE_OPTIONS = [
|
|
29
|
+
{ value: 'count', label: 'count' },
|
|
30
|
+
{ value: 'sum', label: 'sum' },
|
|
31
|
+
{ value: 'avg', label: 'avg' },
|
|
32
|
+
{ value: 'min', label: 'min' },
|
|
33
|
+
{ value: 'max', label: 'max' },
|
|
34
|
+
{ value: 'count_distinct', label: 'count distinct' },
|
|
35
|
+
];
|
|
36
|
+
const DIMENSION_TYPE_OPTIONS = [
|
|
37
|
+
{ value: 'string', label: 'string' },
|
|
38
|
+
{ value: 'number', label: 'number' },
|
|
39
|
+
{ value: 'date', label: 'date' },
|
|
40
|
+
{ value: 'boolean', label: 'boolean' },
|
|
41
|
+
{ value: 'lookup', label: 'lookup' },
|
|
42
|
+
];
|
|
43
|
+
const DATE_GRANULARITY_OPTIONS = [
|
|
44
|
+
{ value: '', label: '— none —' },
|
|
45
|
+
{ value: 'day', label: 'day' },
|
|
46
|
+
{ value: 'week', label: 'week' },
|
|
47
|
+
{ value: 'month', label: 'month' },
|
|
48
|
+
{ value: 'quarter', label: 'quarter' },
|
|
49
|
+
{ value: 'year', label: 'year' },
|
|
50
|
+
];
|
|
51
|
+
const DERIVED_OP_OPTIONS = [
|
|
52
|
+
{ value: 'ratio', label: 'ratio (a ÷ b)' },
|
|
53
|
+
{ value: 'sum', label: 'sum (a + b)' },
|
|
54
|
+
{ value: 'difference', label: 'difference (a − b)' },
|
|
55
|
+
{ value: 'product', label: 'product (a × b)' },
|
|
56
|
+
];
|
|
57
|
+
function SectionHeader({ title, count, onAdd, addLabel }) {
|
|
58
|
+
return (_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Label, { className: "text-xs text-muted-foreground", children: title }), _jsx(Badge, { variant: "outline", className: "text-[10px]", children: count })] }), onAdd && (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", className: "h-6 gap-1 px-1.5 text-[11px]", onClick: onAdd, children: [_jsx(Plus, { className: "h-3 w-3" }), " ", addLabel] }))] }));
|
|
59
|
+
}
|
|
60
|
+
/** Native disclosure for a row's optional / advanced fields. */
|
|
61
|
+
function Advanced({ children }) {
|
|
62
|
+
return (_jsxs("details", { className: "group", children: [_jsx("summary", { className: "cursor-pointer select-none list-none text-[11px] text-muted-foreground hover:text-foreground", children: _jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx(ArrowRight, { className: "h-3 w-3 transition-transform group-open:rotate-90" }), "Advanced"] }) }), _jsx("div", { className: "mt-1.5 space-y-1.5 border-l pl-2.5", children: children })] }));
|
|
63
|
+
}
|
|
64
|
+
export function DatasetDefaultInspector({ draft, onPatch, readOnly }) {
|
|
65
|
+
const label = typeof draft.label === 'string' ? draft.label : '';
|
|
66
|
+
const description = typeof draft.description === 'string' ? draft.description : '';
|
|
67
|
+
const object = typeof draft.object === 'string' ? draft.object : '';
|
|
68
|
+
const include = Array.isArray(draft.include) ? draft.include : [];
|
|
69
|
+
const dimensions = Array.isArray(draft.dimensions) ? draft.dimensions : [];
|
|
70
|
+
const measures = Array.isArray(draft.measures) ? draft.measures : [];
|
|
71
|
+
const datasetName = typeof draft.name === 'string' ? draft.name : undefined;
|
|
72
|
+
const { options: objectOptions, loading: objectsLoading } = useObjectOptions();
|
|
73
|
+
const { relationships, fieldOptions, loading: catalogLoading } = useDatasetFieldCatalog(object, include);
|
|
74
|
+
const usage = useDatasetUsage(datasetName);
|
|
75
|
+
const objectComboOptions = React.useMemo(() => objectOptions.map((o) => ({ value: o.name, label: o.label })), [objectOptions]);
|
|
76
|
+
const relationshipComboOptions = React.useMemo(() => relationships.map((r) => ({ value: r.name, label: r.label, hint: r.referenceTo ? `→ ${r.referenceTo}` : undefined })), [relationships]);
|
|
77
|
+
const fieldComboOptions = React.useMemo(() => fieldOptions.map((f) => ({ value: f.value, label: f.label, hint: f.type, group: f.group })), [fieldOptions]);
|
|
78
|
+
const baseLabel = objectComboOptions.find((o) => o.value === object)?.label ?? object;
|
|
79
|
+
const patchDimension = (i, patch) => onPatch({ dimensions: dimensions.map((d, idx) => (idx === i ? { ...d, ...patch } : d)) });
|
|
80
|
+
const patchMeasure = (i, patch) => onPatch({ measures: measures.map((m, idx) => (idx === i ? { ...m, ...patch } : m)) });
|
|
81
|
+
// Picking a field auto-infers the dimension type from the field's framework
|
|
82
|
+
// type (region:string, close_date:date, …) — the BI "pick field, type follows"
|
|
83
|
+
// convention — while leaving the Type select free to override.
|
|
84
|
+
const pickDimensionField = (i, v) => {
|
|
85
|
+
const opt = fieldOptions.find((o) => o.value === v);
|
|
86
|
+
patchDimension(i, opt?.type ? { field: v, type: fieldTypeToDimensionType(opt.type) } : { field: v });
|
|
87
|
+
};
|
|
88
|
+
return (_jsxs(InspectorShell, { kindLabel: "Dataset", title: String(label || draft.name || 'Dataset'), onClose: () => { }, hideClose: true, children: [datasetName && !usage.loading && (_jsx("p", { className: usage.reports + usage.dashboards > 0
|
|
89
|
+
? 'rounded-md border border-amber-500/30 bg-amber-500/5 px-2.5 py-1.5 text-[11px] text-amber-700 dark:text-amber-300'
|
|
90
|
+
: 'text-[11px] text-muted-foreground', children: usage.reports + usage.dashboards > 0
|
|
91
|
+
? `Bound by ${usage.reports} report${usage.reports === 1 ? '' : 's'} · ${usage.dashboards} dashboard${usage.dashboards === 1 ? '' : 's'} — changes affect them.`
|
|
92
|
+
: 'Not yet bound by any report or dashboard.' })), _jsx(InspectorTextField, { label: "Label", value: label, onCommit: (v) => onPatch({ label: v }), disabled: readOnly }), _jsx(InspectorTextField, { label: "Description", value: description, onCommit: (v) => onPatch({ description: v }), disabled: readOnly }), _jsx(InspectorComboField, { label: "Base object", value: object, onCommit: (v) => onPatch({ object: v }), options: objectComboOptions, loading: objectsLoading, placeholder: "Select an object\u2026", searchPlaceholder: "Search objects\u2026", disabled: readOnly, mono: true }), _jsxs("div", { className: "border-t pt-3 space-y-1.5", children: [_jsx(SectionHeader, { title: "Included relationships", count: include.length, addLabel: "Add", onAdd: readOnly ? undefined : () => onPatch({ include: appendArray(include, '') }) }), include.length === 0 ? (_jsxs("p", { className: "rounded-md border border-dashed bg-muted/30 px-3 py-2 text-center text-[11px] text-muted-foreground", children: ["No joins. Add a relationship (a lookup field on ", _jsx("code", { children: baseLabel || 'the base object' }), ") to use ", _jsx("code", { children: "relationship.field" }), " dimensions/measures."] })) : (include.map((rel, i) => (_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(InspectorComboField, { value: rel, onCommit: (v) => onPatch({ include: include.map((r, idx) => (idx === i ? v : r)) }), options: relationshipComboOptions, loading: catalogLoading, placeholder: "Select a relationship\u2026", searchPlaceholder: "Search relationships\u2026", disabled: readOnly, mono: true }), !readOnly && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7 w-7 shrink-0 p-0", onClick: () => onPatch({ include: spliceArray(include, i, null) }), "aria-label": "Remove relationship", children: _jsx(X, { className: "h-3.5 w-3.5" }) }))] }, i)))), object && include.length > 0 && (_jsxs("div", { className: "flex flex-wrap items-center gap-x-1 gap-y-0.5 pt-0.5 text-[10px] text-muted-foreground", children: [_jsx("span", { className: "font-mono font-medium", children: baseLabel }), include.map((rel, i) => {
|
|
93
|
+
const r = relationships.find((x) => x.name === rel);
|
|
94
|
+
return (_jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx(ArrowRight, { className: "h-3 w-3 opacity-60" }), _jsxs("span", { className: "font-mono", children: [rel, r?.referenceTo ? ` (${r.referenceTo})` : ''] })] }, i));
|
|
95
|
+
})] }))] }), _jsxs("div", { className: "border-t pt-3 space-y-2", children: [_jsx(SectionHeader, { title: "Dimensions", count: dimensions.length, addLabel: "Add dimension", onAdd: readOnly ? undefined : () => onPatch({ dimensions: appendArray(dimensions, { name: '', field: '', type: 'string' }) }) }), dimensions.map((d, i) => (_jsxs("div", { className: "rounded-md border p-2 space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("span", { className: "text-[11px] font-medium text-muted-foreground", children: ["Dimension ", i + 1] }), !readOnly && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": "Remove dimension", title: "Remove dimension", className: "h-6 w-6 shrink-0 p-0 text-muted-foreground hover:text-destructive", onClick: () => onPatch({ dimensions: spliceArray(dimensions, i, null) }), children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }), _jsx(InspectorTextField, { label: "Name", value: d.name ?? '', onCommit: (v) => patchDimension(i, { name: v }), placeholder: "e.g. region", disabled: readOnly, mono: true }), _jsx(InspectorComboField, { label: "Field", value: d.field ?? '', onCommit: (v) => pickDimensionField(i, v), options: fieldComboOptions, loading: catalogLoading, placeholder: "field or relationship.field", searchPlaceholder: "Search fields\u2026", disabled: readOnly, mono: true }), _jsx(InspectorSelectField, { label: "Type", value: d.type, options: DIMENSION_TYPE_OPTIONS, onCommit: (v) => patchDimension(i, { type: v }), disabled: readOnly }), _jsxs(Advanced, { children: [_jsx(InspectorTextField, { label: "Label (optional)", value: d.label ?? '', onCommit: (v) => patchDimension(i, { label: v || undefined }), placeholder: d.name || 'Display label', disabled: readOnly }), d.type === 'date' && (_jsx(InspectorSelectField, { label: "Date bucket", value: d.dateGranularity ?? '', options: DATE_GRANULARITY_OPTIONS, onCommit: (v) => patchDimension(i, { dateGranularity: v || undefined }), disabled: readOnly }))] })] }, i)))] }), _jsxs("div", { className: "border-t pt-3 space-y-2", children: [_jsx(SectionHeader, { title: "Measures", count: measures.length, addLabel: "Add measure", onAdd: readOnly ? undefined : () => onPatch({ measures: appendArray(measures, { name: '', aggregate: 'sum', field: '', certified: false }) }) }), measures.map((m, i) => {
|
|
96
|
+
const otherMeasures = measures.filter((_, idx) => idx !== i).map((x) => x.name).filter((n) => !!n);
|
|
97
|
+
const derived = m.derived;
|
|
98
|
+
return (_jsxs("div", { className: "rounded-md border p-2 space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("span", { className: "text-[11px] font-medium text-muted-foreground", children: ["Measure ", i + 1] }), !readOnly && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", "aria-label": "Remove measure", title: "Remove measure", className: "h-6 w-6 shrink-0 p-0 text-muted-foreground hover:text-destructive", onClick: () => onPatch({ measures: spliceArray(measures, i, null) }), children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }), _jsx(InspectorTextField, { label: "Name", value: m.name ?? '', onCommit: (v) => patchMeasure(i, { name: v }), placeholder: "e.g. revenue", disabled: readOnly, mono: true }), _jsx(InspectorSelectField, { label: "Aggregate", value: m.aggregate, options: AGGREGATE_OPTIONS, onCommit: (v) => patchMeasure(i, { aggregate: v }), disabled: readOnly }), _jsx(InspectorComboField, { label: "Field", value: m.field ?? '', onCommit: (v) => patchMeasure(i, { field: v }), options: fieldComboOptions, loading: catalogLoading, placeholder: "field (optional for count)", searchPlaceholder: "Search fields\u2026", disabled: readOnly, mono: true }), _jsx(InspectorCheckboxField, { label: "Certified", value: !!m.certified, onCommit: (v) => patchMeasure(i, { certified: v }), disabled: readOnly }), _jsxs(Advanced, { children: [_jsx(InspectorTextField, { label: "Label (optional)", value: m.label ?? '', onCommit: (v) => patchMeasure(i, { label: v || undefined }), placeholder: m.name || 'Display label', disabled: readOnly }), _jsxs("div", { className: "grid grid-cols-2 gap-1.5", children: [_jsx(InspectorTextField, { label: "Format", value: m.format ?? '', onCommit: (v) => patchMeasure(i, { format: v || undefined }), placeholder: "$0,0.00", disabled: readOnly, mono: true }), _jsx(InspectorTextField, { label: "Currency", value: m.currency ?? '', onCommit: (v) => patchMeasure(i, { currency: v ? v.toUpperCase().slice(0, 3) : undefined }), placeholder: "USD", disabled: readOnly, mono: true })] }), _jsx(InspectorCheckboxField, { label: "Derived \u2014 computed from other measures", value: !!derived, onCommit: (v) => patchMeasure(i, { derived: v ? { op: 'ratio', of: [] } : undefined }), disabled: readOnly }), derived && (_jsxs("div", { className: "space-y-1.5 rounded-md border border-dashed p-2", children: [_jsx(InspectorSelectField, { label: "Operation", value: derived.op, options: DERIVED_OP_OPTIONS, onCommit: (v) => patchMeasure(i, { derived: { ...derived, op: v } }), disabled: readOnly }), _jsx(Label, { className: "text-xs text-muted-foreground", children: "Operands (other measures)" }), otherMeasures.length === 0 ? (_jsx("p", { className: "text-[11px] italic text-muted-foreground", children: "Add other measures first." })) : (_jsx("div", { className: "space-y-1", children: otherMeasures.map((nm) => {
|
|
99
|
+
const checked = Array.isArray(derived.of) && derived.of.includes(nm);
|
|
100
|
+
return (_jsx(InspectorCheckboxField, { label: nm, value: checked, disabled: readOnly, onCommit: (v) => {
|
|
101
|
+
const current = Array.isArray(derived.of) ? derived.of : [];
|
|
102
|
+
const next = v ? [...current, nm] : current.filter((x) => x !== nm);
|
|
103
|
+
patchMeasure(i, { derived: { ...derived, of: next } });
|
|
104
|
+
} }, nm));
|
|
105
|
+
}) }))] }))] })] }, i));
|
|
106
|
+
})] })] }));
|
|
107
|
+
}
|