@manta-eu/react 0.2.0 → 0.3.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/dist/components/channel-icon/index.d.ts +1 -0
- package/dist/components/channel-icon/index.d.ts.map +1 -1
- package/dist/components/channel-icon/index.js +1 -1
- package/dist/components/channel-icon/index.js.map +1 -1
- package/dist/components/datastore-management/tree.d.ts +1 -1
- package/dist/components/datastore-management/tree.d.ts.map +1 -1
- package/dist/components/datastore-management/tree.js +5 -5
- package/dist/components/datastore-management/tree.js.map +1 -1
- package/dist/components/explain-fact/ExplainFact.js +3 -3
- package/dist/components/explain-fact/ExplainFact.js.map +1 -1
- package/dist/components/planning/ActioningView.d.ts +1 -2
- package/dist/components/planning/ActioningView.d.ts.map +1 -1
- package/dist/components/planning/ActioningView.js +19 -44
- package/dist/components/planning/ActioningView.js.map +1 -1
- package/dist/components/planning/ManualActionCard.d.ts +4 -0
- package/dist/components/planning/ManualActionCard.d.ts.map +1 -1
- package/dist/components/planning/ManualActionCard.js +7 -5
- package/dist/components/planning/ManualActionCard.js.map +1 -1
- package/dist/components/planning/ManualActionList.d.ts +4 -0
- package/dist/components/planning/ManualActionList.d.ts.map +1 -1
- package/dist/components/planning/ManualActionList.js +8 -2
- package/dist/components/planning/ManualActionList.js.map +1 -1
- package/dist/components/planning/actioning-view-utils.d.ts +9 -0
- package/dist/components/planning/actioning-view-utils.d.ts.map +1 -1
- package/dist/components/planning/actioning-view-utils.js +19 -1
- package/dist/components/planning/actioning-view-utils.js.map +1 -1
- package/dist/components/planning/plan-detail-utils.d.ts.map +1 -1
- package/dist/components/planning/plan-detail-utils.js +2 -0
- package/dist/components/planning/plan-detail-utils.js.map +1 -1
- package/dist/components/tool-invocation-card/ListRulesResultDisplay.d.ts +2 -2
- package/dist/components/tool-invocation-card/ListRulesResultDisplay.d.ts.map +1 -1
- package/dist/components/tool-invocation-card/ToolOutputRenderer.d.ts.map +1 -1
- package/dist/components/tool-invocation-card/ToolOutputRenderer.js +7 -4
- package/dist/components/tool-invocation-card/ToolOutputRenderer.js.map +1 -1
- package/dist/components/tool-invocation-card/types.d.ts +5 -5
- package/dist/components/tool-invocation-card/types.d.ts.map +1 -1
- package/dist/components/tool-invocation-card/types.js +5 -5
- package/dist/components/tool-invocation-card/types.js.map +1 -1
- package/dist/components/tooltip/index.d.ts +7 -0
- package/dist/components/tooltip/index.d.ts.map +1 -1
- package/dist/components/tooltip/index.js +10 -1
- package/dist/components/tooltip/index.js.map +1 -1
- package/dist/explain-fact/rule-presentation.js +2 -2
- package/dist/explain-fact/rule-presentation.js.map +1 -1
- package/dist/explain-fact/tree-model.js +2 -2
- package/dist/explain-fact/tree-model.js.map +1 -1
- package/dist/globals.css +19 -6
- package/dist/graph/entity-link.d.ts +14 -0
- package/dist/graph/entity-link.d.ts.map +1 -0
- package/dist/graph/entity-link.js +12 -0
- package/dist/graph/entity-link.js.map +1 -0
- package/dist/graph/graph-list-utils.d.ts +1 -0
- package/dist/graph/graph-list-utils.d.ts.map +1 -1
- package/dist/graph/graph-list-utils.js +1 -0
- package/dist/graph/graph-list-utils.js.map +1 -1
- package/dist/graph/graph-model.d.ts.map +1 -1
- package/dist/graph/graph-model.js +14 -5
- package/dist/graph/graph-model.js.map +1 -1
- package/dist/graph/nodes/BrandAssetAnalysisView.d.ts.map +1 -1
- package/dist/graph/nodes/BrandAssetAnalysisView.js +2 -1
- package/dist/graph/nodes/BrandAssetAnalysisView.js.map +1 -1
- package/dist/graph/nodes/BrandFontSuggestionView.d.ts.map +1 -1
- package/dist/graph/nodes/BrandFontSuggestionView.js +2 -1
- package/dist/graph/nodes/BrandFontSuggestionView.js.map +1 -1
- package/dist/graph/nodes/BrandGuidelinesView.js +1 -1
- package/dist/graph/nodes/BrandGuidelinesView.js.map +1 -1
- package/dist/graph/nodes/NodePropertyTable.d.ts +4 -2
- package/dist/graph/nodes/NodePropertyTable.d.ts.map +1 -1
- package/dist/graph/nodes/NodePropertyTable.js +12 -59
- package/dist/graph/nodes/NodePropertyTable.js.map +1 -1
- package/dist/graph/nodes/node-property-row.d.ts +30 -0
- package/dist/graph/nodes/node-property-row.d.ts.map +1 -0
- package/dist/graph/nodes/node-property-row.js +35 -0
- package/dist/graph/nodes/node-property-row.js.map +1 -0
- package/dist/graph/property-formatters.d.ts +1 -0
- package/dist/graph/property-formatters.d.ts.map +1 -1
- package/dist/graph/property-formatters.js +4 -1
- package/dist/graph/property-formatters.js.map +1 -1
- package/dist/graph/special-nodes/AttributeList.d.ts +27 -0
- package/dist/graph/special-nodes/AttributeList.d.ts.map +1 -0
- package/dist/graph/special-nodes/AttributeList.js +57 -0
- package/dist/graph/special-nodes/AttributeList.js.map +1 -0
- package/dist/graph/special-nodes/EntityPreview.d.ts +5 -0
- package/dist/graph/special-nodes/EntityPreview.d.ts.map +1 -0
- package/dist/graph/special-nodes/EntityPreview.js +19 -0
- package/dist/graph/special-nodes/EntityPreview.js.map +1 -0
- package/dist/graph/special-nodes/EntityValueChip.d.ts +11 -0
- package/dist/graph/special-nodes/EntityValueChip.d.ts.map +1 -0
- package/dist/graph/special-nodes/EntityValueChip.js +40 -0
- package/dist/graph/special-nodes/EntityValueChip.js.map +1 -0
- package/dist/graph/special-nodes/GenericView.d.ts +2 -1
- package/dist/graph/special-nodes/GenericView.d.ts.map +1 -1
- package/dist/graph/special-nodes/GenericView.js +23 -69
- package/dist/graph/special-nodes/GenericView.js.map +1 -1
- package/dist/graph/special-nodes/card-facts.d.ts +52 -0
- package/dist/graph/special-nodes/card-facts.d.ts.map +1 -0
- package/dist/graph/special-nodes/card-facts.js +189 -0
- package/dist/graph/special-nodes/card-facts.js.map +1 -0
- package/dist/graph/special-nodes/property-icons.d.ts +9 -0
- package/dist/graph/special-nodes/property-icons.d.ts.map +1 -0
- package/dist/graph/special-nodes/property-icons.js +39 -0
- package/dist/graph/special-nodes/property-icons.js.map +1 -0
- package/dist/graph/status-utils.d.ts +16 -0
- package/dist/graph/status-utils.d.ts.map +1 -1
- package/dist/graph/status-utils.js +35 -0
- package/dist/graph/status-utils.js.map +1 -1
- package/dist/graph/types.d.ts +5 -0
- package/dist/graph/types.d.ts.map +1 -1
- package/dist/hooks/use-approve-anyway.d.ts +12 -0
- package/dist/hooks/use-approve-anyway.d.ts.map +1 -0
- package/dist/hooks/use-approve-anyway.js +81 -0
- package/dist/hooks/use-approve-anyway.js.map +1 -0
- package/dist/hooks/use-connection-problems.d.ts.map +1 -1
- package/dist/hooks/useValidateActionPlan.d.ts.map +1 -1
- package/dist/ldo-ui.d.ts +2 -1
- package/dist/ldo-ui.d.ts.map +1 -1
- package/dist/ldo-ui.js +1 -0
- package/dist/ldo-ui.js.map +1 -1
- package/dist/lib/find-latest-staged-rule.js +1 -1
- package/dist/lib/find-latest-staged-rule.js.map +1 -1
- package/dist/lib/manual-actions.d.ts +2 -1
- package/dist/lib/manual-actions.d.ts.map +1 -1
- package/dist/lib/manual-actions.js +3 -2
- package/dist/lib/manual-actions.js.map +1 -1
- package/dist/lib/tool-icons.js +6 -6
- package/dist/lib/tool-icons.js.map +1 -1
- package/dist/lib/tool-names.d.ts.map +1 -1
- package/dist/lib/tool-names.js +0 -1
- package/dist/lib/tool-names.js.map +1 -1
- package/dist/ontology/skolem.js +2 -2
- package/dist/ontology/skolem.js.map +1 -1
- package/dist/providers/ActiveChatProvider.js +3 -1
- package/dist/providers/ActiveChatProvider.js.map +1 -1
- package/dist/providers/ChatManagerProvider.d.ts.map +1 -1
- package/dist/providers/ChatManagerProvider.js +3 -1
- package/dist/providers/ChatManagerProvider.js.map +1 -1
- package/dist/providers/KnowledgeGraphStateProvider.js +1 -1
- package/dist/providers/KnowledgeGraphStateProvider.js.map +1 -1
- package/dist/providers/chat-socket-auth.d.ts +7 -2
- package/dist/providers/chat-socket-auth.d.ts.map +1 -1
- package/dist/providers/chat-socket-auth.js +87 -16
- package/dist/providers/chat-socket-auth.js.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +8 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/routes/activity/detail/ActivityChanges.d.ts +2 -0
- package/dist/routes/activity/detail/ActivityChanges.d.ts.map +1 -1
- package/dist/routes/activity/detail/ActivityChanges.js +8 -7
- package/dist/routes/activity/detail/ActivityChanges.js.map +1 -1
- package/dist/routes/activity/detail/ActivityDetail.d.ts +1 -0
- package/dist/routes/activity/detail/ActivityDetail.d.ts.map +1 -1
- package/dist/routes/activity/detail/ActivityDetail.js +5 -4
- package/dist/routes/activity/detail/ActivityDetail.js.map +1 -1
- package/dist/routes/activity/detail/ActivityStepsRail.d.ts +1 -0
- package/dist/routes/activity/detail/ActivityStepsRail.d.ts.map +1 -1
- package/dist/routes/activity/detail/ActivityStepsRail.js +3 -2
- package/dist/routes/activity/detail/ActivityStepsRail.js.map +1 -1
- package/dist/routes/activity/detail/ActivityTimeline.js +2 -2
- package/dist/routes/activity/detail/ActivityTimeline.js.map +1 -1
- package/dist/routes/activity/detail/NoChangesMessage.d.ts +6 -0
- package/dist/routes/activity/detail/NoChangesMessage.d.ts.map +1 -0
- package/dist/routes/activity/detail/NoChangesMessage.js +10 -0
- package/dist/routes/activity/detail/NoChangesMessage.js.map +1 -0
- package/dist/routes/data-explorer/class-properties.js +2 -2
- package/dist/routes/data-explorer/class-properties.js.map +1 -1
- package/dist/routes/data-explorer/classes.js +2 -2
- package/dist/routes/data-explorer/classes.js.map +1 -1
- package/dist/routes/data-explorer/components/RawTriples.js +1 -1
- package/dist/routes/data-explorer/components/RawTriples.js.map +1 -1
- package/dist/routes/data-explorer/components/RecentTypes.js +1 -1
- package/dist/routes/data-explorer/components/RecentTypes.js.map +1 -1
- package/dist/routes/data-explorer/components/class-detail/Instances.js +1 -1
- package/dist/routes/data-explorer/components/class-detail/Instances.js.map +1 -1
- package/dist/routes/data-explorer/components/namespace-detail/NamespaceClassRow.js +1 -1
- package/dist/routes/data-explorer/components/namespace-detail/NamespaceClassRow.js.map +1 -1
- package/dist/routes/data-explorer/components/ontology-coverage/CoverageTermColumns.js +1 -1
- package/dist/routes/data-explorer/components/ontology-coverage/CoverageTermColumns.js.map +1 -1
- package/dist/routes/data-explorer/components/ontology-coverage/UnobservedRuleVocabulary.js +1 -1
- package/dist/routes/data-explorer/components/ontology-coverage/UnobservedRuleVocabulary.js.map +1 -1
- package/dist/routes/data-explorer/components/record-detail/ChatPreview.js +2 -2
- package/dist/routes/data-explorer/components/record-detail/ChatPreview.js.map +1 -1
- package/dist/routes/data-explorer/recents.js +1 -1
- package/dist/routes/data-explorer/recents.js.map +1 -1
- package/dist/routes/data-explorer/shapes.js +2 -2
- package/dist/routes/data-explorer/shapes.js.map +1 -1
- package/dist/routes/integrations/components/connect-forms/CanvaConnectForm.d.ts +3 -0
- package/dist/routes/integrations/components/connect-forms/CanvaConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/CanvaConnectForm.js +12 -0
- package/dist/routes/integrations/components/connect-forms/CanvaConnectForm.js.map +1 -0
- package/dist/routes/integrations/components/connect-forms/DataConnectionConnectForm.d.ts.map +1 -1
- package/dist/routes/integrations/components/connect-forms/DataConnectionConnectForm.js +13 -6
- package/dist/routes/integrations/components/connect-forms/DataConnectionConnectForm.js.map +1 -1
- package/dist/routes/integrations/components/connect-forms/data-connection-fields.d.ts +3 -0
- package/dist/routes/integrations/components/connect-forms/data-connection-fields.d.ts.map +1 -1
- package/dist/routes/integrations/components/connect-forms/data-connection-fields.js +9 -2
- package/dist/routes/integrations/components/connect-forms/data-connection-fields.js.map +1 -1
- package/dist/routes/integrations/connect-forms.d.ts +1 -0
- package/dist/routes/integrations/connect-forms.d.ts.map +1 -1
- package/dist/routes/integrations/connect-forms.js +1 -0
- package/dist/routes/integrations/connect-forms.js.map +1 -1
- package/dist/routes/integrations/connection-view.d.ts.map +1 -1
- package/dist/routes/integrations/connection-view.js +1 -0
- package/dist/routes/integrations/connection-view.js.map +1 -1
- package/dist/routes/integrations/types.d.ts +2 -1
- package/dist/routes/integrations/types.d.ts.map +1 -1
- package/dist/routes/integrations/types.js +1 -0
- package/dist/routes/integrations/types.js.map +1 -1
- package/dist/routes/plans/components/ActionPlanPage.d.ts.map +1 -1
- package/dist/routes/plans/components/ActionPlanPage.js +4 -7
- package/dist/routes/plans/components/ActionPlanPage.js.map +1 -1
- package/dist/routes/plans/components/PlanDetailRouteContent.d.ts.map +1 -1
- package/dist/routes/plans/components/PlanDetailRouteContent.js +2 -1
- package/dist/routes/plans/components/PlanDetailRouteContent.js.map +1 -1
- package/dist/routes/rules/hooks/useRuleFactsPreview.js +4 -4
- package/dist/routes/rules/hooks/useRuleFactsPreview.js.map +1 -1
- package/dist/shacl/form-appearance.d.ts +1 -1
- package/dist/shacl/form-appearance.d.ts.map +1 -1
- package/dist/shacl/form-appearance.js +1 -1
- package/dist/shacl/form-appearance.js.map +1 -1
- package/dist/shared/graph-derived-queries.js +2 -2
- package/dist/shared/graph-derived-queries.js.map +1 -1
- package/package.json +12 -7
- package/src/app-subpaths.json +1 -0
- package/src/components/channel-icon/index.tsx +1 -1
- package/src/components/datastore-management/tree.ts +5 -5
- package/src/components/explain-fact/ExplainFact.tsx +3 -3
- package/src/components/planning/ActioningView.tsx +62 -70
- package/src/components/planning/ManualActionCard.tsx +66 -14
- package/src/components/planning/ManualActionList.tsx +30 -11
- package/src/components/planning/actioning-view-utils.ts +27 -1
- package/src/components/planning/plan-detail-utils.tsx +2 -0
- package/src/components/tool-invocation-card/ListRulesResultDisplay.tsx +2 -2
- package/src/components/tool-invocation-card/ToolOutputRenderer.tsx +10 -8
- package/src/components/tool-invocation-card/types.ts +8 -8
- package/src/components/tooltip/index.tsx +35 -2
- package/src/explain-fact/rule-presentation.ts +2 -2
- package/src/explain-fact/tree-model.ts +2 -2
- package/src/graph/entity-link.tsx +20 -0
- package/src/graph/graph-list-utils.ts +2 -0
- package/src/graph/graph-model.ts +15 -6
- package/src/graph/nodes/BrandAssetAnalysisView.tsx +3 -1
- package/src/graph/nodes/BrandFontSuggestionView.tsx +7 -1
- package/src/graph/nodes/BrandGuidelinesView.tsx +1 -1
- package/src/graph/nodes/NodePropertyTable.tsx +32 -143
- package/src/graph/nodes/node-property-row.tsx +154 -0
- package/src/graph/property-formatters.ts +5 -1
- package/src/graph/special-nodes/AttributeList.tsx +177 -0
- package/src/graph/special-nodes/EntityPreview.tsx +27 -0
- package/src/graph/special-nodes/EntityValueChip.tsx +85 -0
- package/src/graph/special-nodes/GenericView.tsx +49 -139
- package/src/graph/special-nodes/card-facts.ts +278 -0
- package/src/graph/special-nodes/property-icons.ts +59 -0
- package/src/graph/status-utils.ts +40 -0
- package/src/graph/types.ts +4 -0
- package/src/hooks/use-approve-anyway.ts +100 -0
- package/src/ldo-ui.ts +8 -0
- package/src/lib/find-latest-staged-rule.ts +1 -1
- package/src/lib/manual-actions.ts +3 -2
- package/src/lib/tool-icons.ts +6 -6
- package/src/lib/tool-names.ts +0 -1
- package/src/ontology/skolem.ts +2 -2
- package/src/providers/ActiveChatProvider.tsx +3 -1
- package/src/providers/ChatManagerProvider.tsx +3 -1
- package/src/providers/KnowledgeGraphStateProvider.tsx +1 -1
- package/src/providers/chat-socket-auth.ts +95 -16
- package/src/providers/index.tsx +16 -1
- package/src/routes/activity/detail/ActivityChanges.tsx +21 -5
- package/src/routes/activity/detail/ActivityDetail.tsx +24 -5
- package/src/routes/activity/detail/ActivityStepsRail.tsx +4 -2
- package/src/routes/activity/detail/ActivityTimeline.tsx +2 -2
- package/src/routes/activity/detail/NoChangesMessage.tsx +20 -0
- package/src/routes/data-explorer/class-properties.ts +2 -2
- package/src/routes/data-explorer/classes.ts +2 -2
- package/src/routes/data-explorer/components/RawTriples.tsx +1 -1
- package/src/routes/data-explorer/components/RecentTypes.tsx +1 -1
- package/src/routes/data-explorer/components/class-detail/Instances.tsx +1 -1
- package/src/routes/data-explorer/components/namespace-detail/NamespaceClassRow.tsx +1 -1
- package/src/routes/data-explorer/components/ontology-coverage/CoverageTermColumns.tsx +1 -1
- package/src/routes/data-explorer/components/ontology-coverage/UnobservedRuleVocabulary.tsx +1 -1
- package/src/routes/data-explorer/components/record-detail/ChatPreview.tsx +2 -2
- package/src/routes/data-explorer/recents.ts +1 -1
- package/src/routes/data-explorer/shapes.ts +2 -2
- package/src/routes/datastores/flatten.ts +1 -1
- package/src/routes/integrations/components/connect-forms/CanvaConnectForm.tsx +26 -0
- package/src/routes/integrations/components/connect-forms/DataConnectionConnectForm.tsx +38 -12
- package/src/routes/integrations/components/connect-forms/data-connection-fields.ts +11 -2
- package/src/routes/integrations/connect-forms.ts +1 -0
- package/src/routes/integrations/connection-view.ts +1 -0
- package/src/routes/integrations/types.ts +2 -0
- package/src/routes/plans/components/ActionPlanPage.tsx +9 -8
- package/src/routes/plans/components/PlanDetailRouteContent.tsx +4 -3
- package/src/routes/rules/hooks/useRuleFactsPreview.ts +4 -4
- package/src/shacl/form-appearance.ts +1 -1
- package/src/shared/graph-derived-queries.ts +2 -2
- package/dist/components/planning/ExecutionActivityRow.d.ts +0 -9
- package/dist/components/planning/ExecutionActivityRow.d.ts.map +0 -1
- package/dist/components/planning/ExecutionActivityRow.js +0 -53
- package/dist/components/planning/ExecutionActivityRow.js.map +0 -1
- package/dist/components/planning/ExecutionSectionItem.d.ts +0 -20
- package/dist/components/planning/ExecutionSectionItem.d.ts.map +0 -1
- package/dist/components/planning/ExecutionSectionItem.js +0 -66
- package/dist/components/planning/ExecutionSectionItem.js.map +0 -1
- package/dist/components/planning/ExecutionSections.d.ts +0 -16
- package/dist/components/planning/ExecutionSections.d.ts.map +0 -1
- package/dist/components/planning/ExecutionSections.js +0 -32
- package/dist/components/planning/ExecutionSections.js.map +0 -1
- package/dist/components/planning/action-plan-icons.d.ts +0 -3
- package/dist/components/planning/action-plan-icons.d.ts.map +0 -1
- package/dist/components/planning/action-plan-icons.js +0 -25
- package/dist/components/planning/action-plan-icons.js.map +0 -1
- package/dist/components/planning/execution-section-model.d.ts +0 -24
- package/dist/components/planning/execution-section-model.d.ts.map +0 -1
- package/dist/components/planning/execution-section-model.js +0 -150
- package/dist/components/planning/execution-section-model.js.map +0 -1
- package/dist/routes/data-explorer/components/TruncateTooltip.d.ts +0 -7
- package/dist/routes/data-explorer/components/TruncateTooltip.d.ts.map +0 -1
- package/dist/routes/data-explorer/components/TruncateTooltip.js +0 -9
- package/dist/routes/data-explorer/components/TruncateTooltip.js.map +0 -1
- package/src/components/planning/ExecutionActivityRow.tsx +0 -89
- package/src/components/planning/ExecutionSectionItem.tsx +0 -184
- package/src/components/planning/ExecutionSections.tsx +0 -97
- package/src/components/planning/action-plan-icons.tsx +0 -27
- package/src/components/planning/execution-section-model.ts +0 -190
- package/src/routes/data-explorer/components/TruncateTooltip.tsx +0 -25
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Check, ChevronDown, ChevronRight, X } from "lucide-react";
|
|
3
|
-
import { useMemo, useState } from "react";
|
|
4
|
-
import { match, P } from "ts-pattern";
|
|
1
|
+
import { useCallback, useMemo } from "react";
|
|
5
2
|
import { shouldHidePreviewField } from "../graph-list-utils";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
import { AttributeList, useAttributeFacts } from "../special-nodes/AttributeList";
|
|
4
|
+
import type { NodeProperty, NodeRelation } from "../types";
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
NodePropertyExpander,
|
|
8
|
+
NodePropertyList,
|
|
9
|
+
NodePropertyRow,
|
|
10
|
+
PropertyValue,
|
|
11
|
+
type NodePropertyExpanderProps,
|
|
12
|
+
type NodePropertyRowProps,
|
|
13
|
+
} from "./node-property-row";
|
|
13
14
|
|
|
14
15
|
export type NodePropertyTableProps = {
|
|
15
16
|
properties: NodeProperty[];
|
|
@@ -17,145 +18,33 @@ export type NodePropertyTableProps = {
|
|
|
17
18
|
jsonLd?: Record<string, unknown>;
|
|
18
19
|
// Rendered above the table by the card itself, so the table skips them.
|
|
19
20
|
omitPredicates?: readonly string[];
|
|
21
|
+
relations?: NodeRelation[];
|
|
20
22
|
};
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<span className="rounded bg-faint px-1.5 py-[1px] typography-tiny text-default-foreground">{props.children}</span>
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function PropertyValue(props: { display: PropertyDisplay }) {
|
|
29
|
-
return match(props.display)
|
|
30
|
-
.with({ kind: "tags" }, ({ values }) => (
|
|
31
|
-
<div className="flex flex-wrap gap-1">
|
|
32
|
-
{values.map((value) => (
|
|
33
|
-
<Tag key={value}>{value}</Tag>
|
|
34
|
-
))}
|
|
35
|
-
</div>
|
|
36
|
-
))
|
|
37
|
-
.with({ kind: "color" }, ({ hex }) => (
|
|
38
|
-
<span className="flex items-center gap-1.5">
|
|
39
|
-
<span
|
|
40
|
-
aria-hidden
|
|
41
|
-
className="size-3.5 shrink-0 rounded-sm border border-default-border"
|
|
42
|
-
style={{ backgroundColor: hex }}
|
|
43
|
-
/>
|
|
44
|
-
<span className="tabular-nums">{hex}</span>
|
|
45
|
-
</span>
|
|
46
|
-
))
|
|
47
|
-
.with({ kind: "ratio" }, ({ fraction, text }) => (
|
|
48
|
-
<span className="flex items-center gap-1.5">
|
|
49
|
-
<span aria-hidden className="h-1 w-12 shrink-0 overflow-hidden rounded-full bg-faint">
|
|
50
|
-
<span className="block h-full rounded-full bg-accent-foreground" style={{ width: `${fraction * 100}%` }} />
|
|
51
|
-
</span>
|
|
52
|
-
<span className="tabular-nums">{text}</span>
|
|
53
|
-
</span>
|
|
54
|
-
))
|
|
55
|
-
.with({ kind: "number" }, ({ text }) => <span className="tabular-nums">{text}</span>)
|
|
56
|
-
.with({ kind: "boolean" }, ({ value }) =>
|
|
57
|
-
value ? (
|
|
58
|
-
<span className="flex items-center gap-1">
|
|
59
|
-
<Check aria-hidden className="size-3.5 shrink-0 text-success-foreground" />
|
|
60
|
-
true
|
|
61
|
-
</span>
|
|
62
|
-
) : (
|
|
63
|
-
<span className="flex items-center gap-1">
|
|
64
|
-
<X aria-hidden className="size-3.5 shrink-0 text-muted-foreground" />
|
|
65
|
-
false
|
|
66
|
-
</span>
|
|
67
|
-
),
|
|
68
|
-
)
|
|
69
|
-
.with(P.union({ kind: "prose" }, { kind: "text" }), ({ text }) => <span>{text}</span>)
|
|
70
|
-
.exhaustive();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
type Row = { key: string; label: string; display: PropertyDisplay };
|
|
74
|
-
|
|
75
|
-
function PropertyRow(props: { row: Row }) {
|
|
76
|
-
const { row } = props;
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<div className="-mx-1 grid grid-cols-[minmax(0,7rem)_minmax(0,1fr)] items-baseline gap-x-3 gap-y-1 rounded-md border-t border-muted-border/50 px-1 py-1.5 transition-colors first:border-t-0 hover:bg-muted/30">
|
|
80
|
-
<span className="typography-tiny min-w-0 break-words text-muted-foreground first-letter:uppercase">
|
|
81
|
-
{row.label}
|
|
82
|
-
</span>
|
|
83
|
-
<div
|
|
84
|
-
className={cn(
|
|
85
|
-
"typography-tiny min-w-0 break-words text-default-foreground",
|
|
86
|
-
// A long description reads better as its own full-width block under the label.
|
|
87
|
-
row.display.kind === "prose" && "col-span-2",
|
|
88
|
-
)}
|
|
89
|
-
>
|
|
90
|
-
<PropertyValue display={row.display} />
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
);
|
|
94
|
-
}
|
|
24
|
+
const noJsonLd: Record<string, unknown> = {};
|
|
95
25
|
|
|
96
|
-
// Every property the node carries,
|
|
26
|
+
// Every property the node carries, in the same attribute list the fallback card uses. Rule-derived
|
|
97
27
|
// properties are included: they are real on the instance even though no shape declares them.
|
|
98
|
-
// Short values keep the compact two-column rhythm; prose paragraphs collapse behind the same
|
|
99
|
-
// "Show more" expander AttioNodeBody uses, so the card stays a summary until asked.
|
|
100
28
|
export function NodePropertyTable(props: NodePropertyTableProps) {
|
|
101
|
-
const { properties, jsonLd, omitPredicates
|
|
29
|
+
const { properties, jsonLd = noJsonLd, omitPredicates, relations } = props;
|
|
102
30
|
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return withOntologyLabels(properties, terms)
|
|
111
|
-
.filter(
|
|
112
|
-
(property) =>
|
|
113
|
-
!omitted.has(property.iri ?? "") && !shouldHidePreviewField(property.label ?? "", property.iri ?? ""),
|
|
114
|
-
)
|
|
115
|
-
.map((property): Row => {
|
|
116
|
-
const label = property.label || formatPropertyLabel(property.iri ?? property.key);
|
|
117
|
-
const formatted = formatDisplayValue(property.value, {
|
|
118
|
-
predicate: property.iri,
|
|
119
|
-
predicateLabel: property.label,
|
|
120
|
-
valueScale: property.valueScale,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
return {
|
|
124
|
-
key: property.iri ?? property.key,
|
|
125
|
-
label,
|
|
126
|
-
display: classifyPropertyDisplay({ label, formatted, raw: jsonLd?.[property.iri ?? ""] ?? property.value }),
|
|
127
|
-
};
|
|
128
|
-
});
|
|
129
|
-
}, [properties, terms, jsonLd, omitPredicates]);
|
|
130
|
-
|
|
131
|
-
const compactRows = rows.filter((row) => row.display.kind !== "prose");
|
|
132
|
-
const proseRows = rows.filter((row) => row.display.kind === "prose");
|
|
31
|
+
const include = useCallback(
|
|
32
|
+
(property: NodeProperty) =>
|
|
33
|
+
!(omitPredicates ?? []).includes(property.iri ?? "") &&
|
|
34
|
+
!shouldHidePreviewField(property.label ?? "", property.iri ?? ""),
|
|
35
|
+
[omitPredicates],
|
|
36
|
+
);
|
|
133
37
|
|
|
134
|
-
|
|
38
|
+
const visibleRelations = useMemo(
|
|
39
|
+
() =>
|
|
40
|
+
relations?.filter(
|
|
41
|
+
(relation) =>
|
|
42
|
+
!(omitPredicates ?? []).includes(relation.predicate) && !shouldHidePreviewField("", relation.predicate),
|
|
43
|
+
),
|
|
44
|
+
[relations, omitPredicates],
|
|
45
|
+
);
|
|
135
46
|
|
|
136
|
-
|
|
137
|
-
<div className="flex flex-col">
|
|
138
|
-
{compactRows.map((row) => (
|
|
139
|
-
<PropertyRow key={row.key} row={row} />
|
|
140
|
-
))}
|
|
47
|
+
const { facts, previewCount } = useAttributeFacts({ properties, relations: visibleRelations, jsonLd, include });
|
|
141
48
|
|
|
142
|
-
|
|
143
|
-
<>
|
|
144
|
-
{expanded && proseRows.map((row) => <PropertyRow key={row.key} row={row} />)}
|
|
145
|
-
<button
|
|
146
|
-
type="button"
|
|
147
|
-
onClick={() => setExpanded((value) => !value)}
|
|
148
|
-
className="nodrag flex items-center gap-1 pt-1.5 typography-tiny-medium text-muted-foreground hover:text-default-foreground"
|
|
149
|
-
>
|
|
150
|
-
{expanded ? (
|
|
151
|
-
<ChevronDown size={11} className="shrink-0" />
|
|
152
|
-
) : (
|
|
153
|
-
<ChevronRight size={11} className="shrink-0" />
|
|
154
|
-
)}
|
|
155
|
-
{expanded ? "Show less" : `Show ${proseRows.length} more`}
|
|
156
|
-
</button>
|
|
157
|
-
</>
|
|
158
|
-
)}
|
|
159
|
-
</div>
|
|
160
|
-
);
|
|
49
|
+
return <AttributeList facts={facts} previewCount={previewCount} showAll />;
|
|
161
50
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Check, ChevronDown, ChevronRight, type LucideIcon, X } from "lucide-react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { match, P } from "ts-pattern";
|
|
4
|
+
import type { PropertyDisplay } from "./node-property-display";
|
|
5
|
+
import { TruncateTooltip } from "#components/tooltip/index";
|
|
6
|
+
import { cn } from "#style";
|
|
7
|
+
|
|
8
|
+
function Tag(props: { children: string }) {
|
|
9
|
+
return (
|
|
10
|
+
<span className="rounded bg-faint px-1.5 py-[1px] typography-tiny text-default-foreground">{props.children}</span>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function PropertyValue(props: { display: PropertyDisplay }) {
|
|
15
|
+
return match(props.display)
|
|
16
|
+
.with({ kind: "tags" }, ({ values }) => (
|
|
17
|
+
<div className="flex flex-wrap gap-1">
|
|
18
|
+
{values.map((value) => (
|
|
19
|
+
<Tag key={value}>{value}</Tag>
|
|
20
|
+
))}
|
|
21
|
+
</div>
|
|
22
|
+
))
|
|
23
|
+
.with({ kind: "color" }, ({ hex }) => (
|
|
24
|
+
<span className="flex items-center gap-1.5">
|
|
25
|
+
<span
|
|
26
|
+
aria-hidden
|
|
27
|
+
className="size-3.5 shrink-0 rounded-sm border border-default-border"
|
|
28
|
+
style={{ backgroundColor: hex }}
|
|
29
|
+
/>
|
|
30
|
+
<span className="tabular-nums">{hex}</span>
|
|
31
|
+
</span>
|
|
32
|
+
))
|
|
33
|
+
.with({ kind: "ratio" }, ({ fraction, text }) => (
|
|
34
|
+
<span className="flex items-center gap-1.5">
|
|
35
|
+
<span aria-hidden className="h-1 w-12 shrink-0 overflow-hidden rounded-full bg-faint">
|
|
36
|
+
<span className="block h-full rounded-full bg-accent-foreground" style={{ width: `${fraction * 100}%` }} />
|
|
37
|
+
</span>
|
|
38
|
+
<span className="tabular-nums">{text}</span>
|
|
39
|
+
</span>
|
|
40
|
+
))
|
|
41
|
+
.with({ kind: "number" }, ({ text }) => <span className="tabular-nums">{text}</span>)
|
|
42
|
+
.with({ kind: "boolean" }, ({ value }) =>
|
|
43
|
+
value ? (
|
|
44
|
+
<span className="flex items-center gap-1">
|
|
45
|
+
<Check aria-hidden className="size-3.5 shrink-0 text-success-foreground" />
|
|
46
|
+
true
|
|
47
|
+
</span>
|
|
48
|
+
) : (
|
|
49
|
+
<span className="flex items-center gap-1">
|
|
50
|
+
<X aria-hidden className="size-3.5 shrink-0 text-muted-foreground" />
|
|
51
|
+
false
|
|
52
|
+
</span>
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
.with(P.union({ kind: "prose" }, { kind: "text" }), ({ text }) => <span>{text}</span>)
|
|
56
|
+
.exhaustive();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function NodePropertyList(props: { children: ReactNode }) {
|
|
60
|
+
return <dl className="flex flex-col">{props.children}</dl>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type NodePropertyRowProps = {
|
|
64
|
+
label: string;
|
|
65
|
+
icon?: LucideIcon;
|
|
66
|
+
labelPrefix?: ReactNode;
|
|
67
|
+
children: ReactNode;
|
|
68
|
+
fullWidth?: boolean;
|
|
69
|
+
overflow?: "wrap" | "truncate" | "clamp";
|
|
70
|
+
valueTitle?: string;
|
|
71
|
+
divided?: boolean;
|
|
72
|
+
wideLabel?: boolean;
|
|
73
|
+
emphasized?: boolean;
|
|
74
|
+
valueClassName?: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Render inside NodePropertyList so the dt/dd pairs sit in a dl.
|
|
78
|
+
export function NodePropertyRow(props: NodePropertyRowProps) {
|
|
79
|
+
const {
|
|
80
|
+
label,
|
|
81
|
+
icon: Icon,
|
|
82
|
+
labelPrefix,
|
|
83
|
+
children,
|
|
84
|
+
fullWidth = false,
|
|
85
|
+
overflow = "wrap",
|
|
86
|
+
valueTitle,
|
|
87
|
+
divided = true,
|
|
88
|
+
wideLabel = false,
|
|
89
|
+
emphasized = false,
|
|
90
|
+
valueClassName,
|
|
91
|
+
} = props;
|
|
92
|
+
|
|
93
|
+
const clipped = overflow !== "wrap";
|
|
94
|
+
const spansRow = fullWidth || overflow === "clamp";
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div
|
|
98
|
+
className={cn(
|
|
99
|
+
"grid items-baseline gap-x-3 gap-y-1",
|
|
100
|
+
// The wide label column grows with the card, never with the rows shown, so Show all never shifts it.
|
|
101
|
+
wideLabel ? "grid-cols-[minmax(8rem,40%)_minmax(0,1fr)]" : "grid-cols-[minmax(0,7rem)_minmax(0,1fr)]",
|
|
102
|
+
divided ? "border-t border-muted-border/50 py-1.5 first:border-t-0" : "py-1",
|
|
103
|
+
)}
|
|
104
|
+
>
|
|
105
|
+
<dt className={cn("flex min-w-0 items-baseline gap-1.5 text-muted-foreground", spansRow && "col-span-2")}>
|
|
106
|
+
{Icon && <Icon aria-hidden className="size-3 shrink-0 self-center" />}
|
|
107
|
+
{labelPrefix}
|
|
108
|
+
{overflow === "truncate" ? (
|
|
109
|
+
<TruncateTooltip text={label} className="typography-tiny min-w-0 truncate first-letter:uppercase" />
|
|
110
|
+
) : (
|
|
111
|
+
<span className="typography-tiny min-w-0 break-words first-letter:uppercase">{label}</span>
|
|
112
|
+
)}
|
|
113
|
+
</dt>
|
|
114
|
+
<dd
|
|
115
|
+
className={cn(
|
|
116
|
+
emphasized ? "typography-tiny-medium" : "typography-tiny",
|
|
117
|
+
"min-w-0 text-default-foreground",
|
|
118
|
+
overflow === "wrap" && "break-words",
|
|
119
|
+
spansRow && "col-span-2",
|
|
120
|
+
valueClassName,
|
|
121
|
+
)}
|
|
122
|
+
>
|
|
123
|
+
{clipped && valueTitle !== undefined ? (
|
|
124
|
+
<TruncateTooltip
|
|
125
|
+
text={valueTitle}
|
|
126
|
+
className={cn("block min-w-0", overflow === "truncate" ? "truncate" : "line-clamp-4 break-words")}
|
|
127
|
+
>
|
|
128
|
+
{children}
|
|
129
|
+
</TruncateTooltip>
|
|
130
|
+
) : (
|
|
131
|
+
children
|
|
132
|
+
)}
|
|
133
|
+
</dd>
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export type NodePropertyExpanderProps = { expanded: boolean; collapsedLabel: string; onToggle: () => void };
|
|
139
|
+
|
|
140
|
+
export function NodePropertyExpander(props: NodePropertyExpanderProps) {
|
|
141
|
+
const { expanded, collapsedLabel, onToggle } = props;
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<button
|
|
145
|
+
type="button"
|
|
146
|
+
onClick={onToggle}
|
|
147
|
+
aria-expanded={expanded}
|
|
148
|
+
className="nodrag flex items-center gap-1 pt-1.5 typography-tiny-medium text-muted-foreground hover:text-default-foreground"
|
|
149
|
+
>
|
|
150
|
+
{expanded ? <ChevronDown size={11} className="shrink-0" /> : <ChevronRight size={11} className="shrink-0" />}
|
|
151
|
+
{expanded ? "Show less" : collapsedLabel}
|
|
152
|
+
</button>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
@@ -135,6 +135,10 @@ function humanizeToken(value: string): string {
|
|
|
135
135
|
return value;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
export function isIsoDateTime(value: string): boolean {
|
|
139
|
+
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value);
|
|
140
|
+
}
|
|
141
|
+
|
|
138
142
|
export function formatDisplayValue(
|
|
139
143
|
value: string,
|
|
140
144
|
options?: {
|
|
@@ -162,7 +166,7 @@ export function formatDisplayValue(
|
|
|
162
166
|
|
|
163
167
|
if (combinedLabel.includes("currency")) return value;
|
|
164
168
|
|
|
165
|
-
if (
|
|
169
|
+
if (isIsoDateTime(value)) {
|
|
166
170
|
const date = parseISO(value);
|
|
167
171
|
|
|
168
172
|
if (isValid(date)) return format(date, "MMM d, yyyy");
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { isFullIri } from "@manta-eu/client/shared/rdf-namespaces";
|
|
2
|
+
import { isPresent } from "@manta-eu/lib/present";
|
|
3
|
+
import { Search } from "lucide-react";
|
|
4
|
+
import { useMemo, useState } from "react";
|
|
5
|
+
import type { PreviewFieldHints } from "../graph-list-utils";
|
|
6
|
+
import { NodePropertyExpander, NodePropertyList, NodePropertyRow, PropertyValue } from "../nodes/node-property-row";
|
|
7
|
+
import { ontologyTermIrisForProperties, withOntologyLabels } from "../ontology-labels";
|
|
8
|
+
import type { NodeProperty, NodeRelation } from "../types";
|
|
9
|
+
import { buildCardFacts, type CardFact, isAttributeSearchable, searchFacts, visibleChips } from "./card-facts";
|
|
10
|
+
import { EntityValueChip } from "./EntityValueChip";
|
|
11
|
+
import { ChannelIcon, channelDisplayName } from "#components/channel-icon/index";
|
|
12
|
+
import { useOntologyTerms } from "#hooks/use-ontology-terms";
|
|
13
|
+
import { cn } from "#style";
|
|
14
|
+
|
|
15
|
+
export type UseAttributeFactsArgs = {
|
|
16
|
+
properties: NodeProperty[];
|
|
17
|
+
relations?: NodeRelation[];
|
|
18
|
+
jsonLd: Record<string, unknown>;
|
|
19
|
+
title?: string;
|
|
20
|
+
type?: string;
|
|
21
|
+
prefixLabels?: boolean;
|
|
22
|
+
previewHints?: PreviewFieldHints;
|
|
23
|
+
// Applied after ontology labels resolve, so a filter can match on the label too. Keep it stable (useCallback).
|
|
24
|
+
include?: (property: NodeProperty) => boolean;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function useAttributeFacts(args: UseAttributeFactsArgs): { facts: CardFact[]; previewCount: number } {
|
|
28
|
+
const { properties, jsonLd, title, type, prefixLabels, previewHints, include } = args;
|
|
29
|
+
|
|
30
|
+
const relations = useMemo(() => args.relations ?? [], [args.relations]);
|
|
31
|
+
|
|
32
|
+
const terms = useOntologyTerms(
|
|
33
|
+
useMemo(
|
|
34
|
+
() => [
|
|
35
|
+
...ontologyTermIrisForProperties(properties),
|
|
36
|
+
...relations.flatMap((relation) => [relation.predicate, relation.target]).filter(isFullIri),
|
|
37
|
+
],
|
|
38
|
+
[properties, relations],
|
|
39
|
+
),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return useMemo(() => {
|
|
43
|
+
const labeled = withOntologyLabels(properties, terms);
|
|
44
|
+
|
|
45
|
+
return buildCardFacts({
|
|
46
|
+
properties: include ? labeled.filter(include) : labeled,
|
|
47
|
+
relations,
|
|
48
|
+
jsonLd,
|
|
49
|
+
terms,
|
|
50
|
+
title,
|
|
51
|
+
type,
|
|
52
|
+
prefixLabels,
|
|
53
|
+
previewHints,
|
|
54
|
+
});
|
|
55
|
+
}, [properties, terms, include, relations, jsonLd, title, type, prefixLabels, previewHints]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type EntityChipsProps = { iris: string[]; color?: string; expanded: boolean };
|
|
59
|
+
|
|
60
|
+
function EntityChips(props: EntityChipsProps) {
|
|
61
|
+
const { iris, color, expanded } = props;
|
|
62
|
+
|
|
63
|
+
const { shown, hiddenCount } = visibleChips(iris, expanded);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<span className="flex min-w-0 flex-wrap items-center gap-1">
|
|
67
|
+
{shown.map((iri) => (
|
|
68
|
+
<EntityValueChip key={iri} iri={iri} color={color} />
|
|
69
|
+
))}
|
|
70
|
+
{hiddenCount > 0 && <span className="typography-tiny text-muted-foreground">+{hiddenCount}</span>}
|
|
71
|
+
</span>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type AttributeListProps = {
|
|
76
|
+
facts: CardFact[];
|
|
77
|
+
previewCount: number;
|
|
78
|
+
searchClassName?: string;
|
|
79
|
+
bodyClassName?: string;
|
|
80
|
+
// Lists every attribute with no preview, so a card that promises the whole record keeps that promise.
|
|
81
|
+
showAll?: boolean;
|
|
82
|
+
defaultExpanded?: boolean;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export function AttributeList(props: AttributeListProps) {
|
|
86
|
+
const { facts, previewCount, searchClassName, bodyClassName, showAll = false, defaultExpanded = false } = props;
|
|
87
|
+
|
|
88
|
+
const [expandedByUser, setExpanded] = useState(defaultExpanded);
|
|
89
|
+
const expanded = showAll || expandedByUser;
|
|
90
|
+
const [query, setQuery] = useState("");
|
|
91
|
+
|
|
92
|
+
const searchable = isAttributeSearchable(expanded, facts.length);
|
|
93
|
+
const searching = searchable && isPresent(query.trim());
|
|
94
|
+
const matchingFacts = useMemo(
|
|
95
|
+
() => (searching ? searchFacts(facts, query.trim()) : facts),
|
|
96
|
+
[searching, facts, query],
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const visibleFacts = searching || expanded ? matchingFacts : facts.slice(0, previewCount);
|
|
100
|
+
const hasHiddenChips = visibleFacts.some((fact) => visibleChips(fact.entityIris, expanded).hiddenCount > 0);
|
|
101
|
+
const hiddenCount = facts.length - previewCount;
|
|
102
|
+
|
|
103
|
+
if (!isPresent(facts)) return null;
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div className="flex flex-col">
|
|
107
|
+
{searchable && (
|
|
108
|
+
<label
|
|
109
|
+
className={cn(
|
|
110
|
+
"nodrag nopan flex items-center gap-2 border-b border-muted-border py-2 text-muted-foreground",
|
|
111
|
+
searchClassName,
|
|
112
|
+
)}
|
|
113
|
+
>
|
|
114
|
+
<Search aria-hidden className="size-3.5 shrink-0" />
|
|
115
|
+
<input
|
|
116
|
+
type="search"
|
|
117
|
+
value={query}
|
|
118
|
+
onChange={(event) => setQuery(event.target.value)}
|
|
119
|
+
onKeyDown={(event) => event.stopPropagation()}
|
|
120
|
+
placeholder="Search attributes…"
|
|
121
|
+
aria-label="Search attributes"
|
|
122
|
+
className="typography-tiny min-w-0 flex-1 bg-transparent text-default-foreground outline-none placeholder:text-muted-foreground"
|
|
123
|
+
/>
|
|
124
|
+
</label>
|
|
125
|
+
)}
|
|
126
|
+
|
|
127
|
+
<div className={cn("flex flex-col", bodyClassName)}>
|
|
128
|
+
<NodePropertyList>
|
|
129
|
+
{visibleFacts.map(({ key, label, value, display, icon, entityIris, entityColor, highlighted, channel }) => (
|
|
130
|
+
<NodePropertyRow
|
|
131
|
+
key={key}
|
|
132
|
+
label={label}
|
|
133
|
+
icon={channel ? undefined : icon}
|
|
134
|
+
labelPrefix={
|
|
135
|
+
channel && (
|
|
136
|
+
<span
|
|
137
|
+
role="img"
|
|
138
|
+
aria-label={channelDisplayName[channel]}
|
|
139
|
+
title={channelDisplayName[channel]}
|
|
140
|
+
className="shrink-0 self-center"
|
|
141
|
+
>
|
|
142
|
+
<ChannelIcon channel={channel} iconClassName="size-3" />
|
|
143
|
+
</span>
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
overflow={isPresent(entityIris) ? "wrap" : expanded && display.kind === "prose" ? "clamp" : "truncate"}
|
|
147
|
+
valueTitle={value}
|
|
148
|
+
divided={false}
|
|
149
|
+
wideLabel
|
|
150
|
+
emphasized={highlighted}
|
|
151
|
+
valueClassName={cn("tabular-nums", highlighted && "text-primary")}
|
|
152
|
+
>
|
|
153
|
+
{isPresent(entityIris) ? (
|
|
154
|
+
<EntityChips iris={entityIris} color={entityColor} expanded={expanded} />
|
|
155
|
+
) : (
|
|
156
|
+
<PropertyValue display={display} />
|
|
157
|
+
)}
|
|
158
|
+
</NodePropertyRow>
|
|
159
|
+
))}
|
|
160
|
+
</NodePropertyList>
|
|
161
|
+
{searching && !isPresent(visibleFacts) && (
|
|
162
|
+
<span className="typography-tiny py-1 text-muted-foreground">No matching attributes.</span>
|
|
163
|
+
)}
|
|
164
|
+
{!showAll && (hiddenCount > 0 || hasHiddenChips || expanded) && (
|
|
165
|
+
<NodePropertyExpander
|
|
166
|
+
expanded={expanded}
|
|
167
|
+
collapsedLabel={hiddenCount > 0 ? `Show all ${facts.length}` : "Show all"}
|
|
168
|
+
onToggle={() => {
|
|
169
|
+
setExpanded((value) => !value);
|
|
170
|
+
setQuery("");
|
|
171
|
+
}}
|
|
172
|
+
/>
|
|
173
|
+
)}
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { stubNodeData } from "../stub-node";
|
|
3
|
+
import { InsideEntityPreview } from "./EntityValueChip";
|
|
4
|
+
import { GenericView } from "./GenericView";
|
|
5
|
+
import { getEntityView } from "./router";
|
|
6
|
+
import { useEntityNodes } from "#hooks/use-entity-nodes";
|
|
7
|
+
|
|
8
|
+
export type EntityPreviewProps = { iri: string };
|
|
9
|
+
|
|
10
|
+
// Loaded lazily from EntityValueChip: a static import would cycle through the node router back to GenericView.
|
|
11
|
+
export function EntityPreview(props: EntityPreviewProps) {
|
|
12
|
+
const { iri } = props;
|
|
13
|
+
|
|
14
|
+
const iris = useMemo(() => [iri], [iri]);
|
|
15
|
+
const { nodesByIri, isLoading } = useEntityNodes(iris);
|
|
16
|
+
|
|
17
|
+
if (isLoading) return <div className="typography-tiny px-3 py-2 text-muted-foreground">Loading…</div>;
|
|
18
|
+
|
|
19
|
+
const node = nodesByIri.get(iri);
|
|
20
|
+
const View = (node && getEntityView(node.data.type, node.data.actionShape)) ?? GenericView;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<InsideEntityPreview.Provider value={true}>
|
|
24
|
+
<View data={node?.data ?? stubNodeData(iri)} />
|
|
25
|
+
</InsideEntityPreview.Provider>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { humanizeEntityName } from "@manta-eu/client/shared/agent-mode";
|
|
2
|
+
import { iriLocalName } from "@manta-eu/client/shared/rdf-namespaces";
|
|
3
|
+
import { isPresent } from "@manta-eu/lib/present";
|
|
4
|
+
import { createContext, lazy, Suspense, useContext } from "react";
|
|
5
|
+
import { useEntityLink } from "../entity-link";
|
|
6
|
+
import { iriChannel } from "./card-facts";
|
|
7
|
+
import { ChannelIcon, channelDisplayName } from "#components/channel-icon/index";
|
|
8
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "#components/tooltip/index";
|
|
9
|
+
import { useInstanceLabel } from "#hooks/use-instance-labels";
|
|
10
|
+
import { useOntologyTerm } from "#hooks/use-ontology-terms";
|
|
11
|
+
import { cn } from "#style";
|
|
12
|
+
|
|
13
|
+
const EntityPreview = lazy(() => import("./EntityPreview").then((module) => ({ default: module.EntityPreview })));
|
|
14
|
+
|
|
15
|
+
// A preview already shows one entity card; its own chips skip the hover so previews never stack.
|
|
16
|
+
export const InsideEntityPreview = createContext(false);
|
|
17
|
+
|
|
18
|
+
// An ontology term (e.g. an enum concept) has a label but no record of its own to open or preview.
|
|
19
|
+
export function useEntityValueLabel(iri: string): { label: string; isOntologyTerm: boolean } {
|
|
20
|
+
const term = useOntologyTerm(iri);
|
|
21
|
+
const instanceLabel = useInstanceLabel(term?.label ? undefined : iri);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
label: term?.label ?? instanceLabel ?? humanizeEntityName(iriLocalName(iri)),
|
|
25
|
+
isOntologyTerm: isPresent(term?.label),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type EntityValueChipProps = { iri: string; color?: string };
|
|
30
|
+
|
|
31
|
+
export function EntityValueChip(props: EntityValueChipProps) {
|
|
32
|
+
const { iri, color } = props;
|
|
33
|
+
|
|
34
|
+
const { label, isOntologyTerm } = useEntityValueLabel(iri);
|
|
35
|
+
const EntityLink = useEntityLink();
|
|
36
|
+
const insidePreview = useContext(InsideEntityPreview);
|
|
37
|
+
const channel = iriChannel(iri);
|
|
38
|
+
|
|
39
|
+
if (isOntologyTerm) return <span className="min-w-0 truncate">{label}</span>;
|
|
40
|
+
|
|
41
|
+
const chipClassName = cn(
|
|
42
|
+
"nodrag inline-flex min-w-0 max-w-full items-center gap-1.5 rounded bg-faint px-1.5 py-px text-default-foreground",
|
|
43
|
+
EntityLink && "transition-colors hover:bg-accent hover:text-primary",
|
|
44
|
+
);
|
|
45
|
+
const content = (
|
|
46
|
+
<>
|
|
47
|
+
{channel ? (
|
|
48
|
+
<span role="img" aria-label={channelDisplayName[channel]} className="shrink-0">
|
|
49
|
+
<ChannelIcon channel={channel} iconClassName="size-3" />
|
|
50
|
+
</span>
|
|
51
|
+
) : (
|
|
52
|
+
color && <span aria-hidden className="size-1.5 shrink-0 rounded-full" style={{ backgroundColor: color }} />
|
|
53
|
+
)}
|
|
54
|
+
<span className="min-w-0 truncate">{label}</span>
|
|
55
|
+
</>
|
|
56
|
+
);
|
|
57
|
+
const chip = EntityLink ? (
|
|
58
|
+
<EntityLink iri={iri} className={chipClassName}>
|
|
59
|
+
{content}
|
|
60
|
+
</EntityLink>
|
|
61
|
+
) : (
|
|
62
|
+
<span className={chipClassName}>{content}</span>
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (insidePreview) return chip;
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Tooltip>
|
|
69
|
+
<TooltipTrigger asChild>
|
|
70
|
+
<span className="inline-flex min-w-0 max-w-full">{chip}</span>
|
|
71
|
+
</TooltipTrigger>
|
|
72
|
+
<TooltipContent
|
|
73
|
+
side="bottom"
|
|
74
|
+
align="start"
|
|
75
|
+
collisionPadding={8}
|
|
76
|
+
showArrow={false}
|
|
77
|
+
className="rounded-lg-v4 border border-default-border bg-card p-1.5 shadow-v4"
|
|
78
|
+
>
|
|
79
|
+
<Suspense fallback={null}>
|
|
80
|
+
<EntityPreview iri={iri} />
|
|
81
|
+
</Suspense>
|
|
82
|
+
</TooltipContent>
|
|
83
|
+
</Tooltip>
|
|
84
|
+
);
|
|
85
|
+
}
|