@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.
Files changed (262) hide show
  1. package/LICENSE +55 -0
  2. package/dist/document-workspace-surface-slot-renderers.d.ts +58 -0
  3. package/dist/document-workspace-surface-slot-renderers.d.ts.map +1 -0
  4. package/dist/document-workspace-surface-slot-renderers.js +206 -0
  5. package/dist/document-workspace-surface-slot-renderers.js.map +1 -0
  6. package/dist/index.d.ts +53 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +53 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/navigation-shell-frame-renderer-registry.d.ts +8 -0
  11. package/dist/navigation-shell-frame-renderer-registry.d.ts.map +1 -0
  12. package/dist/navigation-shell-frame-renderer-registry.js +2 -0
  13. package/dist/navigation-shell-frame-renderer-registry.js.map +1 -0
  14. package/dist/navigation-shell-slot-renderer-registry.d.ts +9 -0
  15. package/dist/navigation-shell-slot-renderer-registry.d.ts.map +1 -0
  16. package/dist/navigation-shell-slot-renderer-registry.js +2 -0
  17. package/dist/navigation-shell-slot-renderer-registry.js.map +1 -0
  18. package/dist/page-host-unavailable-renderer.d.ts +10 -0
  19. package/dist/page-host-unavailable-renderer.d.ts.map +1 -0
  20. package/dist/page-host-unavailable-renderer.js +27 -0
  21. package/dist/page-host-unavailable-renderer.js.map +1 -0
  22. package/dist/presentation-action-group.d.ts +99 -0
  23. package/dist/presentation-action-group.d.ts.map +1 -0
  24. package/dist/presentation-action-group.js +214 -0
  25. package/dist/presentation-action-group.js.map +1 -0
  26. package/dist/presentation-badge-target-interpreter.d.ts +26 -0
  27. package/dist/presentation-badge-target-interpreter.d.ts.map +1 -0
  28. package/dist/presentation-badge-target-interpreter.js +17 -0
  29. package/dist/presentation-badge-target-interpreter.js.map +1 -0
  30. package/dist/presentation-component-groups.d.ts +968 -0
  31. package/dist/presentation-component-groups.d.ts.map +1 -0
  32. package/dist/presentation-component-groups.js +505 -0
  33. package/dist/presentation-component-groups.js.map +1 -0
  34. package/dist/presentation-component-system-diagnostics.d.ts +27 -0
  35. package/dist/presentation-component-system-diagnostics.d.ts.map +1 -0
  36. package/dist/presentation-component-system-diagnostics.js +399 -0
  37. package/dist/presentation-component-system-diagnostics.js.map +1 -0
  38. package/dist/presentation-component-system.d.ts +31 -0
  39. package/dist/presentation-component-system.d.ts.map +1 -0
  40. package/dist/presentation-component-system.js +61 -0
  41. package/dist/presentation-component-system.js.map +1 -0
  42. package/dist/presentation-icon-diagnostics.d.ts +12 -0
  43. package/dist/presentation-icon-diagnostics.d.ts.map +1 -0
  44. package/dist/presentation-icon-diagnostics.js +17 -0
  45. package/dist/presentation-icon-diagnostics.js.map +1 -0
  46. package/dist/presentation-icon-registry.d.ts +28 -0
  47. package/dist/presentation-icon-registry.d.ts.map +1 -0
  48. package/dist/presentation-icon-registry.js +80 -0
  49. package/dist/presentation-icon-registry.js.map +1 -0
  50. package/dist/presentation-projection-trace-panel.d.ts +10 -0
  51. package/dist/presentation-projection-trace-panel.d.ts.map +1 -0
  52. package/dist/presentation-projection-trace-panel.js +48 -0
  53. package/dist/presentation-projection-trace-panel.js.map +1 -0
  54. package/dist/presentation-routed-surface-host.d.ts +19 -0
  55. package/dist/presentation-routed-surface-host.d.ts.map +1 -0
  56. package/dist/presentation-routed-surface-host.js +12 -0
  57. package/dist/presentation-routed-surface-host.js.map +1 -0
  58. package/dist/presentation-shadcn-component-system.d.ts +22 -0
  59. package/dist/presentation-shadcn-component-system.d.ts.map +1 -0
  60. package/dist/presentation-shadcn-component-system.js +2 -0
  61. package/dist/presentation-shadcn-component-system.js.map +1 -0
  62. package/dist/presentation-standard-renderers.d.ts +168 -0
  63. package/dist/presentation-standard-renderers.d.ts.map +1 -0
  64. package/dist/presentation-standard-renderers.js +440 -0
  65. package/dist/presentation-standard-renderers.js.map +1 -0
  66. package/dist/presentation-surface-renderer.d.ts +23 -0
  67. package/dist/presentation-surface-renderer.d.ts.map +1 -0
  68. package/dist/presentation-surface-renderer.js +119 -0
  69. package/dist/presentation-surface-renderer.js.map +1 -0
  70. package/dist/presentation-value-renderers.d.ts +10 -0
  71. package/dist/presentation-value-renderers.d.ts.map +1 -0
  72. package/dist/presentation-value-renderers.js +14 -0
  73. package/dist/presentation-value-renderers.js.map +1 -0
  74. package/dist/process-task-icons.d.ts +49 -0
  75. package/dist/process-task-icons.d.ts.map +1 -0
  76. package/dist/process-task-icons.js +154 -0
  77. package/dist/process-task-icons.js.map +1 -0
  78. package/dist/projected-activity-state.d.ts +17 -0
  79. package/dist/projected-activity-state.d.ts.map +1 -0
  80. package/dist/projected-activity-state.js +22 -0
  81. package/dist/projected-activity-state.js.map +1 -0
  82. package/dist/projected-collection-action-runtime-diagnostics.d.ts +4 -0
  83. package/dist/projected-collection-action-runtime-diagnostics.d.ts.map +1 -0
  84. package/dist/projected-collection-action-runtime-diagnostics.js +9 -0
  85. package/dist/projected-collection-action-runtime-diagnostics.js.map +1 -0
  86. package/dist/projected-collection-view.d.ts +90 -0
  87. package/dist/projected-collection-view.d.ts.map +1 -0
  88. package/dist/projected-collection-view.js +532 -0
  89. package/dist/projected-collection-view.js.map +1 -0
  90. package/dist/projected-document-action-status-notices.d.ts +15 -0
  91. package/dist/projected-document-action-status-notices.d.ts.map +1 -0
  92. package/dist/projected-document-action-status-notices.js +33 -0
  93. package/dist/projected-document-action-status-notices.js.map +1 -0
  94. package/dist/projected-document-badges.d.ts +24 -0
  95. package/dist/projected-document-badges.d.ts.map +1 -0
  96. package/dist/projected-document-badges.js +53 -0
  97. package/dist/projected-document-badges.js.map +1 -0
  98. package/dist/projected-document-editor-surface.d.ts +46 -0
  99. package/dist/projected-document-editor-surface.d.ts.map +1 -0
  100. package/dist/projected-document-editor-surface.js +399 -0
  101. package/dist/projected-document-editor-surface.js.map +1 -0
  102. package/dist/projected-document-workspace-notices.d.ts +24 -0
  103. package/dist/projected-document-workspace-notices.d.ts.map +1 -0
  104. package/dist/projected-document-workspace-notices.js +14 -0
  105. package/dist/projected-document-workspace-notices.js.map +1 -0
  106. package/dist/projected-document-workspace-prompt-child-view-registry.d.ts +25 -0
  107. package/dist/projected-document-workspace-prompt-child-view-registry.d.ts.map +1 -0
  108. package/dist/projected-document-workspace-prompt-child-view-registry.js +41 -0
  109. package/dist/projected-document-workspace-prompt-child-view-registry.js.map +1 -0
  110. package/dist/projected-document-workspace-prompt-child-view-renderers.d.ts +32 -0
  111. package/dist/projected-document-workspace-prompt-child-view-renderers.d.ts.map +1 -0
  112. package/dist/projected-document-workspace-prompt-child-view-renderers.js +77 -0
  113. package/dist/projected-document-workspace-prompt-child-view-renderers.js.map +1 -0
  114. package/dist/projected-document-workspace-shell.d.ts +100 -0
  115. package/dist/projected-document-workspace-shell.d.ts.map +1 -0
  116. package/dist/projected-document-workspace-shell.js +153 -0
  117. package/dist/projected-document-workspace-shell.js.map +1 -0
  118. package/dist/projected-document-workspace.d.ts +39 -0
  119. package/dist/projected-document-workspace.d.ts.map +1 -0
  120. package/dist/projected-document-workspace.js +63 -0
  121. package/dist/projected-document-workspace.js.map +1 -0
  122. package/dist/projected-field-value-rendering.d.ts +94 -0
  123. package/dist/projected-field-value-rendering.d.ts.map +1 -0
  124. package/dist/projected-field-value-rendering.js +377 -0
  125. package/dist/projected-field-value-rendering.js.map +1 -0
  126. package/dist/projected-inline-list.d.ts +20 -0
  127. package/dist/projected-inline-list.d.ts.map +1 -0
  128. package/dist/projected-inline-list.js +149 -0
  129. package/dist/projected-inline-list.js.map +1 -0
  130. package/dist/projected-input-form.d.ts +143 -0
  131. package/dist/projected-input-form.d.ts.map +1 -0
  132. package/dist/projected-input-form.js +612 -0
  133. package/dist/projected-input-form.js.map +1 -0
  134. package/dist/projected-metric-dashboard.d.ts +26 -0
  135. package/dist/projected-metric-dashboard.d.ts.map +1 -0
  136. package/dist/projected-metric-dashboard.js +20 -0
  137. package/dist/projected-metric-dashboard.js.map +1 -0
  138. package/dist/projected-metric-strip.d.ts +20 -0
  139. package/dist/projected-metric-strip.d.ts.map +1 -0
  140. package/dist/projected-metric-strip.js +131 -0
  141. package/dist/projected-metric-strip.js.map +1 -0
  142. package/dist/projected-navigation-shell-view-host.d.ts +34 -0
  143. package/dist/projected-navigation-shell-view-host.d.ts.map +1 -0
  144. package/dist/projected-navigation-shell-view-host.js +75 -0
  145. package/dist/projected-navigation-shell-view-host.js.map +1 -0
  146. package/dist/projected-navigation-shell.d.ts +33 -0
  147. package/dist/projected-navigation-shell.d.ts.map +1 -0
  148. package/dist/projected-navigation-shell.js +112 -0
  149. package/dist/projected-navigation-shell.js.map +1 -0
  150. package/dist/projected-presentation-badges.d.ts +34 -0
  151. package/dist/projected-presentation-badges.d.ts.map +1 -0
  152. package/dist/projected-presentation-badges.js +22 -0
  153. package/dist/projected-presentation-badges.js.map +1 -0
  154. package/dist/projected-presentation-diagnostics.d.ts +10 -0
  155. package/dist/projected-presentation-diagnostics.d.ts.map +1 -0
  156. package/dist/projected-presentation-diagnostics.js +40 -0
  157. package/dist/projected-presentation-diagnostics.js.map +1 -0
  158. package/dist/projected-presentation-flow-layer.d.ts +33 -0
  159. package/dist/projected-presentation-flow-layer.d.ts.map +1 -0
  160. package/dist/projected-presentation-flow-layer.js +71 -0
  161. package/dist/projected-presentation-flow-layer.js.map +1 -0
  162. package/dist/projected-process-task-notices.d.ts +37 -0
  163. package/dist/projected-process-task-notices.d.ts.map +1 -0
  164. package/dist/projected-process-task-notices.js +225 -0
  165. package/dist/projected-process-task-notices.js.map +1 -0
  166. package/dist/projected-prompt-child-view-renderer.d.ts +101 -0
  167. package/dist/projected-prompt-child-view-renderer.d.ts.map +1 -0
  168. package/dist/projected-prompt-child-view-renderer.js +442 -0
  169. package/dist/projected-prompt-child-view-renderer.js.map +1 -0
  170. package/dist/projected-prompt-document-preview.d.ts +39 -0
  171. package/dist/projected-prompt-document-preview.d.ts.map +1 -0
  172. package/dist/projected-prompt-document-preview.js +50 -0
  173. package/dist/projected-prompt-document-preview.js.map +1 -0
  174. package/dist/projected-prompt-view.d.ts +35 -0
  175. package/dist/projected-prompt-view.d.ts.map +1 -0
  176. package/dist/projected-prompt-view.js +292 -0
  177. package/dist/projected-prompt-view.js.map +1 -0
  178. package/dist/projected-query-form.d.ts +56 -0
  179. package/dist/projected-query-form.d.ts.map +1 -0
  180. package/dist/projected-query-form.js +100 -0
  181. package/dist/projected-query-form.js.map +1 -0
  182. package/dist/projected-record-details.d.ts +20 -0
  183. package/dist/projected-record-details.d.ts.map +1 -0
  184. package/dist/projected-record-details.js +60 -0
  185. package/dist/projected-record-details.js.map +1 -0
  186. package/dist/projected-routed-surface-runtime.d.ts +86 -0
  187. package/dist/projected-routed-surface-runtime.d.ts.map +1 -0
  188. package/dist/projected-routed-surface-runtime.js +332 -0
  189. package/dist/projected-routed-surface-runtime.js.map +1 -0
  190. package/dist/projected-tabs-view.d.ts +21 -0
  191. package/dist/projected-tabs-view.d.ts.map +1 -0
  192. package/dist/projected-tabs-view.js +140 -0
  193. package/dist/projected-tabs-view.js.map +1 -0
  194. package/dist/projected-view-chrome.d.ts +15 -0
  195. package/dist/projected-view-chrome.d.ts.map +1 -0
  196. package/dist/projected-view-chrome.js +67 -0
  197. package/dist/projected-view-chrome.js.map +1 -0
  198. package/dist/projected-view-surface.d.ts +34 -0
  199. package/dist/projected-view-surface.d.ts.map +1 -0
  200. package/dist/projected-view-surface.js +40 -0
  201. package/dist/projected-view-surface.js.map +1 -0
  202. package/dist/standard-collection-chrome-slot-renderers.d.ts +26 -0
  203. package/dist/standard-collection-chrome-slot-renderers.d.ts.map +1 -0
  204. package/dist/standard-collection-chrome-slot-renderers.js +250 -0
  205. package/dist/standard-collection-chrome-slot-renderers.js.map +1 -0
  206. package/dist/standard-view-chrome-slot-renderers.d.ts +57 -0
  207. package/dist/standard-view-chrome-slot-renderers.d.ts.map +1 -0
  208. package/dist/standard-view-chrome-slot-renderers.js +614 -0
  209. package/dist/standard-view-chrome-slot-renderers.js.map +1 -0
  210. package/package.json +68 -0
  211. package/src/document-workspace-surface-slot-renderers.tsx +395 -0
  212. package/src/index.ts +53 -0
  213. package/src/navigation-shell-frame-renderer-registry.ts +29 -0
  214. package/src/navigation-shell-slot-renderer-registry.ts +46 -0
  215. package/src/page-host-unavailable-renderer.tsx +79 -0
  216. package/src/presentation-action-group.tsx +475 -0
  217. package/src/presentation-badge-target-interpreter.ts +54 -0
  218. package/src/presentation-component-groups.ts +2505 -0
  219. package/src/presentation-component-system-diagnostics.ts +848 -0
  220. package/src/presentation-component-system.ts +365 -0
  221. package/src/presentation-icon-diagnostics.ts +53 -0
  222. package/src/presentation-icon-registry.tsx +181 -0
  223. package/src/presentation-projection-trace-panel.tsx +140 -0
  224. package/src/presentation-routed-surface-host.tsx +48 -0
  225. package/src/presentation-shadcn-component-system.ts +102 -0
  226. package/src/presentation-standard-renderers.tsx +1290 -0
  227. package/src/presentation-surface-renderer.tsx +263 -0
  228. package/src/presentation-value-renderers.tsx +40 -0
  229. package/src/process-task-icons.tsx +258 -0
  230. package/src/projected-activity-state.tsx +63 -0
  231. package/src/projected-collection-action-runtime-diagnostics.ts +22 -0
  232. package/src/projected-collection-view.tsx +1273 -0
  233. package/src/projected-document-action-status-notices.tsx +66 -0
  234. package/src/projected-document-badges.tsx +134 -0
  235. package/src/projected-document-editor-surface.tsx +808 -0
  236. package/src/projected-document-workspace-notices.tsx +77 -0
  237. package/src/projected-document-workspace-prompt-child-view-registry.ts +99 -0
  238. package/src/projected-document-workspace-prompt-child-view-renderers.tsx +205 -0
  239. package/src/projected-document-workspace-shell.tsx +427 -0
  240. package/src/projected-document-workspace.tsx +216 -0
  241. package/src/projected-field-value-rendering.tsx +704 -0
  242. package/src/projected-inline-list.tsx +434 -0
  243. package/src/projected-input-form.tsx +1218 -0
  244. package/src/projected-metric-dashboard.tsx +106 -0
  245. package/src/projected-metric-strip.tsx +244 -0
  246. package/src/projected-navigation-shell-view-host.tsx +177 -0
  247. package/src/projected-navigation-shell.tsx +219 -0
  248. package/src/projected-presentation-badges.tsx +95 -0
  249. package/src/projected-presentation-diagnostics.tsx +120 -0
  250. package/src/projected-presentation-flow-layer.tsx +186 -0
  251. package/src/projected-process-task-notices.tsx +501 -0
  252. package/src/projected-prompt-child-view-renderer.tsx +698 -0
  253. package/src/projected-prompt-document-preview.tsx +197 -0
  254. package/src/projected-prompt-view.tsx +592 -0
  255. package/src/projected-query-form.tsx +286 -0
  256. package/src/projected-record-details.tsx +133 -0
  257. package/src/projected-routed-surface-runtime.tsx +713 -0
  258. package/src/projected-tabs-view.tsx +308 -0
  259. package/src/projected-view-chrome.tsx +148 -0
  260. package/src/projected-view-surface.tsx +131 -0
  261. package/src/standard-collection-chrome-slot-renderers.tsx +391 -0
  262. package/src/standard-view-chrome-slot-renderers.tsx +1064 -0
