@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,475 @@
1
+ import { useMemo, type ReactNode } from 'react'
2
+
3
+ import type {
4
+ PresentationShadcnComponentSystem as PresentationComponentSystem,
5
+ } from './presentation-shadcn-component-system'
6
+ import {
7
+ type PresentationActionButtonSize,
8
+ type PresentationActionButtonVariant,
9
+ type PresentationDesignSystem,
10
+ } from '@cohesivesystems/presentation-tailwind'
11
+ import {
12
+ defaultPresentationComponentSet,
13
+ findPresentationAction,
14
+ getPresentationViewProjectedActionPlacements,
15
+ isPresentationViewFetching,
16
+ presentationActionIconIds,
17
+ projectPresentationActionRuntimeBindingDiagnostics,
18
+ createPresentationTestAttributes,
19
+ resolvePresentationViewDataSourceIds,
20
+ type ActionDefinition,
21
+ type ActionPlacementDefinition,
22
+ type PresentationDataSourceResolver,
23
+ type PresentationModuleDefinition,
24
+ type PresentationActionRuntimeRegistry,
25
+ type ViewDefinition,
26
+ } from '@cohesivesystems/presentation-core'
27
+ import {
28
+ renderPresentationIcon,
29
+ standardLucidePresentationIconRegistry,
30
+ type PresentationIconRegistry,
31
+ } from './presentation-icon-registry'
32
+ import {
33
+ projectPresentationActionIconDiagnostics,
34
+ projectPresentationIconDiagnostics,
35
+ } from './presentation-icon-diagnostics'
36
+ import {
37
+ useRegisterPresentationProjectionDiagnostics,
38
+ } from '@cohesivesystems/presentation-react'
39
+
40
+ export interface PresentationActionGroupOptions<TContext> {
41
+ /** Default button size used when no per-action resolver or design rule applies. */
42
+ readonly buttonSize?: PresentationActionButtonSize
43
+
44
+ /** Default button variant used when no per-action resolver or design rule applies. */
45
+ readonly buttonVariant?: PresentationActionButtonVariant
46
+
47
+ /** Host predicate that decides whether a projected action can execute. */
48
+ readonly canExecuteAction?: (context: PresentationActionRenderContext<TContext>) => boolean
49
+
50
+ /** CSS class applied to the action group container. */
51
+ readonly className?: string
52
+
53
+ /** Component set identifier used for icon and diagnostic projection. */
54
+ readonly componentSet?: string
55
+
56
+ /** Concrete component system that supplies the action button implementation. */
57
+ readonly componentSystem?: PresentationComponentSystem
58
+
59
+ /** Design system used to resolve default action button presentation. */
60
+ readonly designSystem?: PresentationDesignSystem
61
+
62
+ /** Host executor invoked when an enabled action button is clicked. */
63
+ readonly executeAction?: (context: PresentationActionRenderContext<TContext>) => Promise<void> | void
64
+
65
+ /** Icon registry used to render action and pending-state icons. */
66
+ readonly iconRegistry?: PresentationIconRegistry<ActionPlacementDefinition>
67
+
68
+ /** Host predicate that reports whether an action has an executable runtime binding. */
69
+ readonly isActionRuntimeBound?: (context: PresentationActionRenderContext<TContext>) => boolean
70
+
71
+ /** Optional override for rendering the icon portion of each action button. */
72
+ readonly renderActionIcon?: (context: PresentationActionRenderContext<TContext>) => ReactNode
73
+
74
+ /** Optional override for rendering the label portion of each action button. */
75
+ readonly renderActionLabel?: (context: PresentationActionRenderContext<TContext>) => ReactNode
76
+
77
+ /** Optional content rendered before the projected action buttons. */
78
+ readonly renderLeading?: (context: PresentationActionGroupRenderContext<TContext>) => ReactNode
79
+
80
+ /** Resolves transient render state such as hidden, disabled, pending, or label override. */
81
+ readonly resolveActionState?: (
82
+ context: PresentationActionRenderContext<TContext>,
83
+ ) => PresentationActionRenderState | null | undefined
84
+
85
+ /** Resolves a per-action button size override. */
86
+ readonly resolveButtonSize?: (
87
+ context: PresentationActionRenderContext<TContext>,
88
+ ) => PresentationActionButtonSize | undefined
89
+
90
+ /** Resolves a per-action button variant override. */
91
+ readonly resolveButtonVariant?: (
92
+ context: PresentationActionRenderContext<TContext>,
93
+ ) => PresentationActionButtonVariant | undefined
94
+
95
+ /** Resolves data sources that block this action while they are busy. */
96
+ readonly resolveInvalidatedDataSourceIds?: (context: PresentationActionRenderContext<TContext>) => readonly string[]
97
+
98
+ /** Runtime registry used for projection diagnostics and host execution binding. */
99
+ readonly runtimes?: PresentationActionRuntimeRegistry<
100
+ PresentationActionRenderContext<TContext>,
101
+ ReactNode
102
+ >
103
+ }
104
+
105
+ /**
106
+ * Host-resolved render state for a projected action button.
107
+ */
108
+ export interface PresentationActionRenderState {
109
+ /** Tooltip text or richer node explaining why the action is disabled. */
110
+ readonly disabledReason?: ReactNode
111
+
112
+ /** Whether the action should render disabled even if runtime checks pass. */
113
+ readonly isDisabled?: boolean
114
+
115
+ /** Whether the action should be omitted from the group. */
116
+ readonly isHidden?: boolean
117
+
118
+ /** Whether the action is currently pending host work. */
119
+ readonly isPending?: boolean
120
+
121
+ /** Label override for the action button. */
122
+ readonly label?: ReactNode
123
+ }
124
+
125
+ /**
126
+ * Shared render context for the whole action group.
127
+ */
128
+ export interface PresentationActionGroupRenderContext<TContext> {
129
+ /** Host-specific context threaded through action rendering and execution. */
130
+ readonly context: TContext
131
+
132
+ /** Resolver used to inspect data-source fetch and block state. */
133
+ readonly dataSourceResolver: PresentationDataSourceResolver
134
+
135
+ /** Module fragment used to resolve actions and targets. */
136
+ readonly module: Pick<PresentationModuleDefinition, 'Actions' | 'Targets'>
137
+
138
+ /** View whose projected actions are rendered by the group. */
139
+ readonly view: ViewDefinition
140
+ }
141
+
142
+ /**
143
+ * Render and execution context for a single projected action placement.
144
+ */
145
+ export interface PresentationActionRenderContext<TContext>
146
+ extends PresentationActionGroupRenderContext<TContext> {
147
+ /** Resolved module action for the placement, or null when the module is incomplete. */
148
+ readonly action: ActionDefinition | null
149
+
150
+ /** Data sources invalidated by the action or implied by the view. */
151
+ readonly invalidatedDataSourceIds: readonly string[]
152
+
153
+ /** Whether the action is blocked by active view/data-source fetch state. */
154
+ readonly isFetching: boolean
155
+
156
+ /** View-local placement that introduced this action button. */
157
+ readonly placement: ActionPlacementDefinition
158
+ }
159
+
160
+ /**
161
+ * Props for rendering a group of actions projected by a presentation view.
162
+ */
163
+ export interface PresentationActionGroupProps<TContext>
164
+ extends PresentationActionGroupRenderContext<TContext> {
165
+ /** Optional host integration and rendering overrides. */
166
+ readonly options?: PresentationActionGroupOptions<TContext>
167
+ }
168
+
169
+ /**
170
+ * Renders the view-scoped action placements declared by Cohesive presentation
171
+ * IR. The component keeps module action semantics, host runtime binding, design
172
+ * defaults, diagnostics, and data-source blocking in a single reusable action
173
+ * surface.
174
+ */
175
+ export function PresentationActionGroup<TContext>({
176
+ context,
177
+ dataSourceResolver,
178
+ module,
179
+ options,
180
+ view,
181
+ }: PresentationActionGroupProps<TContext>) {
182
+ const ActionButton = options?.componentSystem?.actions.ActionButton
183
+ const designSystem = options?.designSystem
184
+ const actionPlacements = useMemo(
185
+ () => getPresentationViewProjectedActionPlacements(view),
186
+ [view],
187
+ )
188
+ const iconRegistry =
189
+ options?.iconRegistry ??
190
+ (standardLucidePresentationIconRegistry as PresentationIconRegistry<ActionPlacementDefinition>)
191
+ const componentSet = options?.componentSet ?? defaultPresentationComponentSet
192
+ const actionIconDiagnostics = useMemo(
193
+ () => [
194
+ ...projectPresentationActionIconDiagnostics({
195
+ actionPlacements,
196
+ module,
197
+ componentSet,
198
+ registry: iconRegistry,
199
+ source: `presentation-action-group-icons:${view.Id}`,
200
+ surfaceId: view.Id,
201
+ surfaceName: view.Name,
202
+ }),
203
+ ...projectPresentationIconDiagnostics({
204
+ icons: actionPlacements.length === 0
205
+ ? []
206
+ : [{
207
+ icon: presentationActionIconIds.pending,
208
+ id: `${view.Id}:pending`,
209
+ kind: 'action-state-icon',
210
+ label: 'Pending action',
211
+ }],
212
+ module,
213
+ componentSet,
214
+ source: `presentation-action-group-icons:${view.Id}`,
215
+ surfaceId: view.Id,
216
+ surfaceName: view.Name,
217
+ }),
218
+ ],
219
+ [
220
+ actionPlacements,
221
+ componentSet,
222
+ iconRegistry,
223
+ module,
224
+ view.Id,
225
+ view.Name,
226
+ ],
227
+ )
228
+ useRegisterPresentationProjectionDiagnostics(
229
+ `presentation-action-group-icons:${view.Id}`,
230
+ actionIconDiagnostics,
231
+ )
232
+
233
+ const actionRuntimeDiagnostics = useMemo(
234
+ () => {
235
+ if (!options?.isActionRuntimeBound) {
236
+ return []
237
+ }
238
+
239
+ const runtimes: Record<
240
+ string,
241
+ NonNullable<PresentationActionRuntimeRegistry<
242
+ PresentationActionRenderContext<TContext>,
243
+ ReactNode
244
+ >[string]>
245
+ > = {}
246
+ for (const [actionId, runtime] of Object.entries(options.runtimes ?? {})) {
247
+ if (runtime) {
248
+ runtimes[actionId] = runtime
249
+ }
250
+ }
251
+ for (const placement of actionPlacements) {
252
+ const actionContext = createActionRenderContext({
253
+ context,
254
+ dataSourceResolver,
255
+ module,
256
+ placement,
257
+ view,
258
+ })
259
+ if (options.isActionRuntimeBound(actionContext)) {
260
+ runtimes[placement.ActionId] ??= { execute: () => undefined }
261
+ }
262
+ }
263
+
264
+ return projectPresentationActionRuntimeBindingDiagnostics({
265
+ actionPlacements,
266
+ module,
267
+ runtimes,
268
+ source: `presentation-action-group-runtime:${view.Id}`,
269
+ })
270
+ },
271
+ [
272
+ actionPlacements,
273
+ context,
274
+ dataSourceResolver,
275
+ module,
276
+ options,
277
+ view,
278
+ ],
279
+ )
280
+ useRegisterPresentationProjectionDiagnostics(
281
+ `presentation-action-group-runtime:${view.Id}`,
282
+ actionRuntimeDiagnostics,
283
+ )
284
+
285
+ if (!ActionButton || !designSystem) {
286
+ return null
287
+ }
288
+ if (actionPlacements.length === 0) {
289
+ return null
290
+ }
291
+
292
+ const groupContext = {
293
+ context,
294
+ dataSourceResolver,
295
+ module,
296
+ view,
297
+ } satisfies PresentationActionGroupRenderContext<TContext>
298
+ const leading = options?.renderLeading?.(groupContext)
299
+
300
+ return (
301
+ <div className={options?.className ?? 'flex flex-wrap items-center gap-2'}>
302
+ {leading}
303
+ {actionPlacements.map((placement) => {
304
+ const actionContext = createActionRenderContext({
305
+ context,
306
+ dataSourceResolver,
307
+ groupContext,
308
+ module,
309
+ placement,
310
+ view,
311
+ })
312
+ const { action, invalidatedDataSourceIds, isFetching } = actionContext
313
+ const resolvedInvalidatedDataSourceIds =
314
+ options?.resolveInvalidatedDataSourceIds?.(actionContext) ??
315
+ invalidatedDataSourceIds
316
+ const isBlocked = resolvedInvalidatedDataSourceIds.some(
317
+ (dataSourceId) => dataSourceResolver.resolve(dataSourceId)?.isBlocked,
318
+ )
319
+ const resolvedActionContext = {
320
+ ...actionContext,
321
+ invalidatedDataSourceIds: resolvedInvalidatedDataSourceIds,
322
+ } satisfies PresentationActionRenderContext<TContext>
323
+ const isRuntimeBound =
324
+ options?.isActionRuntimeBound?.(resolvedActionContext) ??
325
+ Boolean(options?.executeAction)
326
+ const canExecute = options?.canExecuteAction?.(resolvedActionContext) ?? true
327
+ const actionState = options?.resolveActionState?.(resolvedActionContext) ?? {}
328
+ if (actionState.isHidden) {
329
+ return null
330
+ }
331
+ const isPending = Boolean(actionState.isPending)
332
+ const disabledTitle = typeof actionState.disabledReason === 'string'
333
+ ? actionState.disabledReason
334
+ : undefined
335
+ const renderedLabel =
336
+ actionState.label ??
337
+ options?.renderActionLabel?.(resolvedActionContext) ??
338
+ placement.Label ??
339
+ action?.Name ??
340
+ placement.ActionId
341
+
342
+ return (
343
+ <ActionButton
344
+ aria-label={action?.Accessibility?.Label ?? String(renderedLabel)}
345
+ {...createPresentationTestAttributes({
346
+ actionId: placement.ActionId,
347
+ viewId: view.Id,
348
+ })}
349
+ disabled={
350
+ !options?.executeAction ||
351
+ !isRuntimeBound ||
352
+ !canExecute ||
353
+ Boolean(actionState.isDisabled) ||
354
+ isBlocked ||
355
+ isFetching ||
356
+ isPending
357
+ }
358
+ key={`${placement.Region}:${placement.ActionId}`}
359
+ onClick={() => {
360
+ void options?.executeAction?.(resolvedActionContext)
361
+ }}
362
+ size={
363
+ options?.resolveButtonSize?.(resolvedActionContext) ??
364
+ options?.buttonSize ??
365
+ designSystem.components.actionButton.size({
366
+ action,
367
+ placement,
368
+ })
369
+ }
370
+ title={disabledTitle}
371
+ type="button"
372
+ variant={
373
+ options?.resolveButtonVariant?.(resolvedActionContext) ??
374
+ options?.buttonVariant ??
375
+ designSystem.components.actionButton.variant({
376
+ action,
377
+ placement,
378
+ })
379
+ }
380
+ >
381
+ {options?.renderActionIcon?.(resolvedActionContext) ??
382
+ renderDefaultActionIcon({
383
+ iconRegistry: options?.iconRegistry,
384
+ isPending: isFetching || isPending,
385
+ componentSet,
386
+ module,
387
+ placement,
388
+ })}
389
+ {renderedLabel}
390
+ </ActionButton>
391
+ )
392
+ })}
393
+ </div>
394
+ )
395
+ }
396
+
397
+ function createActionRenderContext<TContext>({
398
+ context,
399
+ dataSourceResolver,
400
+ groupContext,
401
+ module,
402
+ placement,
403
+ view,
404
+ }: {
405
+ readonly context: TContext
406
+ readonly dataSourceResolver: PresentationDataSourceResolver
407
+ readonly groupContext?: PresentationActionGroupRenderContext<TContext>
408
+ readonly module: Pick<PresentationModuleDefinition, 'Actions' | 'Targets'>
409
+ readonly placement: ActionPlacementDefinition
410
+ readonly view: ViewDefinition
411
+ }): PresentationActionRenderContext<TContext> {
412
+ const action = findPresentationAction<ActionDefinition>(
413
+ module,
414
+ placement.ActionId,
415
+ )
416
+ const invalidatedDataSourceIds =
417
+ action?.Result?.InvalidateDataSourceIds ??
418
+ resolvePresentationViewDataSourceIds(view)
419
+ const isFetching = isPresentationViewFetching(
420
+ {
421
+ ...view,
422
+ DataSourceIds: invalidatedDataSourceIds,
423
+ },
424
+ dataSourceResolver,
425
+ )
426
+
427
+ return {
428
+ ...(groupContext ?? {
429
+ context,
430
+ dataSourceResolver,
431
+ module,
432
+ view,
433
+ }),
434
+ action,
435
+ invalidatedDataSourceIds,
436
+ isFetching,
437
+ placement,
438
+ } satisfies PresentationActionRenderContext<TContext>
439
+ }
440
+
441
+ function renderDefaultActionIcon({
442
+ componentSet = defaultPresentationComponentSet,
443
+ iconRegistry = standardLucidePresentationIconRegistry as PresentationIconRegistry<ActionPlacementDefinition>,
444
+ isPending,
445
+ module,
446
+ placement,
447
+ }: {
448
+ readonly componentSet?: string
449
+ readonly iconRegistry?: PresentationIconRegistry<ActionPlacementDefinition>
450
+ readonly isPending: boolean
451
+ readonly module: Pick<PresentationModuleDefinition, 'Targets'>
452
+ readonly placement: ActionPlacementDefinition
453
+ }) {
454
+ return isPending ? (
455
+ renderPresentationIcon({
456
+ className: 'size-3.5 animate-spin',
457
+ componentSet,
458
+ icon: presentationActionIconIds.pending,
459
+ module,
460
+ registry: iconRegistry,
461
+ subject: placement,
462
+ }) ?? renderPresentationIcon({
463
+ className: 'size-3.5 animate-spin',
464
+ icon: 'loader-circle',
465
+ registry: iconRegistry,
466
+ subject: placement,
467
+ })
468
+ ) : renderPresentationIcon({
469
+ componentSet,
470
+ icon: placement.Icon,
471
+ module,
472
+ registry: iconRegistry,
473
+ subject: placement,
474
+ })
475
+ }
@@ -0,0 +1,54 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import type {
4
+ PresentationBadgeDefinition,
5
+ PresentationModuleDefinition,
6
+ ProjectedDocumentFieldDefinitionLike,
7
+ ResolvedPresentationBadge,
8
+ } from '@cohesivesystems/presentation-core'
9
+ import type {
10
+ PresentationShadcnComponentSystem as PresentationComponentSystem,
11
+ } from './presentation-shadcn-component-system'
12
+ import type {
13
+ PresentationDesignSystem,
14
+ } from '@cohesivesystems/presentation-tailwind'
15
+
16
+ export interface PresentationBadgeTargetInterpretationContext {
17
+ readonly badge: PresentationBadgeDefinition
18
+ readonly componentSystem: PresentationComponentSystem
19
+ readonly designSystem: PresentationDesignSystem
20
+ readonly field: ProjectedDocumentFieldDefinitionLike | null
21
+ readonly fieldId: string
22
+ readonly module: PresentationModuleDefinition | null
23
+ readonly resolvedBadge: ResolvedPresentationBadge | null
24
+ readonly resource: unknown
25
+ readonly value: unknown
26
+ }
27
+
28
+ export type PresentationBadgeTargetInterpreter = (
29
+ context: PresentationBadgeTargetInterpretationContext,
30
+ ) => ReactNode | undefined
31
+
32
+ export type PresentationBadgeTargetInterpreterRegistry =
33
+ readonly PresentationBadgeTargetInterpreter[]
34
+
35
+ /**
36
+ * Applies target-specific badge interpretations in registry order.
37
+ *
38
+ * Returning `undefined` means an interpreter does not handle the badge.
39
+ * Returning `null` means the interpreter handled the badge and intentionally
40
+ * suppresses rendering.
41
+ */
42
+ export function interpretPresentationBadgeTarget(
43
+ registry: PresentationBadgeTargetInterpreterRegistry | null | undefined,
44
+ context: PresentationBadgeTargetInterpretationContext,
45
+ ) {
46
+ for (const interpreter of registry ?? []) {
47
+ const rendered = interpreter(context)
48
+ if (rendered !== undefined) {
49
+ return rendered
50
+ }
51
+ }
52
+
53
+ return undefined
54
+ }