@cohesivesystems/presentation-react 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/collection-chrome-slot-renderer-registry.d.ts +12 -0
- package/dist/collection-chrome-slot-renderer-registry.d.ts.map +1 -0
- package/dist/collection-chrome-slot-renderer-registry.js +12 -0
- package/dist/collection-chrome-slot-renderer-registry.js.map +1 -0
- package/dist/collection-selection-state-context.d.ts +6 -0
- package/dist/collection-selection-state-context.d.ts.map +1 -0
- package/dist/collection-selection-state-context.js +25 -0
- package/dist/collection-selection-state-context.js.map +1 -0
- package/dist/collection-selection-state.d.ts +3 -0
- package/dist/collection-selection-state.d.ts.map +1 -0
- package/dist/collection-selection-state.js +52 -0
- package/dist/collection-selection-state.js.map +1 -0
- package/dist/document-workspace-runtime.d.ts +262 -0
- package/dist/document-workspace-runtime.d.ts.map +1 -0
- package/dist/document-workspace-runtime.js +699 -0
- package/dist/document-workspace-runtime.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/input-form-endpoint-runtime.d.ts +140 -0
- package/dist/input-form-endpoint-runtime.d.ts.map +1 -0
- package/dist/input-form-endpoint-runtime.js +280 -0
- package/dist/input-form-endpoint-runtime.js.map +1 -0
- package/dist/local-flow-state-data-source-projection.d.ts +21 -0
- package/dist/local-flow-state-data-source-projection.d.ts.map +1 -0
- package/dist/local-flow-state-data-source-projection.js +65 -0
- package/dist/local-flow-state-data-source-projection.js.map +1 -0
- package/dist/navigation-runtime-context.d.ts +22 -0
- package/dist/navigation-runtime-context.d.ts.map +1 -0
- package/dist/navigation-runtime-context.js +23 -0
- package/dist/navigation-runtime-context.js.map +1 -0
- package/dist/navigation-shell-frame-renderer-registry.d.ts +20 -0
- package/dist/navigation-shell-frame-renderer-registry.d.ts.map +1 -0
- package/dist/navigation-shell-frame-renderer-registry.js +9 -0
- package/dist/navigation-shell-frame-renderer-registry.js.map +1 -0
- package/dist/navigation-shell-region-component-registry.d.ts +50 -0
- package/dist/navigation-shell-region-component-registry.d.ts.map +1 -0
- package/dist/navigation-shell-region-component-registry.js +44 -0
- package/dist/navigation-shell-region-component-registry.js.map +1 -0
- package/dist/navigation-shell-slot-renderer-registry.d.ts +22 -0
- package/dist/navigation-shell-slot-renderer-registry.d.ts.map +1 -0
- package/dist/navigation-shell-slot-renderer-registry.js +9 -0
- package/dist/navigation-shell-slot-renderer-registry.js.map +1 -0
- package/dist/page-host-projection.d.ts +127 -0
- package/dist/page-host-projection.d.ts.map +1 -0
- package/dist/page-host-projection.js +59 -0
- package/dist/page-host-projection.js.map +1 -0
- package/dist/page-host-renderer-registry.d.ts +72 -0
- package/dist/page-host-renderer-registry.d.ts.map +1 -0
- package/dist/page-host-renderer-registry.js +63 -0
- package/dist/page-host-renderer-registry.js.map +1 -0
- package/dist/presentation-action-group-runtime-options.d.ts +36 -0
- package/dist/presentation-action-group-runtime-options.d.ts.map +1 -0
- package/dist/presentation-action-group-runtime-options.js +50 -0
- package/dist/presentation-action-group-runtime-options.js.map +1 -0
- package/dist/presentation-action-runtime.d.ts +52 -0
- package/dist/presentation-action-runtime.d.ts.map +1 -0
- package/dist/presentation-action-runtime.js +78 -0
- package/dist/presentation-action-runtime.js.map +1 -0
- package/dist/presentation-data-source-binding-runtime.d.ts +3 -0
- package/dist/presentation-data-source-binding-runtime.d.ts.map +1 -0
- package/dist/presentation-data-source-binding-runtime.js +65 -0
- package/dist/presentation-data-source-binding-runtime.js.map +1 -0
- package/dist/presentation-data-source-binding.d.ts +12 -0
- package/dist/presentation-data-source-binding.d.ts.map +1 -0
- package/dist/presentation-data-source-binding.js +13 -0
- package/dist/presentation-data-source-binding.js.map +1 -0
- package/dist/presentation-flow-runtime.d.ts +139 -0
- package/dist/presentation-flow-runtime.d.ts.map +1 -0
- package/dist/presentation-flow-runtime.js +514 -0
- package/dist/presentation-flow-runtime.js.map +1 -0
- package/dist/presentation-module-context.d.ts +4 -0
- package/dist/presentation-module-context.d.ts.map +1 -0
- package/dist/presentation-module-context.js +6 -0
- package/dist/presentation-module-context.js.map +1 -0
- package/dist/presentation-module-provider.d.ts +7 -0
- package/dist/presentation-module-provider.d.ts.map +1 -0
- package/dist/presentation-module-provider.js +7 -0
- package/dist/presentation-module-provider.js.map +1 -0
- package/dist/presentation-projection-diagnostics-context.d.ts +8 -0
- package/dist/presentation-projection-diagnostics-context.d.ts.map +1 -0
- package/dist/presentation-projection-diagnostics-context.js +3 -0
- package/dist/presentation-projection-diagnostics-context.js.map +1 -0
- package/dist/presentation-projection-diagnostics-hooks.d.ts +4 -0
- package/dist/presentation-projection-diagnostics-hooks.d.ts.map +1 -0
- package/dist/presentation-projection-diagnostics-hooks.js +17 -0
- package/dist/presentation-projection-diagnostics-hooks.js.map +1 -0
- package/dist/presentation-projection-diagnostics-runtime.d.ts +12 -0
- package/dist/presentation-projection-diagnostics-runtime.d.ts.map +1 -0
- package/dist/presentation-projection-diagnostics-runtime.js +54 -0
- package/dist/presentation-projection-diagnostics-runtime.js.map +1 -0
- package/dist/presentation-projection-trace.d.ts +27 -0
- package/dist/presentation-projection-trace.d.ts.map +1 -0
- package/dist/presentation-projection-trace.js +54 -0
- package/dist/presentation-projection-trace.js.map +1 -0
- package/dist/presentation-query-form-runtime.d.ts +30 -0
- package/dist/presentation-query-form-runtime.d.ts.map +1 -0
- package/dist/presentation-query-form-runtime.js +75 -0
- package/dist/presentation-query-form-runtime.js.map +1 -0
- package/dist/presentation-query-form-state-adapter-registry.d.ts +4 -0
- package/dist/presentation-query-form-state-adapter-registry.d.ts.map +1 -0
- package/dist/presentation-query-form-state-adapter-registry.js +13 -0
- package/dist/presentation-query-form-state-adapter-registry.js.map +1 -0
- package/dist/presentation-query-form-state-context.d.ts +20 -0
- package/dist/presentation-query-form-state-context.d.ts.map +1 -0
- package/dist/presentation-query-form-state-context.js +20 -0
- package/dist/presentation-query-form-state-context.js.map +1 -0
- package/dist/presentation-query-form-state.d.ts +29 -0
- package/dist/presentation-query-form-state.d.ts.map +1 -0
- package/dist/presentation-query-form-state.js +99 -0
- package/dist/presentation-query-form-state.js.map +1 -0
- package/dist/presentation-renderer-registry.d.ts +109 -0
- package/dist/presentation-renderer-registry.d.ts.map +1 -0
- package/dist/presentation-renderer-registry.js +25 -0
- package/dist/presentation-renderer-registry.js.map +1 -0
- package/dist/presentation-standards-context.d.ts +4 -0
- package/dist/presentation-standards-context.d.ts.map +1 -0
- package/dist/presentation-standards-context.js +12 -0
- package/dist/presentation-standards-context.js.map +1 -0
- package/dist/presentation-standards-provider.d.ts +14 -0
- package/dist/presentation-standards-provider.d.ts.map +1 -0
- package/dist/presentation-standards-provider.js +16 -0
- package/dist/presentation-standards-provider.js.map +1 -0
- package/dist/projected-document-action-runtime-bindings.d.ts +20 -0
- package/dist/projected-document-action-runtime-bindings.d.ts.map +1 -0
- package/dist/projected-document-action-runtime-bindings.js +88 -0
- package/dist/projected-document-action-runtime-bindings.js.map +1 -0
- package/dist/projected-document-action-runtime.d.ts +76 -0
- package/dist/projected-document-action-runtime.d.ts.map +1 -0
- package/dist/projected-document-action-runtime.js +224 -0
- package/dist/projected-document-action-runtime.js.map +1 -0
- package/dist/projected-document-process-preview-action-runtime-bindings.d.ts +26 -0
- package/dist/projected-document-process-preview-action-runtime-bindings.d.ts.map +1 -0
- package/dist/projected-document-process-preview-action-runtime-bindings.js +106 -0
- package/dist/projected-document-process-preview-action-runtime-bindings.js.map +1 -0
- package/dist/projected-document-process-preview-action-runtime.d.ts +321 -0
- package/dist/projected-document-process-preview-action-runtime.d.ts.map +1 -0
- package/dist/projected-document-process-preview-action-runtime.js +729 -0
- package/dist/projected-document-process-preview-action-runtime.js.map +1 -0
- package/dist/projected-document-process-preview-capability-runtime.d.ts +47 -0
- package/dist/projected-document-process-preview-capability-runtime.d.ts.map +1 -0
- package/dist/projected-document-process-preview-capability-runtime.js +49 -0
- package/dist/projected-document-process-preview-capability-runtime.js.map +1 -0
- package/dist/projected-document-workspace-action-runtime.d.ts +87 -0
- package/dist/projected-document-workspace-action-runtime.d.ts.map +1 -0
- package/dist/projected-document-workspace-action-runtime.js +150 -0
- package/dist/projected-document-workspace-action-runtime.js.map +1 -0
- package/dist/projected-document-workspace-projection-renderer-diagnostics.d.ts +18 -0
- package/dist/projected-document-workspace-projection-renderer-diagnostics.d.ts.map +1 -0
- package/dist/projected-document-workspace-projection-renderer-diagnostics.js +207 -0
- package/dist/projected-document-workspace-projection-renderer-diagnostics.js.map +1 -0
- package/dist/projected-document-workspace-runtime-binding-diagnostics.d.ts +19 -0
- package/dist/projected-document-workspace-runtime-binding-diagnostics.d.ts.map +1 -0
- package/dist/projected-document-workspace-runtime-binding-diagnostics.js +124 -0
- package/dist/projected-document-workspace-runtime-binding-diagnostics.js.map +1 -0
- package/dist/projected-navigation-provider.d.ts +16 -0
- package/dist/projected-navigation-provider.d.ts.map +1 -0
- package/dist/projected-navigation-provider.js +51 -0
- package/dist/projected-navigation-provider.js.map +1 -0
- package/dist/projected-presentation-flow-host.d.ts +16 -0
- package/dist/projected-presentation-flow-host.d.ts.map +1 -0
- package/dist/projected-presentation-flow-host.js +14 -0
- package/dist/projected-presentation-flow-host.js.map +1 -0
- package/dist/projected-query-form-state-adapter.d.ts +12 -0
- package/dist/projected-query-form-state-adapter.d.ts.map +1 -0
- package/dist/projected-query-form-state-adapter.js +361 -0
- package/dist/projected-query-form-state-adapter.js.map +1 -0
- package/dist/react-router-binding.d.ts +133 -0
- package/dist/react-router-binding.d.ts.map +1 -0
- package/dist/react-router-binding.js +76 -0
- package/dist/react-router-binding.js.map +1 -0
- package/dist/react-router-path.d.ts +4 -0
- package/dist/react-router-path.d.ts.map +1 -0
- package/dist/react-router-path.js +11 -0
- package/dist/react-router-path.js.map +1 -0
- package/dist/scope-selection-context.d.ts +28 -0
- package/dist/scope-selection-context.d.ts.map +1 -0
- package/dist/scope-selection-context.js +13 -0
- package/dist/scope-selection-context.js.map +1 -0
- package/dist/scope-selection-provider.d.ts +33 -0
- package/dist/scope-selection-provider.d.ts.map +1 -0
- package/dist/scope-selection-provider.js +162 -0
- package/dist/scope-selection-provider.js.map +1 -0
- package/dist/scope-selection-request.d.ts +6 -0
- package/dist/scope-selection-request.d.ts.map +1 -0
- package/dist/scope-selection-request.js +12 -0
- package/dist/scope-selection-request.js.map +1 -0
- package/dist/standard-navigation-shell-frame-renderers.d.ts +15 -0
- package/dist/standard-navigation-shell-frame-renderers.d.ts.map +1 -0
- package/dist/standard-navigation-shell-frame-renderers.js +22 -0
- package/dist/standard-navigation-shell-frame-renderers.js.map +1 -0
- package/dist/standard-navigation-shell-slot-renderers.d.ts +59 -0
- package/dist/standard-navigation-shell-slot-renderers.d.ts.map +1 -0
- package/dist/standard-navigation-shell-slot-renderers.js +67 -0
- package/dist/standard-navigation-shell-slot-renderers.js.map +1 -0
- package/dist/view-chrome-slot-renderer-registry.d.ts +12 -0
- package/dist/view-chrome-slot-renderer-registry.d.ts.map +1 -0
- package/dist/view-chrome-slot-renderer-registry.js +12 -0
- package/dist/view-chrome-slot-renderer-registry.js.map +1 -0
- package/package.json +60 -0
- package/src/collection-chrome-slot-renderer-registry.ts +66 -0
- package/src/collection-selection-state-context.ts +51 -0
- package/src/collection-selection-state.tsx +101 -0
- package/src/document-workspace-runtime.tsx +1505 -0
- package/src/index.ts +50 -0
- package/src/input-form-endpoint-runtime.ts +632 -0
- package/src/local-flow-state-data-source-projection.ts +133 -0
- package/src/navigation-runtime-context.ts +40 -0
- package/src/navigation-shell-frame-renderer-registry.ts +54 -0
- package/src/navigation-shell-region-component-registry.tsx +127 -0
- package/src/navigation-shell-slot-renderer-registry.ts +88 -0
- package/src/page-host-projection.ts +356 -0
- package/src/page-host-renderer-registry.tsx +353 -0
- package/src/presentation-action-group-runtime-options.ts +121 -0
- package/src/presentation-action-runtime.ts +190 -0
- package/src/presentation-data-source-binding-runtime.ts +109 -0
- package/src/presentation-data-source-binding.tsx +24 -0
- package/src/presentation-flow-runtime.tsx +835 -0
- package/src/presentation-module-context.ts +10 -0
- package/src/presentation-module-provider.tsx +19 -0
- package/src/presentation-projection-diagnostics-context.ts +17 -0
- package/src/presentation-projection-diagnostics-hooks.ts +29 -0
- package/src/presentation-projection-diagnostics-runtime.tsx +95 -0
- package/src/presentation-projection-trace.ts +117 -0
- package/src/presentation-query-form-runtime.ts +148 -0
- package/src/presentation-query-form-state-adapter-registry.ts +25 -0
- package/src/presentation-query-form-state-context.ts +56 -0
- package/src/presentation-query-form-state.tsx +250 -0
- package/src/presentation-renderer-registry.ts +214 -0
- package/src/presentation-standards-context.ts +21 -0
- package/src/presentation-standards-provider.tsx +44 -0
- package/src/projected-document-action-runtime-bindings.ts +132 -0
- package/src/projected-document-action-runtime.ts +414 -0
- package/src/projected-document-process-preview-action-runtime-bindings.ts +231 -0
- package/src/projected-document-process-preview-action-runtime.ts +1725 -0
- package/src/projected-document-process-preview-capability-runtime.ts +216 -0
- package/src/projected-document-workspace-action-runtime.ts +402 -0
- package/src/projected-document-workspace-projection-renderer-diagnostics.ts +342 -0
- package/src/projected-document-workspace-runtime-binding-diagnostics.ts +208 -0
- package/src/projected-navigation-provider.tsx +104 -0
- package/src/projected-presentation-flow-host.tsx +46 -0
- package/src/projected-query-form-state-adapter.ts +578 -0
- package/src/react-router-binding.tsx +417 -0
- package/src/react-router-path.ts +16 -0
- package/src/scope-selection-context.ts +58 -0
- package/src/scope-selection-provider.tsx +324 -0
- package/src/scope-selection-request.ts +21 -0
- package/src/standard-navigation-shell-frame-renderers.tsx +106 -0
- package/src/standard-navigation-shell-slot-renderers.tsx +289 -0
- package/src/view-chrome-slot-renderer-registry.ts +66 -0
|
@@ -0,0 +1,1505 @@
|
|
|
1
|
+
/* eslint-disable react-refresh/only-export-components */
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
useCallback,
|
|
6
|
+
useContext,
|
|
7
|
+
useMemo,
|
|
8
|
+
useState,
|
|
9
|
+
type ReactNode,
|
|
10
|
+
type SetStateAction,
|
|
11
|
+
} from 'react'
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
findPresentationDocumentProfile,
|
|
15
|
+
findPresentationView,
|
|
16
|
+
findPresentationWorkspace,
|
|
17
|
+
type DocumentProfileDefinition,
|
|
18
|
+
type PresentationModuleDefinition,
|
|
19
|
+
type ProjectionDefinition,
|
|
20
|
+
type ViewDefinition,
|
|
21
|
+
type WorkspaceDefinition,
|
|
22
|
+
type WorkspaceRefDefinition,
|
|
23
|
+
} from '@cohesivesystems/presentation-core'
|
|
24
|
+
import { usePresentationModule } from './presentation-module-context'
|
|
25
|
+
import {
|
|
26
|
+
createPresentationEnumDiscriminator,
|
|
27
|
+
findPresentationComponentBinding,
|
|
28
|
+
matchesPresentationEnum,
|
|
29
|
+
} from '@cohesivesystems/presentation-core'
|
|
30
|
+
import {
|
|
31
|
+
coordinationActionKinds,
|
|
32
|
+
coordinationTriggerKinds,
|
|
33
|
+
presentationBindingKinds,
|
|
34
|
+
presentationTargetKinds,
|
|
35
|
+
projectionKindLabels,
|
|
36
|
+
projectionSubjectKindLabels,
|
|
37
|
+
semanticReferenceKindLabels,
|
|
38
|
+
} from '@cohesivesystems/presentation-contracts'
|
|
39
|
+
import type { CoordinationDefinition } from '@cohesivesystems/presentation-contracts'
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Client-side display mode for a document workspace. The available modes are
|
|
43
|
+
* declared by the IR, but the active mode is runtime state owned by the
|
|
44
|
+
* projection boundary.
|
|
45
|
+
*/
|
|
46
|
+
export type DocumentWorkspaceLayout = 'single' | 'split'
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A semantic path is a renderer-neutral addressing for a document location. Tree
|
|
50
|
+
* renderers publish these paths so sibling projections can coordinate without
|
|
51
|
+
* depending on each other's concrete component state.
|
|
52
|
+
*/
|
|
53
|
+
export interface DocumentWorkspaceSemanticPathSegment {
|
|
54
|
+
readonly Kind?: string | number | null
|
|
55
|
+
readonly Id: string
|
|
56
|
+
readonly Label?: string | null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface DocumentWorkspaceSemanticPath {
|
|
60
|
+
readonly Segments: readonly DocumentWorkspaceSemanticPathSegment[]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface DocumentWorkspaceSemanticReference {
|
|
64
|
+
readonly Kind?: string | number | null
|
|
65
|
+
readonly Id: string
|
|
66
|
+
readonly Path?: DocumentWorkspaceSemanticPath | null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface DocumentWorkspaceSemanticSelection {
|
|
70
|
+
readonly Target: DocumentWorkspaceSemanticReference
|
|
71
|
+
readonly SourceProjectionId?: string | null
|
|
72
|
+
readonly SemanticPath?: DocumentWorkspaceSemanticPath | null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface DocumentWorkspaceSemanticTreeNode {
|
|
76
|
+
readonly itemId: string
|
|
77
|
+
readonly relatedSemanticReferences?: readonly DocumentWorkspaceSemanticReference[]
|
|
78
|
+
readonly semanticReference: DocumentWorkspaceSemanticReference
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface DocumentWorkspaceNavigationHistory {
|
|
82
|
+
readonly entries: readonly string[]
|
|
83
|
+
readonly index: number
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface DocumentWorkspaceTreeSearchState {
|
|
87
|
+
readonly activeMatchIndex: number
|
|
88
|
+
readonly isOpen: boolean
|
|
89
|
+
readonly query: string
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface DocumentWorkspaceTreeStateSnapshot {
|
|
93
|
+
readonly expandedItemIds: readonly string[]
|
|
94
|
+
readonly navigationHistory: DocumentWorkspaceNavigationHistory
|
|
95
|
+
readonly search: DocumentWorkspaceTreeSearchState
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface DocumentWorkspaceRuntimeState {
|
|
99
|
+
/**
|
|
100
|
+
* Cross-projection selections, such as a structure-tree item that another
|
|
101
|
+
* document projection may highlight or navigate to.
|
|
102
|
+
*/
|
|
103
|
+
readonly semanticSelections: readonly DocumentWorkspaceSemanticSelection[]
|
|
104
|
+
/**
|
|
105
|
+
* Projection-local interaction state keyed by semantic projection id. Keeping
|
|
106
|
+
* this here lets tree projections remount without losing expansion/search
|
|
107
|
+
* state while the same document workspace instance is active.
|
|
108
|
+
*/
|
|
109
|
+
readonly treeStates: Readonly<Record<string, DocumentWorkspaceTreeStateSnapshot>>
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Fully resolved runtime contract for a semantic document workspace. Route
|
|
114
|
+
* hosts should treat this as the projection boundary: React components receive
|
|
115
|
+
* this snapshot after the IR has resolved the page, workspace, document profile,
|
|
116
|
+
* initial projections, and active client state.
|
|
117
|
+
*/
|
|
118
|
+
export interface DocumentWorkspaceRuntimeSnapshot {
|
|
119
|
+
readonly activeLayoutModeId: string
|
|
120
|
+
readonly activeProjection: ProjectionDefinition | null
|
|
121
|
+
readonly activeProjectionId: string | null
|
|
122
|
+
readonly activeViewId: string | null
|
|
123
|
+
readonly documentProfile: DocumentProfileDefinition
|
|
124
|
+
readonly initialProjectionIds: readonly string[]
|
|
125
|
+
/**
|
|
126
|
+
* Stable key for state that belongs to one opened workspace/document pair.
|
|
127
|
+
* Changing this key intentionally resets local interaction state.
|
|
128
|
+
*/
|
|
129
|
+
readonly instanceKey: string
|
|
130
|
+
readonly layout: DocumentWorkspaceLayout
|
|
131
|
+
/**
|
|
132
|
+
* The route/page-level semantic view. This is distinct from workspaceView:
|
|
133
|
+
* pageView owns page chrome such as document summary metrics and actions.
|
|
134
|
+
*/
|
|
135
|
+
readonly pageView: ViewDefinition
|
|
136
|
+
/**
|
|
137
|
+
* View ids corresponding to the active document profile projections. These
|
|
138
|
+
* ids drive document tabs/switchers, while projection ids remain the semantic
|
|
139
|
+
* identities used by renderers and persisted projection state.
|
|
140
|
+
*/
|
|
141
|
+
readonly projectionViewIds: readonly string[]
|
|
142
|
+
readonly projections: readonly ProjectionDefinition[]
|
|
143
|
+
readonly setActiveViewId: (viewId: string) => void
|
|
144
|
+
readonly setLayout: (layout: DocumentWorkspaceLayout) => void
|
|
145
|
+
readonly state: DocumentWorkspaceRuntimeState
|
|
146
|
+
readonly workspace: WorkspaceDefinition
|
|
147
|
+
readonly workspaceRef: WorkspaceRefDefinition
|
|
148
|
+
/**
|
|
149
|
+
* The semantic workspace surface hosted by the page. This view owns workspace
|
|
150
|
+
* chrome such as view switching, layout switching, and heading trailing state.
|
|
151
|
+
*/
|
|
152
|
+
readonly workspaceView: ViewDefinition
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Context passed to concrete projection renderers after semantic projection
|
|
157
|
+
* resolution. `extra` is intentionally supplied by the app-level binding so the
|
|
158
|
+
* runtime can stay independent from domain-specific document data.
|
|
159
|
+
*/
|
|
160
|
+
export interface DocumentWorkspaceProjectionRendererContext<
|
|
161
|
+
TExtra,
|
|
162
|
+
TComponentSystem = unknown,
|
|
163
|
+
TDesignSystem = unknown,
|
|
164
|
+
> {
|
|
165
|
+
readonly componentSystem: TComponentSystem
|
|
166
|
+
readonly designSystem: TDesignSystem
|
|
167
|
+
readonly extra: TExtra
|
|
168
|
+
readonly projection: ProjectionDefinition
|
|
169
|
+
readonly runtime: DocumentWorkspaceRuntimeSnapshot
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* React renderer for one semantic document workspace projection.
|
|
174
|
+
*
|
|
175
|
+
* Implementations should interpret the resolved `projection` from the supplied
|
|
176
|
+
* context and render only that projection's view/content. Cross-projection
|
|
177
|
+
* coordination, active workspace state, component-system bindings, and
|
|
178
|
+
* app-specific document data are provided through the context so projection
|
|
179
|
+
* renderers can remain ordinary pure React adapters.
|
|
180
|
+
*
|
|
181
|
+
* @typeParam TExtra - App-specific document data and callbacks supplied by the host.
|
|
182
|
+
* @typeParam TComponentSystem - Concrete component-system type used by the renderer.
|
|
183
|
+
* @typeParam TDesignSystem - Concrete design-system type used by the renderer.
|
|
184
|
+
* @param context - Fully resolved projection rendering context.
|
|
185
|
+
* @returns React content for the projection, or `null` when the projection cannot render.
|
|
186
|
+
*/
|
|
187
|
+
export type DocumentWorkspaceProjectionRenderer<
|
|
188
|
+
TExtra,
|
|
189
|
+
TComponentSystem = unknown,
|
|
190
|
+
TDesignSystem = unknown,
|
|
191
|
+
> = (
|
|
192
|
+
context: DocumentWorkspaceProjectionRendererContext<
|
|
193
|
+
TExtra,
|
|
194
|
+
TComponentSystem,
|
|
195
|
+
TDesignSystem
|
|
196
|
+
>,
|
|
197
|
+
) => ReactNode
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Registry of frontend interpretations for document projections. Resolution is
|
|
201
|
+
* target-first: ordinary projections bind through ProjectionRenderer target
|
|
202
|
+
* bindings declared by the backend IR and interpreted by a frontend component
|
|
203
|
+
* pack. Semantic-kind lookup is retained as a diagnostic fallback for older
|
|
204
|
+
* projections whose target component semantics have not been declared yet.
|
|
205
|
+
*/
|
|
206
|
+
export interface DocumentWorkspaceProjectionRendererRegistry<
|
|
207
|
+
TExtra,
|
|
208
|
+
TComponentSystem = unknown,
|
|
209
|
+
TDesignSystem = unknown,
|
|
210
|
+
> {
|
|
211
|
+
/** Renderers keyed by semantic component roles from ProjectionRenderer target bindings. */
|
|
212
|
+
readonly byComponentRole?: Readonly<
|
|
213
|
+
Record<string, DocumentWorkspaceProjectionRenderer<TExtra, TComponentSystem, TDesignSystem>>
|
|
214
|
+
>
|
|
215
|
+
/** Concrete component-key overrides from ProjectionRenderer target bindings. */
|
|
216
|
+
readonly byComponentKey?: Readonly<
|
|
217
|
+
Record<string, DocumentWorkspaceProjectionRenderer<TExtra, TComponentSystem, TDesignSystem>>
|
|
218
|
+
>
|
|
219
|
+
/** Compatibility renderers keyed by projection kind enum value or label. */
|
|
220
|
+
readonly byProjectionKind?: Readonly<
|
|
221
|
+
Record<string, DocumentWorkspaceProjectionRenderer<TExtra, TComponentSystem, TDesignSystem>>
|
|
222
|
+
>
|
|
223
|
+
/** Compatibility renderers keyed by projection subject kind enum value or label. */
|
|
224
|
+
readonly bySubjectKind?: Readonly<
|
|
225
|
+
Record<string, DocumentWorkspaceProjectionRenderer<TExtra, TComponentSystem, TDesignSystem>>
|
|
226
|
+
>
|
|
227
|
+
/** Compatibility renderers keyed by coordinate semantic-reference kind enum value or label. */
|
|
228
|
+
readonly bySemanticReferenceKind?: Readonly<
|
|
229
|
+
Record<string, DocumentWorkspaceProjectionRenderer<TExtra, TComponentSystem, TDesignSystem>>
|
|
230
|
+
>
|
|
231
|
+
/** Last-resort escape hatch keyed by projection id. */
|
|
232
|
+
readonly byProjectionId?: Readonly<
|
|
233
|
+
Record<string, DocumentWorkspaceProjectionRenderer<TExtra, TComponentSystem, TDesignSystem>>
|
|
234
|
+
>
|
|
235
|
+
/** Last-resort escape hatch keyed by projection view id. */
|
|
236
|
+
readonly byViewId?: Readonly<
|
|
237
|
+
Record<string, DocumentWorkspaceProjectionRenderer<TExtra, TComponentSystem, TDesignSystem>>
|
|
238
|
+
>
|
|
239
|
+
/** Renderer used when no semantic or explicit binding matches. */
|
|
240
|
+
readonly fallback?: DocumentWorkspaceProjectionRenderer<
|
|
241
|
+
TExtra,
|
|
242
|
+
TComponentSystem,
|
|
243
|
+
TDesignSystem
|
|
244
|
+
>
|
|
245
|
+
/** Frontend projection capabilities used by IR coordination diagnostics. */
|
|
246
|
+
readonly capabilities?: DocumentWorkspaceProjectionCapabilityRegistry
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface DocumentWorkspaceProjectionCapabilityRegistry {
|
|
250
|
+
readonly revealSemanticSelection?: DocumentWorkspaceProjectionCapabilityBindings
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface DocumentWorkspaceProjectionCapabilityBindings {
|
|
254
|
+
/** Capability keyed by semantic component roles from ProjectionRenderer target bindings. */
|
|
255
|
+
readonly byComponentRole?: Readonly<Record<string, boolean>>
|
|
256
|
+
/** Capability keyed by concrete component-key overrides from ProjectionRenderer target bindings. */
|
|
257
|
+
readonly byComponentKey?: Readonly<Record<string, boolean>>
|
|
258
|
+
/** Capability keyed by projection kind enum value or label. */
|
|
259
|
+
readonly byProjectionKind?: Readonly<Record<string, boolean>>
|
|
260
|
+
/** Capability keyed by projection subject kind enum value or label. */
|
|
261
|
+
readonly bySubjectKind?: Readonly<Record<string, boolean>>
|
|
262
|
+
/** Capability keyed by coordinate semantic-reference kind enum value or label. */
|
|
263
|
+
readonly bySemanticReferenceKind?: Readonly<Record<string, boolean>>
|
|
264
|
+
/** Last-resort escape hatch keyed by projection id. */
|
|
265
|
+
readonly byProjectionId?: Readonly<Record<string, boolean>>
|
|
266
|
+
/** Last-resort escape hatch keyed by projection view id. */
|
|
267
|
+
readonly byViewId?: Readonly<Record<string, boolean>>
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export type DocumentWorkspaceProjectionRendererResolutionSource =
|
|
271
|
+
| 'component-key'
|
|
272
|
+
| 'component-role'
|
|
273
|
+
| 'fallback'
|
|
274
|
+
| 'projection-id'
|
|
275
|
+
| 'projection-kind'
|
|
276
|
+
| 'semantic-reference-kind'
|
|
277
|
+
| 'subject-kind'
|
|
278
|
+
| 'view-id'
|
|
279
|
+
|
|
280
|
+
export interface DocumentWorkspaceProjectionRendererResolution<
|
|
281
|
+
TExtra,
|
|
282
|
+
TComponentSystem = unknown,
|
|
283
|
+
TDesignSystem = unknown,
|
|
284
|
+
> {
|
|
285
|
+
readonly componentKey: string | null
|
|
286
|
+
readonly componentRole: string | null
|
|
287
|
+
readonly projection: ProjectionDefinition | null
|
|
288
|
+
readonly renderer:
|
|
289
|
+
| DocumentWorkspaceProjectionRenderer<TExtra, TComponentSystem, TDesignSystem>
|
|
290
|
+
| null
|
|
291
|
+
readonly rendererKey: string | null
|
|
292
|
+
readonly resolutionSource: DocumentWorkspaceProjectionRendererResolutionSource | null
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export interface RenderDocumentWorkspaceProjectionOptions<
|
|
296
|
+
TComponentSystem = unknown,
|
|
297
|
+
TDesignSystem = unknown,
|
|
298
|
+
> {
|
|
299
|
+
readonly componentSet?: string | null
|
|
300
|
+
readonly componentSystem: TComponentSystem
|
|
301
|
+
readonly designSystem: TDesignSystem
|
|
302
|
+
readonly module?: PresentationModuleDefinition | null
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
interface DocumentWorkspaceRuntimeContextValue {
|
|
306
|
+
readonly coordination: readonly CoordinationDefinition[]
|
|
307
|
+
readonly instanceKey: string
|
|
308
|
+
readonly selectSemanticReference: (
|
|
309
|
+
selection: DocumentWorkspaceSemanticSelection,
|
|
310
|
+
) => void
|
|
311
|
+
readonly setTreeExpandedItemIds: (
|
|
312
|
+
projectionId: string,
|
|
313
|
+
value: SetStateAction<readonly string[]>,
|
|
314
|
+
) => void
|
|
315
|
+
readonly setTreeNavigationHistory: (
|
|
316
|
+
projectionId: string,
|
|
317
|
+
value: SetStateAction<DocumentWorkspaceNavigationHistory>,
|
|
318
|
+
) => void
|
|
319
|
+
readonly setTreeSearch: (
|
|
320
|
+
projectionId: string,
|
|
321
|
+
value: SetStateAction<DocumentWorkspaceTreeSearchState>,
|
|
322
|
+
) => void
|
|
323
|
+
readonly state: DocumentWorkspaceRuntimeState
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const DocumentWorkspaceRuntimeContext =
|
|
327
|
+
createContext<DocumentWorkspaceRuntimeContextValue | null>(null)
|
|
328
|
+
|
|
329
|
+
export interface DocumentWorkspaceRuntimeProps {
|
|
330
|
+
readonly children: (runtime: DocumentWorkspaceRuntimeSnapshot) => ReactNode
|
|
331
|
+
readonly documentInstanceId?: string | null
|
|
332
|
+
readonly fallback?: ReactNode
|
|
333
|
+
/**
|
|
334
|
+
* Route/page semantic view id. The runtime resolves the workspace hosted by
|
|
335
|
+
* this page rather than assuming the page itself is the workspace surface.
|
|
336
|
+
*/
|
|
337
|
+
readonly pageViewId: string
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Resolves the document workspace declared by the presentation IR and provides
|
|
342
|
+
* the active client-side runtime snapshot to projection renderers. This is the
|
|
343
|
+
* point where a route page becomes a semantic workspace instance.
|
|
344
|
+
*/
|
|
345
|
+
export function DocumentWorkspaceRuntime({
|
|
346
|
+
children,
|
|
347
|
+
documentInstanceId,
|
|
348
|
+
fallback = null,
|
|
349
|
+
pageViewId,
|
|
350
|
+
}: DocumentWorkspaceRuntimeProps) {
|
|
351
|
+
const module = usePresentationModule()
|
|
352
|
+
const resolution = useMemo(
|
|
353
|
+
() => resolveDocumentWorkspace(module, pageViewId),
|
|
354
|
+
[module, pageViewId],
|
|
355
|
+
)
|
|
356
|
+
const documentProfile = resolution?.documentProfile ?? null
|
|
357
|
+
const projections = useMemo(
|
|
358
|
+
() => documentProfile?.Projections ?? [],
|
|
359
|
+
[documentProfile],
|
|
360
|
+
)
|
|
361
|
+
const initialProjectionIds = useMemo(
|
|
362
|
+
() => resolveInitialProjectionIds(resolution?.workspaceRef ?? null, projections),
|
|
363
|
+
[projections, resolution?.workspaceRef],
|
|
364
|
+
)
|
|
365
|
+
const projectionViewIds = useMemo(
|
|
366
|
+
() =>
|
|
367
|
+
initialProjectionIds
|
|
368
|
+
.map((projectionId) => projections.find((projection) => projection.Id === projectionId))
|
|
369
|
+
.map((projection) => projection?.ViewId ?? projection?.Id ?? null)
|
|
370
|
+
.filter((viewId): viewId is string => Boolean(viewId)),
|
|
371
|
+
[initialProjectionIds, projections],
|
|
372
|
+
)
|
|
373
|
+
const defaultProjectionId = initialProjectionIds[0] ?? projections[0]?.Id ?? null
|
|
374
|
+
// Scope runtime state to the semantic workspace/profile and the concrete
|
|
375
|
+
// document instance so navigating between documents does not leak state.
|
|
376
|
+
const instanceKey = [
|
|
377
|
+
resolution?.workspaceRef.WorkspaceId ?? pageViewId,
|
|
378
|
+
resolution?.documentProfile.Id ?? 'default',
|
|
379
|
+
documentInstanceId ?? 'transient',
|
|
380
|
+
].join(':')
|
|
381
|
+
const [activeProjectionState, setActiveProjectionState] = useState<{
|
|
382
|
+
readonly instanceKey: string
|
|
383
|
+
readonly projectionId: string | null
|
|
384
|
+
}>(() => ({ instanceKey, projectionId: defaultProjectionId }))
|
|
385
|
+
const [layoutState, setLayoutState] = useState<{
|
|
386
|
+
readonly instanceKey: string
|
|
387
|
+
readonly value: DocumentWorkspaceLayout
|
|
388
|
+
}>(() => ({
|
|
389
|
+
instanceKey,
|
|
390
|
+
value: resolveDefaultLayout(resolution?.documentProfile.Layout ?? null),
|
|
391
|
+
}))
|
|
392
|
+
const [runtimeState, setRuntimeState] = useState<{
|
|
393
|
+
readonly instanceKey: string
|
|
394
|
+
readonly value: DocumentWorkspaceRuntimeState
|
|
395
|
+
}>(() => ({ instanceKey, value: createInitialRuntimeState() }))
|
|
396
|
+
|
|
397
|
+
const runtimeStateValue =
|
|
398
|
+
runtimeState.instanceKey === instanceKey
|
|
399
|
+
? runtimeState.value
|
|
400
|
+
: createInitialRuntimeState()
|
|
401
|
+
const coordinationDefinitions = resolution?.documentProfile.Coordination
|
|
402
|
+
const coordination = useMemo(
|
|
403
|
+
() => coordinationDefinitions ?? [],
|
|
404
|
+
[coordinationDefinitions],
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
const setTreeExpandedItemIds = useCallback(
|
|
408
|
+
(projectionId: string, value: SetStateAction<readonly string[]>) => {
|
|
409
|
+
setRuntimeState((current) =>
|
|
410
|
+
updateRuntimeTreeState(current, instanceKey, projectionId, (treeState) => ({
|
|
411
|
+
...treeState,
|
|
412
|
+
expandedItemIds: applySetStateAction(value, treeState.expandedItemIds),
|
|
413
|
+
})),
|
|
414
|
+
)
|
|
415
|
+
},
|
|
416
|
+
[instanceKey],
|
|
417
|
+
)
|
|
418
|
+
const setTreeNavigationHistory = useCallback(
|
|
419
|
+
(
|
|
420
|
+
projectionId: string,
|
|
421
|
+
value: SetStateAction<DocumentWorkspaceNavigationHistory>,
|
|
422
|
+
) => {
|
|
423
|
+
setRuntimeState((current) =>
|
|
424
|
+
updateRuntimeTreeState(current, instanceKey, projectionId, (treeState) => ({
|
|
425
|
+
...treeState,
|
|
426
|
+
navigationHistory: applySetStateAction(value, treeState.navigationHistory),
|
|
427
|
+
})),
|
|
428
|
+
)
|
|
429
|
+
},
|
|
430
|
+
[instanceKey],
|
|
431
|
+
)
|
|
432
|
+
const setTreeSearch = useCallback(
|
|
433
|
+
(projectionId: string, value: SetStateAction<DocumentWorkspaceTreeSearchState>) => {
|
|
434
|
+
setRuntimeState((current) =>
|
|
435
|
+
updateRuntimeTreeState(current, instanceKey, projectionId, (treeState) => ({
|
|
436
|
+
...treeState,
|
|
437
|
+
search: applySetStateAction(value, treeState.search),
|
|
438
|
+
})),
|
|
439
|
+
)
|
|
440
|
+
},
|
|
441
|
+
[instanceKey],
|
|
442
|
+
)
|
|
443
|
+
const selectSemanticReference = useCallback(
|
|
444
|
+
(selection: DocumentWorkspaceSemanticSelection) => {
|
|
445
|
+
setRuntimeState((current) => {
|
|
446
|
+
const base =
|
|
447
|
+
current.instanceKey === instanceKey
|
|
448
|
+
? current.value
|
|
449
|
+
: createInitialRuntimeState()
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
instanceKey,
|
|
453
|
+
value: {
|
|
454
|
+
...base,
|
|
455
|
+
semanticSelections: createCoordinatedSemanticSelections(selection, coordination),
|
|
456
|
+
},
|
|
457
|
+
}
|
|
458
|
+
})
|
|
459
|
+
},
|
|
460
|
+
[coordination, instanceKey],
|
|
461
|
+
)
|
|
462
|
+
const contextValue = useMemo<DocumentWorkspaceRuntimeContextValue>(
|
|
463
|
+
() => ({
|
|
464
|
+
coordination,
|
|
465
|
+
instanceKey,
|
|
466
|
+
selectSemanticReference,
|
|
467
|
+
setTreeExpandedItemIds,
|
|
468
|
+
setTreeNavigationHistory,
|
|
469
|
+
setTreeSearch,
|
|
470
|
+
state: runtimeStateValue,
|
|
471
|
+
}),
|
|
472
|
+
[
|
|
473
|
+
coordination,
|
|
474
|
+
instanceKey,
|
|
475
|
+
runtimeStateValue,
|
|
476
|
+
selectSemanticReference,
|
|
477
|
+
setTreeExpandedItemIds,
|
|
478
|
+
setTreeNavigationHistory,
|
|
479
|
+
setTreeSearch,
|
|
480
|
+
],
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
if (!resolution || !defaultProjectionId) {
|
|
484
|
+
return <>{fallback}</>
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const activeProjectionId =
|
|
488
|
+
activeProjectionState.instanceKey === instanceKey &&
|
|
489
|
+
activeProjectionState.projectionId &&
|
|
490
|
+
initialProjectionIds.includes(activeProjectionState.projectionId)
|
|
491
|
+
? activeProjectionState.projectionId
|
|
492
|
+
: defaultProjectionId
|
|
493
|
+
const activeProjection =
|
|
494
|
+
projections.find((projection) => projection.Id === activeProjectionId) ?? null
|
|
495
|
+
const activeViewId = activeProjection?.ViewId ?? activeProjection?.Id ?? null
|
|
496
|
+
const layout =
|
|
497
|
+
layoutState.instanceKey === instanceKey
|
|
498
|
+
? layoutState.value
|
|
499
|
+
: resolveDefaultLayout(resolution.documentProfile.Layout ?? null)
|
|
500
|
+
const activeLayoutModeId = resolveActiveLayoutModeId(
|
|
501
|
+
layout,
|
|
502
|
+
resolution.documentProfile.Layout ?? null,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
function setActiveViewId(viewId: string) {
|
|
506
|
+
const projection = projections.find(
|
|
507
|
+
(candidate) => candidate.ViewId === viewId || candidate.Id === viewId,
|
|
508
|
+
)
|
|
509
|
+
if (!projection) {
|
|
510
|
+
return
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
setActiveProjectionState({ instanceKey, projectionId: projection.Id })
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function setLayout(value: DocumentWorkspaceLayout) {
|
|
517
|
+
setLayoutState({ instanceKey, value })
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const snapshot: DocumentWorkspaceRuntimeSnapshot = {
|
|
521
|
+
activeLayoutModeId,
|
|
522
|
+
activeProjection,
|
|
523
|
+
activeProjectionId,
|
|
524
|
+
activeViewId,
|
|
525
|
+
documentProfile: resolution.documentProfile,
|
|
526
|
+
initialProjectionIds,
|
|
527
|
+
instanceKey,
|
|
528
|
+
layout,
|
|
529
|
+
pageView: resolution.pageView,
|
|
530
|
+
projectionViewIds,
|
|
531
|
+
projections,
|
|
532
|
+
setActiveViewId,
|
|
533
|
+
setLayout,
|
|
534
|
+
state: runtimeStateValue,
|
|
535
|
+
workspace: resolution.workspace,
|
|
536
|
+
workspaceRef: resolution.workspaceRef,
|
|
537
|
+
workspaceView: resolution.workspaceView,
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
return (
|
|
541
|
+
<DocumentWorkspaceRuntimeContext.Provider value={contextValue}>
|
|
542
|
+
{children(snapshot)}
|
|
543
|
+
</DocumentWorkspaceRuntimeContext.Provider>
|
|
544
|
+
)
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
export function renderDocumentWorkspaceProjection<
|
|
548
|
+
TExtra,
|
|
549
|
+
TComponentSystem,
|
|
550
|
+
TDesignSystem,
|
|
551
|
+
>(
|
|
552
|
+
runtime: DocumentWorkspaceRuntimeSnapshot,
|
|
553
|
+
renderers: DocumentWorkspaceProjectionRendererRegistry<
|
|
554
|
+
TExtra,
|
|
555
|
+
TComponentSystem,
|
|
556
|
+
TDesignSystem
|
|
557
|
+
>,
|
|
558
|
+
rendererContext: TExtra,
|
|
559
|
+
viewId: string,
|
|
560
|
+
options: RenderDocumentWorkspaceProjectionOptions<TComponentSystem, TDesignSystem>,
|
|
561
|
+
) {
|
|
562
|
+
const resolution = resolveDocumentWorkspaceProjectionRenderer({
|
|
563
|
+
componentSet: options.componentSet,
|
|
564
|
+
module: options.module ?? null,
|
|
565
|
+
registry: renderers,
|
|
566
|
+
runtime,
|
|
567
|
+
viewId,
|
|
568
|
+
})
|
|
569
|
+
|
|
570
|
+
if (!resolution.projection || !resolution.renderer) {
|
|
571
|
+
return null
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
return resolution.renderer({
|
|
575
|
+
componentSystem: options.componentSystem,
|
|
576
|
+
designSystem: options.designSystem,
|
|
577
|
+
extra: rendererContext,
|
|
578
|
+
projection: resolution.projection,
|
|
579
|
+
runtime,
|
|
580
|
+
})
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export function resolveDocumentWorkspaceProjectionRenderer<
|
|
584
|
+
TExtra,
|
|
585
|
+
TComponentSystem = unknown,
|
|
586
|
+
TDesignSystem = unknown,
|
|
587
|
+
>({
|
|
588
|
+
componentSet,
|
|
589
|
+
module,
|
|
590
|
+
registry,
|
|
591
|
+
runtime,
|
|
592
|
+
viewId,
|
|
593
|
+
}: {
|
|
594
|
+
readonly componentSet?: string | null
|
|
595
|
+
readonly module?: PresentationModuleDefinition | null
|
|
596
|
+
readonly registry: DocumentWorkspaceProjectionRendererRegistry<
|
|
597
|
+
TExtra,
|
|
598
|
+
TComponentSystem,
|
|
599
|
+
TDesignSystem
|
|
600
|
+
>
|
|
601
|
+
readonly runtime: DocumentWorkspaceRuntimeSnapshot
|
|
602
|
+
readonly viewId: string
|
|
603
|
+
}): DocumentWorkspaceProjectionRendererResolution<TExtra, TComponentSystem, TDesignSystem> {
|
|
604
|
+
// View ids are used by workspace chrome, while projection ids and renderer
|
|
605
|
+
// keys are semantic/runtime binding points. Accept either id at this boundary.
|
|
606
|
+
const projection = runtime.projections.find(
|
|
607
|
+
(candidate) => candidate.ViewId === viewId || candidate.Id === viewId,
|
|
608
|
+
)
|
|
609
|
+
if (!projection) {
|
|
610
|
+
return createDocumentProjectionRendererResolution(null)
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const componentBinding = resolveDocumentWorkspaceProjectionComponentBinding(
|
|
614
|
+
module ?? null,
|
|
615
|
+
projection,
|
|
616
|
+
componentSet,
|
|
617
|
+
)
|
|
618
|
+
const componentKey = componentBinding?.componentKey ?? null
|
|
619
|
+
const componentRole = componentBinding?.componentRole ?? null
|
|
620
|
+
const rendererKey = projection.RendererKey ?? null
|
|
621
|
+
|
|
622
|
+
const componentRoleRenderer = componentRole
|
|
623
|
+
? registry.byComponentRole?.[componentRole]
|
|
624
|
+
: undefined
|
|
625
|
+
if (componentRoleRenderer) {
|
|
626
|
+
return {
|
|
627
|
+
componentKey,
|
|
628
|
+
componentRole,
|
|
629
|
+
projection,
|
|
630
|
+
renderer: componentRoleRenderer,
|
|
631
|
+
rendererKey,
|
|
632
|
+
resolutionSource: 'component-role',
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const componentRenderer = componentKey
|
|
637
|
+
? registry.byComponentKey?.[componentKey]
|
|
638
|
+
: undefined
|
|
639
|
+
if (componentRenderer) {
|
|
640
|
+
return {
|
|
641
|
+
componentKey,
|
|
642
|
+
componentRole,
|
|
643
|
+
projection,
|
|
644
|
+
renderer: componentRenderer,
|
|
645
|
+
rendererKey,
|
|
646
|
+
resolutionSource: 'component-key',
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const semanticReferenceRenderer = findDiscriminatorRenderer(
|
|
651
|
+
registry.bySemanticReferenceKind,
|
|
652
|
+
projection.Coordinates?.SemanticReferenceKind,
|
|
653
|
+
semanticReferenceKindLabels,
|
|
654
|
+
)
|
|
655
|
+
if (semanticReferenceRenderer) {
|
|
656
|
+
return {
|
|
657
|
+
componentKey,
|
|
658
|
+
componentRole,
|
|
659
|
+
projection,
|
|
660
|
+
renderer: semanticReferenceRenderer,
|
|
661
|
+
rendererKey,
|
|
662
|
+
resolutionSource: 'semantic-reference-kind',
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const subjectKindRenderer = findDiscriminatorRenderer(
|
|
667
|
+
registry.bySubjectKind,
|
|
668
|
+
projection.Subject.Kind,
|
|
669
|
+
projectionSubjectKindLabels,
|
|
670
|
+
)
|
|
671
|
+
if (subjectKindRenderer) {
|
|
672
|
+
return {
|
|
673
|
+
componentKey,
|
|
674
|
+
componentRole,
|
|
675
|
+
projection,
|
|
676
|
+
renderer: subjectKindRenderer,
|
|
677
|
+
rendererKey,
|
|
678
|
+
resolutionSource: 'subject-kind',
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const projectionKindRenderer = findDiscriminatorRenderer(
|
|
683
|
+
registry.byProjectionKind,
|
|
684
|
+
projection.Kind,
|
|
685
|
+
projectionKindLabels,
|
|
686
|
+
)
|
|
687
|
+
if (projectionKindRenderer) {
|
|
688
|
+
return {
|
|
689
|
+
componentKey,
|
|
690
|
+
componentRole,
|
|
691
|
+
projection,
|
|
692
|
+
renderer: projectionKindRenderer,
|
|
693
|
+
rendererKey,
|
|
694
|
+
resolutionSource: 'projection-kind',
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const projectionIdRenderer = registry.byProjectionId?.[projection.Id]
|
|
699
|
+
if (projectionIdRenderer) {
|
|
700
|
+
return {
|
|
701
|
+
componentKey,
|
|
702
|
+
componentRole,
|
|
703
|
+
projection,
|
|
704
|
+
renderer: projectionIdRenderer,
|
|
705
|
+
rendererKey,
|
|
706
|
+
resolutionSource: 'projection-id',
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const viewIdRenderer = projection.ViewId ? registry.byViewId?.[projection.ViewId] : undefined
|
|
711
|
+
if (viewIdRenderer) {
|
|
712
|
+
return {
|
|
713
|
+
componentKey,
|
|
714
|
+
componentRole,
|
|
715
|
+
projection,
|
|
716
|
+
renderer: viewIdRenderer,
|
|
717
|
+
rendererKey,
|
|
718
|
+
resolutionSource: 'view-id',
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
if (registry.fallback) {
|
|
723
|
+
return {
|
|
724
|
+
componentKey,
|
|
725
|
+
componentRole,
|
|
726
|
+
projection,
|
|
727
|
+
renderer: registry.fallback,
|
|
728
|
+
rendererKey,
|
|
729
|
+
resolutionSource: 'fallback',
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
return {
|
|
734
|
+
componentKey,
|
|
735
|
+
componentRole,
|
|
736
|
+
projection,
|
|
737
|
+
renderer: null,
|
|
738
|
+
rendererKey,
|
|
739
|
+
resolutionSource: null,
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
export function resolveDocumentWorkspaceProjectionCapability<
|
|
744
|
+
TExtra,
|
|
745
|
+
TComponentSystem = unknown,
|
|
746
|
+
TDesignSystem = unknown,
|
|
747
|
+
>({
|
|
748
|
+
capability,
|
|
749
|
+
componentSet,
|
|
750
|
+
module,
|
|
751
|
+
projection,
|
|
752
|
+
registry,
|
|
753
|
+
}: {
|
|
754
|
+
readonly capability: keyof DocumentWorkspaceProjectionCapabilityRegistry
|
|
755
|
+
readonly componentSet?: string | null
|
|
756
|
+
readonly module?: PresentationModuleDefinition | null
|
|
757
|
+
readonly projection: ProjectionDefinition
|
|
758
|
+
readonly registry: DocumentWorkspaceProjectionRendererRegistry<
|
|
759
|
+
TExtra,
|
|
760
|
+
TComponentSystem,
|
|
761
|
+
TDesignSystem
|
|
762
|
+
>
|
|
763
|
+
}) {
|
|
764
|
+
const bindings = registry.capabilities?.[capability]
|
|
765
|
+
if (!bindings) {
|
|
766
|
+
return false
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
const componentBinding = resolveDocumentWorkspaceProjectionComponentBinding(
|
|
770
|
+
module ?? null,
|
|
771
|
+
projection,
|
|
772
|
+
componentSet,
|
|
773
|
+
)
|
|
774
|
+
const componentKey = componentBinding?.componentKey ?? null
|
|
775
|
+
const componentRole = componentBinding?.componentRole ?? null
|
|
776
|
+
|
|
777
|
+
if (componentRole && bindings.byComponentRole?.[componentRole]) {
|
|
778
|
+
return true
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
if (componentKey && bindings.byComponentKey?.[componentKey]) {
|
|
782
|
+
return true
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (findDiscriminatorRenderer(
|
|
786
|
+
bindings.bySemanticReferenceKind,
|
|
787
|
+
projection.Coordinates?.SemanticReferenceKind,
|
|
788
|
+
semanticReferenceKindLabels,
|
|
789
|
+
)) {
|
|
790
|
+
return true
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if (
|
|
794
|
+
findDiscriminatorRenderer(
|
|
795
|
+
bindings.bySubjectKind,
|
|
796
|
+
projection.Subject.Kind,
|
|
797
|
+
projectionSubjectKindLabels,
|
|
798
|
+
)
|
|
799
|
+
) {
|
|
800
|
+
return true
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if (
|
|
804
|
+
findDiscriminatorRenderer(
|
|
805
|
+
bindings.byProjectionKind,
|
|
806
|
+
projection.Kind,
|
|
807
|
+
projectionKindLabels,
|
|
808
|
+
)
|
|
809
|
+
) {
|
|
810
|
+
return true
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
return Boolean(
|
|
814
|
+
bindings.byProjectionId?.[projection.Id] ||
|
|
815
|
+
(projection.ViewId ? bindings.byViewId?.[projection.ViewId] : false),
|
|
816
|
+
)
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Resolves the frontend renderer role and optional concrete component override
|
|
821
|
+
* declared by a presentation target binding for a document projection.
|
|
822
|
+
*/
|
|
823
|
+
function resolveDocumentWorkspaceProjectionComponentBinding(
|
|
824
|
+
module: PresentationModuleDefinition | null,
|
|
825
|
+
projection: ProjectionDefinition,
|
|
826
|
+
componentSet?: string | null,
|
|
827
|
+
) {
|
|
828
|
+
if (!module) {
|
|
829
|
+
return null
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
const binding = findPresentationComponentBinding(module, {
|
|
833
|
+
bindingKind: createPresentationEnumDiscriminator(
|
|
834
|
+
presentationBindingKinds,
|
|
835
|
+
'projectionRenderer',
|
|
836
|
+
'ProjectionRenderer',
|
|
837
|
+
),
|
|
838
|
+
componentSet,
|
|
839
|
+
id: projection.Id,
|
|
840
|
+
targetKind: createPresentationEnumDiscriminator(
|
|
841
|
+
presentationTargetKinds,
|
|
842
|
+
'react',
|
|
843
|
+
'React',
|
|
844
|
+
),
|
|
845
|
+
})
|
|
846
|
+
|
|
847
|
+
if (!binding) {
|
|
848
|
+
return null
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
return {
|
|
852
|
+
componentKey: binding.ComponentKey ?? null,
|
|
853
|
+
componentRole: binding.ComponentRole ?? null,
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* Shared tree-state hook for document projections. It uses workspace runtime
|
|
859
|
+
* state when mounted under DocumentWorkspaceRuntime, and falls back to local
|
|
860
|
+
* component state when a tree renderer is tested or rendered in isolation.
|
|
861
|
+
*/
|
|
862
|
+
export function useDocumentWorkspaceTreeState({
|
|
863
|
+
createSemanticSelection,
|
|
864
|
+
initialExpandedItemIds,
|
|
865
|
+
projectionId,
|
|
866
|
+
semanticReferenceKind,
|
|
867
|
+
}: {
|
|
868
|
+
readonly createSemanticSelection?: (
|
|
869
|
+
itemId: string,
|
|
870
|
+
) => DocumentWorkspaceSemanticSelection | null
|
|
871
|
+
readonly initialExpandedItemIds: readonly string[]
|
|
872
|
+
readonly projectionId?: string | null
|
|
873
|
+
readonly semanticReferenceKind?: string | number | null
|
|
874
|
+
}) {
|
|
875
|
+
const runtime = useContext(DocumentWorkspaceRuntimeContext)
|
|
876
|
+
const runtimeState = runtime?.state
|
|
877
|
+
const selectSemanticReference = runtime?.selectSemanticReference
|
|
878
|
+
const setRuntimeTreeExpandedItemIds = runtime?.setTreeExpandedItemIds
|
|
879
|
+
const setRuntimeTreeNavigationHistory = runtime?.setTreeNavigationHistory
|
|
880
|
+
const setRuntimeTreeSearch = runtime?.setTreeSearch
|
|
881
|
+
const initialExpandedItemKey = initialExpandedItemIds.join('\u0000')
|
|
882
|
+
const [localExpandedItemState, setLocalExpandedItemState] = useState(() => ({
|
|
883
|
+
expandedItemIds: initialExpandedItemIds,
|
|
884
|
+
initialExpandedItemKey,
|
|
885
|
+
isUserControlled: false,
|
|
886
|
+
}))
|
|
887
|
+
const [localNavigationHistory, setLocalNavigationHistory] =
|
|
888
|
+
useState<DocumentWorkspaceNavigationHistory>(createInitialNavigationHistory)
|
|
889
|
+
const [localSearch, setLocalSearch] =
|
|
890
|
+
useState<DocumentWorkspaceTreeSearchState>(createInitialTreeSearchState)
|
|
891
|
+
const persistedTreeState =
|
|
892
|
+
runtimeState && projectionId ? runtimeState.treeStates[projectionId] : undefined
|
|
893
|
+
const treeState =
|
|
894
|
+
persistedTreeState ??
|
|
895
|
+
(runtimeState && projectionId ? createInitialTreeState() : null)
|
|
896
|
+
const localExpandedItemIds =
|
|
897
|
+
localExpandedItemState.isUserControlled ||
|
|
898
|
+
localExpandedItemState.initialExpandedItemKey === initialExpandedItemKey
|
|
899
|
+
? localExpandedItemState.expandedItemIds
|
|
900
|
+
: initialExpandedItemIds
|
|
901
|
+
const expandedItemIds =
|
|
902
|
+
persistedTreeState?.expandedItemIds ??
|
|
903
|
+
(runtimeState && projectionId
|
|
904
|
+
? initialExpandedItemIds
|
|
905
|
+
: localExpandedItemIds)
|
|
906
|
+
const navigationHistory = treeState?.navigationHistory ?? localNavigationHistory
|
|
907
|
+
const search = treeState?.search ?? localSearch
|
|
908
|
+
|
|
909
|
+
const setExpandedItemIds = useCallback(
|
|
910
|
+
(value: SetStateAction<readonly string[]>) => {
|
|
911
|
+
if (setRuntimeTreeExpandedItemIds && projectionId) {
|
|
912
|
+
setRuntimeTreeExpandedItemIds(projectionId, value)
|
|
913
|
+
} else {
|
|
914
|
+
setLocalExpandedItemState((current) => ({
|
|
915
|
+
expandedItemIds: applySetStateAction(
|
|
916
|
+
value,
|
|
917
|
+
current.isUserControlled ||
|
|
918
|
+
current.initialExpandedItemKey === initialExpandedItemKey
|
|
919
|
+
? current.expandedItemIds
|
|
920
|
+
: initialExpandedItemIds,
|
|
921
|
+
),
|
|
922
|
+
initialExpandedItemKey,
|
|
923
|
+
isUserControlled: true,
|
|
924
|
+
}))
|
|
925
|
+
}
|
|
926
|
+
},
|
|
927
|
+
[
|
|
928
|
+
initialExpandedItemIds,
|
|
929
|
+
initialExpandedItemKey,
|
|
930
|
+
projectionId,
|
|
931
|
+
setRuntimeTreeExpandedItemIds,
|
|
932
|
+
],
|
|
933
|
+
)
|
|
934
|
+
const setNavigationHistory = useCallback(
|
|
935
|
+
(value: SetStateAction<DocumentWorkspaceNavigationHistory>) => {
|
|
936
|
+
if (setRuntimeTreeNavigationHistory && projectionId) {
|
|
937
|
+
setRuntimeTreeNavigationHistory(projectionId, value)
|
|
938
|
+
} else {
|
|
939
|
+
setLocalNavigationHistory(value)
|
|
940
|
+
}
|
|
941
|
+
},
|
|
942
|
+
[projectionId, setRuntimeTreeNavigationHistory],
|
|
943
|
+
)
|
|
944
|
+
const setSearch = useCallback(
|
|
945
|
+
(value: SetStateAction<DocumentWorkspaceTreeSearchState>) => {
|
|
946
|
+
if (setRuntimeTreeSearch && projectionId) {
|
|
947
|
+
setRuntimeTreeSearch(projectionId, value)
|
|
948
|
+
} else {
|
|
949
|
+
setLocalSearch(value)
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
[projectionId, setRuntimeTreeSearch],
|
|
953
|
+
)
|
|
954
|
+
const publishSelectedItem = useCallback(
|
|
955
|
+
(itemId: string | null) => {
|
|
956
|
+
if (!selectSemanticReference || !projectionId || !itemId) {
|
|
957
|
+
return
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
selectSemanticReference(
|
|
961
|
+
normalizeTreeSemanticSelection(
|
|
962
|
+
createSemanticSelection?.(itemId) ??
|
|
963
|
+
createDefaultTreeSemanticSelection(itemId, projectionId, semanticReferenceKind),
|
|
964
|
+
projectionId,
|
|
965
|
+
semanticReferenceKind,
|
|
966
|
+
),
|
|
967
|
+
)
|
|
968
|
+
},
|
|
969
|
+
[createSemanticSelection, projectionId, selectSemanticReference, semanticReferenceKind],
|
|
970
|
+
)
|
|
971
|
+
const semanticSelections = useMemo(
|
|
972
|
+
() =>
|
|
973
|
+
filterSemanticSelectionsForProjection({
|
|
974
|
+
coordination: runtime?.coordination ?? [],
|
|
975
|
+
projectionId,
|
|
976
|
+
selections: runtimeState?.semanticSelections ?? [],
|
|
977
|
+
}),
|
|
978
|
+
[projectionId, runtime?.coordination, runtimeState?.semanticSelections],
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
return {
|
|
982
|
+
expandedItemIds,
|
|
983
|
+
navigationHistory,
|
|
984
|
+
onSelectedItemIdChange: publishSelectedItem,
|
|
985
|
+
search,
|
|
986
|
+
semanticSelections,
|
|
987
|
+
setExpandedItemIds,
|
|
988
|
+
setNavigationHistory,
|
|
989
|
+
setSearch,
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
export function useDocumentWorkspaceSemanticSelections({
|
|
994
|
+
projectionId,
|
|
995
|
+
}: {
|
|
996
|
+
readonly projectionId?: string | null
|
|
997
|
+
}) {
|
|
998
|
+
const runtime = useContext(DocumentWorkspaceRuntimeContext)
|
|
999
|
+
const runtimeState = runtime?.state
|
|
1000
|
+
return useMemo(
|
|
1001
|
+
() =>
|
|
1002
|
+
filterSemanticSelectionsForProjection({
|
|
1003
|
+
coordination: runtime?.coordination ?? [],
|
|
1004
|
+
projectionId,
|
|
1005
|
+
selections: runtimeState?.semanticSelections ?? [],
|
|
1006
|
+
}),
|
|
1007
|
+
[projectionId, runtime?.coordination, runtimeState?.semanticSelections],
|
|
1008
|
+
)
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
export function useDocumentWorkspaceSemanticSelectionPublisher({
|
|
1012
|
+
projectionId,
|
|
1013
|
+
semanticReferenceKind,
|
|
1014
|
+
}: {
|
|
1015
|
+
readonly projectionId?: string | null
|
|
1016
|
+
readonly semanticReferenceKind?: string | number | null
|
|
1017
|
+
}) {
|
|
1018
|
+
const runtime = useContext(DocumentWorkspaceRuntimeContext)
|
|
1019
|
+
const selectSemanticReference = runtime?.selectSemanticReference
|
|
1020
|
+
return useCallback(
|
|
1021
|
+
(selection: DocumentWorkspaceSemanticSelection) => {
|
|
1022
|
+
if (!projectionId || !selectSemanticReference) {
|
|
1023
|
+
return
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
selectSemanticReference(
|
|
1027
|
+
normalizeTreeSemanticSelection(
|
|
1028
|
+
selection,
|
|
1029
|
+
projectionId,
|
|
1030
|
+
semanticReferenceKind,
|
|
1031
|
+
),
|
|
1032
|
+
)
|
|
1033
|
+
},
|
|
1034
|
+
[projectionId, selectSemanticReference, semanticReferenceKind],
|
|
1035
|
+
)
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
export function createDocumentWorkspaceSemanticPath(
|
|
1039
|
+
segments: readonly DocumentWorkspaceSemanticPathSegment[],
|
|
1040
|
+
): DocumentWorkspaceSemanticPath {
|
|
1041
|
+
return { Segments: segments }
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
export function createDocumentWorkspaceSemanticReference({
|
|
1045
|
+
id,
|
|
1046
|
+
kind,
|
|
1047
|
+
label,
|
|
1048
|
+
path,
|
|
1049
|
+
}: {
|
|
1050
|
+
readonly id: string
|
|
1051
|
+
readonly kind?: string | number | null
|
|
1052
|
+
readonly label?: string | null
|
|
1053
|
+
readonly path?: DocumentWorkspaceSemanticPath | null
|
|
1054
|
+
}): DocumentWorkspaceSemanticReference {
|
|
1055
|
+
return {
|
|
1056
|
+
Id: id,
|
|
1057
|
+
Kind: kind,
|
|
1058
|
+
Path:
|
|
1059
|
+
path ??
|
|
1060
|
+
createDocumentWorkspaceSemanticPath([
|
|
1061
|
+
{
|
|
1062
|
+
Id: id,
|
|
1063
|
+
Kind: kind,
|
|
1064
|
+
Label: label ?? id,
|
|
1065
|
+
},
|
|
1066
|
+
]),
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
export function documentWorkspaceSemanticReferenceMatchesSelection(
|
|
1071
|
+
reference: DocumentWorkspaceSemanticReference,
|
|
1072
|
+
selection: DocumentWorkspaceSemanticSelection,
|
|
1073
|
+
) {
|
|
1074
|
+
return (
|
|
1075
|
+
semanticReferencesMatch(reference, selection.Target) ||
|
|
1076
|
+
semanticPathMatches(reference.Path ?? null, selection.SemanticPath ?? null) ||
|
|
1077
|
+
semanticPathMatches(reference.Path ?? null, selection.Target.Path ?? null) ||
|
|
1078
|
+
semanticPathContainsReference(selection.SemanticPath ?? null, reference) ||
|
|
1079
|
+
semanticPathContainsReference(selection.Target.Path ?? null, reference)
|
|
1080
|
+
)
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
export function findDocumentWorkspaceSemanticTreeItemId(
|
|
1084
|
+
selection: DocumentWorkspaceSemanticSelection,
|
|
1085
|
+
nodes: Iterable<DocumentWorkspaceSemanticTreeNode>,
|
|
1086
|
+
) {
|
|
1087
|
+
for (const node of nodes) {
|
|
1088
|
+
const references = [
|
|
1089
|
+
node.semanticReference,
|
|
1090
|
+
...(node.relatedSemanticReferences ?? []),
|
|
1091
|
+
]
|
|
1092
|
+
if (
|
|
1093
|
+
references.some((reference) =>
|
|
1094
|
+
documentWorkspaceSemanticReferenceMatchesSelection(reference, selection),
|
|
1095
|
+
)
|
|
1096
|
+
) {
|
|
1097
|
+
return node.itemId
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
return null
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
function resolveDocumentWorkspace(
|
|
1105
|
+
module: PresentationModuleDefinition | null,
|
|
1106
|
+
pageViewId: string,
|
|
1107
|
+
) {
|
|
1108
|
+
// A route addresses a page view. The workspace may be the page itself or a
|
|
1109
|
+
// hosted child surface within one of the page regions.
|
|
1110
|
+
const pageView = findPresentationView(module, pageViewId)
|
|
1111
|
+
if (!pageView) {
|
|
1112
|
+
return null
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
const workspaceView = findWorkspaceHostView(module, pageView)
|
|
1116
|
+
const workspaceRef = workspaceView?.Workspace ?? null
|
|
1117
|
+
if (!workspaceView || !workspaceRef) {
|
|
1118
|
+
return null
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
const workspace = findPresentationWorkspace(module, workspaceRef.WorkspaceId)
|
|
1122
|
+
if (!workspace) {
|
|
1123
|
+
return null
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
const documentProfileId =
|
|
1127
|
+
workspaceRef.DocumentProfileId ??
|
|
1128
|
+
workspace.DefaultDocumentProfileId ??
|
|
1129
|
+
workspace.DocumentProfiles?.[0]?.Id ??
|
|
1130
|
+
null
|
|
1131
|
+
const documentProfile = documentProfileId
|
|
1132
|
+
? findPresentationDocumentProfile(workspace, documentProfileId)
|
|
1133
|
+
: null
|
|
1134
|
+
if (!documentProfile) {
|
|
1135
|
+
return null
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
return { documentProfile, pageView, workspace, workspaceRef, workspaceView }
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
function findWorkspaceHostView(
|
|
1142
|
+
module: PresentationModuleDefinition | null,
|
|
1143
|
+
pageView: ViewDefinition | null,
|
|
1144
|
+
) {
|
|
1145
|
+
if (pageView?.Workspace) {
|
|
1146
|
+
return pageView
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
for (const region of pageView?.Regions ?? []) {
|
|
1150
|
+
for (const viewId of region.ViewIds ?? []) {
|
|
1151
|
+
const view = findPresentationView(module, viewId)
|
|
1152
|
+
if (view?.Workspace) {
|
|
1153
|
+
return view
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return null
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
function resolveInitialProjectionIds(
|
|
1162
|
+
workspaceRef: WorkspaceRefDefinition | null,
|
|
1163
|
+
projections: readonly ProjectionDefinition[],
|
|
1164
|
+
) {
|
|
1165
|
+
// Workspace refs can narrow the active profile to a preferred projection set.
|
|
1166
|
+
// If that declaration is absent or stale, expose every profile projection.
|
|
1167
|
+
const projectionIds = new Set(projections.map((projection) => projection.Id))
|
|
1168
|
+
const initialProjectionIds = (workspaceRef?.InitialProjectionIds ?? []).filter(
|
|
1169
|
+
(projectionId) => projectionIds.has(projectionId),
|
|
1170
|
+
)
|
|
1171
|
+
|
|
1172
|
+
return initialProjectionIds.length > 0
|
|
1173
|
+
? initialProjectionIds
|
|
1174
|
+
: projections.map((projection) => projection.Id)
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
function resolveDefaultLayout(
|
|
1178
|
+
layout: { readonly DefaultModeId?: string | null } | null,
|
|
1179
|
+
): DocumentWorkspaceLayout {
|
|
1180
|
+
return layout?.DefaultModeId === 'split' ? 'split' : 'single'
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
function resolveActiveLayoutModeId(
|
|
1184
|
+
layout: DocumentWorkspaceLayout,
|
|
1185
|
+
layoutDefinition: {
|
|
1186
|
+
readonly DefaultModeId?: string | null
|
|
1187
|
+
readonly Modes?: readonly { readonly Id: string }[]
|
|
1188
|
+
} | null,
|
|
1189
|
+
) {
|
|
1190
|
+
if (layout === 'split') {
|
|
1191
|
+
return layoutDefinition?.Modes?.find(
|
|
1192
|
+
(mode) => mode.Id.toLocaleLowerCase() === 'split',
|
|
1193
|
+
)?.Id ?? 'split'
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
return layoutDefinition?.DefaultModeId ?? layoutDefinition?.Modes?.[0]?.Id ?? 'single'
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
function createInitialRuntimeState(): DocumentWorkspaceRuntimeState {
|
|
1200
|
+
return {
|
|
1201
|
+
semanticSelections: [],
|
|
1202
|
+
treeStates: {},
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
function createInitialTreeState(): DocumentWorkspaceTreeStateSnapshot {
|
|
1207
|
+
return {
|
|
1208
|
+
expandedItemIds: [],
|
|
1209
|
+
navigationHistory: createInitialNavigationHistory(),
|
|
1210
|
+
search: createInitialTreeSearchState(),
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
function createInitialNavigationHistory(): DocumentWorkspaceNavigationHistory {
|
|
1215
|
+
return { entries: [], index: -1 }
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
function createInitialTreeSearchState(): DocumentWorkspaceTreeSearchState {
|
|
1219
|
+
return { activeMatchIndex: 0, isOpen: false, query: '' }
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
function createDefaultTreeSemanticSelection(
|
|
1223
|
+
itemId: string,
|
|
1224
|
+
projectionId: string,
|
|
1225
|
+
semanticReferenceKind?: string | number | null,
|
|
1226
|
+
): DocumentWorkspaceSemanticSelection {
|
|
1227
|
+
const path = createDocumentWorkspaceSemanticPath([
|
|
1228
|
+
{ Id: projectionId, Kind: semanticReferenceKind, Label: projectionId },
|
|
1229
|
+
{ Id: itemId, Kind: semanticReferenceKind, Label: itemId },
|
|
1230
|
+
])
|
|
1231
|
+
return {
|
|
1232
|
+
SourceProjectionId: projectionId,
|
|
1233
|
+
SemanticPath: path,
|
|
1234
|
+
Target: {
|
|
1235
|
+
Id: itemId,
|
|
1236
|
+
Kind: semanticReferenceKind,
|
|
1237
|
+
Path: path,
|
|
1238
|
+
},
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
function normalizeTreeSemanticSelection(
|
|
1243
|
+
selection: DocumentWorkspaceSemanticSelection,
|
|
1244
|
+
projectionId: string,
|
|
1245
|
+
semanticReferenceKind?: string | number | null,
|
|
1246
|
+
): DocumentWorkspaceSemanticSelection {
|
|
1247
|
+
const targetPath =
|
|
1248
|
+
selection.Target.Path ??
|
|
1249
|
+
selection.SemanticPath ??
|
|
1250
|
+
createDocumentWorkspaceSemanticPath([
|
|
1251
|
+
{
|
|
1252
|
+
Id: selection.Target.Id,
|
|
1253
|
+
Kind: selection.Target.Kind ?? semanticReferenceKind,
|
|
1254
|
+
Label: selection.Target.Id,
|
|
1255
|
+
},
|
|
1256
|
+
])
|
|
1257
|
+
return {
|
|
1258
|
+
SourceProjectionId: selection.SourceProjectionId ?? projectionId,
|
|
1259
|
+
SemanticPath: selection.SemanticPath ?? targetPath,
|
|
1260
|
+
Target: {
|
|
1261
|
+
...selection.Target,
|
|
1262
|
+
Kind: selection.Target.Kind ?? semanticReferenceKind,
|
|
1263
|
+
Path: targetPath,
|
|
1264
|
+
},
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
function createCoordinatedSemanticSelections(
|
|
1269
|
+
selection: DocumentWorkspaceSemanticSelection,
|
|
1270
|
+
coordination: readonly CoordinationDefinition[],
|
|
1271
|
+
): readonly DocumentWorkspaceSemanticSelection[] {
|
|
1272
|
+
if (!hasSemanticSelectionCoordination(coordination, selection.SourceProjectionId ?? null)) {
|
|
1273
|
+
return [selection]
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
return [selection]
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
function filterSemanticSelectionsForProjection({
|
|
1280
|
+
coordination,
|
|
1281
|
+
projectionId,
|
|
1282
|
+
selections,
|
|
1283
|
+
}: {
|
|
1284
|
+
readonly coordination: readonly CoordinationDefinition[]
|
|
1285
|
+
readonly projectionId?: string | null
|
|
1286
|
+
readonly selections: readonly DocumentWorkspaceSemanticSelection[]
|
|
1287
|
+
}): readonly DocumentWorkspaceSemanticSelection[] {
|
|
1288
|
+
if (!projectionId) {
|
|
1289
|
+
return selections
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
return selections.filter((selection) =>
|
|
1293
|
+
canRevealSemanticSelectionForProjection(coordination, projectionId, selection),
|
|
1294
|
+
)
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
function hasSemanticSelectionCoordination(
|
|
1298
|
+
coordination: readonly CoordinationDefinition[],
|
|
1299
|
+
sourceProjectionId: string | null,
|
|
1300
|
+
) {
|
|
1301
|
+
return coordination.some(
|
|
1302
|
+
(definition) =>
|
|
1303
|
+
matchesCoordinationTrigger(definition, sourceProjectionId) &&
|
|
1304
|
+
matchesPresentationEnum(definition.Action.Kind, setSemanticSelectionActionKind),
|
|
1305
|
+
)
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
function canRevealSemanticSelectionForProjection(
|
|
1309
|
+
coordination: readonly CoordinationDefinition[],
|
|
1310
|
+
projectionId: string,
|
|
1311
|
+
selection: DocumentWorkspaceSemanticSelection,
|
|
1312
|
+
) {
|
|
1313
|
+
const revealDefinitions = coordination.filter(
|
|
1314
|
+
(definition) =>
|
|
1315
|
+
matchesCoordinationTrigger(definition, selection.SourceProjectionId ?? null) &&
|
|
1316
|
+
matchesPresentationEnum(definition.Action.Kind, revealSemanticSelectionActionKind),
|
|
1317
|
+
)
|
|
1318
|
+
if (revealDefinitions.length === 0) {
|
|
1319
|
+
return true
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
return revealDefinitions.some((definition) =>
|
|
1323
|
+
definition.Action.TargetProjectionIds.includes(projectionId),
|
|
1324
|
+
)
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
function matchesCoordinationTrigger(
|
|
1328
|
+
definition: CoordinationDefinition,
|
|
1329
|
+
sourceProjectionId: string | null,
|
|
1330
|
+
) {
|
|
1331
|
+
return (
|
|
1332
|
+
matchesPresentationEnum(definition.Trigger.Kind, selectionChangedTriggerKind) &&
|
|
1333
|
+
(!definition.Trigger.SourceProjectionId ||
|
|
1334
|
+
definition.Trigger.SourceProjectionId === sourceProjectionId)
|
|
1335
|
+
)
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
function semanticReferencesMatch(
|
|
1339
|
+
left: DocumentWorkspaceSemanticReference,
|
|
1340
|
+
right: DocumentWorkspaceSemanticReference,
|
|
1341
|
+
) {
|
|
1342
|
+
return (
|
|
1343
|
+
left.Id === right.Id &&
|
|
1344
|
+
semanticReferenceKindsMatch(left.Kind ?? null, right.Kind ?? null)
|
|
1345
|
+
)
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
function semanticPathMatches(
|
|
1349
|
+
left: DocumentWorkspaceSemanticPath | null,
|
|
1350
|
+
right: DocumentWorkspaceSemanticPath | null,
|
|
1351
|
+
) {
|
|
1352
|
+
if (!left || !right || left.Segments.length !== right.Segments.length) {
|
|
1353
|
+
return false
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
return left.Segments.every((leftSegment, index) => {
|
|
1357
|
+
const rightSegment = right.Segments[index]
|
|
1358
|
+
return (
|
|
1359
|
+
leftSegment.Id === rightSegment.Id &&
|
|
1360
|
+
semanticReferenceKindsMatch(leftSegment.Kind ?? null, rightSegment.Kind ?? null)
|
|
1361
|
+
)
|
|
1362
|
+
})
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
function semanticPathContainsReference(
|
|
1366
|
+
path: DocumentWorkspaceSemanticPath | null,
|
|
1367
|
+
reference: DocumentWorkspaceSemanticReference,
|
|
1368
|
+
) {
|
|
1369
|
+
return (
|
|
1370
|
+
path?.Segments.some(
|
|
1371
|
+
(segment) =>
|
|
1372
|
+
segment.Id === reference.Id &&
|
|
1373
|
+
semanticReferenceKindsMatch(segment.Kind ?? null, reference.Kind ?? null),
|
|
1374
|
+
) ?? false
|
|
1375
|
+
)
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
function semanticReferenceKindsMatch(
|
|
1379
|
+
left: string | number | null,
|
|
1380
|
+
right: string | number | null,
|
|
1381
|
+
) {
|
|
1382
|
+
if (left === null || right === null) {
|
|
1383
|
+
return true
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
const leftKeys = createDiscriminatorKeys(left, semanticReferenceKindLabels)
|
|
1387
|
+
const rightKeys = createDiscriminatorKeys(right, semanticReferenceKindLabels)
|
|
1388
|
+
for (const key of leftKeys) {
|
|
1389
|
+
if (rightKeys.has(key)) {
|
|
1390
|
+
return true
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
return false
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
function createDocumentProjectionRendererResolution<
|
|
1398
|
+
TExtra,
|
|
1399
|
+
TComponentSystem = unknown,
|
|
1400
|
+
TDesignSystem = unknown,
|
|
1401
|
+
>(
|
|
1402
|
+
projection: ProjectionDefinition | null,
|
|
1403
|
+
): DocumentWorkspaceProjectionRendererResolution<TExtra, TComponentSystem, TDesignSystem> {
|
|
1404
|
+
return {
|
|
1405
|
+
componentKey: null,
|
|
1406
|
+
componentRole: null,
|
|
1407
|
+
projection,
|
|
1408
|
+
renderer: null,
|
|
1409
|
+
rendererKey: projection?.RendererKey ?? null,
|
|
1410
|
+
resolutionSource: null,
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
const selectionChangedTriggerKind = createPresentationEnumDiscriminator(
|
|
1415
|
+
coordinationTriggerKinds,
|
|
1416
|
+
'selectionChanged',
|
|
1417
|
+
'SelectionChanged',
|
|
1418
|
+
)
|
|
1419
|
+
const setSemanticSelectionActionKind = createPresentationEnumDiscriminator(
|
|
1420
|
+
coordinationActionKinds,
|
|
1421
|
+
'setSemanticSelection',
|
|
1422
|
+
'SetSemanticSelection',
|
|
1423
|
+
)
|
|
1424
|
+
const revealSemanticSelectionActionKind = createPresentationEnumDiscriminator(
|
|
1425
|
+
coordinationActionKinds,
|
|
1426
|
+
'revealSemanticSelection',
|
|
1427
|
+
'RevealSemanticSelection',
|
|
1428
|
+
)
|
|
1429
|
+
|
|
1430
|
+
function findDiscriminatorRenderer<TValue>(
|
|
1431
|
+
renderers: Readonly<Record<string, TValue>> | undefined,
|
|
1432
|
+
value: string | number | null | undefined,
|
|
1433
|
+
labels: Readonly<Record<string | number, string>>,
|
|
1434
|
+
) {
|
|
1435
|
+
if (!renderers || value === null || value === undefined) {
|
|
1436
|
+
return undefined
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
for (const key of createDiscriminatorKeys(value, labels)) {
|
|
1440
|
+
const renderer = renderers[key]
|
|
1441
|
+
if (renderer) {
|
|
1442
|
+
return renderer
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
return undefined
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
function createDiscriminatorKeys(
|
|
1450
|
+
value: string | number,
|
|
1451
|
+
labels: Readonly<Record<string | number, string>>,
|
|
1452
|
+
) {
|
|
1453
|
+
const keys = new Set<string>([String(value)])
|
|
1454
|
+
const directLabel = labels[value]
|
|
1455
|
+
if (directLabel) {
|
|
1456
|
+
keys.add(directLabel)
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
if (typeof value === 'string') {
|
|
1460
|
+
const normalizedValue = value.toLocaleLowerCase()
|
|
1461
|
+
for (const [enumValue, label] of Object.entries(labels)) {
|
|
1462
|
+
if (label.toLocaleLowerCase() === normalizedValue) {
|
|
1463
|
+
keys.add(enumValue)
|
|
1464
|
+
keys.add(label)
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
return keys
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
function updateRuntimeTreeState(
|
|
1473
|
+
current: {
|
|
1474
|
+
readonly instanceKey: string
|
|
1475
|
+
readonly value: DocumentWorkspaceRuntimeState
|
|
1476
|
+
},
|
|
1477
|
+
instanceKey: string,
|
|
1478
|
+
projectionId: string,
|
|
1479
|
+
update: (
|
|
1480
|
+
state: DocumentWorkspaceTreeStateSnapshot,
|
|
1481
|
+
) => DocumentWorkspaceTreeStateSnapshot,
|
|
1482
|
+
) {
|
|
1483
|
+
// Treat an instance-key change as a new workspace session. This prevents tree
|
|
1484
|
+
// state from one document/profile from being reused by another.
|
|
1485
|
+
const base =
|
|
1486
|
+
current.instanceKey === instanceKey ? current.value : createInitialRuntimeState()
|
|
1487
|
+
const currentTreeState = base.treeStates[projectionId] ?? createInitialTreeState()
|
|
1488
|
+
|
|
1489
|
+
return {
|
|
1490
|
+
instanceKey,
|
|
1491
|
+
value: {
|
|
1492
|
+
...base,
|
|
1493
|
+
treeStates: {
|
|
1494
|
+
...base.treeStates,
|
|
1495
|
+
[projectionId]: update(currentTreeState),
|
|
1496
|
+
},
|
|
1497
|
+
},
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
function applySetStateAction<T>(action: SetStateAction<T>, current: T) {
|
|
1502
|
+
return typeof action === 'function'
|
|
1503
|
+
? (action as (current: T) => T)(current)
|
|
1504
|
+
: action
|
|
1505
|
+
}
|