@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,704 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import type {
4
+ FieldPresentationDefinition,
5
+ NavigationRouteParameters,
6
+ PresentationModuleDefinition,
7
+ } from '@cohesivesystems/presentation-core'
8
+ import {
9
+ ResourceLinkBadge,
10
+ TextBadge,
11
+ } from './projected-document-badges'
12
+ import type {
13
+ PresentationTextBadgeTone,
14
+ } from '@cohesivesystems/presentation-tailwind'
15
+ import {
16
+ resolvePresentationFieldValueLabel,
17
+ resolvePresentationFieldValueIcon,
18
+ resolvePresentationFieldValueTone,
19
+ } from '@cohesivesystems/presentation-core'
20
+ import {
21
+ renderPresentationIcon,
22
+ } from './presentation-icon-registry'
23
+ import type {
24
+ PresentationShadcnComponentSystem as PresentationComponentSystem,
25
+ } from './presentation-shadcn-component-system'
26
+ import { formatBadgeValue } from '@cohesivesystems/presentation-core'
27
+ import {
28
+ projectDocumentFieldNavigationBinding,
29
+ } from '@cohesivesystems/presentation-core'
30
+ import {
31
+ readObjectPath,
32
+ } from '@cohesivesystems/presentation-core'
33
+ import {
34
+ isProjectedDocumentBadgeField,
35
+ isProjectedDocumentEntityReferenceField,
36
+ matchesFieldDisplayKind,
37
+ } from '@cohesivesystems/presentation-core'
38
+ import {
39
+ fieldEntityReferenceFallbackKinds,
40
+ fieldDisplayKinds,
41
+ fieldJsonDisplayModes,
42
+ formatKinds,
43
+ } from '@cohesivesystems/presentation-contracts'
44
+
45
+ /**
46
+ * Runtime inputs required to render a projected field value through the
47
+ * shadcn-oriented presentation component system.
48
+ */
49
+ export interface ProjectedFieldValueRenderContext {
50
+ /** Component-system implementation used for scalar, code, JSON, and composite field value chrome. */
51
+ readonly componentSystem: PresentationComponentSystem
52
+ /** Optional route-to-href adapter used when entity-reference fields declare navigation bindings. */
53
+ readonly createHref?:
54
+ | ((routeId: string, parameters?: NavigationRouteParameters) => string | null)
55
+ | undefined
56
+ /** Fallback node used when the value is absent and the field has no modeled empty-value label. */
57
+ readonly emptyValueFallback: ReactNode
58
+ /** Presentation field definition that declares display kind, formatting, fallbacks, badges, and navigation. */
59
+ readonly field: FieldPresentationDefinition
60
+ /** Optional caller formatter for values that need host-specific text or node rendering. */
61
+ readonly formatValue?: (context: ProjectedFieldValueFormatContext) => ReactNode
62
+ /** Presentation module fragment used to resolve target bindings and icon definitions. */
63
+ readonly module: Pick<PresentationModuleDefinition, 'Targets'> | null
64
+ /** Optional navigation callback invoked for resolved entity-reference links. */
65
+ readonly navigateHref?: (href: string) => void
66
+ /** Current resource object that field paths, supporting values, and navigation bindings read from. */
67
+ readonly resource: unknown
68
+ /** Default rendering style for entity references that do not resolve to a navigation binding. */
69
+ readonly unboundEntityReferenceStyle?: 'badge' | 'code'
70
+ /** Raw field value supplied by the caller before display fallbacks are applied. */
71
+ readonly value: unknown
72
+ }
73
+
74
+ /**
75
+ * Value formatting context passed to host-provided field value formatters.
76
+ */
77
+ export interface ProjectedFieldValueFormatContext {
78
+ /** Presentation field definition associated with the value being formatted. */
79
+ readonly field: FieldPresentationDefinition
80
+ /** Resource object that owns the rendered field. */
81
+ readonly resource: unknown
82
+ /** Field value after display fallback resolution. */
83
+ readonly value: unknown
84
+ }
85
+
86
+ /**
87
+ * Renders a projected presentation field value as React content.
88
+ *
89
+ * The renderer applies modeled display fallbacks, supporting values, inline
90
+ * badges, entity-reference navigation bindings, value labels, tones, icons,
91
+ * and scalar formatting before delegating chrome to the component system.
92
+ */
93
+ export function renderProjectedFieldValue({
94
+ componentSystem,
95
+ createHref,
96
+ emptyValueFallback,
97
+ field,
98
+ formatValue,
99
+ module,
100
+ navigateHref,
101
+ resource,
102
+ unboundEntityReferenceStyle = 'code',
103
+ value,
104
+ }: ProjectedFieldValueRenderContext) {
105
+ const displayValue = resolveProjectedDisplayValue(field, resource, value)
106
+ const supportingValues = readProjectedSupportingFieldValues(field, resource)
107
+ const inlineBadges = renderProjectedInlineBadges(componentSystem, field, resource)
108
+
109
+ if (isAbsentValue(displayValue)) {
110
+ if (supportingValues.length > 0 || inlineBadges.length > 0) {
111
+ return renderProjectedCompositeValue({
112
+ componentSystem,
113
+ inlineBadges,
114
+ primaryValue: null,
115
+ supportingValues,
116
+ })
117
+ }
118
+
119
+ return renderEmptyValueFallback(componentSystem, field, emptyValueFallback)
120
+ }
121
+
122
+ const primaryValue = renderProjectedFieldPrimaryValue({
123
+ componentSystem,
124
+ createHref,
125
+ field,
126
+ formatValue,
127
+ module,
128
+ navigateHref,
129
+ resource,
130
+ unboundEntityReferenceStyle,
131
+ value: displayValue,
132
+ })
133
+
134
+ return supportingValues.length > 0 || inlineBadges.length > 0
135
+ ? renderProjectedCompositeValue({
136
+ componentSystem,
137
+ inlineBadges,
138
+ primaryValue,
139
+ supportingValues,
140
+ })
141
+ : primaryValue
142
+ }
143
+
144
+ function renderProjectedFieldPrimaryValue({
145
+ componentSystem,
146
+ createHref,
147
+ field,
148
+ formatValue,
149
+ module,
150
+ navigateHref,
151
+ resource,
152
+ unboundEntityReferenceStyle = 'code',
153
+ value,
154
+ }: Omit<ProjectedFieldValueRenderContext, 'emptyValueFallback'>) {
155
+ if (isProjectedDocumentEntityReferenceField(field)) {
156
+ return renderProjectedEntityReferenceValue({
157
+ componentSystem,
158
+ createHref,
159
+ field,
160
+ module,
161
+ navigateHref,
162
+ resource,
163
+ unboundEntityReferenceStyle,
164
+ value,
165
+ })
166
+ }
167
+
168
+ if (matchesFieldDisplayKind(field, fieldDisplayKinds.code, 'Code')) {
169
+ return renderCodeValue(componentSystem, formatProjectedScalarValue(value, field, resource))
170
+ }
171
+
172
+ if (isProjectedDocumentBadgeField(field)) {
173
+ const text =
174
+ formatValue?.({ field, resource, value }) ??
175
+ formatProjectedScalarValue(value, field, resource)
176
+ const icon = renderProjectedFieldValueIcon({ field, module, value })
177
+
178
+ return (
179
+ <TextBadge
180
+ componentSystem={componentSystem}
181
+ tone={resolveProjectedFieldBadgeTone(field, resource, value)}
182
+ >
183
+ {icon ? (
184
+ <span className="inline-flex min-w-0 items-center gap-1">
185
+ {icon}
186
+ <span className="truncate">{text}</span>
187
+ </span>
188
+ ) : text}
189
+ </TextBadge>
190
+ )
191
+ }
192
+
193
+ if (matchesFieldDisplayKind(field, fieldDisplayKinds.date, 'Date') ||
194
+ matchesFieldDisplayKind(field, fieldDisplayKinds.dateTime, 'DateTime')) {
195
+ return formatProjectedDateTime(componentSystem, String(value), field)
196
+ }
197
+
198
+ if (matchesFieldDisplayKind(field, fieldDisplayKinds.boolean, 'Boolean')) {
199
+ return renderScalarValue(componentSystem, value ? 'Yes' : 'No', {
200
+ tone: resolveProjectedFieldBadgeTone(field, resource),
201
+ })
202
+ }
203
+
204
+ if (matchesFieldDisplayKind(field, fieldDisplayKinds.json, 'Json')) {
205
+ return renderJsonValue(componentSystem, value, field, resource)
206
+ }
207
+
208
+ return renderScalarValue(componentSystem, formatProjectedScalarValue(value, field, resource), {
209
+ tone: resolveProjectedFieldBadgeTone(field, resource),
210
+ })
211
+ }
212
+
213
+ /**
214
+ * Returns whether the standard shadcn field-value renderer understands the
215
+ * supplied field's modeled display semantics.
216
+ */
217
+ export function canRenderProjectedFieldValue(field: FieldPresentationDefinition) {
218
+ return isProjectedDocumentEntityReferenceField(field) ||
219
+ isProjectedDocumentBadgeField(field) ||
220
+ matchesFieldDisplayKind(field, fieldDisplayKinds.code, 'Code') ||
221
+ matchesFieldDisplayKind(field, fieldDisplayKinds.date, 'Date') ||
222
+ matchesFieldDisplayKind(field, fieldDisplayKinds.dateTime, 'DateTime') ||
223
+ matchesFieldDisplayKind(field, fieldDisplayKinds.boolean, 'Boolean') ||
224
+ matchesFieldDisplayKind(field, fieldDisplayKinds.json, 'Json') ||
225
+ matchesFieldDisplayKind(field, fieldDisplayKinds.text, 'Text') ||
226
+ matchesFieldDisplayKind(field, fieldDisplayKinds.number, 'Number')
227
+ }
228
+
229
+ /**
230
+ * Runtime inputs for rendering a projected entity-reference field value.
231
+ */
232
+ export interface ProjectedEntityReferenceValueRenderContext {
233
+ /** Component-system implementation used for fallback code, text, and badge rendering. */
234
+ readonly componentSystem: PresentationComponentSystem
235
+ /** Optional route-to-href adapter used to resolve modeled navigation bindings. */
236
+ readonly createHref?:
237
+ | ((routeId: string, parameters?: NavigationRouteParameters) => string | null)
238
+ | undefined
239
+ /** Entity-reference field definition. */
240
+ readonly field: FieldPresentationDefinition
241
+ /** Presentation module fragment containing target bindings used by navigation projection. */
242
+ readonly module: Pick<PresentationModuleDefinition, 'Targets'> | null
243
+ /** Optional callback invoked when a resolved entity-reference link is activated. */
244
+ readonly navigateHref?: (href: string) => void
245
+ /** Resource object that owns the entity-reference value. */
246
+ readonly resource: unknown
247
+ /** Caller fallback style used when the field does not declare its own fallback style. */
248
+ readonly unboundEntityReferenceStyle: 'badge' | 'code'
249
+ /** Entity-reference scalar value to render. */
250
+ readonly value: unknown
251
+ }
252
+
253
+ /**
254
+ * Renders an entity-reference field as a navigation badge when the modeled
255
+ * binding resolves, or as the field's configured fallback style otherwise.
256
+ */
257
+ export function renderProjectedEntityReferenceValue({
258
+ componentSystem,
259
+ createHref,
260
+ field,
261
+ module,
262
+ navigateHref,
263
+ resource,
264
+ unboundEntityReferenceStyle: fallbackStyle,
265
+ value,
266
+ }: ProjectedEntityReferenceValueRenderContext) {
267
+ const unboundEntityReferenceStyle =
268
+ resolveProjectedEntityReferenceFallbackStyle(field) ?? fallbackStyle
269
+ const binding = projectDocumentFieldNavigationBinding({
270
+ createHref,
271
+ field,
272
+ module,
273
+ resource,
274
+ value,
275
+ })
276
+ const label = binding?.label ?? formatProjectedScalarValue(value, field, resource)
277
+ if (!label) {
278
+ return null
279
+ }
280
+
281
+ if (binding && navigateHref) {
282
+ return (
283
+ <ResourceLinkBadge
284
+ componentSystem={componentSystem}
285
+ disabled={!binding.href}
286
+ label={binding.label}
287
+ onClick={() => {
288
+ if (binding.href) {
289
+ navigateHref(binding.href)
290
+ }
291
+ }}
292
+ prefix={binding.prefix}
293
+ />
294
+ )
295
+ }
296
+
297
+ if (unboundEntityReferenceStyle === 'badge') {
298
+ return <TextBadge componentSystem={componentSystem} tone="sky">{label}</TextBadge>
299
+ }
300
+
301
+ if (unboundEntityReferenceStyle === 'text') {
302
+ return <span className="wrap-break-word">{label}</span>
303
+ }
304
+
305
+ return renderCodeValue(componentSystem, label)
306
+ }
307
+
308
+ /**
309
+ * Resolves the visual tone for a projected field value badge or scalar chip.
310
+ *
311
+ * Value-specific tone rules take precedence over the field's general design
312
+ * tone, and the result is coerced into the shadcn badge tone set.
313
+ */
314
+ export function resolveProjectedFieldBadgeTone(
315
+ field: FieldPresentationDefinition,
316
+ resource?: unknown,
317
+ value?: unknown,
318
+ ): PresentationTextBadgeTone {
319
+ const fieldValue = value ?? resolveFieldValue(field, resource)
320
+
321
+ return coerceProjectedTextBadgeTone(
322
+ resolvePresentationFieldValueTone(field, fieldValue, resource) ??
323
+ field.Design?.Tone,
324
+ )
325
+ }
326
+
327
+ function renderProjectedFieldValueIcon({
328
+ field,
329
+ module,
330
+ value,
331
+ }: {
332
+ readonly field: FieldPresentationDefinition
333
+ readonly module: Pick<PresentationModuleDefinition, 'Targets'> | null
334
+ readonly value: unknown
335
+ }) {
336
+ const icon = resolvePresentationFieldValueIcon(field, value)
337
+ if (!icon) {
338
+ return null
339
+ }
340
+
341
+ return renderPresentationIcon({
342
+ className: 'size-3 shrink-0',
343
+ icon,
344
+ module,
345
+ })
346
+ }
347
+
348
+ function coerceProjectedTextBadgeTone(tone: string | null | undefined): PresentationTextBadgeTone {
349
+ switch (tone?.toLocaleLowerCase()) {
350
+ case 'accent':
351
+ case 'primary':
352
+ case 'violet':
353
+ return 'violet'
354
+ case 'amber':
355
+ case 'warning':
356
+ return 'amber'
357
+ case 'danger':
358
+ case 'error':
359
+ case 'red':
360
+ return 'red'
361
+ case 'info':
362
+ case 'sky':
363
+ return 'sky'
364
+ case 'success':
365
+ case 'teal':
366
+ return 'teal'
367
+ default:
368
+ return 'slate'
369
+ }
370
+ }
371
+
372
+ function renderEmptyValueFallback(
373
+ componentSystem: PresentationComponentSystem,
374
+ field: FieldPresentationDefinition,
375
+ fallback: ReactNode,
376
+ ) {
377
+ if (!field.Display?.EmptyValueLabel) {
378
+ return fallback
379
+ }
380
+
381
+ const FieldValueEmpty = componentSystem.fieldValues.FieldValueEmpty
382
+ return <FieldValueEmpty label={field.Display.EmptyValueLabel} />
383
+ }
384
+
385
+ /**
386
+ * Resolves the fallback rendering style declared for an entity-reference field.
387
+ *
388
+ * Returns `null` when the field leaves fallback style selection to the caller.
389
+ */
390
+ export function resolveProjectedEntityReferenceFallbackStyle(
391
+ field: FieldPresentationDefinition,
392
+ ): 'badge' | 'code' | 'text' | null {
393
+ const fallback = field.Display?.EntityReferenceFallback
394
+ if (fallback === null || fallback === undefined) {
395
+ return null
396
+ }
397
+
398
+ if (matchesGeneratedEnum(fallback, fieldEntityReferenceFallbackKinds.badge, 'Badge')) {
399
+ return 'badge'
400
+ }
401
+
402
+ if (matchesGeneratedEnum(fallback, fieldEntityReferenceFallbackKinds.text, 'Text')) {
403
+ return 'text'
404
+ }
405
+
406
+ return 'code'
407
+ }
408
+
409
+ function renderCodeValue(
410
+ componentSystem: PresentationComponentSystem,
411
+ value: string,
412
+ ) {
413
+ const FieldValueCode = componentSystem.fieldValues.FieldValueCode
414
+ return <FieldValueCode value={value} />
415
+ }
416
+
417
+ function resolveProjectedDisplayValue(
418
+ field: FieldPresentationDefinition,
419
+ resource: unknown,
420
+ value: unknown,
421
+ ) {
422
+ if (!isAbsentValue(value)) {
423
+ return value
424
+ }
425
+
426
+ for (const fallbackFieldPath of field.Display?.FallbackFieldPaths ?? []) {
427
+ const fallbackValue = readObjectPath(resource, fallbackFieldPath)
428
+ if (!isAbsentValue(fallbackValue)) {
429
+ return fallbackValue
430
+ }
431
+ }
432
+
433
+ return value
434
+ }
435
+
436
+ function readProjectedSupportingFieldValues(
437
+ field: FieldPresentationDefinition,
438
+ resource: unknown,
439
+ ) {
440
+ const pathValues = (field.Display?.SupportingFieldPaths ?? []).flatMap((fieldPath) => {
441
+ const value = readObjectPath(resource, fieldPath)
442
+ if (isAbsentValue(value)) {
443
+ return []
444
+ }
445
+
446
+ const label = formatProjectedSupportingValue(value)
447
+ return label ? [label] : []
448
+ })
449
+
450
+ const definedValues = (field.Display?.SupportingValues ?? []).flatMap((definition) => {
451
+ const value = readObjectPath(resource, definition.FieldPath)
452
+ if (isAbsentValue(value)) {
453
+ return []
454
+ }
455
+
456
+ const label = formatProjectedSupportingValue(value, definition.Separator ?? undefined)
457
+ return label
458
+ ? [`${definition.Prefix ?? ''}${label}${definition.Suffix ?? ''}`]
459
+ : []
460
+ })
461
+
462
+ return [...pathValues, ...definedValues]
463
+ }
464
+
465
+ function renderProjectedCompositeValue({
466
+ componentSystem,
467
+ inlineBadges,
468
+ primaryValue,
469
+ supportingValues,
470
+ }: {
471
+ readonly componentSystem: PresentationComponentSystem
472
+ readonly inlineBadges: readonly ReactNode[]
473
+ readonly primaryValue: ReactNode
474
+ readonly supportingValues: readonly string[]
475
+ }) {
476
+ const FieldValueComposite = componentSystem.fieldValues.FieldValueComposite
477
+ const FieldValueSupportingValue = componentSystem.fieldValues.FieldValueSupportingValue
478
+ return (
479
+ <FieldValueComposite
480
+ inlineBadges={inlineBadges}
481
+ primaryValue={primaryValue}
482
+ supportingValues={supportingValues.map((value, index) => (
483
+ <FieldValueSupportingValue key={`${value}:${index}`}>
484
+ {value}
485
+ </FieldValueSupportingValue>
486
+ ))}
487
+ />
488
+ )
489
+ }
490
+
491
+ function renderProjectedInlineBadges(
492
+ componentSystem: PresentationComponentSystem,
493
+ field: FieldPresentationDefinition,
494
+ resource: unknown,
495
+ ) {
496
+ return (field.Display?.InlineBadges ?? []).flatMap((badge) => {
497
+ const value = readObjectPath(resource, badge.FieldPath)
498
+ if (isAbsentValue(value)) {
499
+ return []
500
+ }
501
+
502
+ const label = formatProjectedSupportingValue(value)
503
+ if (!label) {
504
+ return []
505
+ }
506
+
507
+ const tone =
508
+ badge.ToneFieldPath
509
+ ? readOptionalString(readObjectPath(resource, badge.ToneFieldPath))
510
+ : null
511
+
512
+ return [
513
+ <TextBadge
514
+ componentSystem={componentSystem}
515
+ key={badge.FieldPath}
516
+ tone={coerceProjectedTextBadgeTone(tone ?? badge.Tone)}
517
+ >
518
+ {label}
519
+ </TextBadge>,
520
+ ]
521
+ })
522
+ }
523
+
524
+ function formatProjectedSupportingValue(
525
+ value: unknown,
526
+ separator = ', ',
527
+ ): string | null {
528
+ if (Array.isArray(value)) {
529
+ const items: string[] = value
530
+ .map((item) => formatProjectedSupportingValue(item, separator))
531
+ .filter((item): item is string => Boolean(item))
532
+
533
+ return items.length > 0 ? items.join(separator) : null
534
+ }
535
+
536
+ const scalarValue = formatBadgeValue(value)
537
+ if (scalarValue !== null) {
538
+ return scalarValue
539
+ }
540
+
541
+ try {
542
+ return JSON.stringify(value)
543
+ } catch {
544
+ return String(value)
545
+ }
546
+ }
547
+
548
+ function formatProjectedScalarValue(
549
+ value: unknown,
550
+ field: FieldPresentationDefinition,
551
+ resource?: unknown,
552
+ ) {
553
+ const label =
554
+ readFirstDisplayFieldPathValue(field.Display?.LabelFieldPaths, resource) ??
555
+ resolvePresentationFieldValueLabel(field, value) ??
556
+ formatBadgeValue(value)
557
+ if (label === null || label === undefined) {
558
+ return String(value)
559
+ }
560
+
561
+ return field.Display?.ValuePrefix
562
+ ? `${field.Display.ValuePrefix}${label}`
563
+ : label
564
+ }
565
+
566
+ function readFirstDisplayFieldPathValue(
567
+ fieldPaths: readonly string[] | null | undefined,
568
+ resource: unknown,
569
+ ) {
570
+ if (!resource) {
571
+ return null
572
+ }
573
+
574
+ for (const fieldPath of fieldPaths ?? []) {
575
+ const value = readObjectPath(resource, fieldPath)
576
+ if (!isAbsentValue(value)) {
577
+ return formatProjectedSupportingValue(value)
578
+ }
579
+ }
580
+
581
+ return null
582
+ }
583
+
584
+ function isAbsentValue(value: unknown) {
585
+ return value === null || value === undefined || value === ''
586
+ }
587
+
588
+ function resolveFieldValue(
589
+ field: FieldPresentationDefinition,
590
+ resource: unknown,
591
+ ) {
592
+ return readObjectPath(resource, field.Field)
593
+ }
594
+
595
+ function renderJsonValue(
596
+ componentSystem: PresentationComponentSystem,
597
+ value: unknown,
598
+ field: FieldPresentationDefinition,
599
+ resource: unknown,
600
+ ) {
601
+ const formattedValue = JSON.stringify(value) ?? String(value)
602
+ if (matchesGeneratedEnum(field.Display?.JsonMode, fieldJsonDisplayModes.inline, 'Inline')) {
603
+ return renderScalarValue(componentSystem, formattedValue, {
604
+ tone: resolveProjectedFieldBadgeTone(field, resource),
605
+ })
606
+ }
607
+
608
+ const tone = resolveProjectedFieldBadgeTone(field, resource)
609
+ const FieldValueJson = componentSystem.fieldValues.FieldValueJson
610
+
611
+ return (
612
+ <FieldValueJson
613
+ formattedValue={JSON.stringify(value, null, 2) ?? String(value)}
614
+ tone={tone}
615
+ />
616
+ )
617
+ }
618
+
619
+ function renderScalarValue(
620
+ componentSystem: PresentationComponentSystem,
621
+ value: ReactNode,
622
+ options: {
623
+ readonly title?: string
624
+ readonly tone?: ReturnType<typeof resolveProjectedFieldBadgeTone>
625
+ } = {},
626
+ ) {
627
+ const FieldValueScalar = componentSystem.fieldValues.FieldValueScalar
628
+ return (
629
+ <FieldValueScalar title={options.title} tone={options.tone}>
630
+ {value}
631
+ </FieldValueScalar>
632
+ )
633
+ }
634
+
635
+ function formatProjectedDateTime(
636
+ componentSystem: PresentationComponentSystem,
637
+ value: string,
638
+ field: FieldPresentationDefinition,
639
+ ) {
640
+ const parsed = new Date(value)
641
+ if (Number.isNaN(parsed.getTime())) {
642
+ return renderScalarValue(componentSystem, value)
643
+ }
644
+
645
+ const text = formatDateTimeValue(parsed, field)
646
+
647
+ return renderScalarValue(componentSystem, text, { title: value })
648
+ }
649
+
650
+ function formatDateTimeValue(
651
+ value: Date,
652
+ field: FieldPresentationDefinition,
653
+ ) {
654
+ if (field.Format?.Pattern === 'iso') {
655
+ return value.toISOString()
656
+ }
657
+
658
+ const options: Intl.DateTimeFormatOptions = {
659
+ dateStyle: 'medium',
660
+ }
661
+ const formatKind = field.Format?.Kind
662
+ const dateOnly =
663
+ matchesGeneratedEnum(formatKind, formatKinds.date, 'Date') ||
664
+ matchesFieldDisplayKind(field, fieldDisplayKinds.date, 'Date')
665
+ const timeZone = normalizeTimeZone(field.Format?.TimeZone)
666
+
667
+ if (!dateOnly) {
668
+ options.timeStyle = 'short'
669
+ }
670
+
671
+ if (timeZone) {
672
+ options.timeZone = timeZone
673
+ }
674
+
675
+ try {
676
+ return new Intl.DateTimeFormat(undefined, options).format(value)
677
+ } catch {
678
+ return dateOnly
679
+ ? value.toLocaleDateString()
680
+ : value.toLocaleString()
681
+ }
682
+ }
683
+
684
+ function normalizeTimeZone(value: string | null | undefined) {
685
+ if (!value) {
686
+ return undefined
687
+ }
688
+
689
+ return value.toLocaleLowerCase() === 'utc' ? 'UTC' : value
690
+ }
691
+
692
+ function matchesGeneratedEnum(
693
+ value: number | string | null | undefined,
694
+ numericValue: number | string,
695
+ label: string,
696
+ ) {
697
+ return value === numericValue ||
698
+ String(value) === String(numericValue) ||
699
+ value?.toString().toLocaleLowerCase() === label.toLocaleLowerCase()
700
+ }
701
+
702
+ function readOptionalString(value: unknown) {
703
+ return typeof value === 'string' && value.length > 0 ? value : null
704
+ }