@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,308 @@
1
+ import { Fragment, useEffect, useMemo, useState, type ReactNode } from 'react'
2
+
3
+ import {
4
+ findPresentationView,
5
+ getRegionViewIds,
6
+ isViewChromeSlotKind,
7
+ type ViewChromeSlotDefinition,
8
+ type ViewDefinition,
9
+ type ViewRegionDefinition,
10
+ } from '@cohesivesystems/presentation-core'
11
+ import type {
12
+ PresentationShadcnComponentSystem as PresentationComponentSystem,
13
+ } from './presentation-shadcn-component-system'
14
+ import {
15
+ renderPresentationIcon,
16
+ } from './presentation-icon-registry'
17
+ import {
18
+ projectPresentationIconDiagnostics,
19
+ } from './presentation-icon-diagnostics'
20
+ import {
21
+ usePresentationModule,
22
+ useRegisterPresentationProjectionDiagnostics,
23
+ } from '@cohesivesystems/presentation-react'
24
+ import { ProjectedStatusBlock } from './projected-activity-state'
25
+ import {
26
+ ProjectedViewChrome,
27
+ } from './projected-view-chrome'
28
+ import type {
29
+ ProjectedViewSurfaceChromeSlotRenderer,
30
+ } from './projected-view-surface'
31
+ import {
32
+ viewChromeSlotKinds,
33
+ viewChromeSlotPlacements,
34
+ viewRegionKinds,
35
+ } from '@cohesivesystems/presentation-contracts'
36
+
37
+ export interface ProjectedTabsViewProps {
38
+ readonly chromeAfterContentClassName?: string
39
+ readonly chromeBeforeContentClassName?: string
40
+ readonly chromeFooterClassName?: string
41
+ readonly chromeHeaderClassName?: string
42
+ readonly className?: string
43
+ readonly componentSystem: PresentationComponentSystem
44
+ readonly defaultRegionId?: string
45
+ readonly fallbackRegions?: readonly ViewRegionDefinition[]
46
+ readonly iconByRegionId?: Readonly<Record<string, ReactNode>>
47
+ readonly renderChromeSlot?: ProjectedViewSurfaceChromeSlotRenderer
48
+ readonly renderView: (viewId: string) => ReactNode
49
+ readonly view?: ViewDefinition | null
50
+ readonly viewId: string
51
+ }
52
+
53
+ export function ProjectedTabsView({
54
+ chromeAfterContentClassName,
55
+ chromeBeforeContentClassName,
56
+ chromeFooterClassName,
57
+ chromeHeaderClassName,
58
+ className,
59
+ componentSystem,
60
+ defaultRegionId,
61
+ fallbackRegions,
62
+ iconByRegionId,
63
+ renderChromeSlot,
64
+ renderView,
65
+ view: viewOverride,
66
+ viewId,
67
+ }: ProjectedTabsViewProps) {
68
+ const module = usePresentationModule()
69
+ const view = viewOverride ?? findPresentationView<ViewDefinition>(module, viewId)
70
+ const tabRegions = useMemo(
71
+ () => getTabRegions(view, fallbackRegions),
72
+ [fallbackRegions, view],
73
+ )
74
+ const iconDiagnosticSource = `projected-tabs-view-icons:${view?.Id ?? viewId}`
75
+ const iconDiagnostics = useMemo(
76
+ () => projectPresentationIconDiagnostics({
77
+ icons: resolveTabRegionIconSubjects(tabRegions),
78
+ module,
79
+ source: iconDiagnosticSource,
80
+ surfaceId: view?.Id ?? viewId,
81
+ surfaceName: view?.Name,
82
+ }),
83
+ [
84
+ iconDiagnosticSource,
85
+ module,
86
+ tabRegions,
87
+ view?.Id,
88
+ view?.Name,
89
+ viewId,
90
+ ],
91
+ )
92
+ useRegisterPresentationProjectionDiagnostics(iconDiagnosticSource, iconDiagnostics)
93
+ const defaultValue =
94
+ defaultRegionId ??
95
+ view?.State?.find((state) => state.Type === 'region-id')?.DefaultValue ??
96
+ tabRegions[0]?.Id
97
+ const [requestedActiveRegionId, setActiveRegionId] = useState(defaultValue ?? '')
98
+ const activeRegionId = tabRegions.some((region) => region.Id === requestedActiveRegionId)
99
+ ? requestedActiveRegionId
100
+ : defaultValue ?? ''
101
+ const [visitedRegionIds, setVisitedRegionIds] = useState<ReadonlySet<string>>(
102
+ () => new Set(activeRegionId ? [activeRegionId] : []),
103
+ )
104
+
105
+ useEffect(() => {
106
+ if (!activeRegionId) {
107
+ return
108
+ }
109
+
110
+ setVisitedRegionIds((current) => {
111
+ if (current.has(activeRegionId)) {
112
+ return current
113
+ }
114
+
115
+ return new Set([...current, activeRegionId])
116
+ })
117
+ }, [activeRegionId])
118
+
119
+ if (!module && tabRegions.length === 0) {
120
+ return <ProjectedStatusBlock label="Presentation module is not available." />
121
+ }
122
+
123
+ if (!view && tabRegions.length === 0) {
124
+ return <ProjectedStatusBlock label={`Presentation view '${viewId}' is not available.`} />
125
+ }
126
+
127
+ if (!defaultValue || tabRegions.length === 0) {
128
+ return <ProjectedStatusBlock label={`Presentation view '${view?.Name ?? view?.Id ?? viewId}' has no tabs.`} />
129
+ }
130
+
131
+ const renderTabsChromeSlot = (slot: ViewChromeSlotDefinition, chromeView: ViewDefinition | null) => {
132
+ if (isViewChromeSlotKind(slot, viewChromeSlotKinds.viewSwitch)) {
133
+ return renderTabsList({
134
+ activeRegionId: activeRegionId || defaultValue,
135
+ componentSystem,
136
+ iconByRegionId,
137
+ module,
138
+ tabRegions,
139
+ viewId: view?.Id ?? viewId,
140
+ })
141
+ }
142
+
143
+ return renderChromeSlot?.(slot, chromeView) ?? null
144
+ }
145
+ const hasTabSwitchChrome = hasTabSwitchChromeSlot(view)
146
+
147
+ return componentSystem.tabs.TabsLayout({
148
+ className,
149
+ onValueChange: setActiveRegionId,
150
+ value: activeRegionId || defaultValue,
151
+ viewId: view?.Id ?? viewId,
152
+ children: (
153
+ <>
154
+ <ProjectedViewChrome
155
+ className={chromeHeaderClassName}
156
+ componentSystem={componentSystem}
157
+ placement={viewChromeSlotPlacements.header}
158
+ renderSlot={(slot) => renderTabsChromeSlot(slot, view)}
159
+ view={view}
160
+ />
161
+
162
+ {hasTabSwitchChrome ? null : renderTabsList({
163
+ activeRegionId: activeRegionId || defaultValue,
164
+ componentSystem,
165
+ iconByRegionId,
166
+ module,
167
+ tabRegions,
168
+ viewId: view?.Id ?? viewId,
169
+ })}
170
+
171
+ <ProjectedViewChrome
172
+ className={chromeBeforeContentClassName}
173
+ componentSystem={componentSystem}
174
+ placement={viewChromeSlotPlacements.beforeContent}
175
+ renderSlot={(slot) => renderTabsChromeSlot(slot, view)}
176
+ view={view}
177
+ />
178
+
179
+ {tabRegions.map((region) => {
180
+ const viewIds = getRegionViewIds(region)
181
+ const shouldRenderRegion =
182
+ region.Id === activeRegionId || visitedRegionIds.has(region.Id)
183
+ return (
184
+ <Fragment key={region.Id}>
185
+ {componentSystem.tabs.TabsPanel({
186
+ region,
187
+ value: region.Id,
188
+ viewId: view?.Id ?? viewId,
189
+ children: (
190
+ <>
191
+ {shouldRenderRegion
192
+ ? viewIds.map((childViewId) => (
193
+ <div
194
+ className="flex min-h-0 flex-1 flex-col overflow-hidden"
195
+ key={childViewId}
196
+ >
197
+ {renderView(childViewId)}
198
+ </div>
199
+ ))
200
+ : null}
201
+ </>
202
+ ),
203
+ })}
204
+ </Fragment>
205
+ )
206
+ })}
207
+
208
+ <ProjectedViewChrome
209
+ className={chromeAfterContentClassName}
210
+ componentSystem={componentSystem}
211
+ placement={viewChromeSlotPlacements.afterContent}
212
+ renderSlot={(slot) => renderTabsChromeSlot(slot, view)}
213
+ view={view}
214
+ />
215
+
216
+ <ProjectedViewChrome
217
+ className={chromeFooterClassName}
218
+ componentSystem={componentSystem}
219
+ placement={viewChromeSlotPlacements.footer}
220
+ renderSlot={(slot) => renderTabsChromeSlot(slot, view)}
221
+ view={view}
222
+ />
223
+ </>
224
+ ),
225
+ })
226
+ }
227
+
228
+ function renderTabsList({
229
+ activeRegionId,
230
+ componentSystem,
231
+ iconByRegionId,
232
+ module,
233
+ tabRegions,
234
+ viewId,
235
+ }: {
236
+ readonly activeRegionId: string
237
+ readonly componentSystem: PresentationComponentSystem
238
+ readonly iconByRegionId: Readonly<Record<string, ReactNode>> | undefined
239
+ readonly module: ReturnType<typeof usePresentationModule>
240
+ readonly tabRegions: readonly ViewRegionDefinition[]
241
+ readonly viewId: string | null
242
+ }) {
243
+ return componentSystem.tabs.TabsList({
244
+ viewId,
245
+ children: tabRegions.map((region) => (
246
+ <Fragment key={region.Id}>
247
+ {componentSystem.tabs.TabsTrigger({
248
+ isActive: activeRegionId === region.Id,
249
+ region,
250
+ value: region.Id,
251
+ viewId,
252
+ children: (
253
+ <>
254
+ {iconByRegionId?.[region.Id] ?? renderPresentationIcon({
255
+ className: 'size-3.5',
256
+ icon: region.Icon,
257
+ module,
258
+ })}
259
+ {region.Name ?? region.Id}
260
+ </>
261
+ ),
262
+ })}
263
+ </Fragment>
264
+ )),
265
+ })
266
+ }
267
+
268
+ function resolveTabRegionIconSubjects(
269
+ tabRegions: readonly ViewRegionDefinition[],
270
+ ) {
271
+ return tabRegions.flatMap((region) => {
272
+ if (!region.Icon) {
273
+ return []
274
+ }
275
+
276
+ return [{
277
+ details: {
278
+ regionId: region.Id,
279
+ },
280
+ icon: region.Icon,
281
+ id: region.Id,
282
+ kind: 'tab-region-icon',
283
+ label: region.Name,
284
+ }]
285
+ })
286
+ }
287
+
288
+ function getTabRegions(
289
+ view: ViewDefinition | null,
290
+ fallbackRegions: readonly ViewRegionDefinition[] | undefined,
291
+ ) {
292
+ const regions = view?.Regions ?? fallbackRegions ?? []
293
+ return (
294
+ regions.filter(
295
+ (region): region is ViewRegionDefinition =>
296
+ hasRegionKind(region.Kind, 'Tab', 6) && getRegionViewIds(region).length > 0,
297
+ )
298
+ )
299
+ }
300
+
301
+ function hasRegionKind(value: string | number, name: string, numericValue: number) {
302
+ return value === name || value === numericValue || value === viewRegionKinds.tab
303
+ }
304
+
305
+ function hasTabSwitchChromeSlot(view: ViewDefinition | null) {
306
+ return view?.Chrome?.Slots.some((slot) =>
307
+ isViewChromeSlotKind(slot, viewChromeSlotKinds.viewSwitch)) ?? false
308
+ }
@@ -0,0 +1,148 @@
1
+ import { useMemo, type ReactNode } from 'react'
2
+
3
+ import type {
4
+ ViewChromeSlotDefinition,
5
+ ViewChromeSlotPlacement,
6
+ ViewDefinition,
7
+ } from '@cohesivesystems/presentation-core'
8
+ import type {
9
+ PresentationShadcnComponentSystem as PresentationComponentSystem,
10
+ } from './presentation-shadcn-component-system'
11
+ import {
12
+ isViewChromeSlotKind,
13
+ isViewChromeSlotPlacementValue,
14
+ resolveViewChromeIconSubjects,
15
+ } from '@cohesivesystems/presentation-core'
16
+ import {
17
+ projectPresentationIconDiagnostics,
18
+ } from './presentation-icon-diagnostics'
19
+ import {
20
+ usePresentationModule,
21
+ useRegisterPresentationProjectionDiagnostics,
22
+ } from '@cohesivesystems/presentation-react'
23
+ import {
24
+ viewChromeSlotKinds,
25
+ viewChromeSlotPlacements,
26
+ } from '@cohesivesystems/presentation-contracts'
27
+
28
+ export interface ProjectedViewChromeProps {
29
+ readonly className?: string
30
+ readonly componentSystem: PresentationComponentSystem
31
+ readonly placement: ViewChromeSlotPlacement | string | number
32
+ readonly renderContainer?: (children: ReactNode) => ReactNode
33
+ readonly renderSlot: (slot: ViewChromeSlotDefinition) => ReactNode
34
+ readonly view: ViewDefinition | null
35
+ }
36
+
37
+ export function ProjectedViewChrome({
38
+ className,
39
+ componentSystem,
40
+ placement,
41
+ renderContainer,
42
+ renderSlot,
43
+ view,
44
+ }: ProjectedViewChromeProps) {
45
+ const module = usePresentationModule()
46
+ const slots = useMemo(
47
+ () => resolveViewChromeSlots(view, placement),
48
+ [placement, view],
49
+ )
50
+ const iconDiagnostics = useMemo(
51
+ () => projectPresentationIconDiagnostics({
52
+ icons: resolveViewChromeIconSubjects(slots),
53
+ module,
54
+ source: `projected-view-chrome-icons:${view?.Id ?? 'unknown'}:${String(placement)}`,
55
+ surfaceId: view?.Id,
56
+ surfaceName: view?.Name,
57
+ }),
58
+ [
59
+ module,
60
+ placement,
61
+ slots,
62
+ view?.Id,
63
+ view?.Name,
64
+ ],
65
+ )
66
+ useRegisterPresentationProjectionDiagnostics(
67
+ `projected-view-chrome-icons:${view?.Id ?? 'unknown'}:${String(placement)}`,
68
+ iconDiagnostics,
69
+ )
70
+
71
+ const ViewChromeSlot = componentSystem.viewChrome.ViewChromeSlot
72
+ const renderedSlots = slots
73
+ .map((slot) => {
74
+ const rendered = renderSlot(slot)
75
+ return isRenderableNode(rendered) ? (
76
+ <ViewChromeSlot
77
+ key={slot.Id}
78
+ placement={placement}
79
+ slot={slot}
80
+ viewId={view?.Id ?? null}
81
+ >
82
+ {rendered}
83
+ </ViewChromeSlot>
84
+ ) : null
85
+ })
86
+ .filter(isRenderableNode)
87
+
88
+ if (renderedSlots.length === 0) {
89
+ return null
90
+ }
91
+
92
+ if (renderContainer) {
93
+ return renderContainer(renderedSlots)
94
+ }
95
+
96
+ return className ? (
97
+ <div className={cn(className)}>{renderedSlots}</div>
98
+ ) : (
99
+ <>{renderedSlots}</>
100
+ )
101
+ }
102
+
103
+ export function resolveViewChromeSlots(
104
+ view: ViewDefinition | null,
105
+ placement: ViewChromeSlotPlacement | string | number,
106
+ ) {
107
+ return (view?.Chrome?.Slots ?? []).filter((slot) =>
108
+ isViewChromeSlotPlacementValue(
109
+ resolveViewChromeSlotPlacement(slot),
110
+ placement,
111
+ ))
112
+ }
113
+
114
+ export function resolveViewChromeSlotPlacement(
115
+ slot: ViewChromeSlotDefinition,
116
+ ): ViewChromeSlotPlacement {
117
+ if (slot.Placement !== undefined && slot.Placement !== null) {
118
+ return slot.Placement
119
+ }
120
+
121
+ if (
122
+ isViewChromeSlotKind(slot, viewChromeSlotKinds.actions) ||
123
+ isViewChromeSlotKind(slot, viewChromeSlotKinds.viewSwitch) ||
124
+ isViewChromeSlotKind(slot, viewChromeSlotKinds.layoutSwitch) ||
125
+ isViewChromeSlotKind(slot, viewChromeSlotKinds.headingTrailing) ||
126
+ isViewChromeSlotKind(slot, viewChromeSlotKinds.status)
127
+ ) {
128
+ return viewChromeSlotPlacements.header
129
+ }
130
+
131
+ if (isViewChromeSlotKind(slot, viewChromeSlotKinds.badgeStrip)) {
132
+ return viewChromeSlotPlacements.beforeContent
133
+ }
134
+
135
+ if (isViewChromeSlotKind(slot, viewChromeSlotKinds.metricStrip)) {
136
+ return viewChromeSlotPlacements.afterContent
137
+ }
138
+
139
+ return viewChromeSlotPlacements.none
140
+ }
141
+
142
+ function isRenderableNode(node: ReactNode): node is Exclude<ReactNode, null | undefined | false> {
143
+ return node !== null && node !== undefined && node !== false
144
+ }
145
+
146
+ function cn(...values: readonly (false | null | string | undefined)[]) {
147
+ return values.filter(Boolean).join(' ')
148
+ }
@@ -0,0 +1,131 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import {
4
+ findPresentationView,
5
+ type ViewChromeSlotDefinition,
6
+ type ViewChromeSlotPlacement,
7
+ type ViewDefinition,
8
+ } from '@cohesivesystems/presentation-core'
9
+ import type {
10
+ PresentationShadcnComponentSystem as PresentationComponentSystem,
11
+ } from './presentation-shadcn-component-system'
12
+ import type {
13
+ PresentationViewSurfaceContentTopInset,
14
+ PresentationViewSurfaceVerticalResizeOptions,
15
+ } from './presentation-component-groups'
16
+ import { usePresentationModule } from '@cohesivesystems/presentation-react'
17
+ import {
18
+ ProjectedViewChrome,
19
+ resolveViewChromeSlots,
20
+ } from './projected-view-chrome'
21
+ import { viewChromeSlotPlacements } from '@cohesivesystems/presentation-contracts'
22
+
23
+ interface ProjectedViewSurfaceProps {
24
+ readonly action?: ReactNode
25
+ readonly children?: ReactNode
26
+ readonly className?: string
27
+ readonly chromeAfterContentClassName?: string
28
+ readonly chromeBeforeContentClassName?: string
29
+ readonly chromeFooterClassName?: string
30
+ readonly chromeHeaderClassName?: string
31
+ readonly collapsible?: boolean
32
+ readonly collapsed?: boolean
33
+ readonly collapseLabel?: string
34
+ readonly componentSystem: PresentationComponentSystem
35
+ readonly contentClassName?: string
36
+ readonly contentTopInset?: PresentationViewSurfaceContentTopInset | null
37
+ readonly defaultCollapsed?: boolean
38
+ readonly description?: string
39
+ readonly eyebrow?: string
40
+ readonly onCollapsedChange?: (collapsed: boolean) => void
41
+ readonly renderChromeSlot?: ProjectedViewSurfaceChromeSlotRenderer
42
+ readonly title?: string
43
+ readonly verticalResize?: boolean | PresentationViewSurfaceVerticalResizeOptions | null
44
+ readonly view?: ViewDefinition | null
45
+ readonly viewId?: string
46
+ }
47
+
48
+ export type ProjectedViewSurfaceChromeSlotRenderer = (
49
+ slot: ViewChromeSlotDefinition,
50
+ view: ViewDefinition | null,
51
+ ) => ReactNode
52
+
53
+ export type ProjectedViewSurfaceContentTopInset =
54
+ PresentationViewSurfaceContentTopInset
55
+
56
+ export type ProjectedViewSurfaceVerticalResizeOptions =
57
+ PresentationViewSurfaceVerticalResizeOptions
58
+
59
+ export function ProjectedViewSurface({
60
+ action,
61
+ children,
62
+ chromeAfterContentClassName,
63
+ chromeBeforeContentClassName,
64
+ chromeFooterClassName,
65
+ chromeHeaderClassName,
66
+ collapsible,
67
+ componentSystem,
68
+ renderChromeSlot,
69
+ title,
70
+ view: viewOverride,
71
+ viewId,
72
+ ...props
73
+ }: ProjectedViewSurfaceProps) {
74
+ const module = usePresentationModule()
75
+ const view = viewOverride ?? (viewId
76
+ ? findPresentationView<ViewDefinition>(module, viewId)
77
+ : null)
78
+ const renderChromePlacement = (
79
+ placement: ViewChromeSlotPlacement,
80
+ className?: string,
81
+ ) => renderChromeSlot && resolveViewChromeSlots(view, placement).length > 0 ? (
82
+ <ProjectedViewChrome
83
+ componentSystem={componentSystem}
84
+ placement={placement}
85
+ renderContainer={(children) =>
86
+ componentSystem.surfaces.ViewSurfaceChromePlacement({
87
+ children,
88
+ className,
89
+ placement,
90
+ viewId: view?.Id ?? null,
91
+ })}
92
+ renderSlot={(slot) => renderChromeSlot(slot, view)}
93
+ view={view}
94
+ />
95
+ ) : null
96
+ const headerChrome = renderChromePlacement(
97
+ viewChromeSlotPlacements.header,
98
+ chromeHeaderClassName,
99
+ )
100
+ const beforeContentChrome = renderChromePlacement(
101
+ viewChromeSlotPlacements.beforeContent,
102
+ chromeBeforeContentClassName,
103
+ )
104
+ const afterContentChrome = renderChromePlacement(
105
+ viewChromeSlotPlacements.afterContent,
106
+ chromeAfterContentClassName,
107
+ )
108
+ const footerChrome = renderChromePlacement(
109
+ viewChromeSlotPlacements.footer,
110
+ chromeFooterClassName,
111
+ )
112
+
113
+ return componentSystem.surfaces.ViewSurface({
114
+ ...props,
115
+ action: componentSystem.surfaces.ViewSurfaceHeaderActions({
116
+ action,
117
+ chrome: headerChrome,
118
+ viewId: view?.Id ?? null,
119
+ }),
120
+ children: componentSystem.surfaces.ViewSurfaceContent({
121
+ afterContentChrome,
122
+ beforeContentChrome,
123
+ children,
124
+ footerChrome,
125
+ viewId: view?.Id ?? null,
126
+ }),
127
+ collapsible: collapsible ?? view?.Chrome?.Collapsible,
128
+ title: title ?? view?.Name,
129
+ viewId: view?.Id ?? null,
130
+ })
131
+ }