@@ -0,0 +1,1290 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import {
4
+ createPresentationViewActivityState,
5
+ createPresentationTestAttributes,
6
+ createRelationQueryInputFormTarget,
7
+ defaultPresentationComponentSet,
8
+ findPresentationQueryForm,
9
+ findPresentationInputForm,
10
+ findPresentationInputFormForView,
11
+ findPresentationQueryFormForView,
12
+ getRegionViewIds,
13
+ isViewChromeSlotKind,
14
+ readPresentationDataSourceItems,
15
+ resolveCollectionChromeSlotRenderer,
16
+ type CollectionChromeSlotRendererRegistry,
17
+ type CollectionChromeSlotDefinition,
18
+ type PresentationActionRuntimeBinding,
19
+ type PresentationDataSourceState,
20
+ type ProjectedCollectionActionExecutionContext,
21
+ type ProjectedCollectionPaginationInput,
22
+ type ProjectedCollectionRuntime,
23
+ type ViewChromeSlotDefinition,
24
+ type ViewDefinition,
25
+ type ViewRegionDefinition,
26
+ } from '@cohesivesystems/presentation-core'
27
+ import {
28
+ renderStandardCollectionChromeSlot,
29
+ } from './standard-collection-chrome-slot-renderers'
30
+ import type {
31
+ PresentationShadcnComponentSystem as PresentationComponentSystem,
32
+ } from './presentation-shadcn-component-system'
33
+ import {
34
+ renderStandardViewChromeSlot,
35
+ } from './standard-view-chrome-slot-renderers'
36
+ import type {
37
+ PresentationDesignSystem,
38
+ } from '@cohesivesystems/presentation-tailwind'
39
+ import type {
40
+ ProjectedCollectionChromeSlotRenderContext,
41
+ ProjectedCollectionDetailFieldRenderContext,
42
+ ProjectedFieldRenderContext,
43
+ } from './projected-collection-view'
44
+ import {
45
+ mergePresentationRendererRegistries,
46
+ type PresentationRendererRegistry,
47
+ type PresentationViewRenderContext,
48
+ type PresentationViewRenderer,
49
+ } from '@cohesivesystems/presentation-react'
50
+ import {
51
+ ProjectedActivityStateBoundary,
52
+ ProjectedStatusBlock,
53
+ } from './projected-activity-state'
54
+ import { ProjectedCollectionView } from './projected-collection-view'
55
+ import { ProjectedMetricDashboard } from './projected-metric-dashboard'
56
+ import { ProjectedMetricStrip } from './projected-metric-strip'
57
+ import {
58
+ ProjectedRecordDetails,
59
+ type ProjectedRecordFieldRenderContext,
60
+ } from './projected-record-details'
61
+ import {
62
+ ProjectedInputForm,
63
+ type ProjectedInputFormChoiceClassNameResolver,
64
+ type ProjectedInputFormChoiceToneResolver,
65
+ type ProjectedInputFormFieldRenderer,
66
+ type ProjectedInputFormRuntime,
67
+ } from './projected-input-form'
68
+ import { ProjectedInlineList } from './projected-inline-list'
69
+ import {
70
+ ProjectedQueryForm,
71
+ type ProjectedQueryFormChoiceClassNameResolver,
72
+ type ProjectedQueryFormChoiceToneResolver,
73
+ type ProjectedQueryFormFieldRenderer,
74
+ type ProjectedQueryFormRuntime,
75
+ } from './projected-query-form'
76
+ import { ProjectedTabsView } from './projected-tabs-view'
77
+ import {
78
+ ProjectedViewSurface,
79
+ type ProjectedViewSurfaceContentTopInset,
80
+ type ProjectedViewSurfaceVerticalResizeOptions,
81
+ } from './projected-view-surface'
82
+ import {
83
+ PresentationActionGroup,
84
+ type PresentationActionGroupOptions,
85
+ } from './presentation-action-group'
86
+ import {
87
+ type ViewKind,
88
+ fieldDisplayKinds,
89
+ viewChromeSlotKinds,
90
+ viewKindLabels,
91
+ viewKinds,
92
+ viewRegionKindLabels,
93
+ viewRegionKinds,
94
+ } from '@cohesivesystems/presentation-contracts'
95
+ import {
96
+ presentationViewComponentRoles,
97
+ } from '@cohesivesystems/presentation-contracts'
98
+
99
+ export type PresentationFieldRenderer<TData extends object = object> = (
100
+ context: ProjectedFieldRenderContext<TData>,
101
+ ) => ReactNode
102
+
103
+ export type PresentationRecordFieldRenderer<TData extends object = object> = (
104
+ context: ProjectedRecordFieldRenderContext<TData>,
105
+ ) => ReactNode
106
+
107
+ export type PresentationCollectionDetailFieldRenderer<TData extends object = object> = (
108
+ context: ProjectedCollectionDetailFieldRenderContext<TData>,
109
+ ) => ReactNode
110
+
111
+ /**
112
+ * App-level customization for the standard presentation renderer set. The
113
+ * options style is intentionally declarative: callers provide styling hooks,
114
+ * icon maps, field renderers, and escape hatches while traversal and data-source
115
+ * resolution stay owned by the projection runtime.
116
+ */
117
+ export interface PresentationStandardRendererOptions<TContext> {
118
+ /** Component implementation set used by standard projected renderers. */
119
+ readonly componentSystem: PresentationRequiredValueOption<TContext, PresentationComponentSystem>
120
+
121
+ /** Component binding set used to resolve target-specific component keys. */
122
+ readonly componentSet?: PresentationStringOption<TContext>
123
+
124
+ /** Design-system interpretation used by standard projected renderers. */
125
+ readonly designSystem: PresentationRequiredValueOption<TContext, PresentationDesignSystem>
126
+
127
+ /** Configures rendering and execution behavior for view action placements. */
128
+ readonly actionGroup?: PresentationActionGroupOptions<TContext>
129
+
130
+ /** Customizes table-like collection views rendered from primary data sources. */
131
+ readonly collection?: {
132
+ /** Frontend-local interpretations for projected collection row/selection actions. */
133
+ readonly actionRuntimeBindings?: PresentationValueOption<
134
+ TContext,
135
+ readonly PresentationActionRuntimeBinding<
136
+ ProjectedCollectionActionExecutionContext<object>,
137
+ ReactNode
138
+ >[]
139
+ >
140
+
141
+ /** Message shown when the resolved collection has no visible records. */
142
+ readonly emptyMessage?: PresentationStringOption<TContext>
143
+
144
+ /** Field-specific cell renderers keyed by presentation field id or field path. */
145
+ readonly fieldRenderers?: PresentationValueOption<
146
+ TContext,
147
+ Readonly<Record<string, PresentationFieldRenderer>>
148
+ >
149
+
150
+ /** Field-specific renderers for inline detail views projected from selected rows. */
151
+ readonly detailFieldRenderers?: PresentationValueOption<
152
+ TContext,
153
+ Readonly<Record<string, PresentationCollectionDetailFieldRenderer>>
154
+ >
155
+
156
+ /** Resolves pagination/windowing runtime state for the collection projection. */
157
+ readonly pagination?: (
158
+ context: PresentationCollectionRenderContext<TContext>,
159
+ ) => ProjectedCollectionPaginationInput | null | undefined
160
+
161
+ /** Additional class name applied to metric summary slots. */
162
+ readonly summaryClassName?: PresentationStringOption<TContext>
163
+
164
+ /** Renders supplemental collection chrome such as pagination controls. */
165
+ readonly renderFooter?: (
166
+ context: PresentationCollectionRenderContext<TContext>,
167
+ ) => ReactNode
168
+
169
+ /** Slot renderer registry keyed by Collection.Chrome slot kind and placement. */
170
+ readonly chromeSlotRenderers?: PresentationValueOption<
171
+ TContext,
172
+ CollectionChromeSlotRendererRegistry<
173
+ PresentationCollectionChromeSlotRenderContext<TContext>,
174
+ ReactNode
175
+ >
176
+ >
177
+
178
+ /** Interprets collection chrome slots declared by Collection.Chrome. */
179
+ readonly renderChromeSlot?: (
180
+ context: PresentationCollectionChromeSlotRenderContext<TContext>,
181
+ ) => ReactNode
182
+ }
183
+
184
+ /** Customizes compact list views rendered from projected collection fields. */
185
+ readonly inlineList?: {
186
+ /** Message or node-compatible text shown when the inline list has no items. */
187
+ readonly emptyMessage?: PresentationStringOption<TContext>
188
+
189
+ /** Optional escape hatch for app-specific inline-list item rendering. */
190
+ readonly renderItems?: (
191
+ context: PresentationInlineListRenderContext<TContext>,
192
+ ) => ReactNode
193
+ }
194
+
195
+ /** Customizes dashboard and metric-summary views. */
196
+ readonly metricDashboard?: {
197
+ /** Additional class name applied to the dashboard container. */
198
+ readonly className?: PresentationStringOption<TContext>
199
+
200
+ /** Optional dashboard description shown alongside the projected metrics. */
201
+ readonly description?: PresentationStringOption<TContext>
202
+
203
+ /** Icons keyed by metric field id for projected metric cards. */
204
+ readonly iconByFieldId?: PresentationValueOption<
205
+ TContext,
206
+ Readonly<Record<string, ReactNode>>
207
+ >
208
+
209
+ /** Loading label used while dashboard data sources are pending. */
210
+ readonly pendingLabel?: PresentationStringOption<TContext>
211
+ }
212
+
213
+ /** Customizes generic input-form views rendered from InputFormDefinition. */
214
+ readonly inputForm?: {
215
+ /** Maps projected choices to target toggle classes, usually through an app design-system binding. */
216
+ readonly choiceToggleClassName?: PresentationValueOption<
217
+ TContext,
218
+ ProjectedInputFormChoiceClassNameResolver
219
+ >
220
+
221
+ /** Resolves semantic tone for projected choices before design-system class binding. */
222
+ readonly choiceTone?: PresentationValueOption<
223
+ TContext,
224
+ ProjectedInputFormChoiceToneResolver
225
+ >
226
+
227
+ /** Additional class name applied to the input form surface. */
228
+ readonly className?: PresentationStringOption<TContext>
229
+
230
+ /** Additional class name applied to the input form content area. */
231
+ readonly contentClassName?: PresentationStringOption<TContext>
232
+
233
+ /** Field-specific input controls keyed by input-form field id or presentation field id. */
234
+ readonly fieldRenderers?: PresentationValueOption<
235
+ TContext,
236
+ Readonly<Record<string, ProjectedInputFormFieldRenderer>>
237
+ >
238
+
239
+ /** Resolves the mutable form runtime for the current route context. */
240
+ readonly runtime: (
241
+ context: PresentationViewRenderContext<TContext>,
242
+ ) => ProjectedInputFormRuntime | null
243
+ }
244
+
245
+ /** Customizes query-form views rendered from InputFormDefinition plus a relation-query target. */
246
+ readonly queryForm?: {
247
+ /** Maps projected choices to target toggle classes, usually through an app design-system binding. */
248
+ readonly choiceToggleClassName?: PresentationValueOption<
249
+ TContext,
250
+ ProjectedQueryFormChoiceClassNameResolver
251
+ >
252
+
253
+ /** Resolves semantic tone for projected choices before design-system class binding. */
254
+ readonly choiceTone?: PresentationValueOption<
255
+ TContext,
256
+ ProjectedQueryFormChoiceToneResolver
257
+ >
258
+
259
+ /** Additional class name applied to the query form surface. */
260
+ readonly className?: PresentationStringOption<TContext>
261
+
262
+ /** Additional class name applied to the query form content area. */
263
+ readonly contentClassName?: PresentationStringOption<TContext>
264
+
265
+ /** Field-specific query controls keyed by query-form field id or presentation field id. */
266
+ readonly fieldRenderers?: PresentationValueOption<
267
+ TContext,
268
+ Readonly<Record<string, ProjectedQueryFormFieldRenderer>>
269
+ >
270
+
271
+ /** Resolves the mutable form runtime for the current route context. */
272
+ readonly runtime: (
273
+ context: PresentationViewRenderContext<TContext>,
274
+ ) => ProjectedQueryFormRuntime | null
275
+ }
276
+
277
+ /** Final registry layer that can replace or extend standard renderer choices. */
278
+ readonly overrides?: PresentationRendererRegistry<TContext>
279
+
280
+ /** Customizes record-detail views rendered from a primary record data source. */
281
+ readonly recordDetail?: {
282
+ /** Additional class name applied to the detail surface container. */
283
+ readonly className?: PresentationStringOption<TContext>
284
+
285
+ /** Additional class name applied to the detail surface content area. */
286
+ readonly contentClassName?: PresentationStringOption<TContext>
287
+
288
+ /** Message shown when no record detail data is available. */
289
+ readonly emptyMessage?: PresentationStringOption<TContext>
290
+
291
+ /** Field-specific detail renderers keyed by presentation field id or field path. */
292
+ readonly fieldRenderers?: PresentationValueOption<
293
+ TContext,
294
+ Readonly<Record<string, PresentationRecordFieldRenderer>>
295
+ >
296
+
297
+ /** Detail field ids or field paths whose label cell should not be rendered. */
298
+ readonly hiddenFieldLabels?: PresentationValueOption<TContext, readonly string[]>
299
+
300
+ /** Title override for the detail surface; defaults to the view name. */
301
+ readonly title?: PresentationStringOption<TContext>
302
+ }
303
+
304
+ /** Customizes general nested surface views. */
305
+ readonly surface?: {
306
+ /** Additional class name applied to the surface container. */
307
+ readonly className?: PresentationStringOption<TContext>
308
+
309
+ /** Additional class name applied to the surface content area. */
310
+ readonly contentClassName?: PresentationStringOption<TContext>
311
+
312
+ /** Controls the semantic top inset between surface chrome and content. */
313
+ readonly contentTopInset?: PresentationValueOption<
314
+ TContext,
315
+ ProjectedViewSurfaceContentTopInset | null
316
+ >
317
+
318
+ /** Small label rendered above the surface title. */
319
+ readonly eyebrow?: PresentationStringOption<TContext>
320
+
321
+ /** Title override for the surface; defaults to the projected view chrome. */
322
+ readonly title?: PresentationStringOption<TContext>
323
+
324
+ /** Enables vertical resize affordances for the surface. */
325
+ readonly verticalResize?: PresentationValueOption<
326
+ TContext,
327
+ boolean | ProjectedViewSurfaceVerticalResizeOptions | null
328
+ >
329
+ }
330
+
331
+ /** Customizes tabbed-surface views. */
332
+ readonly tabs?: {
333
+ /** Icons keyed by tab region id. */
334
+ readonly iconByRegionId?: PresentationValueOption<
335
+ TContext,
336
+ Readonly<Record<string, ReactNode>>
337
+ >
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Specialized list renderers receive already resolved items so app code does
343
+ * not need to know how a semantic view binds to its primary data source.
344
+ */
345
+ export interface PresentationInlineListRenderContext<TContext>
346
+ extends PresentationViewRenderContext<TContext> {
347
+ readonly dataSource: PresentationDataSourceState | undefined
348
+ readonly items: readonly object[]
349
+ }
350
+
351
+ /**
352
+ * Collection escape-hatch renderers receive the same resolved data source and
353
+ * item projection as the standard table renderer.
354
+ */
355
+ export interface PresentationCollectionRenderContext<TContext>
356
+ extends PresentationViewRenderContext<TContext> {
357
+ readonly collectionRuntime?: ProjectedCollectionRuntime<object>
358
+ readonly dataSource: PresentationDataSourceState | undefined
359
+ readonly items: readonly object[]
360
+ }
361
+
362
+ export type PresentationCollectionChromeSlotRenderContext<TContext> =
363
+ PresentationCollectionRenderContext<TContext> &
364
+ ProjectedCollectionChromeSlotRenderContext<object> & {
365
+ readonly collectionRuntime: ProjectedCollectionRuntime<object>
366
+ }
367
+
368
+ type PresentationStringOption<TContext> =
369
+ | ((context: PresentationViewRenderContext<TContext>) => string | undefined)
370
+ | string
371
+
372
+ type PresentationValueOption<TContext, TValue> =
373
+ | ((context: PresentationViewRenderContext<TContext>) => TValue | undefined)
374
+ | TValue
375
+
376
+ type PresentationRequiredValueOption<TContext, TValue> =
377
+ | ((context: PresentationViewRenderContext<TContext>) => TValue)
378
+ | TValue
379
+
380
+ /**
381
+ * Creates the app-wide default renderer registry for common semantic view
382
+ * constructs: surface roots, dashboards, surfaces, tabbed surfaces, collections,
383
+ * and simple inline lists. Specific apps can layer overrides on top without
384
+ * reimplementing recursive rendering or activity/data-source handling.
385
+ */
386
+ export function createStandardPresentationRendererRegistry<TContext>(
387
+ options: PresentationStandardRendererOptions<TContext>,
388
+ ): PresentationRendererRegistry<TContext> {
389
+ const collectionRenderer = createCollectionRenderer(options)
390
+ const inputFormRenderer = createInputFormRenderer(options)
391
+ const drawerRenderer = createDrawerRenderer()
392
+ const metricDashboardRenderer = createMetricDashboardRenderer(options)
393
+ const queryFormRenderer = createQueryFormRenderer(options)
394
+ const recordDetailRenderer = createRecordDetailRenderer(options)
395
+ const surfaceRenderer = createSurfaceRenderer(options)
396
+ const tabsRenderer = createTabsRenderer(options)
397
+
398
+ const registry = {
399
+ composites: {
400
+ byComponentRole: {
401
+ [presentationViewComponentRoles.collectionView]: collectionRenderer,
402
+ [presentationViewComponentRoles.inputForm]: inputFormRenderer,
403
+ [presentationViewComponentRoles.metricDashboard]: metricDashboardRenderer,
404
+ [presentationViewComponentRoles.queryForm]: queryFormRenderer,
405
+ [presentationViewComponentRoles.tabsView]: tabsRenderer,
406
+ [presentationViewComponentRoles.viewSurface]: surfaceRenderer,
407
+ },
408
+ bySemanticRole: {
409
+ collection: collectionRenderer,
410
+ dashboard: metricDashboardRenderer,
411
+ detail: recordDetailRenderer,
412
+ drawer: drawerRenderer,
413
+ form: surfaceRenderer,
414
+ 'inline-list': createInlineListRenderer(options),
415
+ 'input-form': inputFormRenderer,
416
+ prompt: surfaceRenderer,
417
+ query: queryFormRenderer,
418
+ recordDetail: recordDetailRenderer,
419
+ search: queryFormRenderer,
420
+ summary: metricDashboardRenderer,
421
+ surface: surfaceRenderer,
422
+ 'surface-root': renderSurfaceRoot,
423
+ tabs: tabsRenderer,
424
+ 'record-detail': recordDetailRenderer,
425
+ },
426
+ byViewKind: createStandardViewKindRenderers({
427
+ collectionRenderer,
428
+ drawerRenderer,
429
+ metricDashboardRenderer,
430
+ queryFormRenderer,
431
+ recordDetailRenderer,
432
+ surfaceRenderer,
433
+ tabsRenderer,
434
+ }),
435
+ },
436
+ } satisfies PresentationRendererRegistry<TContext>
437
+
438
+ return options.overrides
439
+ ? mergePresentationRendererRegistries(registry, options.overrides)
440
+ : registry
441
+ }
442
+
443
+ function createStandardViewKindRenderers<TContext>({
444
+ collectionRenderer,
445
+ drawerRenderer,
446
+ metricDashboardRenderer,
447
+ queryFormRenderer,
448
+ recordDetailRenderer,
449
+ surfaceRenderer,
450
+ tabsRenderer,
451
+ }: {
452
+ readonly collectionRenderer: PresentationViewRenderer<TContext>
453
+ readonly drawerRenderer: PresentationViewRenderer<TContext>
454
+ readonly metricDashboardRenderer: PresentationViewRenderer<TContext>
455
+ readonly queryFormRenderer: PresentationViewRenderer<TContext>
456
+ readonly recordDetailRenderer: PresentationViewRenderer<TContext>
457
+ readonly surfaceRenderer: PresentationViewRenderer<TContext>
458
+ readonly tabsRenderer: PresentationViewRenderer<TContext>
459
+ }) {
460
+ const renderers: Record<string, PresentationViewRenderer<TContext>> = {}
461
+
462
+ addViewKindRenderer(renderers, viewKinds.collection, collectionRenderer)
463
+ addViewKindRenderer(renderers, viewKinds.dashboard, metricDashboardRenderer)
464
+ addViewKindRenderer(renderers, viewKinds.drawer, drawerRenderer)
465
+ addViewKindRenderer(renderers, viewKinds.form, surfaceRenderer)
466
+ addViewKindRenderer(renderers, viewKinds.graph, surfaceRenderer)
467
+ addViewKindRenderer(renderers, viewKinds.page, renderSurfaceRoot)
468
+ addViewKindRenderer(renderers, viewKinds.prompt, surfaceRenderer)
469
+ addViewKindRenderer(renderers, viewKinds.recordDetail, recordDetailRenderer)
470
+ addViewKindRenderer(renderers, viewKinds.search, queryFormRenderer)
471
+ addViewKindRenderer(renderers, viewKinds.surface, surfaceRenderer)
472
+ addViewKindRenderer(renderers, viewKinds.tabbedSurface, tabsRenderer)
473
+ addViewKindRenderer(renderers, viewKinds.timeline, surfaceRenderer)
474
+
475
+ return renderers
476
+ }
477
+
478
+ function createDrawerRenderer<TContext>() {
479
+ return function renderDrawer(context: PresentationViewRenderContext<TContext>) {
480
+ return <div className="grid gap-2">{context.renderRegions(context.view)}</div>
481
+ }
482
+ }
483
+
484
+ function addViewKindRenderer<TContext>(
485
+ renderers: Record<string, PresentationViewRenderer<TContext>>,
486
+ kind: ViewKind,
487
+ renderer: PresentationViewRenderer<TContext>,
488
+ ) {
489
+ const label = viewKindLabels[kind]
490
+
491
+ renderers[String(kind)] = renderer
492
+ renderers[label] = renderer
493
+ renderers[label.charAt(0).toLowerCase() + label.slice(1)] = renderer
494
+ }
495
+
496
+ /**
497
+ * Root surfaces are structural containers. Their renderer should project child
498
+ * regions and avoid adding visual chrome of its own.
499
+ */
500
+ function renderSurfaceRoot<TContext>({
501
+ renderView,
502
+ view,
503
+ }: PresentationViewRenderContext<TContext>) {
504
+ return (
505
+ <div
506
+ className="flex min-h-0 flex-1 flex-col gap-5"
507
+ {...createPresentationTestAttributes({ viewId: view.Id })}
508
+ >
509
+ {renderSurfaceRegionViews({
510
+ regions: orderSurfaceRegions(view.Regions),
511
+ renderView,
512
+ stretchPrimaryRegions: true,
513
+ })}
514
+ </div>
515
+ )
516
+ }
517
+
518
+ function createMetricDashboardRenderer<TContext>(
519
+ options: PresentationStandardRendererOptions<TContext>,
520
+ ) {
521
+ return function renderMetricDashboard(
522
+ context: PresentationViewRenderContext<TContext>,
523
+ ) {
524
+ const { dataSourceResolver, view } = context
525
+ const dataSource = dataSourceResolver.resolveViewPrimary(view)
526
+
527
+ return (
528
+ <ProjectedActivityStateBoundary
529
+ state={createPresentationViewActivityState({
530
+ dataSourceResolver,
531
+ pendingLabel:
532
+ resolveStringOption(options.metricDashboard?.pendingLabel, context) ??
533
+ 'Loading view data...',
534
+ view,
535
+ })}
536
+ >
537
+ <ProjectedMetricDashboard
538
+ action={renderPresentationViewActionFallback({ context, options, view })}
539
+ className={resolveStringOption(options.metricDashboard?.className, context)}
540
+ componentSystem={resolveRequiredValueOption(options.componentSystem, context)}
541
+ dataSourceResolver={dataSourceResolver}
542
+ description={resolveStringOption(options.metricDashboard?.description, context)}
543
+ iconByFieldId={resolveValueOption(
544
+ options.metricDashboard?.iconByFieldId,
545
+ context,
546
+ )}
547
+ renderChromeSlot={createPresentationViewChromeSlotRenderer({
548
+ context,
549
+ options,
550
+ resource: dataSource?.data,
551
+ })}
552
+ view={view}
553
+ viewId={view.Id}
554
+ />
555
+ </ProjectedActivityStateBoundary>
556
+ )
557
+ }
558
+ }
559
+
560
+ function createInputFormRenderer<TContext>(
561
+ options: PresentationStandardRendererOptions<TContext>,
562
+ ) {
563
+ return function renderInputForm(context: PresentationViewRenderContext<TContext>) {
564
+ const { dataSourceResolver, module, view } = context
565
+ const inputForm = findPresentationInputFormForView(module, view)
566
+ const dataSource = dataSourceResolver.resolveViewPrimary(view)
567
+
568
+ return (
569
+ <ProjectedInputForm
570
+ choiceToggleClassName={resolveValueOption(
571
+ options.inputForm?.choiceToggleClassName,
572
+ context,
573
+ )}
574
+ choiceTone={resolveValueOption(options.inputForm?.choiceTone, context)}
575
+ className={resolveStringOption(options.inputForm?.className, context)}
576
+ componentSet={resolveStandardPresentationComponentSet(options, context)}
577
+ componentSystem={resolveRequiredValueOption(options.componentSystem, context)}
578
+ contentClassName={resolveStringOption(options.inputForm?.contentClassName, context)}
579
+ dataSourceResolver={dataSourceResolver}
580
+ designSystem={resolveRequiredValueOption(options.designSystem, context)}
581
+ fieldRenderers={resolveValueOption(options.inputForm?.fieldRenderers, context)}
582
+ inputForm={inputForm}
583
+ module={module}
584
+ renderChromeSlot={createPresentationViewChromeSlotRenderer({
585
+ context,
586
+ options,
587
+ resource: dataSource?.data,
588
+ })}
589
+ runtime={options.inputForm?.runtime(context) ?? null}
590
+ view={view}
591
+ />
592
+ )
593
+ }
594
+ }
595
+
596
+ function createQueryFormRenderer<TContext>(
597
+ options: PresentationStandardRendererOptions<TContext>,
598
+ ) {
599
+ return function renderQueryForm(context: PresentationViewRenderContext<TContext>) {
600
+ const { dataSourceResolver, module, view } = context
601
+ const queryForm = findPresentationQueryFormForView(module, view)
602
+ const dataSource = dataSourceResolver.resolveViewPrimary(view)
603
+
604
+ if (queryForm && options.queryForm) {
605
+ return (
606
+ <ProjectedQueryForm
607
+ choiceToggleClassName={resolveValueOption(
608
+ options.queryForm.choiceToggleClassName,
609
+ context,
610
+ )}
611
+ choiceTone={resolveValueOption(options.queryForm.choiceTone, context)}
612
+ className={resolveStringOption(options.queryForm.className, context)}
613
+ componentSet={resolveStandardPresentationComponentSet(options, context)}
614
+ componentSystem={resolveRequiredValueOption(options.componentSystem, context)}
615
+ contentClassName={resolveStringOption(options.queryForm.contentClassName, context)}
616
+ dataSourceResolver={dataSourceResolver}
617
+ designSystem={resolveRequiredValueOption(options.designSystem, context)}
618
+ fieldRenderers={resolveValueOption(options.queryForm.fieldRenderers, context)}
619
+ module={module}
620
+ queryForm={queryForm}
621
+ renderChromeSlot={createPresentationViewChromeSlotRenderer({
622
+ context,
623
+ options,
624
+ resource: dataSource?.data,
625
+ })}
626
+ runtime={options.queryForm.runtime(context)}
627
+ view={view}
628
+ />
629
+ )
630
+ }
631
+
632
+ if (options.inputForm) {
633
+ const inputForm = queryForm
634
+ ? findPresentationInputForm(module, queryForm.FormId)
635
+ : findPresentationInputFormForView(module, view)
636
+ const target = queryForm && inputForm
637
+ ? createRelationQueryInputFormTarget({
638
+ dataSourceResolver,
639
+ inputForm,
640
+ queryForm,
641
+ view,
642
+ })
643
+ : null
644
+
645
+ return (
646
+ <ProjectedInputForm
647
+ choiceToggleClassName={resolveValueOption(
648
+ options.inputForm.choiceToggleClassName,
649
+ context,
650
+ )}
651
+ choiceTone={resolveValueOption(options.inputForm.choiceTone, context)}
652
+ className={resolveStringOption(options.inputForm.className, context)}
653
+ componentSet={resolveStandardPresentationComponentSet(options, context)}
654
+ componentSystem={resolveRequiredValueOption(options.componentSystem, context)}
655
+ contentClassName={resolveStringOption(options.inputForm.contentClassName, context)}
656
+ dataSourceResolver={dataSourceResolver}
657
+ designSystem={resolveRequiredValueOption(options.designSystem, context)}
658
+ fieldRenderers={resolveValueOption(options.inputForm.fieldRenderers, context)}
659
+ inputForm={inputForm}
660
+ module={module}
661
+ renderChromeSlot={createPresentationViewChromeSlotRenderer({
662
+ context,
663
+ options,
664
+ resource: dataSource?.data,
665
+ })}
666
+ runtime={options.inputForm.runtime(context)}
667
+ target={target}
668
+ view={view}
669
+ />
670
+ )
671
+ }
672
+
673
+ return (
674
+ <ProjectedQueryForm
675
+ choiceToggleClassName={resolveValueOption(
676
+ options.queryForm?.choiceToggleClassName,
677
+ context,
678
+ )}
679
+ choiceTone={resolveValueOption(options.queryForm?.choiceTone, context)}
680
+ className={resolveStringOption(options.queryForm?.className, context)}
681
+ componentSet={resolveStandardPresentationComponentSet(options, context)}
682
+ componentSystem={resolveRequiredValueOption(options.componentSystem, context)}
683
+ contentClassName={resolveStringOption(options.queryForm?.contentClassName, context)}
684
+ dataSourceResolver={dataSourceResolver}
685
+ designSystem={resolveRequiredValueOption(options.designSystem, context)}
686
+ fieldRenderers={resolveValueOption(options.queryForm?.fieldRenderers, context)}
687
+ module={module}
688
+ queryForm={queryForm}
689
+ renderChromeSlot={createPresentationViewChromeSlotRenderer({
690
+ context,
691
+ options,
692
+ resource: dataSource?.data,
693
+ })}
694
+ runtime={options.queryForm?.runtime(context) ?? null}
695
+ view={view}
696
+ />
697
+ )
698
+ }
699
+ }
700
+
701
+ function createSurfaceRenderer<TContext>(
702
+ options: PresentationStandardRendererOptions<TContext>,
703
+ ) {
704
+ return function renderSurface(context: PresentationViewRenderContext<TContext>) {
705
+ const { dataSourceResolver, renderView, view } = context
706
+ const dataSource = dataSourceResolver.resolveViewPrimary(view)
707
+ const verticalResize = resolveValueOption(options.surface?.verticalResize, context)
708
+ const isVerticallyResizable = isSurfaceVerticalResizeEnabled(verticalResize)
709
+ const contentClassName = resolveSurfaceContentClassName({
710
+ className: resolveStringOption(
711
+ options.surface?.contentClassName,
712
+ context,
713
+ ),
714
+ isVerticallyResizable,
715
+ view,
716
+ })
717
+
718
+ return (
719
+ <ProjectedViewSurface
720
+ action={renderPresentationViewActionFallback({ context, options, view })}
721
+ className={resolveStringOption(options.surface?.className, context)}
722
+ componentSystem={resolveRequiredValueOption(options.componentSystem, context)}
723
+ contentClassName={contentClassName}
724
+ contentTopInset={resolveValueOption(options.surface?.contentTopInset, context)}
725
+ eyebrow={resolveStringOption(options.surface?.eyebrow, context)}
726
+ renderChromeSlot={createPresentationViewChromeSlotRenderer({
727
+ context,
728
+ options,
729
+ resource: dataSource?.data,
730
+ })}
731
+ title={resolveStringOption(options.surface?.title, context)}
732
+ verticalResize={verticalResize}
733
+ view={view}
734
+ >
735
+ {renderSurfaceRegionViews({
736
+ regions: orderSurfaceRegions(view.Regions),
737
+ renderView,
738
+ stretchPrimaryRegions: isVerticallyResizable,
739
+ })}
740
+ </ProjectedViewSurface>
741
+ )
742
+ }
743
+ }
744
+
745
+ function createTabsRenderer<TContext>(
746
+ options: PresentationStandardRendererOptions<TContext>,
747
+ ) {
748
+ return function renderTabs(context: PresentationViewRenderContext<TContext>) {
749
+ const { dataSourceResolver, renderView, view } = context
750
+ const dataSource = dataSourceResolver.resolveViewPrimary(view)
751
+ const componentSystem = resolveRequiredValueOption(options.componentSystem, context)
752
+
753
+ return (
754
+ <ProjectedTabsView
755
+ componentSystem={componentSystem}
756
+ iconByRegionId={resolveValueOption(options.tabs?.iconByRegionId, context)}
757
+ renderChromeSlot={createPresentationViewChromeSlotRenderer({
758
+ context,
759
+ options,
760
+ resource: dataSource?.data,
761
+ })}
762
+ renderView={renderView}
763
+ view={view}
764
+ viewId={view.Id}
765
+ />
766
+ )
767
+ }
768
+ }
769
+
770
+ function createCollectionRenderer<TContext>(
771
+ options: PresentationStandardRendererOptions<TContext>,
772
+ ) {
773
+ return function renderCollection(
774
+ context: PresentationViewRenderContext<TContext>,
775
+ ) {
776
+ const { dataSourceResolver, view } = context
777
+ const dataSource = dataSourceResolver.resolveViewPrimary(view)
778
+ const items = readPresentationDataSourceItems(dataSource)
779
+ const chromeSlotRenderers = resolveValueOption(
780
+ options.collection?.chromeSlotRenderers,
781
+ context,
782
+ )
783
+ const componentSystem = resolveRequiredValueOption(options.componentSystem, context)
784
+ const collectionContext = {
785
+ ...context,
786
+ dataSource,
787
+ items,
788
+ } satisfies PresentationCollectionRenderContext<TContext>
789
+
790
+ return (
791
+ <ProjectedActivityStateBoundary
792
+ state={createPresentationViewActivityState({ dataSourceResolver, view })}
793
+ >
794
+ <ProjectedCollectionView
795
+ actionRuntimeBindings={resolveValueOption(
796
+ options.collection?.actionRuntimeBindings,
797
+ context,
798
+ )}
799
+ componentSet={resolveStandardPresentationComponentSet(options, context)}
800
+ componentSystem={componentSystem}
801
+ data={items}
802
+ detailFieldRenderers={resolveValueOption(
803
+ options.collection?.detailFieldRenderers,
804
+ context,
805
+ )}
806
+ emptyMessage={
807
+ resolveStringOption(options.collection?.emptyMessage, context) ??
808
+ dataSource?.emptyMessage ??
809
+ `No ${view.Name.toLocaleLowerCase()} records are visible.`
810
+ }
811
+ fieldRenderers={resolveValueOption(
812
+ options.collection?.fieldRenderers,
813
+ context,
814
+ )}
815
+ footer={options.collection?.renderFooter
816
+ ? (collectionRuntime) =>
817
+ options.collection?.renderFooter?.({
818
+ ...collectionContext,
819
+ collectionRuntime: collectionRuntime as ProjectedCollectionRuntime<object>,
820
+ })
821
+ : null}
822
+ pagination={options.collection?.pagination?.(collectionContext)}
823
+ renderChromeSlot={({ collectionRuntime, slot, ...slotContext }) =>
824
+ renderPresentationCollectionChromeSlot({
825
+ context: {
826
+ ...collectionContext,
827
+ ...slotContext,
828
+ collectionRuntime: collectionRuntime as ProjectedCollectionRuntime<object>,
829
+ module: collectionContext.module,
830
+ renderQueryFormSlot: (slot) =>
831
+ renderPresentationCollectionQueryFormSlot({
832
+ context: collectionContext,
833
+ options,
834
+ slot,
835
+ }),
836
+ renderSummarySlot: (slot) =>
837
+ renderPresentationCollectionSummarySlot({
838
+ context: collectionContext,
839
+ options,
840
+ slot,
841
+ }),
842
+ slot,
843
+ },
844
+ registry: chromeSlotRenderers,
845
+ renderFallback: options.collection?.renderChromeSlot,
846
+ })}
847
+ viewId={view.Id}
848
+ />
849
+ </ProjectedActivityStateBoundary>
850
+ )
851
+ }
852
+ }
853
+
854
+ function renderPresentationCollectionChromeSlot<TContext>({
855
+ context,
856
+ registry,
857
+ renderFallback,
858
+ }: {
859
+ readonly context: PresentationCollectionChromeSlotRenderContext<TContext>
860
+ readonly registry:
861
+ | CollectionChromeSlotRendererRegistry<
862
+ PresentationCollectionChromeSlotRenderContext<TContext>,
863
+ ReactNode
864
+ >
865
+ | undefined
866
+ readonly renderFallback?: (
867
+ context: PresentationCollectionChromeSlotRenderContext<TContext>,
868
+ ) => ReactNode
869
+ }) {
870
+ const renderer = resolveCollectionChromeSlotRenderer(registry, context.slot)
871
+ return renderer?.(context) ??
872
+ renderStandardCollectionChromeSlot(context) ??
873
+ renderFallback?.(context) ??
874
+ null
875
+ }
876
+
877
+ function renderPresentationCollectionQueryFormSlot<TContext>({
878
+ context,
879
+ options,
880
+ slot,
881
+ }: {
882
+ readonly context: PresentationCollectionRenderContext<TContext>
883
+ readonly options: PresentationStandardRendererOptions<TContext>
884
+ readonly slot: CollectionChromeSlotDefinition
885
+ }) {
886
+ const { dataSourceResolver, module, view } = context
887
+ const queryForm = slot.QueryFormId
888
+ ? findPresentationQueryForm(module, slot.QueryFormId)
889
+ : findPresentationQueryFormForView(module, view)
890
+
891
+ return (
892
+ <ProjectedQueryForm
893
+ choiceToggleClassName={resolveValueOption(
894
+ options.queryForm?.choiceToggleClassName,
895
+ context,
896
+ )}
897
+ choiceTone={resolveValueOption(options.queryForm?.choiceTone, context)}
898
+ className={resolveStringOption(options.queryForm?.className, context)}
899
+ componentSet={resolveStandardPresentationComponentSet(options, context)}
900
+ componentSystem={resolveRequiredValueOption(options.componentSystem, context)}
901
+ contentClassName={resolveStringOption(options.queryForm?.contentClassName, context)}
902
+ dataSourceResolver={dataSourceResolver}
903
+ designSystem={resolveRequiredValueOption(options.designSystem, context)}
904
+ fieldRenderers={resolveValueOption(options.queryForm?.fieldRenderers, context)}
905
+ module={module}
906
+ queryForm={queryForm}
907
+ runtime={options.queryForm?.runtime(context) ?? null}
908
+ view={view}
909
+ />
910
+ )
911
+ }
912
+
913
+ function resolveSurfaceContentClassName({
914
+ className,
915
+ isVerticallyResizable,
916
+ view,
917
+ }: {
918
+ readonly className?: string
919
+ readonly isVerticallyResizable: boolean
920
+ readonly view: ViewDefinition
921
+ }) {
922
+ if (hasSidecarSurfaceRegions(view.Regions)) {
923
+ return cn(
924
+ 'grid min-h-0 flex-1 gap-y-4 lg:grid-cols-[minmax(0,1fr)_auto] lg:items-start lg:gap-x-0 lg:has-[>[data-surface-region-kind=sidecar]:not(:empty)]:gap-x-4',
925
+ className,
926
+ )
927
+ }
928
+
929
+ if (isVerticallyResizable) {
930
+ return cn(
931
+ 'flex min-h-0 flex-1 flex-col gap-4 overflow-hidden',
932
+ className,
933
+ )
934
+ }
935
+
936
+ return className ?? (view.Regions.length > 0 ? 'grid w-full min-w-0 gap-4' : undefined)
937
+ }
938
+
939
+ function orderSurfaceRegions(
940
+ regions: readonly ViewRegionDefinition[],
941
+ ): readonly ViewRegionDefinition[] {
942
+ if (!hasSidecarSurfaceRegions(regions)) {
943
+ return regions
944
+ }
945
+
946
+ return [
947
+ ...regions.filter((region) => isPrimarySurfaceRegionKind(region.Kind)),
948
+ ...regions.filter((region) => isSidecarSurfaceRegionKind(region.Kind)),
949
+ ...regions.filter((region) =>
950
+ !isPrimarySurfaceRegionKind(region.Kind) &&
951
+ !isSidecarSurfaceRegionKind(region.Kind)),
952
+ ]
953
+ }
954
+
955
+ function renderSurfaceRegionViews({
956
+ regions,
957
+ renderView,
958
+ stretchPrimaryRegions,
959
+ }: {
960
+ readonly regions: readonly ViewRegionDefinition[]
961
+ readonly renderView: (viewId: string) => ReactNode
962
+ readonly stretchPrimaryRegions?: boolean
963
+ }) {
964
+ return regions.flatMap((region) =>
965
+ getRegionViewIds(region).map((childViewId) => (
966
+ <div
967
+ className={resolveSurfaceRegionClassName(region, { stretchPrimaryRegions })}
968
+ data-surface-region-kind={resolveSurfaceRegionLayoutKind(region)}
969
+ key={`${region.Id}:${childViewId}`}
970
+ >
971
+ {renderView(childViewId)}
972
+ </div>
973
+ )),
974
+ )
975
+ }
976
+
977
+ function hasSidecarSurfaceRegions(regions: readonly ViewRegionDefinition[]) {
978
+ if (regions.length < 2) {
979
+ return false
980
+ }
981
+
982
+ return regions.some((region) => isPrimarySurfaceRegionKind(region.Kind)) &&
983
+ regions.some((region) => isSidecarSurfaceRegionKind(region.Kind))
984
+ }
985
+
986
+ function resolveSurfaceRegionClassName(
987
+ region: ViewRegionDefinition,
988
+ options: { readonly stretchPrimaryRegions?: boolean } = {},
989
+ ) {
990
+ const layoutKind = resolveSurfaceRegionLayoutKind(region)
991
+ if (layoutKind === 'sidecar') {
992
+ return 'min-h-0 min-w-0 empty:hidden lg:w-[clamp(18rem,25vw,24rem)]'
993
+ }
994
+
995
+ if (layoutKind === 'primary' && options.stretchPrimaryRegions) {
996
+ return 'flex min-h-0 w-full min-w-0 flex-1 flex-col overflow-hidden'
997
+ }
998
+
999
+ return 'min-h-0 w-full min-w-0'
1000
+ }
1001
+
1002
+ function resolveSurfaceRegionLayoutKind(region: ViewRegionDefinition) {
1003
+ if (isSidecarSurfaceRegionKind(region.Kind)) {
1004
+ return 'sidecar'
1005
+ }
1006
+
1007
+ if (isPrimarySurfaceRegionKind(region.Kind)) {
1008
+ return 'primary'
1009
+ }
1010
+
1011
+ return 'supporting'
1012
+ }
1013
+
1014
+ function isPrimarySurfaceRegionKind(kind: string | number) {
1015
+ return matchesViewRegionKind(kind, viewRegionKinds.primary) ||
1016
+ matchesViewRegionKind(kind, viewRegionKinds.content) ||
1017
+ matchesViewRegionKind(kind, viewRegionKinds.surface) ||
1018
+ matchesViewRegionKind(kind, viewRegionKinds.collection) ||
1019
+ matchesViewRegionKind(kind, viewRegionKinds.detail)
1020
+ }
1021
+
1022
+ function isSidecarSurfaceRegionKind(kind: string | number) {
1023
+ return matchesViewRegionKind(kind, viewRegionKinds.sidebar) ||
1024
+ matchesViewRegionKind(kind, viewRegionKinds.statusList) ||
1025
+ matchesViewRegionKind(kind, viewRegionKinds.list) ||
1026
+ matchesViewRegionKind(kind, viewRegionKinds.inspector) ||
1027
+ matchesViewRegionKind(kind, viewRegionKinds.panel)
1028
+ }
1029
+
1030
+ function matchesViewRegionKind(kind: string | number, expected: string | number) {
1031
+ const expectedLabel =
1032
+ viewRegionKindLabels[expected as keyof typeof viewRegionKindLabels] ?? expected
1033
+ return kind === expected ||
1034
+ normalizeEnumDiscriminator(kind) === normalizeEnumDiscriminator(expectedLabel)
1035
+ }
1036
+
1037
+ function renderPresentationCollectionSummarySlot<TContext>({
1038
+ context,
1039
+ options,
1040
+ slot,
1041
+ }: {
1042
+ readonly context: PresentationCollectionRenderContext<TContext>
1043
+ readonly options: PresentationStandardRendererOptions<TContext>
1044
+ readonly slot: CollectionChromeSlotDefinition
1045
+ }) {
1046
+ const fieldIds = resolveCollectionSummaryFieldIds(context.module, slot)
1047
+ if (fieldIds.length === 0) {
1048
+ return null
1049
+ }
1050
+
1051
+ return (
1052
+ <ProjectedMetricStrip
1053
+ className={resolveStringOption(options.collection?.summaryClassName, context)}
1054
+ componentSystem={resolveRequiredValueOption(options.componentSystem, context)}
1055
+ dataSourceResolver={context.dataSourceResolver}
1056
+ fieldIds={fieldIds}
1057
+ iconByFieldId={resolveValueOption(options.metricDashboard?.iconByFieldId, context)}
1058
+ />
1059
+ )
1060
+ }
1061
+
1062
+ function resolveCollectionSummaryFieldIds(
1063
+ module: PresentationCollectionRenderContext<unknown>['module'],
1064
+ slot: CollectionChromeSlotDefinition,
1065
+ ) {
1066
+ if (slot.FieldIds.length > 0) {
1067
+ return slot.FieldIds
1068
+ }
1069
+
1070
+ const slotDataSourceIds = new Set(slot.DataSourceIds)
1071
+ return module.Fields
1072
+ .filter((field) =>
1073
+ field.Source?.DataSourceId &&
1074
+ slotDataSourceIds.has(field.Source.DataSourceId) &&
1075
+ isMetricField(field.DisplayKind))
1076
+ .map((field) => field.Id)
1077
+ }
1078
+
1079
+ function isMetricField(displayKind: unknown) {
1080
+ return displayKind === fieldDisplayKinds.metric ||
1081
+ String(displayKind).toLocaleLowerCase() === 'metric'
1082
+ }
1083
+
1084
+ function createPresentationViewChromeSlotRenderer<TContext>({
1085
+ context,
1086
+ options,
1087
+ resource,
1088
+ }: {
1089
+ readonly context: PresentationViewRenderContext<TContext>
1090
+ readonly options: PresentationStandardRendererOptions<TContext>
1091
+ readonly resource?: unknown
1092
+ }) {
1093
+ return (slot: ViewChromeSlotDefinition, view: ViewDefinition | null) =>
1094
+ renderStandardViewChromeSlot({
1095
+ actionContext: context.context,
1096
+ actionGroupOptions: options.actionGroup,
1097
+ componentSystem: resolveRequiredValueOption(options.componentSystem, context),
1098
+ dataSourceResolver: context.dataSourceResolver,
1099
+ designSystem: resolveRequiredValueOption(options.designSystem, context),
1100
+ documentViewIds: slot.ViewIds,
1101
+ module: context.module,
1102
+ resource,
1103
+ slot,
1104
+ view: view ?? context.view,
1105
+ workspaceView: view ?? context.view,
1106
+ })
1107
+ }
1108
+
1109
+ function renderPresentationViewActionFallback<TContext>({
1110
+ context,
1111
+ options,
1112
+ view,
1113
+ }: {
1114
+ readonly context: PresentationViewRenderContext<TContext>
1115
+ readonly options: PresentationStandardRendererOptions<TContext>
1116
+ readonly view: ViewDefinition
1117
+ }) {
1118
+ if (!options.actionGroup || hasViewActionsChromeSlot(view)) {
1119
+ return null
1120
+ }
1121
+
1122
+ return (
1123
+ <PresentationActionGroup
1124
+ context={context.context}
1125
+ dataSourceResolver={context.dataSourceResolver}
1126
+ module={context.module}
1127
+ options={options.actionGroup}
1128
+ view={view}
1129
+ />
1130
+ )
1131
+ }
1132
+
1133
+ function hasViewActionsChromeSlot(view: ViewDefinition) {
1134
+ return view.Chrome?.Slots.some((slot) =>
1135
+ isViewChromeSlotKind(slot, viewChromeSlotKinds.actions)) ?? false
1136
+ }
1137
+
1138
+ function createRecordDetailRenderer<TContext>(
1139
+ options: PresentationStandardRendererOptions<TContext>,
1140
+ ) {
1141
+ return function renderRecordDetail(context: PresentationViewRenderContext<TContext>) {
1142
+ const { dataSourceResolver, view } = context
1143
+ const dataSource = dataSourceResolver.resolveViewPrimary(view)
1144
+ const data = dataSource?.data
1145
+ const componentSystem = resolveRequiredValueOption(options.componentSystem, context)
1146
+
1147
+ return (
1148
+ <ProjectedActivityStateBoundary
1149
+ state={createPresentationViewActivityState({ dataSourceResolver, view })}
1150
+ >
1151
+ <ProjectedViewSurface
1152
+ action={renderPresentationViewActionFallback({ context, options, view })}
1153
+ className={resolveStringOption(options.recordDetail?.className, context)}
1154
+ componentSystem={componentSystem}
1155
+ contentClassName={
1156
+ resolveStringOption(options.recordDetail?.contentClassName, context) ??
1157
+ 'grid gap-4'
1158
+ }
1159
+ renderChromeSlot={createPresentationViewChromeSlotRenderer({
1160
+ context,
1161
+ options,
1162
+ resource: data,
1163
+ })}
1164
+ title={resolveStringOption(options.recordDetail?.title, context) ?? view.Name}
1165
+ view={view}
1166
+ >
1167
+ {data && typeof data === 'object' ? (
1168
+ <ProjectedRecordDetails
1169
+ componentSystem={componentSystem}
1170
+ data={data as object}
1171
+ emptyMessage={
1172
+ resolveStringOption(options.recordDetail?.emptyMessage, context) ??
1173
+ dataSource?.emptyMessage
1174
+ }
1175
+ fieldRenderers={resolveValueOption(
1176
+ options.recordDetail?.fieldRenderers,
1177
+ context,
1178
+ )}
1179
+ hiddenFieldLabels={
1180
+ resolveValueOption(options.recordDetail?.hiddenFieldLabels, context)
1181
+ }
1182
+ viewId={view.Id}
1183
+ />
1184
+ ) : (
1185
+ <ProjectedStatusBlock
1186
+ label={
1187
+ resolveStringOption(options.recordDetail?.emptyMessage, context) ??
1188
+ dataSource?.emptyMessage ??
1189
+ 'No details are available.'
1190
+ }
1191
+ />
1192
+ )}
1193
+ </ProjectedViewSurface>
1194
+ </ProjectedActivityStateBoundary>
1195
+ )
1196
+ }
1197
+ }
1198
+
1199
+ function createInlineListRenderer<TContext>(
1200
+ options: PresentationStandardRendererOptions<TContext>,
1201
+ ) {
1202
+ return function renderInlineList(context: PresentationViewRenderContext<TContext>) {
1203
+ const { dataSourceResolver, view } = context
1204
+ const dataSource = dataSourceResolver.resolveViewPrimary(view)
1205
+ const items = readPresentationDataSourceItems(dataSource)
1206
+ const componentSystem = resolveRequiredValueOption(options.componentSystem, context)
1207
+
1208
+ return (
1209
+ <ProjectedActivityStateBoundary
1210
+ state={createPresentationViewActivityState({ dataSourceResolver, view })}
1211
+ >
1212
+ {items.length === 0 && options.inlineList?.renderItems ? (
1213
+ resolveStringOption(options.inlineList?.emptyMessage, context) ??
1214
+ dataSource?.emptyMessage ??
1215
+ null
1216
+ ) : (
1217
+ options.inlineList?.renderItems?.({ ...context, dataSource, items }) ?? (
1218
+ <ProjectedInlineList
1219
+ actionRuntimeBindings={resolveValueOption(
1220
+ options.collection?.actionRuntimeBindings,
1221
+ context,
1222
+ )}
1223
+ componentSet={resolveStandardPresentationComponentSet(options, context)}
1224
+ componentSystem={componentSystem}
1225
+ data={items}
1226
+ emptyMessage={
1227
+ resolveStringOption(options.inlineList?.emptyMessage, context) ??
1228
+ dataSource?.emptyMessage
1229
+ }
1230
+ fieldRenderers={resolveValueOption(
1231
+ options.collection?.fieldRenderers,
1232
+ context,
1233
+ )}
1234
+ title={view.Name}
1235
+ viewId={view.Id}
1236
+ />
1237
+ )
1238
+ )}
1239
+ </ProjectedActivityStateBoundary>
1240
+ )
1241
+ }
1242
+ }
1243
+
1244
+ function resolveStandardPresentationComponentSet<TContext>(
1245
+ options: PresentationStandardRendererOptions<TContext>,
1246
+ context: PresentationViewRenderContext<TContext>,
1247
+ ) {
1248
+ return resolveStringOption(options.componentSet, context) ??
1249
+ defaultPresentationComponentSet
1250
+ }
1251
+
1252
+ function resolveStringOption<TContext>(
1253
+ option: PresentationStringOption<TContext> | undefined,
1254
+ context: PresentationViewRenderContext<TContext>,
1255
+ ) {
1256
+ return resolveValueOption(option, context)
1257
+ }
1258
+
1259
+ function resolveRequiredValueOption<TContext, TValue>(
1260
+ option: PresentationRequiredValueOption<TContext, TValue>,
1261
+ context: PresentationViewRenderContext<TContext>,
1262
+ ) {
1263
+ return typeof option === 'function'
1264
+ ? (option as (context: PresentationViewRenderContext<TContext>) => TValue)(context)
1265
+ : option
1266
+ }
1267
+
1268
+ function resolveValueOption<TContext, TValue>(
1269
+ option: PresentationValueOption<TContext, TValue> | undefined,
1270
+ context: PresentationViewRenderContext<TContext>,
1271
+ ) {
1272
+ return typeof option === 'function'
1273
+ ? (option as (context: PresentationViewRenderContext<TContext>) => TValue | undefined)(context)
1274
+ : option
1275
+ }
1276
+
1277
+ function normalizeEnumDiscriminator(value: string | number) {
1278
+ return String(value).replace(/[-_\s]/g, '').toLocaleLowerCase()
1279
+ }
1280
+
1281
+ function isSurfaceVerticalResizeEnabled(
1282
+ value: boolean | ProjectedViewSurfaceVerticalResizeOptions | null | undefined,
1283
+ ) {
1284
+ return value === true ||
1285
+ (typeof value === 'object' && value !== null && value.enabled !== false)
1286
+ }
1287
+
1288
+ function cn(...values: readonly (false | null | string | undefined)[]) {
1289
+ return values.filter(Boolean).join(' ')
1290
+ }