@cohesivesystems/presentation-react-shadcn 0.1.0-alpha.1
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/LICENSE +55 -0
- package/dist/document-workspace-surface-slot-renderers.d.ts +58 -0
- package/dist/document-workspace-surface-slot-renderers.d.ts.map +1 -0
- package/dist/document-workspace-surface-slot-renderers.js +206 -0
- package/dist/document-workspace-surface-slot-renderers.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/navigation-shell-frame-renderer-registry.d.ts +8 -0
- package/dist/navigation-shell-frame-renderer-registry.d.ts.map +1 -0
- package/dist/navigation-shell-frame-renderer-registry.js +2 -0
- package/dist/navigation-shell-frame-renderer-registry.js.map +1 -0
- package/dist/navigation-shell-slot-renderer-registry.d.ts +9 -0
- package/dist/navigation-shell-slot-renderer-registry.d.ts.map +1 -0
- package/dist/navigation-shell-slot-renderer-registry.js +2 -0
- package/dist/navigation-shell-slot-renderer-registry.js.map +1 -0
- package/dist/page-host-unavailable-renderer.d.ts +10 -0
- package/dist/page-host-unavailable-renderer.d.ts.map +1 -0
- package/dist/page-host-unavailable-renderer.js +27 -0
- package/dist/page-host-unavailable-renderer.js.map +1 -0
- package/dist/presentation-action-group.d.ts +99 -0
- package/dist/presentation-action-group.d.ts.map +1 -0
- package/dist/presentation-action-group.js +214 -0
- package/dist/presentation-action-group.js.map +1 -0
- package/dist/presentation-badge-target-interpreter.d.ts +26 -0
- package/dist/presentation-badge-target-interpreter.d.ts.map +1 -0
- package/dist/presentation-badge-target-interpreter.js +17 -0
- package/dist/presentation-badge-target-interpreter.js.map +1 -0
- package/dist/presentation-component-groups.d.ts +968 -0
- package/dist/presentation-component-groups.d.ts.map +1 -0
- package/dist/presentation-component-groups.js +505 -0
- package/dist/presentation-component-groups.js.map +1 -0
- package/dist/presentation-component-system-diagnostics.d.ts +27 -0
- package/dist/presentation-component-system-diagnostics.d.ts.map +1 -0
- package/dist/presentation-component-system-diagnostics.js +399 -0
- package/dist/presentation-component-system-diagnostics.js.map +1 -0
- package/dist/presentation-component-system.d.ts +31 -0
- package/dist/presentation-component-system.d.ts.map +1 -0
- package/dist/presentation-component-system.js +61 -0
- package/dist/presentation-component-system.js.map +1 -0
- package/dist/presentation-icon-diagnostics.d.ts +12 -0
- package/dist/presentation-icon-diagnostics.d.ts.map +1 -0
- package/dist/presentation-icon-diagnostics.js +17 -0
- package/dist/presentation-icon-diagnostics.js.map +1 -0
- package/dist/presentation-icon-registry.d.ts +28 -0
- package/dist/presentation-icon-registry.d.ts.map +1 -0
- package/dist/presentation-icon-registry.js +80 -0
- package/dist/presentation-icon-registry.js.map +1 -0
- package/dist/presentation-projection-trace-panel.d.ts +10 -0
- package/dist/presentation-projection-trace-panel.d.ts.map +1 -0
- package/dist/presentation-projection-trace-panel.js +48 -0
- package/dist/presentation-projection-trace-panel.js.map +1 -0
- package/dist/presentation-routed-surface-host.d.ts +19 -0
- package/dist/presentation-routed-surface-host.d.ts.map +1 -0
- package/dist/presentation-routed-surface-host.js +12 -0
- package/dist/presentation-routed-surface-host.js.map +1 -0
- package/dist/presentation-shadcn-component-system.d.ts +22 -0
- package/dist/presentation-shadcn-component-system.d.ts.map +1 -0
- package/dist/presentation-shadcn-component-system.js +2 -0
- package/dist/presentation-shadcn-component-system.js.map +1 -0
- package/dist/presentation-standard-renderers.d.ts +168 -0
- package/dist/presentation-standard-renderers.d.ts.map +1 -0
- package/dist/presentation-standard-renderers.js +440 -0
- package/dist/presentation-standard-renderers.js.map +1 -0
- package/dist/presentation-surface-renderer.d.ts +23 -0
- package/dist/presentation-surface-renderer.d.ts.map +1 -0
- package/dist/presentation-surface-renderer.js +119 -0
- package/dist/presentation-surface-renderer.js.map +1 -0
- package/dist/presentation-value-renderers.d.ts +10 -0
- package/dist/presentation-value-renderers.d.ts.map +1 -0
- package/dist/presentation-value-renderers.js +14 -0
- package/dist/presentation-value-renderers.js.map +1 -0
- package/dist/process-task-icons.d.ts +49 -0
- package/dist/process-task-icons.d.ts.map +1 -0
- package/dist/process-task-icons.js +154 -0
- package/dist/process-task-icons.js.map +1 -0
- package/dist/projected-activity-state.d.ts +17 -0
- package/dist/projected-activity-state.d.ts.map +1 -0
- package/dist/projected-activity-state.js +22 -0
- package/dist/projected-activity-state.js.map +1 -0
- package/dist/projected-collection-action-runtime-diagnostics.d.ts +4 -0
- package/dist/projected-collection-action-runtime-diagnostics.d.ts.map +1 -0
- package/dist/projected-collection-action-runtime-diagnostics.js +9 -0
- package/dist/projected-collection-action-runtime-diagnostics.js.map +1 -0
- package/dist/projected-collection-view.d.ts +90 -0
- package/dist/projected-collection-view.d.ts.map +1 -0
- package/dist/projected-collection-view.js +532 -0
- package/dist/projected-collection-view.js.map +1 -0
- package/dist/projected-document-action-status-notices.d.ts +15 -0
- package/dist/projected-document-action-status-notices.d.ts.map +1 -0
- package/dist/projected-document-action-status-notices.js +33 -0
- package/dist/projected-document-action-status-notices.js.map +1 -0
- package/dist/projected-document-badges.d.ts +24 -0
- package/dist/projected-document-badges.d.ts.map +1 -0
- package/dist/projected-document-badges.js +53 -0
- package/dist/projected-document-badges.js.map +1 -0
- package/dist/projected-document-editor-surface.d.ts +46 -0
- package/dist/projected-document-editor-surface.d.ts.map +1 -0
- package/dist/projected-document-editor-surface.js +399 -0
- package/dist/projected-document-editor-surface.js.map +1 -0
- package/dist/projected-document-workspace-notices.d.ts +24 -0
- package/dist/projected-document-workspace-notices.d.ts.map +1 -0
- package/dist/projected-document-workspace-notices.js +14 -0
- package/dist/projected-document-workspace-notices.js.map +1 -0
- package/dist/projected-document-workspace-prompt-child-view-registry.d.ts +25 -0
- package/dist/projected-document-workspace-prompt-child-view-registry.d.ts.map +1 -0
- package/dist/projected-document-workspace-prompt-child-view-registry.js +41 -0
- package/dist/projected-document-workspace-prompt-child-view-registry.js.map +1 -0
- package/dist/projected-document-workspace-prompt-child-view-renderers.d.ts +32 -0
- package/dist/projected-document-workspace-prompt-child-view-renderers.d.ts.map +1 -0
- package/dist/projected-document-workspace-prompt-child-view-renderers.js +77 -0
- package/dist/projected-document-workspace-prompt-child-view-renderers.js.map +1 -0
- package/dist/projected-document-workspace-shell.d.ts +100 -0
- package/dist/projected-document-workspace-shell.d.ts.map +1 -0
- package/dist/projected-document-workspace-shell.js +153 -0
- package/dist/projected-document-workspace-shell.js.map +1 -0
- package/dist/projected-document-workspace.d.ts +39 -0
- package/dist/projected-document-workspace.d.ts.map +1 -0
- package/dist/projected-document-workspace.js +63 -0
- package/dist/projected-document-workspace.js.map +1 -0
- package/dist/projected-field-value-rendering.d.ts +94 -0
- package/dist/projected-field-value-rendering.d.ts.map +1 -0
- package/dist/projected-field-value-rendering.js +377 -0
- package/dist/projected-field-value-rendering.js.map +1 -0
- package/dist/projected-inline-list.d.ts +20 -0
- package/dist/projected-inline-list.d.ts.map +1 -0
- package/dist/projected-inline-list.js +149 -0
- package/dist/projected-inline-list.js.map +1 -0
- package/dist/projected-input-form.d.ts +143 -0
- package/dist/projected-input-form.d.ts.map +1 -0
- package/dist/projected-input-form.js +612 -0
- package/dist/projected-input-form.js.map +1 -0
- package/dist/projected-metric-dashboard.d.ts +26 -0
- package/dist/projected-metric-dashboard.d.ts.map +1 -0
- package/dist/projected-metric-dashboard.js +20 -0
- package/dist/projected-metric-dashboard.js.map +1 -0
- package/dist/projected-metric-strip.d.ts +20 -0
- package/dist/projected-metric-strip.d.ts.map +1 -0
- package/dist/projected-metric-strip.js +131 -0
- package/dist/projected-metric-strip.js.map +1 -0
- package/dist/projected-navigation-shell-view-host.d.ts +34 -0
- package/dist/projected-navigation-shell-view-host.d.ts.map +1 -0
- package/dist/projected-navigation-shell-view-host.js +75 -0
- package/dist/projected-navigation-shell-view-host.js.map +1 -0
- package/dist/projected-navigation-shell.d.ts +33 -0
- package/dist/projected-navigation-shell.d.ts.map +1 -0
- package/dist/projected-navigation-shell.js +112 -0
- package/dist/projected-navigation-shell.js.map +1 -0
- package/dist/projected-presentation-badges.d.ts +34 -0
- package/dist/projected-presentation-badges.d.ts.map +1 -0
- package/dist/projected-presentation-badges.js +22 -0
- package/dist/projected-presentation-badges.js.map +1 -0
- package/dist/projected-presentation-diagnostics.d.ts +10 -0
- package/dist/projected-presentation-diagnostics.d.ts.map +1 -0
- package/dist/projected-presentation-diagnostics.js +40 -0
- package/dist/projected-presentation-diagnostics.js.map +1 -0
- package/dist/projected-presentation-flow-layer.d.ts +33 -0
- package/dist/projected-presentation-flow-layer.d.ts.map +1 -0
- package/dist/projected-presentation-flow-layer.js +71 -0
- package/dist/projected-presentation-flow-layer.js.map +1 -0
- package/dist/projected-process-task-notices.d.ts +37 -0
- package/dist/projected-process-task-notices.d.ts.map +1 -0
- package/dist/projected-process-task-notices.js +225 -0
- package/dist/projected-process-task-notices.js.map +1 -0
- package/dist/projected-prompt-child-view-renderer.d.ts +101 -0
- package/dist/projected-prompt-child-view-renderer.d.ts.map +1 -0
- package/dist/projected-prompt-child-view-renderer.js +442 -0
- package/dist/projected-prompt-child-view-renderer.js.map +1 -0
- package/dist/projected-prompt-document-preview.d.ts +39 -0
- package/dist/projected-prompt-document-preview.d.ts.map +1 -0
- package/dist/projected-prompt-document-preview.js +50 -0
- package/dist/projected-prompt-document-preview.js.map +1 -0
- package/dist/projected-prompt-view.d.ts +35 -0
- package/dist/projected-prompt-view.d.ts.map +1 -0
- package/dist/projected-prompt-view.js +292 -0
- package/dist/projected-prompt-view.js.map +1 -0
- package/dist/projected-query-form.d.ts +56 -0
- package/dist/projected-query-form.d.ts.map +1 -0
- package/dist/projected-query-form.js +100 -0
- package/dist/projected-query-form.js.map +1 -0
- package/dist/projected-record-details.d.ts +20 -0
- package/dist/projected-record-details.d.ts.map +1 -0
- package/dist/projected-record-details.js +60 -0
- package/dist/projected-record-details.js.map +1 -0
- package/dist/projected-routed-surface-runtime.d.ts +86 -0
- package/dist/projected-routed-surface-runtime.d.ts.map +1 -0
- package/dist/projected-routed-surface-runtime.js +332 -0
- package/dist/projected-routed-surface-runtime.js.map +1 -0
- package/dist/projected-tabs-view.d.ts +21 -0
- package/dist/projected-tabs-view.d.ts.map +1 -0
- package/dist/projected-tabs-view.js +140 -0
- package/dist/projected-tabs-view.js.map +1 -0
- package/dist/projected-view-chrome.d.ts +15 -0
- package/dist/projected-view-chrome.d.ts.map +1 -0
- package/dist/projected-view-chrome.js +67 -0
- package/dist/projected-view-chrome.js.map +1 -0
- package/dist/projected-view-surface.d.ts +34 -0
- package/dist/projected-view-surface.d.ts.map +1 -0
- package/dist/projected-view-surface.js +40 -0
- package/dist/projected-view-surface.js.map +1 -0
- package/dist/standard-collection-chrome-slot-renderers.d.ts +26 -0
- package/dist/standard-collection-chrome-slot-renderers.d.ts.map +1 -0
- package/dist/standard-collection-chrome-slot-renderers.js +250 -0
- package/dist/standard-collection-chrome-slot-renderers.js.map +1 -0
- package/dist/standard-view-chrome-slot-renderers.d.ts +57 -0
- package/dist/standard-view-chrome-slot-renderers.d.ts.map +1 -0
- package/dist/standard-view-chrome-slot-renderers.js +614 -0
- package/dist/standard-view-chrome-slot-renderers.js.map +1 -0
- package/package.json +68 -0
- package/src/document-workspace-surface-slot-renderers.tsx +395 -0
- package/src/index.ts +53 -0
- package/src/navigation-shell-frame-renderer-registry.ts +29 -0
- package/src/navigation-shell-slot-renderer-registry.ts +46 -0
- package/src/page-host-unavailable-renderer.tsx +79 -0
- package/src/presentation-action-group.tsx +475 -0
- package/src/presentation-badge-target-interpreter.ts +54 -0
- package/src/presentation-component-groups.ts +2505 -0
- package/src/presentation-component-system-diagnostics.ts +848 -0
- package/src/presentation-component-system.ts +365 -0
- package/src/presentation-icon-diagnostics.ts +53 -0
- package/src/presentation-icon-registry.tsx +181 -0
- package/src/presentation-projection-trace-panel.tsx +140 -0
- package/src/presentation-routed-surface-host.tsx +48 -0
- package/src/presentation-shadcn-component-system.ts +102 -0
- package/src/presentation-standard-renderers.tsx +1290 -0
- package/src/presentation-surface-renderer.tsx +263 -0
- package/src/presentation-value-renderers.tsx +40 -0
- package/src/process-task-icons.tsx +258 -0
- package/src/projected-activity-state.tsx +63 -0
- package/src/projected-collection-action-runtime-diagnostics.ts +22 -0
- package/src/projected-collection-view.tsx +1273 -0
- package/src/projected-document-action-status-notices.tsx +66 -0
- package/src/projected-document-badges.tsx +134 -0
- package/src/projected-document-editor-surface.tsx +808 -0
- package/src/projected-document-workspace-notices.tsx +77 -0
- package/src/projected-document-workspace-prompt-child-view-registry.ts +99 -0
- package/src/projected-document-workspace-prompt-child-view-renderers.tsx +205 -0
- package/src/projected-document-workspace-shell.tsx +427 -0
- package/src/projected-document-workspace.tsx +216 -0
- package/src/projected-field-value-rendering.tsx +704 -0
- package/src/projected-inline-list.tsx +434 -0
- package/src/projected-input-form.tsx +1218 -0
- package/src/projected-metric-dashboard.tsx +106 -0
- package/src/projected-metric-strip.tsx +244 -0
- package/src/projected-navigation-shell-view-host.tsx +177 -0
- package/src/projected-navigation-shell.tsx +219 -0
- package/src/projected-presentation-badges.tsx +95 -0
- package/src/projected-presentation-diagnostics.tsx +120 -0
- package/src/projected-presentation-flow-layer.tsx +186 -0
- package/src/projected-process-task-notices.tsx +501 -0
- package/src/projected-prompt-child-view-renderer.tsx +698 -0
- package/src/projected-prompt-document-preview.tsx +197 -0
- package/src/projected-prompt-view.tsx +592 -0
- package/src/projected-query-form.tsx +286 -0
- package/src/projected-record-details.tsx +133 -0
- package/src/projected-routed-surface-runtime.tsx +713 -0
- package/src/projected-tabs-view.tsx +308 -0
- package/src/projected-view-chrome.tsx +148 -0
- package/src/projected-view-surface.tsx +131 -0
- package/src/standard-collection-chrome-slot-renderers.tsx +391 -0
- package/src/standard-view-chrome-slot-renderers.tsx +1064 -0
|
@@ -0,0 +1,1273 @@
|
|
|
1
|
+
import type { ColumnDef } from '@tanstack/react-table'
|
|
2
|
+
import type {
|
|
3
|
+
PresentationCollectionDetailLayoutMode,
|
|
4
|
+
} from './presentation-component-groups'
|
|
5
|
+
import { Fragment, useCallback, useEffect, useMemo, type ReactNode } from 'react'
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
collectionChromeIconIds,
|
|
9
|
+
createPresentationTestAttributes,
|
|
10
|
+
defaultPresentationComponentSet,
|
|
11
|
+
resolveCollectionChromeIconSubjects,
|
|
12
|
+
} from '@cohesivesystems/presentation-core'
|
|
13
|
+
import {
|
|
14
|
+
findPresentationView,
|
|
15
|
+
isCollectionChromeSlotKind,
|
|
16
|
+
isCollectionChromeSlotPlacement,
|
|
17
|
+
isCollectionChromeSlotPlacementValue,
|
|
18
|
+
projectPresentationActionRuntimeRegistry,
|
|
19
|
+
projectProjectedCollectionActionRuntimeBindings,
|
|
20
|
+
type CollectionChromeSlotDefinition,
|
|
21
|
+
type CollectionChromeSlotPlacement,
|
|
22
|
+
type FieldPresentationDefinition,
|
|
23
|
+
type NavigationRouteParameters,
|
|
24
|
+
type PresentationActionRuntimeBinding,
|
|
25
|
+
type ViewDefinition,
|
|
26
|
+
} from '@cohesivesystems/presentation-core'
|
|
27
|
+
import {
|
|
28
|
+
projectProjectedCollectionActionRuntimeDiagnostics,
|
|
29
|
+
} from './projected-collection-action-runtime-diagnostics'
|
|
30
|
+
import {
|
|
31
|
+
projectPresentationIconDiagnostics,
|
|
32
|
+
} from './presentation-icon-diagnostics'
|
|
33
|
+
import type {
|
|
34
|
+
PresentationShadcnComponentSystem as PresentationComponentSystem,
|
|
35
|
+
} from './presentation-shadcn-component-system'
|
|
36
|
+
import {
|
|
37
|
+
useCollectionSelectionState,
|
|
38
|
+
usePresentationModule,
|
|
39
|
+
usePresentationNavigationRuntime,
|
|
40
|
+
useRegisterPresentationProjectionDiagnostics,
|
|
41
|
+
} from '@cohesivesystems/presentation-react'
|
|
42
|
+
import {
|
|
43
|
+
renderPresentationIcon,
|
|
44
|
+
} from './presentation-icon-registry'
|
|
45
|
+
import {
|
|
46
|
+
renderProjectedFieldValue,
|
|
47
|
+
} from './projected-field-value-rendering'
|
|
48
|
+
import {
|
|
49
|
+
ProjectedRecordDetails,
|
|
50
|
+
type ProjectedRecordFieldRenderContext,
|
|
51
|
+
} from './projected-record-details'
|
|
52
|
+
import {
|
|
53
|
+
createProjectedCollectionRuntime,
|
|
54
|
+
isCollectionSelectionEnabled,
|
|
55
|
+
resolveProjectedCollectionSelectionMode,
|
|
56
|
+
resolveProjectedCollectionSelectionStateId,
|
|
57
|
+
type ProjectedCollectionActionExecutionContext,
|
|
58
|
+
type ProjectedCollectionActionRuntimeRegistry,
|
|
59
|
+
type ProjectedCollectionPaginationInput,
|
|
60
|
+
type ProjectedActionPlacementLike,
|
|
61
|
+
type ProjectedCollectionRuntime,
|
|
62
|
+
type ResolvedProjectedCollectionRowAction,
|
|
63
|
+
type ResolvedProjectedCollectionSelectionAction,
|
|
64
|
+
} from '@cohesivesystems/presentation-core'
|
|
65
|
+
import {
|
|
66
|
+
collectionChromeSlotKinds,
|
|
67
|
+
collectionChromeSlotPlacements,
|
|
68
|
+
} from '@cohesivesystems/presentation-contracts'
|
|
69
|
+
|
|
70
|
+
export type {
|
|
71
|
+
ProjectedActionPlacementLike,
|
|
72
|
+
ProjectedRowActionExecutionContext,
|
|
73
|
+
ProjectedSelectionActionExecutionContext,
|
|
74
|
+
} from '@cohesivesystems/presentation-core'
|
|
75
|
+
|
|
76
|
+
type ProjectedFieldDefinitionLike = FieldPresentationDefinition
|
|
77
|
+
type ProjectedViewDefinitionLike = ViewDefinition
|
|
78
|
+
type ProjectedModuleDefinitionLike = NonNullable<ReturnType<typeof usePresentationModule>>
|
|
79
|
+
|
|
80
|
+
export interface ProjectedFieldRenderContext<TData extends object> {
|
|
81
|
+
readonly field: ProjectedFieldDefinitionLike
|
|
82
|
+
readonly row: TData
|
|
83
|
+
readonly value: unknown
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface ProjectedCollectionViewProps<TData extends object> {
|
|
87
|
+
readonly actionRuntimeBindings?: readonly PresentationActionRuntimeBinding<
|
|
88
|
+
ProjectedCollectionActionExecutionContext<TData>,
|
|
89
|
+
ReactNode
|
|
90
|
+
>[]
|
|
91
|
+
readonly createHref?: (
|
|
92
|
+
routeId: string,
|
|
93
|
+
parameters?: NavigationRouteParameters,
|
|
94
|
+
) => string | null
|
|
95
|
+
readonly componentSet?: string
|
|
96
|
+
readonly data: readonly TData[]
|
|
97
|
+
readonly emptyMessage: string
|
|
98
|
+
readonly componentSystem: PresentationComponentSystem
|
|
99
|
+
readonly fieldRenderers?: Record<
|
|
100
|
+
string,
|
|
101
|
+
(context: ProjectedFieldRenderContext<TData>) => ReactNode
|
|
102
|
+
>
|
|
103
|
+
readonly footer?:
|
|
104
|
+
| ReactNode
|
|
105
|
+
| ((runtime: ProjectedCollectionRuntime<TData>) => ReactNode)
|
|
106
|
+
readonly detailFieldRenderers?: Record<
|
|
107
|
+
string,
|
|
108
|
+
(context: ProjectedCollectionDetailFieldRenderContext<TData>) => ReactNode
|
|
109
|
+
>
|
|
110
|
+
readonly getRowLabel?: (row: TData) => string
|
|
111
|
+
readonly navigateHref?: (href: string) => void
|
|
112
|
+
readonly pagination?: ProjectedCollectionPaginationInput | null
|
|
113
|
+
readonly renderActionIcon?: (placement: ProjectedActionPlacementLike) => ReactNode
|
|
114
|
+
readonly renderChromeSlot?: (
|
|
115
|
+
context: ProjectedCollectionChromeSlotRenderContext<TData>,
|
|
116
|
+
) => ReactNode
|
|
117
|
+
readonly viewId: string
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface ProjectedCollectionChromeSlotRenderContext<TData extends object> {
|
|
121
|
+
readonly actionRuntimes?: ProjectedCollectionActionRuntimeRegistry<TData>
|
|
122
|
+
readonly canExecuteAction: (
|
|
123
|
+
context: ProjectedCollectionActionExecutionContext<TData>,
|
|
124
|
+
) => boolean
|
|
125
|
+
readonly collectionRuntime: ProjectedCollectionRuntime<TData>
|
|
126
|
+
readonly componentSet: string
|
|
127
|
+
readonly componentSystem: PresentationComponentSystem
|
|
128
|
+
readonly createHref?: (
|
|
129
|
+
routeId: string,
|
|
130
|
+
parameters?: NavigationRouteParameters,
|
|
131
|
+
) => string | null
|
|
132
|
+
readonly executeAction: (
|
|
133
|
+
context: ProjectedCollectionActionExecutionContext<TData>,
|
|
134
|
+
) => Promise<void> | void
|
|
135
|
+
readonly module?: ProjectedModuleDefinitionLike | null
|
|
136
|
+
readonly navigateHref?: (href: string) => void
|
|
137
|
+
readonly renderActionIcon?: (placement: ProjectedActionPlacementLike) => ReactNode
|
|
138
|
+
readonly renderBodySlot?: (slot: CollectionChromeSlotDefinition) => ReactNode
|
|
139
|
+
readonly renderDetailSlot?: (slot: CollectionChromeSlotDefinition) => ReactNode
|
|
140
|
+
readonly renderQueryFormSlot?: (slot: CollectionChromeSlotDefinition) => ReactNode
|
|
141
|
+
readonly renderRowActionsSlot?: (
|
|
142
|
+
slot: CollectionChromeSlotDefinition,
|
|
143
|
+
row: TData,
|
|
144
|
+
rowLabel: string | null,
|
|
145
|
+
) => ReactNode
|
|
146
|
+
readonly renderSummarySlot?: (slot: CollectionChromeSlotDefinition) => ReactNode
|
|
147
|
+
readonly row?: TData
|
|
148
|
+
readonly rowLabel?: string | null
|
|
149
|
+
readonly slot: CollectionChromeSlotDefinition
|
|
150
|
+
readonly viewId: string
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function ProjectedCollectionView<TData extends object>({
|
|
154
|
+
actionRuntimeBindings,
|
|
155
|
+
componentSet = defaultPresentationComponentSet,
|
|
156
|
+
componentSystem,
|
|
157
|
+
createHref: createHrefOverride,
|
|
158
|
+
data,
|
|
159
|
+
detailFieldRenderers,
|
|
160
|
+
emptyMessage,
|
|
161
|
+
fieldRenderers,
|
|
162
|
+
footer,
|
|
163
|
+
getRowLabel,
|
|
164
|
+
navigateHref: navigateHrefOverride,
|
|
165
|
+
pagination,
|
|
166
|
+
renderActionIcon,
|
|
167
|
+
renderChromeSlot,
|
|
168
|
+
viewId,
|
|
169
|
+
}: ProjectedCollectionViewProps<TData>) {
|
|
170
|
+
const module = usePresentationModule()
|
|
171
|
+
const navigation = usePresentationNavigationRuntime()
|
|
172
|
+
const view = findPresentationView<ProjectedViewDefinitionLike>(module, viewId)
|
|
173
|
+
const createHref = createHrefOverride ?? navigation.createHref
|
|
174
|
+
const navigateHref = navigateHrefOverride ?? navigation.navigateHref
|
|
175
|
+
const selectionMode = resolveProjectedCollectionSelectionMode(view?.Collection)
|
|
176
|
+
const selectionStateId = resolveProjectedCollectionSelectionStateId(view?.Collection)
|
|
177
|
+
const selectionState = useCollectionSelectionState(
|
|
178
|
+
isCollectionSelectionEnabled(selectionMode)
|
|
179
|
+
? selectionStateId
|
|
180
|
+
: null,
|
|
181
|
+
)
|
|
182
|
+
const collectionRuntime = useMemo(
|
|
183
|
+
() => createProjectedCollectionRuntime({
|
|
184
|
+
createHref,
|
|
185
|
+
data,
|
|
186
|
+
module,
|
|
187
|
+
pagination,
|
|
188
|
+
selectionState,
|
|
189
|
+
view,
|
|
190
|
+
}),
|
|
191
|
+
[createHref, data, module, pagination, selectionState, view],
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
const getTableRowId = collectionRuntime.getRowId
|
|
195
|
+
const isRowSelected = collectionRuntime.selection.isRowSelected
|
|
196
|
+
const activatedRowAction = collectionRuntime.actions.activatedRowAction
|
|
197
|
+
const collectionActionIds = useMemo(
|
|
198
|
+
() =>
|
|
199
|
+
[
|
|
200
|
+
...collectionRuntime.actions.rowActions.map(({ action }) => action.Id),
|
|
201
|
+
...collectionRuntime.actions.selectionActions.map(({ action }) => action.Id),
|
|
202
|
+
],
|
|
203
|
+
[
|
|
204
|
+
collectionRuntime.actions.rowActions,
|
|
205
|
+
collectionRuntime.actions.selectionActions,
|
|
206
|
+
],
|
|
207
|
+
)
|
|
208
|
+
const actionRuntimes = useMemo(
|
|
209
|
+
() =>
|
|
210
|
+
projectPresentationActionRuntimeRegistry<
|
|
211
|
+
ProjectedCollectionActionExecutionContext<TData>,
|
|
212
|
+
ReactNode
|
|
213
|
+
>({
|
|
214
|
+
actionIds: collectionActionIds,
|
|
215
|
+
module,
|
|
216
|
+
projections: [
|
|
217
|
+
...(actionRuntimeBindings ?? []),
|
|
218
|
+
...projectProjectedCollectionActionRuntimeBindings<TData, ReactNode>({
|
|
219
|
+
navigateHref,
|
|
220
|
+
}),
|
|
221
|
+
],
|
|
222
|
+
}) as ProjectedCollectionActionRuntimeRegistry<TData>,
|
|
223
|
+
[
|
|
224
|
+
actionRuntimeBindings,
|
|
225
|
+
collectionActionIds,
|
|
226
|
+
module,
|
|
227
|
+
navigateHref,
|
|
228
|
+
],
|
|
229
|
+
)
|
|
230
|
+
const actionRuntimeDiagnostics = useMemo(
|
|
231
|
+
() =>
|
|
232
|
+
[
|
|
233
|
+
...projectProjectedCollectionActionRuntimeDiagnostics({
|
|
234
|
+
actionRuntimes,
|
|
235
|
+
collectionRuntime,
|
|
236
|
+
module,
|
|
237
|
+
view,
|
|
238
|
+
}),
|
|
239
|
+
...projectPresentationIconDiagnostics({
|
|
240
|
+
icons: resolveCollectionChromeIconSubjects(collectionRuntime.chrome),
|
|
241
|
+
module,
|
|
242
|
+
source: `projected-collection-chrome-icons.${viewId}`,
|
|
243
|
+
surfaceId: view?.Id ?? viewId,
|
|
244
|
+
surfaceName: view?.Name ?? viewId,
|
|
245
|
+
}),
|
|
246
|
+
],
|
|
247
|
+
[
|
|
248
|
+
actionRuntimes,
|
|
249
|
+
collectionRuntime,
|
|
250
|
+
module,
|
|
251
|
+
view,
|
|
252
|
+
viewId,
|
|
253
|
+
],
|
|
254
|
+
)
|
|
255
|
+
useRegisterPresentationProjectionDiagnostics(
|
|
256
|
+
`projected-collection-action-runtime.${viewId}`,
|
|
257
|
+
actionRuntimeDiagnostics,
|
|
258
|
+
)
|
|
259
|
+
const bodySlot = collectionRuntime.chrome.bodySlot
|
|
260
|
+
const resolvedFooter =
|
|
261
|
+
typeof footer === 'function' ? footer(collectionRuntime) : footer
|
|
262
|
+
useEffect(() => {
|
|
263
|
+
collectionRuntime.selection.pruneToVisibleRows()
|
|
264
|
+
}, [collectionRuntime])
|
|
265
|
+
const canExecuteAction = useCallback(
|
|
266
|
+
(context: ProjectedCollectionActionExecutionContext<TData>) =>
|
|
267
|
+
collectionRuntime.actions.canInvokeAction(context, actionRuntimes),
|
|
268
|
+
[actionRuntimes, collectionRuntime],
|
|
269
|
+
)
|
|
270
|
+
const executeAction = useCallback(
|
|
271
|
+
(context: ProjectedCollectionActionExecutionContext<TData>) =>
|
|
272
|
+
collectionRuntime.actions.invokeAction(context, actionRuntimes),
|
|
273
|
+
[actionRuntimes, collectionRuntime],
|
|
274
|
+
)
|
|
275
|
+
const shouldSelectOnRowClick = Boolean(
|
|
276
|
+
bodySlot?.SelectOnRowClick && collectionRuntime.selection.state,
|
|
277
|
+
)
|
|
278
|
+
const handleRowClick = useCallback(
|
|
279
|
+
(row: TData, index: number) => {
|
|
280
|
+
if (shouldSelectOnRowClick) {
|
|
281
|
+
collectionRuntime.selection.activateRow(row, index)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (!bodySlot?.ActivateOnRowClick || !activatedRowAction) {
|
|
285
|
+
return
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const actionContext = collectionRuntime.actions.createRowActionContext(
|
|
289
|
+
activatedRowAction,
|
|
290
|
+
row,
|
|
291
|
+
)
|
|
292
|
+
if (canExecuteAction(actionContext)) {
|
|
293
|
+
void executeAction(actionContext)
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
[
|
|
297
|
+
activatedRowAction,
|
|
298
|
+
bodySlot?.ActivateOnRowClick,
|
|
299
|
+
canExecuteAction,
|
|
300
|
+
collectionRuntime,
|
|
301
|
+
executeAction,
|
|
302
|
+
shouldSelectOnRowClick,
|
|
303
|
+
],
|
|
304
|
+
)
|
|
305
|
+
const shouldHandleRowClick = Boolean(
|
|
306
|
+
shouldSelectOnRowClick ||
|
|
307
|
+
(bodySlot?.ActivateOnRowClick && activatedRowAction),
|
|
308
|
+
)
|
|
309
|
+
const renderDetailSlot = useCallback(
|
|
310
|
+
(slot: CollectionChromeSlotDefinition) => createCollectionDetailProjection({
|
|
311
|
+
componentSystem,
|
|
312
|
+
componentSet,
|
|
313
|
+
createHref,
|
|
314
|
+
data: collectionRuntime.detail.data,
|
|
315
|
+
emptyMessage: collectionRuntime.detail.emptyMessage,
|
|
316
|
+
fieldRenderers: detailFieldRenderers,
|
|
317
|
+
navigateHref,
|
|
318
|
+
onClose: collectionRuntime.selection.state
|
|
319
|
+
? () => collectionRuntime.selection.state?.clearSelection()
|
|
320
|
+
: undefined,
|
|
321
|
+
title: collectionRuntime.detail.title,
|
|
322
|
+
viewId: slot.DetailViewId ?? collectionRuntime.detail.viewId,
|
|
323
|
+
}),
|
|
324
|
+
[
|
|
325
|
+
collectionRuntime.detail.data,
|
|
326
|
+
collectionRuntime.detail.emptyMessage,
|
|
327
|
+
collectionRuntime.detail.title,
|
|
328
|
+
collectionRuntime.detail.viewId,
|
|
329
|
+
collectionRuntime.selection.state,
|
|
330
|
+
componentSet,
|
|
331
|
+
componentSystem,
|
|
332
|
+
createHref,
|
|
333
|
+
detailFieldRenderers,
|
|
334
|
+
navigateHref,
|
|
335
|
+
],
|
|
336
|
+
)
|
|
337
|
+
const renderRowActionsSlot = useCallback(
|
|
338
|
+
(slot: CollectionChromeSlotDefinition, row: TData, rowLabel: string | null) => (
|
|
339
|
+
<ProjectedCollectionRowActions
|
|
340
|
+
canExecuteAction={canExecuteAction}
|
|
341
|
+
collectionRuntime={collectionRuntime}
|
|
342
|
+
componentSet={componentSet}
|
|
343
|
+
componentSystem={componentSystem}
|
|
344
|
+
executeAction={executeAction}
|
|
345
|
+
module={module}
|
|
346
|
+
renderActionIcon={renderActionIcon}
|
|
347
|
+
row={row}
|
|
348
|
+
rowLabel={rowLabel}
|
|
349
|
+
slot={slot}
|
|
350
|
+
viewId={viewId}
|
|
351
|
+
/>
|
|
352
|
+
),
|
|
353
|
+
[
|
|
354
|
+
canExecuteAction,
|
|
355
|
+
collectionRuntime,
|
|
356
|
+
componentSet,
|
|
357
|
+
componentSystem,
|
|
358
|
+
executeAction,
|
|
359
|
+
module,
|
|
360
|
+
renderActionIcon,
|
|
361
|
+
viewId,
|
|
362
|
+
],
|
|
363
|
+
)
|
|
364
|
+
const columns = useMemo(
|
|
365
|
+
() =>
|
|
366
|
+
view
|
|
367
|
+
? createProjectedColumns({
|
|
368
|
+
actionRuntimes,
|
|
369
|
+
canExecuteAction,
|
|
370
|
+
collectionRuntime,
|
|
371
|
+
componentSet,
|
|
372
|
+
componentSystem,
|
|
373
|
+
createHref,
|
|
374
|
+
executeAction,
|
|
375
|
+
fieldRenderers,
|
|
376
|
+
getRowLabel,
|
|
377
|
+
module,
|
|
378
|
+
navigateHref,
|
|
379
|
+
renderChromeSlot,
|
|
380
|
+
renderActionIcon,
|
|
381
|
+
renderRowActionsSlot,
|
|
382
|
+
viewId,
|
|
383
|
+
})
|
|
384
|
+
: [],
|
|
385
|
+
[
|
|
386
|
+
actionRuntimes,
|
|
387
|
+
canExecuteAction,
|
|
388
|
+
componentSystem,
|
|
389
|
+
componentSet,
|
|
390
|
+
createHref,
|
|
391
|
+
executeAction,
|
|
392
|
+
fieldRenderers,
|
|
393
|
+
getRowLabel,
|
|
394
|
+
collectionRuntime,
|
|
395
|
+
module,
|
|
396
|
+
navigateHref,
|
|
397
|
+
renderChromeSlot,
|
|
398
|
+
renderActionIcon,
|
|
399
|
+
renderRowActionsSlot,
|
|
400
|
+
view,
|
|
401
|
+
viewId,
|
|
402
|
+
],
|
|
403
|
+
)
|
|
404
|
+
const chromeSlotContext = {
|
|
405
|
+
actionRuntimes,
|
|
406
|
+
canExecuteAction,
|
|
407
|
+
collectionRuntime,
|
|
408
|
+
componentSet,
|
|
409
|
+
componentSystem,
|
|
410
|
+
createHref,
|
|
411
|
+
executeAction,
|
|
412
|
+
module,
|
|
413
|
+
navigateHref,
|
|
414
|
+
renderActionIcon,
|
|
415
|
+
renderDetailSlot,
|
|
416
|
+
renderRowActionsSlot,
|
|
417
|
+
viewId,
|
|
418
|
+
} satisfies Omit<ProjectedCollectionChromeSlotRenderContext<TData>, 'slot'>
|
|
419
|
+
const leadingChrome = renderCollectionChromeSlots({
|
|
420
|
+
context: chromeSlotContext,
|
|
421
|
+
placements: [
|
|
422
|
+
collectionChromeSlotPlacements.header,
|
|
423
|
+
collectionChromeSlotPlacements.toolbar,
|
|
424
|
+
collectionChromeSlotPlacements.above,
|
|
425
|
+
],
|
|
426
|
+
renderChromeSlot,
|
|
427
|
+
})
|
|
428
|
+
const footerChrome = renderCollectionChromeSlots({
|
|
429
|
+
context: chromeSlotContext,
|
|
430
|
+
placements: [collectionChromeSlotPlacements.footer],
|
|
431
|
+
renderChromeSlot,
|
|
432
|
+
})
|
|
433
|
+
const renderBodySlot = () => (
|
|
434
|
+
<ProjectedCollectionBody
|
|
435
|
+
columns={columns}
|
|
436
|
+
componentSystem={componentSystem}
|
|
437
|
+
data={data}
|
|
438
|
+
emptyMessage={emptyMessage}
|
|
439
|
+
footer={footerChrome.length > 0 ? footerChrome : resolvedFooter}
|
|
440
|
+
getRowId={getTableRowId}
|
|
441
|
+
isRowSelected={isRowSelected}
|
|
442
|
+
onRowClick={shouldHandleRowClick ? handleRowClick : undefined}
|
|
443
|
+
viewId={viewId}
|
|
444
|
+
/>
|
|
445
|
+
)
|
|
446
|
+
const bodyChromeSlotContext = {
|
|
447
|
+
...chromeSlotContext,
|
|
448
|
+
renderBodySlot,
|
|
449
|
+
} satisfies Omit<ProjectedCollectionChromeSlotRenderContext<TData>, 'slot'>
|
|
450
|
+
const bodyChrome = renderCollectionChromeSlots({
|
|
451
|
+
context: bodyChromeSlotContext,
|
|
452
|
+
includeSlot: (slot) => isCollectionChromeSlotKind(
|
|
453
|
+
slot,
|
|
454
|
+
collectionChromeSlotKinds.body,
|
|
455
|
+
),
|
|
456
|
+
placements: [collectionChromeSlotPlacements.inline],
|
|
457
|
+
renderChromeSlot,
|
|
458
|
+
renderFallbackSlot: () => renderBodySlot(),
|
|
459
|
+
})
|
|
460
|
+
const collectionBody = bodyChrome.length > 0 ? bodyChrome : (
|
|
461
|
+
<ProjectionStatusBlock
|
|
462
|
+
label={
|
|
463
|
+
`Presentation view '${view?.Name ?? viewId}' does not declare a collection Body chrome slot.`
|
|
464
|
+
}
|
|
465
|
+
/>
|
|
466
|
+
)
|
|
467
|
+
const collectionDetailSlot = collectionRuntime.chrome.detailSlot
|
|
468
|
+
const detailChrome = renderCollectionChromeSlots({
|
|
469
|
+
context: chromeSlotContext,
|
|
470
|
+
includeSlot: (slot) => isCollectionChromeSlotKind(
|
|
471
|
+
slot,
|
|
472
|
+
collectionChromeSlotKinds.detail,
|
|
473
|
+
),
|
|
474
|
+
placements: [
|
|
475
|
+
collectionChromeSlotPlacements.inline,
|
|
476
|
+
collectionChromeSlotPlacements.sidePanel,
|
|
477
|
+
collectionChromeSlotPlacements.drawer,
|
|
478
|
+
],
|
|
479
|
+
renderChromeSlot,
|
|
480
|
+
renderFallbackSlot: ({ slot }) => renderDetailSlot(slot),
|
|
481
|
+
})
|
|
482
|
+
const collectionDetail = detailChrome.length > 0 ? detailChrome : null
|
|
483
|
+
const table = (
|
|
484
|
+
<div className="grid w-full min-w-0 gap-4">
|
|
485
|
+
{leadingChrome}
|
|
486
|
+
{collectionBody}
|
|
487
|
+
</div>
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
if (!module) {
|
|
491
|
+
return <ProjectionStatusBlock label="Presentation module is not available." />
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (!view) {
|
|
495
|
+
return <ProjectionStatusBlock label={`Presentation view '${viewId}' is not available.`} />
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (!collectionRuntime.chrome.bodySlot) {
|
|
499
|
+
return (
|
|
500
|
+
<ProjectionStatusBlock
|
|
501
|
+
label={`Presentation view '${view.Name}' does not declare a collection Body chrome slot.`}
|
|
502
|
+
/>
|
|
503
|
+
)
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (columns.length === 0) {
|
|
507
|
+
return <ProjectionStatusBlock label={`Presentation view '${view.Name}' has no projected fields.`} />
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return (
|
|
511
|
+
<ProjectedCollectionDetailLayout
|
|
512
|
+
componentSystem={componentSystem}
|
|
513
|
+
detail={collectionDetail}
|
|
514
|
+
placement={collectionDetailSlot?.Placement ?? collectionChromeSlotPlacements.none}
|
|
515
|
+
table={table}
|
|
516
|
+
/>
|
|
517
|
+
)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export type ProjectedCollectionDetailFieldRenderContext<TData extends object> =
|
|
521
|
+
ProjectedRecordFieldRenderContext<TData>
|
|
522
|
+
|
|
523
|
+
export function ProjectedCollectionBody<TData extends object>({
|
|
524
|
+
columns,
|
|
525
|
+
componentSystem,
|
|
526
|
+
data,
|
|
527
|
+
emptyMessage,
|
|
528
|
+
footer,
|
|
529
|
+
getRowId,
|
|
530
|
+
isRowSelected,
|
|
531
|
+
onRowClick,
|
|
532
|
+
viewId,
|
|
533
|
+
}: {
|
|
534
|
+
readonly columns: readonly ColumnDef<TData>[]
|
|
535
|
+
readonly componentSystem: PresentationComponentSystem
|
|
536
|
+
readonly data: readonly TData[]
|
|
537
|
+
readonly emptyMessage: string
|
|
538
|
+
readonly footer?: ReactNode
|
|
539
|
+
readonly getRowId?: (row: TData, index: number) => string
|
|
540
|
+
readonly isRowSelected?: (row: TData, index: number) => boolean
|
|
541
|
+
readonly onRowClick?: (row: TData, index: number) => void
|
|
542
|
+
readonly viewId: string
|
|
543
|
+
}) {
|
|
544
|
+
return componentSystem.collections.DataTable({
|
|
545
|
+
columns,
|
|
546
|
+
data,
|
|
547
|
+
emptyMessage,
|
|
548
|
+
footer,
|
|
549
|
+
getRowId,
|
|
550
|
+
isRowSelected,
|
|
551
|
+
onRowClick,
|
|
552
|
+
viewId,
|
|
553
|
+
})
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function ProjectedCollectionDetail<TData extends object>({
|
|
557
|
+
componentSet,
|
|
558
|
+
componentSystem,
|
|
559
|
+
createHref,
|
|
560
|
+
data,
|
|
561
|
+
emptyMessage,
|
|
562
|
+
fieldRenderers,
|
|
563
|
+
navigateHref,
|
|
564
|
+
onClose,
|
|
565
|
+
title,
|
|
566
|
+
viewId,
|
|
567
|
+
}: {
|
|
568
|
+
readonly componentSet: string
|
|
569
|
+
readonly componentSystem: PresentationComponentSystem
|
|
570
|
+
readonly createHref?: (
|
|
571
|
+
routeId: string,
|
|
572
|
+
parameters?: NavigationRouteParameters,
|
|
573
|
+
) => string | null
|
|
574
|
+
readonly data: TData | null
|
|
575
|
+
readonly emptyMessage?: string | null
|
|
576
|
+
readonly fieldRenderers?: Record<
|
|
577
|
+
string,
|
|
578
|
+
(context: ProjectedCollectionDetailFieldRenderContext<TData>) => ReactNode
|
|
579
|
+
>
|
|
580
|
+
readonly navigateHref?: (href: string) => void
|
|
581
|
+
readonly onClose?: () => void
|
|
582
|
+
readonly title?: string | null
|
|
583
|
+
readonly viewId: string
|
|
584
|
+
}) {
|
|
585
|
+
const module = usePresentationModule()
|
|
586
|
+
const detailView = findPresentationView<ProjectedViewDefinitionLike>(module, viewId)
|
|
587
|
+
const ActionButton = componentSystem.actions.ActionButton
|
|
588
|
+
|
|
589
|
+
return (
|
|
590
|
+
<section className="grid gap-2">
|
|
591
|
+
<div className="flex items-center justify-between gap-2">
|
|
592
|
+
{detailView || title ? (
|
|
593
|
+
<h2 className="text-sm font-semibold text-slate-800">{title ?? detailView?.Name}</h2>
|
|
594
|
+
) : <span />}
|
|
595
|
+
{onClose ? (
|
|
596
|
+
<ActionButton
|
|
597
|
+
aria-label="Close details"
|
|
598
|
+
onClick={onClose}
|
|
599
|
+
size="icon-sm"
|
|
600
|
+
type="button"
|
|
601
|
+
variant="ghost"
|
|
602
|
+
>
|
|
603
|
+
{renderPresentationIcon({
|
|
604
|
+
className: 'size-4',
|
|
605
|
+
componentSet,
|
|
606
|
+
icon: collectionChromeIconIds.detailClose,
|
|
607
|
+
module,
|
|
608
|
+
})}
|
|
609
|
+
</ActionButton>
|
|
610
|
+
) : null}
|
|
611
|
+
</div>
|
|
612
|
+
{data ? (
|
|
613
|
+
<ProjectedRecordDetails
|
|
614
|
+
componentSystem={componentSystem}
|
|
615
|
+
createHref={createHref}
|
|
616
|
+
data={data}
|
|
617
|
+
fieldRenderers={fieldRenderers}
|
|
618
|
+
navigateHref={navigateHref}
|
|
619
|
+
viewId={viewId}
|
|
620
|
+
/>
|
|
621
|
+
) : (
|
|
622
|
+
<ProjectionStatusBlock label={emptyMessage ?? 'Select a row to inspect it.'} />
|
|
623
|
+
)}
|
|
624
|
+
</section>
|
|
625
|
+
)
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function ProjectedCollectionDetailLayout({
|
|
629
|
+
componentSystem,
|
|
630
|
+
detail,
|
|
631
|
+
placement,
|
|
632
|
+
table,
|
|
633
|
+
}: {
|
|
634
|
+
readonly componentSystem: PresentationComponentSystem
|
|
635
|
+
readonly detail: ReactNode
|
|
636
|
+
readonly placement: CollectionChromeSlotPlacement | null | undefined
|
|
637
|
+
readonly table: ReactNode
|
|
638
|
+
}) {
|
|
639
|
+
const CollectionDetailLayout = componentSystem.collections.CollectionDetailLayout
|
|
640
|
+
return (
|
|
641
|
+
<CollectionDetailLayout
|
|
642
|
+
detail={detail}
|
|
643
|
+
mode={resolveCollectionDetailLayoutMode(placement)}
|
|
644
|
+
table={table}
|
|
645
|
+
/>
|
|
646
|
+
)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function resolveCollectionDetailLayoutMode(
|
|
650
|
+
placement: CollectionChromeSlotPlacement | null | undefined,
|
|
651
|
+
): PresentationCollectionDetailLayoutMode {
|
|
652
|
+
if (
|
|
653
|
+
isCollectionChromeSlotPlacementValue(
|
|
654
|
+
placement,
|
|
655
|
+
collectionChromeSlotPlacements.none,
|
|
656
|
+
)
|
|
657
|
+
) {
|
|
658
|
+
return 'none'
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (
|
|
662
|
+
isCollectionChromeSlotPlacementValue(
|
|
663
|
+
placement,
|
|
664
|
+
collectionChromeSlotPlacements.sidePanel,
|
|
665
|
+
)
|
|
666
|
+
) {
|
|
667
|
+
return 'side-panel'
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (
|
|
671
|
+
isCollectionChromeSlotPlacementValue(
|
|
672
|
+
placement,
|
|
673
|
+
collectionChromeSlotPlacements.drawer,
|
|
674
|
+
)
|
|
675
|
+
) {
|
|
676
|
+
return 'drawer'
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return 'stack'
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function createCollectionDetailProjection<TData extends object>({
|
|
683
|
+
componentSet,
|
|
684
|
+
componentSystem,
|
|
685
|
+
createHref,
|
|
686
|
+
data,
|
|
687
|
+
emptyMessage,
|
|
688
|
+
fieldRenderers,
|
|
689
|
+
navigateHref,
|
|
690
|
+
onClose,
|
|
691
|
+
title,
|
|
692
|
+
viewId,
|
|
693
|
+
}: {
|
|
694
|
+
readonly componentSet: string
|
|
695
|
+
readonly componentSystem: PresentationComponentSystem
|
|
696
|
+
readonly createHref?: (
|
|
697
|
+
routeId: string,
|
|
698
|
+
parameters?: NavigationRouteParameters,
|
|
699
|
+
) => string | null
|
|
700
|
+
readonly data: TData | null
|
|
701
|
+
readonly emptyMessage?: string | null
|
|
702
|
+
readonly fieldRenderers?: Record<
|
|
703
|
+
string,
|
|
704
|
+
(context: ProjectedCollectionDetailFieldRenderContext<TData>) => ReactNode
|
|
705
|
+
>
|
|
706
|
+
readonly navigateHref?: (href: string) => void
|
|
707
|
+
readonly onClose?: () => void
|
|
708
|
+
readonly title?: string | null
|
|
709
|
+
readonly viewId: string | null
|
|
710
|
+
}) {
|
|
711
|
+
if (!viewId || (!data && !emptyMessage)) {
|
|
712
|
+
return null
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
return (
|
|
716
|
+
<ProjectedCollectionDetail
|
|
717
|
+
componentSet={componentSet}
|
|
718
|
+
componentSystem={componentSystem}
|
|
719
|
+
createHref={createHref}
|
|
720
|
+
data={data}
|
|
721
|
+
emptyMessage={emptyMessage}
|
|
722
|
+
fieldRenderers={fieldRenderers}
|
|
723
|
+
navigateHref={navigateHref}
|
|
724
|
+
onClose={data ? onClose : undefined}
|
|
725
|
+
title={title}
|
|
726
|
+
viewId={viewId}
|
|
727
|
+
/>
|
|
728
|
+
)
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function renderCollectionChromeSlots<TData extends object>({
|
|
732
|
+
context,
|
|
733
|
+
includeSlot,
|
|
734
|
+
placements,
|
|
735
|
+
renderFallbackSlot,
|
|
736
|
+
renderChromeSlot,
|
|
737
|
+
}: {
|
|
738
|
+
readonly context: Omit<ProjectedCollectionChromeSlotRenderContext<TData>, 'slot'>
|
|
739
|
+
readonly includeSlot?: (slot: CollectionChromeSlotDefinition) => boolean
|
|
740
|
+
readonly placements: readonly number[]
|
|
741
|
+
readonly renderFallbackSlot?: (
|
|
742
|
+
context: ProjectedCollectionChromeSlotRenderContext<TData>,
|
|
743
|
+
) => ReactNode
|
|
744
|
+
readonly renderChromeSlot?: (
|
|
745
|
+
context: ProjectedCollectionChromeSlotRenderContext<TData>,
|
|
746
|
+
) => ReactNode
|
|
747
|
+
}) {
|
|
748
|
+
if (!renderChromeSlot && !renderFallbackSlot) {
|
|
749
|
+
return []
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return context.collectionRuntime.chrome.slots.flatMap((slot) => {
|
|
753
|
+
if (includeSlot && !includeSlot(slot)) {
|
|
754
|
+
return []
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if (!placements.some((placement) => isCollectionChromeSlotPlacement(slot, placement))) {
|
|
758
|
+
return []
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
const slotContext = { ...context, slot }
|
|
762
|
+
const rendered = renderChromeSlot
|
|
763
|
+
? renderChromeSlot(slotContext)
|
|
764
|
+
: renderFallbackSlot?.(slotContext)
|
|
765
|
+
if (rendered === null || rendered === undefined || rendered === false) {
|
|
766
|
+
return []
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
return [
|
|
770
|
+
<Fragment key={slot.Id}>{rendered}</Fragment>,
|
|
771
|
+
]
|
|
772
|
+
})
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
interface CreateProjectedColumnsOptions<TData extends object> {
|
|
776
|
+
readonly actionRuntimes?: ProjectedCollectionActionRuntimeRegistry<TData>
|
|
777
|
+
readonly canExecuteAction: (
|
|
778
|
+
context: ProjectedCollectionActionExecutionContext<TData>,
|
|
779
|
+
) => boolean
|
|
780
|
+
readonly collectionRuntime: ProjectedCollectionRuntime<TData>
|
|
781
|
+
readonly componentSet: string
|
|
782
|
+
readonly componentSystem: PresentationComponentSystem
|
|
783
|
+
readonly createHref?: (
|
|
784
|
+
routeId: string,
|
|
785
|
+
parameters?: NavigationRouteParameters,
|
|
786
|
+
) => string | null
|
|
787
|
+
readonly executeAction: (
|
|
788
|
+
context: ProjectedCollectionActionExecutionContext<TData>,
|
|
789
|
+
) => Promise<void> | void
|
|
790
|
+
readonly fieldRenderers?: Record<
|
|
791
|
+
string,
|
|
792
|
+
(context: ProjectedFieldRenderContext<TData>) => ReactNode
|
|
793
|
+
>
|
|
794
|
+
readonly getRowLabel?: (row: TData) => string
|
|
795
|
+
readonly module: ProjectedModuleDefinitionLike | null
|
|
796
|
+
readonly navigateHref?: (href: string) => void
|
|
797
|
+
readonly renderActionIcon?: (placement: ProjectedActionPlacementLike) => ReactNode
|
|
798
|
+
readonly renderChromeSlot?: (
|
|
799
|
+
context: ProjectedCollectionChromeSlotRenderContext<TData>,
|
|
800
|
+
) => ReactNode
|
|
801
|
+
readonly renderRowActionsSlot?: (
|
|
802
|
+
slot: CollectionChromeSlotDefinition,
|
|
803
|
+
row: TData,
|
|
804
|
+
rowLabel: string | null,
|
|
805
|
+
) => ReactNode
|
|
806
|
+
readonly viewId: string
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
function createProjectedColumns<TData extends object>({
|
|
810
|
+
actionRuntimes,
|
|
811
|
+
canExecuteAction,
|
|
812
|
+
collectionRuntime,
|
|
813
|
+
componentSet,
|
|
814
|
+
componentSystem,
|
|
815
|
+
createHref,
|
|
816
|
+
executeAction,
|
|
817
|
+
fieldRenderers,
|
|
818
|
+
getRowLabel,
|
|
819
|
+
module,
|
|
820
|
+
navigateHref,
|
|
821
|
+
renderActionIcon,
|
|
822
|
+
renderChromeSlot,
|
|
823
|
+
renderRowActionsSlot,
|
|
824
|
+
viewId,
|
|
825
|
+
}: CreateProjectedColumnsOptions<TData>): readonly ColumnDef<TData>[] {
|
|
826
|
+
if (!module) {
|
|
827
|
+
return []
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const fieldColumns = collectionRuntime.columns.flatMap<ColumnDef<TData>>((columnRuntime) => {
|
|
831
|
+
return [
|
|
832
|
+
{
|
|
833
|
+
cell: ({ row }) => {
|
|
834
|
+
const field = columnRuntime.field
|
|
835
|
+
const value = columnRuntime.readValue(row.original)
|
|
836
|
+
const renderer =
|
|
837
|
+
fieldRenderers?.[field.Id] ??
|
|
838
|
+
fieldRenderers?.[field.Field]
|
|
839
|
+
return renderer
|
|
840
|
+
? renderer({ field, row: row.original, value })
|
|
841
|
+
: renderProjectedFieldValue({
|
|
842
|
+
componentSystem,
|
|
843
|
+
createHref,
|
|
844
|
+
emptyValueFallback: <span className="text-slate-400">none</span>,
|
|
845
|
+
field,
|
|
846
|
+
module,
|
|
847
|
+
navigateHref,
|
|
848
|
+
resource: row.original,
|
|
849
|
+
value,
|
|
850
|
+
})
|
|
851
|
+
},
|
|
852
|
+
header: columnRuntime.header,
|
|
853
|
+
id: columnRuntime.id,
|
|
854
|
+
},
|
|
855
|
+
]
|
|
856
|
+
})
|
|
857
|
+
|
|
858
|
+
const rowActionColumns = createProjectedRowActionChromeColumns({
|
|
859
|
+
actionRuntimes,
|
|
860
|
+
canExecuteAction,
|
|
861
|
+
collectionRuntime,
|
|
862
|
+
componentSet,
|
|
863
|
+
componentSystem,
|
|
864
|
+
createHref,
|
|
865
|
+
executeAction,
|
|
866
|
+
getRowLabel,
|
|
867
|
+
module,
|
|
868
|
+
navigateHref,
|
|
869
|
+
renderChromeSlot,
|
|
870
|
+
renderActionIcon,
|
|
871
|
+
renderRowActionsSlot,
|
|
872
|
+
viewId,
|
|
873
|
+
})
|
|
874
|
+
|
|
875
|
+
return [...fieldColumns, ...rowActionColumns]
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
function createProjectedRowActionChromeColumns<TData extends object>({
|
|
879
|
+
actionRuntimes,
|
|
880
|
+
canExecuteAction,
|
|
881
|
+
collectionRuntime,
|
|
882
|
+
componentSet,
|
|
883
|
+
componentSystem,
|
|
884
|
+
executeAction,
|
|
885
|
+
getRowLabel,
|
|
886
|
+
module,
|
|
887
|
+
navigateHref,
|
|
888
|
+
renderActionIcon,
|
|
889
|
+
renderChromeSlot,
|
|
890
|
+
renderRowActionsSlot,
|
|
891
|
+
viewId,
|
|
892
|
+
}: CreateProjectedColumnsOptions<TData>): readonly ColumnDef<TData>[] {
|
|
893
|
+
const slots = collectionRuntime.chrome.findSlots(
|
|
894
|
+
collectionChromeSlotKinds.rowActions,
|
|
895
|
+
collectionChromeSlotPlacements.inline,
|
|
896
|
+
)
|
|
897
|
+
if (slots.length === 0) {
|
|
898
|
+
return []
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
return slots.map((slot) => ({
|
|
902
|
+
cell: ({ row }) => {
|
|
903
|
+
const rowLabel = getRowLabel?.(row.original) ??
|
|
904
|
+
collectionRuntime.readRowLabel(row.original)
|
|
905
|
+
const slotContext = {
|
|
906
|
+
actionRuntimes,
|
|
907
|
+
canExecuteAction,
|
|
908
|
+
collectionRuntime,
|
|
909
|
+
componentSet,
|
|
910
|
+
componentSystem,
|
|
911
|
+
executeAction,
|
|
912
|
+
module,
|
|
913
|
+
navigateHref,
|
|
914
|
+
renderActionIcon,
|
|
915
|
+
renderRowActionsSlot,
|
|
916
|
+
row: row.original,
|
|
917
|
+
rowLabel,
|
|
918
|
+
slot,
|
|
919
|
+
viewId,
|
|
920
|
+
} satisfies ProjectedCollectionChromeSlotRenderContext<TData>
|
|
921
|
+
const rendered = renderChromeSlot
|
|
922
|
+
? renderChromeSlot(slotContext)
|
|
923
|
+
: renderRowActionsSlot?.(slot, row.original, rowLabel)
|
|
924
|
+
|
|
925
|
+
return rendered ?? null
|
|
926
|
+
},
|
|
927
|
+
header: () => <span className="sr-only">{slot.Name || 'Actions'}</span>,
|
|
928
|
+
id: slot.Id || 'collection-row-actions',
|
|
929
|
+
}))
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
export function ProjectedCollectionRowActions<TData extends object>({
|
|
933
|
+
canExecuteAction,
|
|
934
|
+
collectionRuntime,
|
|
935
|
+
componentSet,
|
|
936
|
+
componentSystem,
|
|
937
|
+
executeAction,
|
|
938
|
+
module,
|
|
939
|
+
renderActionIcon,
|
|
940
|
+
row,
|
|
941
|
+
rowLabel,
|
|
942
|
+
slot,
|
|
943
|
+
viewId,
|
|
944
|
+
}: {
|
|
945
|
+
readonly canExecuteAction: (
|
|
946
|
+
context: ProjectedCollectionActionExecutionContext<TData>,
|
|
947
|
+
) => boolean
|
|
948
|
+
readonly collectionRuntime: ProjectedCollectionRuntime<TData>
|
|
949
|
+
readonly componentSet: string
|
|
950
|
+
readonly componentSystem: PresentationComponentSystem
|
|
951
|
+
readonly executeAction: (
|
|
952
|
+
context: ProjectedCollectionActionExecutionContext<TData>,
|
|
953
|
+
) => Promise<void> | void
|
|
954
|
+
readonly module?: ProjectedModuleDefinitionLike | null
|
|
955
|
+
readonly renderActionIcon?: (placement: ProjectedActionPlacementLike) => ReactNode
|
|
956
|
+
readonly row: TData
|
|
957
|
+
readonly rowLabel: string | null
|
|
958
|
+
readonly slot: CollectionChromeSlotDefinition
|
|
959
|
+
readonly viewId: string
|
|
960
|
+
}) {
|
|
961
|
+
const ActionButton = componentSystem.actions.ActionButton
|
|
962
|
+
const primaryRowActions = resolveSlotRowActions(
|
|
963
|
+
slot,
|
|
964
|
+
collectionRuntime.actions.primaryRowActions,
|
|
965
|
+
)
|
|
966
|
+
const contextRowActions = resolveSlotRowActions(
|
|
967
|
+
slot,
|
|
968
|
+
collectionRuntime.actions.contextRowActions,
|
|
969
|
+
)
|
|
970
|
+
const primaryItems = collectionRuntime.actions.resolveRowActionItems(
|
|
971
|
+
row,
|
|
972
|
+
primaryRowActions,
|
|
973
|
+
{ canExecuteAction },
|
|
974
|
+
)
|
|
975
|
+
|
|
976
|
+
if (primaryItems.length === 0 && contextRowActions.length === 0) {
|
|
977
|
+
return null
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
return componentSystem.collectionChrome.CollectionRowActions({
|
|
981
|
+
children: (
|
|
982
|
+
<>
|
|
983
|
+
{primaryItems.map(({ action, actionContext, actionRef, isEnabled, rowAction }) => {
|
|
984
|
+
const label = actionRef.placement.Label ?? rowAction.Label ?? action.Name
|
|
985
|
+
return (
|
|
986
|
+
<ActionButton
|
|
987
|
+
aria-label={rowLabel ? `${label}: ${rowLabel}` : label}
|
|
988
|
+
{...createPresentationTestAttributes({
|
|
989
|
+
actionId: rowAction.ActionId,
|
|
990
|
+
collectionSlotId: slot.Id,
|
|
991
|
+
viewId,
|
|
992
|
+
})}
|
|
993
|
+
disabled={
|
|
994
|
+
!isEnabled ||
|
|
995
|
+
!canExecuteAction(actionContext)
|
|
996
|
+
}
|
|
997
|
+
key={actionRef.id}
|
|
998
|
+
onClick={(event) => {
|
|
999
|
+
event.stopPropagation()
|
|
1000
|
+
void executeAction(actionContext)
|
|
1001
|
+
}}
|
|
1002
|
+
size="icon-sm"
|
|
1003
|
+
type="button"
|
|
1004
|
+
variant="ghost"
|
|
1005
|
+
>
|
|
1006
|
+
{renderActionIcon?.(actionRef.placement) ??
|
|
1007
|
+
renderDefaultActionIcon(actionRef.placement, module, componentSet)}
|
|
1008
|
+
</ActionButton>
|
|
1009
|
+
)
|
|
1010
|
+
})}
|
|
1011
|
+
<ProjectedRowActionMenu
|
|
1012
|
+
canExecuteAction={canExecuteAction}
|
|
1013
|
+
collectionRuntime={collectionRuntime}
|
|
1014
|
+
componentSystem={componentSystem}
|
|
1015
|
+
componentSet={componentSet}
|
|
1016
|
+
executeAction={executeAction}
|
|
1017
|
+
module={module}
|
|
1018
|
+
renderActionIcon={renderActionIcon}
|
|
1019
|
+
row={row}
|
|
1020
|
+
rowActions={contextRowActions}
|
|
1021
|
+
rowLabel={rowLabel}
|
|
1022
|
+
slotId={slot.Id}
|
|
1023
|
+
viewId={viewId}
|
|
1024
|
+
/>
|
|
1025
|
+
</>
|
|
1026
|
+
),
|
|
1027
|
+
rowLabel,
|
|
1028
|
+
slotId: slot.Id,
|
|
1029
|
+
})
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
function resolveSlotRowActions(
|
|
1033
|
+
slot: CollectionChromeSlotDefinition,
|
|
1034
|
+
rowActions: readonly ResolvedProjectedCollectionRowAction[],
|
|
1035
|
+
) {
|
|
1036
|
+
const scopedActions = rowActions.filter((rowAction) => rowAction.slotId === slot.Id)
|
|
1037
|
+
if (scopedActions.length > 0) {
|
|
1038
|
+
return scopedActions
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const actionIds = new Set(
|
|
1042
|
+
slot.RowActions.length > 0
|
|
1043
|
+
? slot.RowActions.flatMap((rowAction) => [rowAction.Id, rowAction.ActionId])
|
|
1044
|
+
: slot.ActionIds,
|
|
1045
|
+
)
|
|
1046
|
+
if (actionIds.size === 0) {
|
|
1047
|
+
return rowActions
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
return rowActions.filter(({ action, actionRef, rowAction }) =>
|
|
1051
|
+
actionIds.has(action.Id) ||
|
|
1052
|
+
actionIds.has(actionRef.actionId) ||
|
|
1053
|
+
actionIds.has(actionRef.id) ||
|
|
1054
|
+
actionIds.has(rowAction.ActionId) ||
|
|
1055
|
+
actionIds.has(rowAction.Id))
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
function ProjectedRowActionMenu<TData extends object>({
|
|
1059
|
+
canExecuteAction,
|
|
1060
|
+
collectionRuntime,
|
|
1061
|
+
componentSet,
|
|
1062
|
+
componentSystem,
|
|
1063
|
+
executeAction,
|
|
1064
|
+
module,
|
|
1065
|
+
renderActionIcon,
|
|
1066
|
+
row,
|
|
1067
|
+
rowActions,
|
|
1068
|
+
rowLabel,
|
|
1069
|
+
slotId,
|
|
1070
|
+
viewId,
|
|
1071
|
+
}: {
|
|
1072
|
+
readonly canExecuteAction: (
|
|
1073
|
+
context: ProjectedCollectionActionExecutionContext<TData>,
|
|
1074
|
+
) => boolean
|
|
1075
|
+
readonly collectionRuntime: ProjectedCollectionRuntime<TData>
|
|
1076
|
+
readonly componentSet: string
|
|
1077
|
+
readonly componentSystem: PresentationComponentSystem
|
|
1078
|
+
readonly executeAction: (
|
|
1079
|
+
context: ProjectedCollectionActionExecutionContext<TData>,
|
|
1080
|
+
) => Promise<void> | void
|
|
1081
|
+
readonly module?: ProjectedModuleDefinitionLike | null
|
|
1082
|
+
readonly renderActionIcon?: (placement: ProjectedActionPlacementLike) => ReactNode
|
|
1083
|
+
readonly row: TData
|
|
1084
|
+
readonly rowActions: readonly ResolvedProjectedCollectionRowAction[]
|
|
1085
|
+
readonly rowLabel: string | null
|
|
1086
|
+
readonly slotId: string
|
|
1087
|
+
readonly viewId: string
|
|
1088
|
+
}) {
|
|
1089
|
+
const RowActionMenu = componentSystem.collections.RowActionMenu
|
|
1090
|
+
const RowActionMenuItem = componentSystem.collections.RowActionMenuItem
|
|
1091
|
+
const RowActionMenuTrigger = componentSystem.collections.RowActionMenuTrigger
|
|
1092
|
+
const items = collectionRuntime.actions.resolveRowActionItems(
|
|
1093
|
+
row,
|
|
1094
|
+
rowActions,
|
|
1095
|
+
{ canExecuteAction },
|
|
1096
|
+
)
|
|
1097
|
+
|
|
1098
|
+
if (items.length === 0) {
|
|
1099
|
+
return null
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
return (
|
|
1103
|
+
<RowActionMenu
|
|
1104
|
+
trigger={(
|
|
1105
|
+
<RowActionMenuTrigger aria-label={rowLabel ? `Actions: ${rowLabel}` : 'Row actions'}>
|
|
1106
|
+
{renderPresentationIcon({
|
|
1107
|
+
className: 'size-4',
|
|
1108
|
+
componentSet,
|
|
1109
|
+
icon: collectionChromeIconIds.rowActionsMenu,
|
|
1110
|
+
module,
|
|
1111
|
+
})}
|
|
1112
|
+
</RowActionMenuTrigger>
|
|
1113
|
+
)}
|
|
1114
|
+
>
|
|
1115
|
+
{items.map(({ action, actionContext, actionRef, isEnabled, rowAction }) => {
|
|
1116
|
+
const label = actionRef.placement.Label ?? rowAction.Label ?? action.Name
|
|
1117
|
+
return (
|
|
1118
|
+
<RowActionMenuItem
|
|
1119
|
+
actionId={rowAction.ActionId}
|
|
1120
|
+
collectionSlotId={slotId}
|
|
1121
|
+
disabled={
|
|
1122
|
+
!isEnabled ||
|
|
1123
|
+
!canExecuteAction(actionContext)
|
|
1124
|
+
}
|
|
1125
|
+
key={actionRef.id}
|
|
1126
|
+
onClick={(event) => {
|
|
1127
|
+
event.stopPropagation()
|
|
1128
|
+
closeContainingDetails(event.currentTarget)
|
|
1129
|
+
void executeAction(actionContext)
|
|
1130
|
+
}}
|
|
1131
|
+
viewId={viewId}
|
|
1132
|
+
>
|
|
1133
|
+
{renderActionIcon?.(actionRef.placement) ??
|
|
1134
|
+
renderDefaultActionIcon(actionRef.placement, module, componentSet)}
|
|
1135
|
+
<span>{label}</span>
|
|
1136
|
+
</RowActionMenuItem>
|
|
1137
|
+
)
|
|
1138
|
+
})}
|
|
1139
|
+
</RowActionMenu>
|
|
1140
|
+
)
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
export function ProjectedCollectionSelectionActionToolbar<TData extends object>({
|
|
1144
|
+
canExecuteAction,
|
|
1145
|
+
collectionRuntime,
|
|
1146
|
+
componentSet,
|
|
1147
|
+
componentSystem,
|
|
1148
|
+
executeAction,
|
|
1149
|
+
module,
|
|
1150
|
+
renderActionIcon,
|
|
1151
|
+
slot,
|
|
1152
|
+
}: {
|
|
1153
|
+
readonly canExecuteAction: (
|
|
1154
|
+
context: ProjectedCollectionActionExecutionContext<TData>,
|
|
1155
|
+
) => boolean
|
|
1156
|
+
readonly collectionRuntime: ProjectedCollectionRuntime<TData>
|
|
1157
|
+
readonly componentSet: string
|
|
1158
|
+
readonly componentSystem: PresentationComponentSystem
|
|
1159
|
+
readonly executeAction: (
|
|
1160
|
+
context: ProjectedCollectionActionExecutionContext<TData>,
|
|
1161
|
+
) => Promise<void> | void
|
|
1162
|
+
readonly module?: ProjectedModuleDefinitionLike | null
|
|
1163
|
+
readonly renderActionIcon?: (placement: ProjectedActionPlacementLike) => ReactNode
|
|
1164
|
+
readonly slot?: CollectionChromeSlotDefinition | null
|
|
1165
|
+
}) {
|
|
1166
|
+
const ActionButton = componentSystem.actions.ActionButton
|
|
1167
|
+
const selectedRowIds = collectionRuntime.selection.selectedRowIds
|
|
1168
|
+
if (selectedRowIds.length === 0) {
|
|
1169
|
+
return null
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
const items = collectionRuntime.actions.resolveSelectionActionItems({
|
|
1173
|
+
canExecuteAction,
|
|
1174
|
+
selectionActions: slot
|
|
1175
|
+
? resolveSlotSelectionActions(slot, collectionRuntime.actions.selectionActions)
|
|
1176
|
+
: undefined,
|
|
1177
|
+
})
|
|
1178
|
+
|
|
1179
|
+
if (items.length === 0) {
|
|
1180
|
+
return null
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
const selectedLabel =
|
|
1184
|
+
selectedRowIds.length === 1 ? '1 row selected' : `${selectedRowIds.length} rows selected`
|
|
1185
|
+
|
|
1186
|
+
return componentSystem.collectionChrome.CollectionSelectionActionToolbar({
|
|
1187
|
+
actions: (
|
|
1188
|
+
<>
|
|
1189
|
+
{items.map(({ action, actionContext, actionRef, isEnabled, selectionAction }) => {
|
|
1190
|
+
const label = actionRef.placement.Label ?? selectionAction.Label ?? action.Name
|
|
1191
|
+
return (
|
|
1192
|
+
<ActionButton
|
|
1193
|
+
disabled={
|
|
1194
|
+
!isEnabled ||
|
|
1195
|
+
!canExecuteAction(actionContext)
|
|
1196
|
+
}
|
|
1197
|
+
key={actionRef.id}
|
|
1198
|
+
onClick={() => {
|
|
1199
|
+
void executeAction(actionContext)
|
|
1200
|
+
}}
|
|
1201
|
+
size="sm"
|
|
1202
|
+
type="button"
|
|
1203
|
+
variant="outline"
|
|
1204
|
+
>
|
|
1205
|
+
{renderActionIcon?.(actionRef.placement) ??
|
|
1206
|
+
renderDefaultActionIcon(actionRef.placement, module, componentSet)}
|
|
1207
|
+
{label}
|
|
1208
|
+
</ActionButton>
|
|
1209
|
+
)
|
|
1210
|
+
})}
|
|
1211
|
+
</>
|
|
1212
|
+
),
|
|
1213
|
+
selectedCount: selectedRowIds.length,
|
|
1214
|
+
selectedLabel,
|
|
1215
|
+
slotId: slot?.Id ?? null,
|
|
1216
|
+
})
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
function resolveSlotSelectionActions(
|
|
1220
|
+
slot: CollectionChromeSlotDefinition,
|
|
1221
|
+
selectionActions: readonly ResolvedProjectedCollectionSelectionAction[],
|
|
1222
|
+
) {
|
|
1223
|
+
const scopedActions = selectionActions.filter((selectionAction) =>
|
|
1224
|
+
selectionAction.slotId === slot.Id)
|
|
1225
|
+
if (scopedActions.length > 0) {
|
|
1226
|
+
return scopedActions
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
const actionIds = new Set(
|
|
1230
|
+
slot.SelectionActions.length > 0
|
|
1231
|
+
? slot.SelectionActions.flatMap((selectionAction) => [
|
|
1232
|
+
selectionAction.Id,
|
|
1233
|
+
selectionAction.ActionId,
|
|
1234
|
+
])
|
|
1235
|
+
: slot.ActionIds,
|
|
1236
|
+
)
|
|
1237
|
+
if (actionIds.size === 0) {
|
|
1238
|
+
return selectionActions
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
return selectionActions.filter(({ action, actionRef, selectionAction }) =>
|
|
1242
|
+
actionIds.has(action.Id) ||
|
|
1243
|
+
actionIds.has(actionRef.actionId) ||
|
|
1244
|
+
actionIds.has(actionRef.id) ||
|
|
1245
|
+
actionIds.has(selectionAction.ActionId) ||
|
|
1246
|
+
actionIds.has(selectionAction.Id))
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
function renderDefaultActionIcon(
|
|
1250
|
+
placement: ProjectedActionPlacementLike,
|
|
1251
|
+
module?: ProjectedModuleDefinitionLike | null,
|
|
1252
|
+
componentSet = defaultPresentationComponentSet,
|
|
1253
|
+
) {
|
|
1254
|
+
return renderPresentationIcon({
|
|
1255
|
+
className: 'size-4',
|
|
1256
|
+
componentSet,
|
|
1257
|
+
icon: placement.Icon ?? 'square-arrow-out-up-right',
|
|
1258
|
+
module,
|
|
1259
|
+
subject: placement,
|
|
1260
|
+
})
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
function closeContainingDetails(element: HTMLElement) {
|
|
1264
|
+
element.closest('details')?.removeAttribute('open')
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
function ProjectionStatusBlock({ label }: { readonly label: string }) {
|
|
1268
|
+
return (
|
|
1269
|
+
<div className="rounded-2xl border border-slate-950/8 bg-white/65 px-4 py-3 text-sm text-slate-500">
|
|
1270
|
+
{label}
|
|
1271
|
+
</div>
|
|
1272
|
+
)
|
|
1273
|
+
}
|