@conduction/nextcloud-vue 2.56.0-beta.36 → 2.56.0-beta.38

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 (382) hide show
  1. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
  2. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +131 -3
  3. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  4. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +54 -18
  5. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  6. package/dist/esm/components/CnAdvancedFormDialog/CnMetadataTab.vue.js +1 -1
  7. package/dist/esm/components/CnAdvancedFormDialog/CnMetadataTab.vue2.js +3 -2
  8. package/dist/esm/components/CnAdvancedFormDialog/CnMetadataTab.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAdvancedFormDialog/CnMetadataTab.vue3.js.map +1 -1
  10. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue.js +1 -1
  11. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue2.js +5 -4
  12. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue2.js.map +1 -1
  13. package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue3.js.map +1 -1
  14. package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue.js +1 -1
  15. package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue2.js +2 -1
  16. package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue2.js.map +1 -1
  17. package/dist/esm/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue3.js.map +1 -1
  18. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue.js +1 -1
  19. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +2 -1
  20. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
  21. package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
  22. package/dist/esm/components/CnBoardView/CnBoardView.vue.js +1 -1
  23. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +376 -6
  24. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  25. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +146 -71
  26. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  27. package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue.js +1 -1
  28. package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue2.js +2 -1
  29. package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue2.js.map +1 -1
  30. package/dist/esm/components/CnCalendarEventPicker/CnCalendarEventPicker.vue3.js.map +1 -1
  31. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue.js +1 -1
  32. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js +2 -1
  33. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js.map +1 -1
  34. package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue3.js.map +1 -1
  35. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  36. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +6 -5
  37. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  38. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  39. package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue.js +1 -1
  40. package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue2.js +2 -1
  41. package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue2.js.map +1 -1
  42. package/dist/esm/components/CnCollectivePagePicker/CnCollectivePagePicker.vue3.js.map +1 -1
  43. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +1 -1
  44. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js +2 -1
  45. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -1
  46. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -1
  47. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue.js +1 -1
  48. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js +2 -1
  49. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue2.js.map +1 -1
  50. package/dist/esm/components/CnCountdownWidget/CnCountdownWidget.vue3.js.map +1 -1
  51. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  52. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +47 -1
  53. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  54. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +22 -12
  55. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  56. package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue.js +1 -1
  57. package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue2.js +3 -1
  58. package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue2.js.map +1 -1
  59. package/dist/esm/components/CnDataMatrix/CnDataMatrix.vue3.js.map +1 -1
  60. package/dist/esm/components/CnDetailPage/CnDetailHeaderChips.vue2.js +2 -1
  61. package/dist/esm/components/CnDetailPage/CnDetailHeaderChips.vue2.js.map +1 -1
  62. package/dist/esm/components/CnDetailPage/CnDetailHeaderChips.vue3.js.map +1 -1
  63. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +23 -4
  64. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  65. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  66. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  67. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +3 -1
  68. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  69. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  70. package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue.js +1 -1
  71. package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue2.js +2 -1
  72. package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue2.js.map +1 -1
  73. package/dist/esm/components/CnEmailPicker/CnEmailPicker.vue3.js.map +1 -1
  74. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  75. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +18 -5
  76. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  77. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  78. package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue.js +1 -1
  79. package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue2.js +3 -1
  80. package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue2.js.map +1 -1
  81. package/dist/esm/components/CnFederationStatus/CnFederationStatus.vue3.js.map +1 -1
  82. package/dist/esm/components/CnFileManager/CnFileManager.vue.js +1 -1
  83. package/dist/esm/components/CnFileManager/CnFileManager.vue2.js +3 -1
  84. package/dist/esm/components/CnFileManager/CnFileManager.vue2.js.map +1 -1
  85. package/dist/esm/components/CnFileManager/CnFileManager.vue3.js.map +1 -1
  86. package/dist/esm/components/CnFilePreview/CnFilePreview.vue.js +1 -1
  87. package/dist/esm/components/CnFilePreview/CnFilePreview.vue2.js +2 -1
  88. package/dist/esm/components/CnFilePreview/CnFilePreview.vue2.js.map +1 -1
  89. package/dist/esm/components/CnFilePreview/CnFilePreview.vue3.js.map +1 -1
  90. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  91. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +49 -1
  92. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  93. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +3 -3
  94. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  95. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue.js +1 -1
  96. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js +2 -1
  97. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js.map +1 -1
  98. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue3.js.map +1 -1
  99. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +571 -77
  100. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  101. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +191 -82
  102. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  103. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +11 -2
  104. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  105. package/dist/esm/components/CnNotesCard/CnNotesCard.vue.js +1 -1
  106. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js +2 -1
  107. package/dist/esm/components/CnNotesCard/CnNotesCard.vue2.js.map +1 -1
  108. package/dist/esm/components/CnNotesCard/CnNotesCard.vue3.js.map +1 -1
  109. package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue.js +1 -1
  110. package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue2.js +3 -2
  111. package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue2.js.map +1 -1
  112. package/dist/esm/components/CnObjectCalendar/CnObjectCalendar.vue3.js.map +1 -1
  113. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue.js +1 -1
  114. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +28 -0
  115. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
  116. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +82 -75
  117. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
  118. package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue.js +1 -1
  119. package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue2.js +5 -4
  120. package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue2.js.map +1 -1
  121. package/dist/esm/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue3.js.map +1 -1
  122. package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue.js +1 -1
  123. package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue2.js +2 -1
  124. package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue2.js.map +1 -1
  125. package/dist/esm/components/CnObjectSidebar/CnAuditTrailTab.vue3.js.map +1 -1
  126. package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue.js +1 -1
  127. package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue2.js +2 -1
  128. package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue2.js.map +1 -1
  129. package/dist/esm/components/CnObjectSidebar/CnFlowTasksPanel.vue3.js.map +1 -1
  130. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue.js +1 -1
  131. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +2 -1
  132. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  133. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  134. package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue.js +1 -1
  135. package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue2.js +2 -1
  136. package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue2.js.map +1 -1
  137. package/dist/esm/components/CnObjectSidebar/CnTasksTab.vue3.js.map +1 -1
  138. package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue.js +1 -1
  139. package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue2.js +2 -1
  140. package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue2.js.map +1 -1
  141. package/dist/esm/components/CnPropertySourceField/CnPropertySourceField.vue3.js.map +1 -1
  142. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  143. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +70 -2
  144. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  145. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  146. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue.js +1 -1
  147. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js +67 -5
  148. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue2.js.map +1 -1
  149. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js +181 -124
  150. package/dist/esm/components/CnRegisterMapping/CnRegisterMapping.vue3.js.map +1 -1
  151. package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue.js +1 -1
  152. package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue2.js +2 -1
  153. package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue2.js.map +1 -1
  154. package/dist/esm/components/CnRoadmapItem/CnRoadmapItem.vue3.js.map +1 -1
  155. package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue2.js +2 -0
  156. package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue2.js.map +1 -1
  157. package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue3.js +2 -2
  158. package/dist/esm/components/CnSchemaFormDialog/CnSchemaFormDialog.vue3.js.map +1 -1
  159. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
  160. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +5 -1
  161. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
  162. package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
  163. package/dist/esm/components/CnStatWidget/CnStatWidget.vue.js +1 -1
  164. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +134 -10
  165. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  166. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +6 -2
  167. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  168. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue.js +1 -1
  169. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js +2 -1
  170. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js.map +1 -1
  171. package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue3.js.map +1 -1
  172. package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue.js +1 -1
  173. package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue2.js +2 -1
  174. package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue2.js.map +1 -1
  175. package/dist/esm/components/CnSummaryAggregates/CnSummaryAggregates.vue3.js.map +1 -1
  176. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  177. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +6 -3
  178. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  179. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  180. package/dist/esm/components/CnTasksCard/CnTasksCard.vue.js +1 -1
  181. package/dist/esm/components/CnTasksCard/CnTasksCard.vue2.js +2 -1
  182. package/dist/esm/components/CnTasksCard/CnTasksCard.vue2.js.map +1 -1
  183. package/dist/esm/components/CnTasksCard/CnTasksCard.vue3.js.map +1 -1
  184. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +1 -1
  185. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +2 -1
  186. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -1
  187. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -1
  188. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +1 -1
  189. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +140 -7
  190. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -1
  191. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -1
  192. package/dist/esm/components/CnTranslatedBadge/CnTranslatedBadge.vue.js +1 -1
  193. package/dist/esm/components/CnTranslatedBadge/CnTranslatedBadge.vue2.js +2 -1
  194. package/dist/esm/components/CnTranslatedBadge/CnTranslatedBadge.vue2.js.map +1 -1
  195. package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue.js +1 -1
  196. package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue2.js +2 -1
  197. package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue2.js.map +1 -1
  198. package/dist/esm/components/CnVersionHistory/CnVersionHistory.vue3.js.map +1 -1
  199. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
  200. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js +37 -9
  201. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
  202. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +6 -5
  203. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
  204. package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue2.js +53 -1
  205. package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue2.js.map +1 -1
  206. package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue3.js +3 -2
  207. package/dist/esm/components/CnWidgetCardGrid/CnWidgetCardGrid.vue3.js.map +1 -1
  208. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
  209. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +135 -0
  210. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  211. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  212. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue.js +1 -1
  213. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +19 -0
  214. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
  215. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +37 -19
  216. package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
  217. package/dist/esm/composables/useListView.js +21 -0
  218. package/dist/esm/composables/useListView.js.map +1 -1
  219. package/dist/esm/dialogs/CnNoteHistoryDialog.vue.js +1 -1
  220. package/dist/esm/dialogs/CnNoteHistoryDialog.vue2.js +2 -1
  221. package/dist/esm/dialogs/CnNoteHistoryDialog.vue2.js.map +1 -1
  222. package/dist/esm/dialogs/CnNoteHistoryDialog.vue3.js.map +1 -1
  223. package/dist/esm/integrations/builtin/activity/CnActivityCard.vue.js +1 -1
  224. package/dist/esm/integrations/builtin/activity/CnActivityCard.vue2.js +3 -2
  225. package/dist/esm/integrations/builtin/activity/CnActivityCard.vue2.js.map +1 -1
  226. package/dist/esm/integrations/builtin/activity/CnActivityCard.vue3.js.map +1 -1
  227. package/dist/esm/integrations/builtin/activity/CnActivityTab.vue.js +1 -1
  228. package/dist/esm/integrations/builtin/activity/CnActivityTab.vue2.js +4 -3
  229. package/dist/esm/integrations/builtin/activity/CnActivityTab.vue2.js.map +1 -1
  230. package/dist/esm/integrations/builtin/activity/CnActivityTab.vue3.js.map +1 -1
  231. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue.js +1 -1
  232. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -2
  233. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
  234. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
  235. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue.js +1 -1
  236. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js +2 -1
  237. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue2.js.map +1 -1
  238. package/dist/esm/integrations/builtin/calendar/CnCalendarCard.vue3.js.map +1 -1
  239. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue.js +1 -1
  240. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js +6 -5
  241. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue2.js.map +1 -1
  242. package/dist/esm/integrations/builtin/calendar/CnCalendarTab.vue3.js.map +1 -1
  243. package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue.js +1 -1
  244. package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue2.js +2 -1
  245. package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue2.js.map +1 -1
  246. package/dist/esm/integrations/builtin/contactmoment/CnContactmomentCard.vue3.js.map +1 -1
  247. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue.js +1 -1
  248. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
  249. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
  250. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
  251. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue.js +1 -1
  252. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
  253. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  254. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  255. package/dist/esm/integrations/builtin/email/CnEmailCard.vue.js +1 -1
  256. package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js +2 -1
  257. package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
  258. package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
  259. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue.js +1 -1
  260. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +2 -1
  261. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
  262. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
  263. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue.js +1 -1
  264. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue2.js +2 -1
  265. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue2.js.map +1 -1
  266. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionTab.vue3.js.map +1 -1
  267. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue.js +1 -1
  268. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
  269. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
  270. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
  271. package/dist/esm/l10n/en.json.js +57 -2
  272. package/dist/esm/l10n/en.json.js.map +1 -1
  273. package/dist/esm/l10n/nl.json.js +78 -2
  274. package/dist/esm/l10n/nl.json.js.map +1 -1
  275. package/dist/esm/library-style.js +1 -1
  276. package/dist/esm/nextcloud-vue.css +1541 -1166
  277. package/dist/esm/store/useObjectStore.js +23 -4
  278. package/dist/esm/store/useObjectStore.js.map +1 -1
  279. package/dist/esm/utils/boardColumns.js +58 -2
  280. package/dist/esm/utils/boardColumns.js.map +1 -1
  281. package/dist/esm/utils/boardReferenceColumns.js +330 -0
  282. package/dist/esm/utils/boardReferenceColumns.js.map +1 -0
  283. package/dist/esm/utils/buildManifest.js +4 -1
  284. package/dist/esm/utils/buildManifest.js.map +1 -1
  285. package/dist/esm/utils/schema.js +5 -4
  286. package/dist/esm/utils/schema.js.map +1 -1
  287. package/dist/esm/utils/userLocale.js +30 -0
  288. package/dist/esm/utils/userLocale.js.map +1 -0
  289. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  290. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  291. package/dist/esm/utils/widgetDispatch.js +8 -2
  292. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  293. package/dist/nextcloud-vue.cjs.js +30291 -27618
  294. package/dist/nextcloud-vue.cjs.js.map +2 -2
  295. package/dist/nextcloud-vue.css +1541 -1166
  296. package/l10n/en.json +57 -2
  297. package/l10n/nl.json +78 -2
  298. package/package.json +1 -1
  299. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +160 -8
  300. package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +3 -2
  301. package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +5 -4
  302. package/src/components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue +2 -1
  303. package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +2 -1
  304. package/src/components/CnBoardView/CnBoardView.vue +433 -12
  305. package/src/components/CnCalendarEventPicker/CnCalendarEventPicker.vue +2 -1
  306. package/src/components/CnCalendarWidget/CnCalendarWidget.vue +2 -1
  307. package/src/components/CnChartWidget/CnChartWidget.vue +6 -5
  308. package/src/components/CnCollectivePagePicker/CnCollectivePagePicker.vue +2 -1
  309. package/src/components/CnConversationThread/CnConversationThread.vue +2 -1
  310. package/src/components/CnCountdownWidget/CnCountdownWidget.vue +2 -1
  311. package/src/components/CnDashboardPage/CnDashboardPage.vue +59 -3
  312. package/src/components/CnDataMatrix/CnDataMatrix.vue +2 -1
  313. package/src/components/CnDetailPage/CnDetailHeaderChips.vue +2 -1
  314. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  315. package/src/components/CnDetailPage/CnDetailPage.vue +23 -4
  316. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +3 -1
  317. package/src/components/CnEmailPicker/CnEmailPicker.vue +2 -1
  318. package/src/components/CnFactsWidget/CnFactsWidget.vue +18 -5
  319. package/src/components/CnFederationStatus/CnFederationStatus.vue +3 -1
  320. package/src/components/CnFileManager/CnFileManager.vue +2 -1
  321. package/src/components/CnFilePreview/CnFilePreview.vue +2 -1
  322. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +54 -6
  323. package/src/components/CnFollowToggle/CnFollowToggle.vue +2 -1
  324. package/src/components/CnIndexPage/CnIndexPage.vue +610 -25
  325. package/src/components/CnIndexPage/useSelfFetchList.js +10 -1
  326. package/src/components/CnNotesCard/CnNotesCard.vue +2 -1
  327. package/src/components/CnObjectCalendar/CnObjectCalendar.vue +3 -2
  328. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +36 -0
  329. package/src/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue +5 -4
  330. package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +2 -1
  331. package/src/components/CnObjectSidebar/CnFlowTasksPanel.vue +2 -1
  332. package/src/components/CnObjectSidebar/CnNotesTab.vue +2 -1
  333. package/src/components/CnObjectSidebar/CnTasksTab.vue +2 -1
  334. package/src/components/CnPropertySourceField/CnPropertySourceField.vue +2 -1
  335. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +70 -2
  336. package/src/components/CnRegisterMapping/CnRegisterMapping.md +5 -0
  337. package/src/components/CnRegisterMapping/CnRegisterMapping.vue +231 -96
  338. package/src/components/CnRoadmapItem/CnRoadmapItem.vue +2 -1
  339. package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +4 -2
  340. package/src/components/CnSettingsPage/CnSettingsPage.vue +5 -1
  341. package/src/components/CnStatWidget/CnStatWidget.vue +136 -11
  342. package/src/components/CnStatsBlock/CnStatsBlock.vue +2 -1
  343. package/src/components/CnSummaryAggregates/CnSummaryAggregates.vue +2 -1
  344. package/src/components/CnTabsWidget/CnTabsWidget.vue +6 -3
  345. package/src/components/CnTasksCard/CnTasksCard.vue +2 -1
  346. package/src/components/CnTimelineWidget/CnTimelineWidget.vue +2 -1
  347. package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +140 -7
  348. package/src/components/CnTranslatedBadge/CnTranslatedBadge.vue +2 -1
  349. package/src/components/CnVersionHistory/CnVersionHistory.vue +2 -1
  350. package/src/components/CnVersionInfoCard/CnVersionInfoCard.md +2 -1
  351. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +42 -13
  352. package/src/components/CnWidgetCardGrid/CnWidgetCardGrid.vue +55 -2
  353. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +135 -0
  354. package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +10 -0
  355. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +44 -1
  356. package/src/composables/useListView.js +21 -0
  357. package/src/css/board.css +105 -4
  358. package/src/css/dialog.css +4 -2
  359. package/src/css/index-page.css +49 -0
  360. package/src/css/look-board.css +77 -7
  361. package/src/dialogs/CnNoteHistoryDialog.vue +2 -1
  362. package/src/integrations/builtin/activity/CnActivityCard.vue +3 -2
  363. package/src/integrations/builtin/activity/CnActivityTab.vue +4 -3
  364. package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +3 -2
  365. package/src/integrations/builtin/calendar/CnCalendarCard.vue +2 -1
  366. package/src/integrations/builtin/calendar/CnCalendarTab.vue +6 -5
  367. package/src/integrations/builtin/contactmoment/CnContactmomentCard.vue +2 -1
  368. package/src/integrations/builtin/cospend/CnCospendTab.vue +2 -1
  369. package/src/integrations/builtin/deck/CnDeckTab.vue +2 -1
  370. package/src/integrations/builtin/email/CnEmailCard.vue +2 -1
  371. package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +2 -1
  372. package/src/integrations/builtin/field-inspection/CnFieldInspectionTab.vue +2 -1
  373. package/src/integrations/builtin/photos/CnPhotosTab.vue +2 -1
  374. package/src/schemas/app-manifest-v2.schema.json +116 -5
  375. package/src/store/useObjectStore.js +23 -4
  376. package/src/utils/boardColumns.js +57 -1
  377. package/src/utils/boardReferenceColumns.js +326 -0
  378. package/src/utils/buildManifest.js +4 -1
  379. package/src/utils/schema.js +5 -4
  380. package/src/utils/userLocale.js +26 -0
  381. package/src/utils/validateManifestV2.compiled.js +3 -3
  382. package/src/utils/widgetDispatch.js +8 -2
@@ -2,7 +2,7 @@ import script from './CnDataMatrix.vue2.js';
2
2
  import { render } from './CnDataMatrix.vue3.js';
3
3
 
4
4
  script.render = render;
5
- script.__scopeId = "data-v-21e92577";
5
+ script.__scopeId = "data-v-b800caca";
6
6
  const __sfc_main = script;
7
7
 
8
8
  export { __sfc_main as default };
@@ -1,3 +1,5 @@
1
+ import { userLocale } from '../../utils/userLocale.js';
2
+
1
3
  /**
2
4
  * CnDataMatrix — Inline-edit data grid for matrix-shaped data
3
5
  * (rows × columns). Each cell is click-to-edit; commits emit
@@ -154,7 +156,7 @@ var script = {
154
156
  if (!Number.isFinite(n)) {
155
157
  return value
156
158
  }
157
- return n.toLocaleString()
159
+ return n.toLocaleString(userLocale())
158
160
  }
159
161
  return value
160
162
  },
@@ -1 +1 @@
1
- {"version":3,"file":"CnDataMatrix.vue2.js","sources":["../../../../src/components/CnDataMatrix/CnDataMatrix.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-data-matrix\" data-testid=\"cn-data-matrix\">\n\t\t<header v-if=\"title || description\" class=\"cn-data-matrix__header\">\n\t\t\t<h3 v-if=\"title\" class=\"cn-data-matrix__title\">\n\t\t\t\t{{ title }}\n\t\t\t</h3>\n\t\t\t<p v-if=\"description\" class=\"cn-data-matrix__description\">\n\t\t\t\t{{ description }}\n\t\t\t</p>\n\t\t</header>\n\n\t\t<div class=\"cn-data-matrix__scroller\">\n\t\t\t<table class=\"cn-data-matrix__table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__row-header-corner\">\n\t\t\t\t\t\t\t{{ rowHeader }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__col-header\"\n\t\t\t\t\t\t\t:style=\"col.width ? { width: col.width } : null\">\n\t\t\t\t\t\t\t{{ col.label || col.key }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th v-if=\"showRowTotals\" class=\"cn-data-matrix__total-header\">\n\t\t\t\t\t\t\t{{ rowTotalsLabel }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr v-for=\"row in rows\"\n\t\t\t\t\t\t:key=\"row[rowIdKey]\"\n\t\t\t\t\t\tclass=\"cn-data-matrix__row\">\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__row-header\" :title=\"row[rowLabelKey]\">\n\t\t\t\t\t\t\t{{ row[rowLabelKey] }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<td v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell\"\n\t\t\t\t\t\t\t:class=\"cellClass(row, col)\"\n\t\t\t\t\t\t\t:data-row-id=\"row[rowIdKey]\"\n\t\t\t\t\t\t\t:data-col-key=\"col.key\">\n\t\t\t\t\t\t\t<input v-if=\"isEditing(row, col)\"\n\t\t\t\t\t\t\t\tref=\"activeInput\"\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell-input\"\n\t\t\t\t\t\t\t\t:value=\"cellValue(row, col)\"\n\t\t\t\t\t\t\t\t:disabled=\"readOnly || isCellReadOnly(row, col)\"\n\t\t\t\t\t\t\t\t@blur=\"commitEdit(row, col, $event.target.value)\"\n\t\t\t\t\t\t\t\t@keydown.enter=\"commitEdit(row, col, $event.target.value)\"\n\t\t\t\t\t\t\t\t@keydown.esc=\"cancelEdit\">\n\t\t\t\t\t\t\t<span v-else\n\t\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell-value\"\n\t\t\t\t\t\t\t\t@click=\"startEdit(row, col)\">\n\t\t\t\t\t\t\t\t{{ formatCell(cellValue(row, col), col) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td v-if=\"showRowTotals\" class=\"cn-data-matrix__total-cell\">\n\t\t\t\t\t\t\t{{ formatCell(rowTotal(row), { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr v-if=\"showColumnTotals\" class=\"cn-data-matrix__totals-row\">\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__totals-label\">\n\t\t\t\t\t\t\t{{ columnTotalsLabel }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<td v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__total-cell\">\n\t\t\t\t\t\t\t{{ formatCell(columnTotal(col), { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td v-if=\"showRowTotals\" class=\"cn-data-matrix__total-cell cn-data-matrix__grand-total\">\n\t\t\t\t\t\t\t{{ formatCell(grandTotal, { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\n\t\t<p v-if=\"rows.length === 0\" class=\"cn-data-matrix__empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\n/**\n * CnDataMatrix — Inline-edit data grid for matrix-shaped data\n * (rows × columns). Each cell is click-to-edit; commits emit\n * `@cell-edit({ rowId, colKey, value })` so the parent can persist.\n * Optional row / column / grand totals computed from the live data\n * with the column's `aggregate` setting (`sum` by default).\n *\n * Use for gradebooks, weight matrices, allocation tables, scoring\n * rubrics — the \"spreadsheet\" shape where cells are independently\n * editable values rather than a row/object form.\n *\n * Not a full spreadsheet engine — there's no formula language, no\n * cell-range references, no charts. Consumers wanting that should\n * embed a dedicated library (e.g. handsontable) and use this for\n * the simpler cases.\n *\n * ```vue\n * <CnDataMatrix\n * title=\"Cohort gradebook\"\n * row-header=\"Student\"\n * :rows=\"students\"\n * :columns=\"[\n * { key: 'midterm', label: 'Midterm', type: 'number', aggregate: 'sum' },\n * { key: 'final', label: 'Final', type: 'number', aggregate: 'sum' },\n * { key: 'notes', label: 'Notes', type: 'string', readOnly: true },\n * ]\"\n * :show-row-totals=\"true\"\n * :show-column-totals=\"true\"\n * @cell-edit=\"onCellEdit\" />\n * ```\n */\nexport default {\n\tname: 'CnDataMatrix',\n\tprops: {\n\t\t/**\n\t\t * Row records. Each record holds an `id`, an optional row\n\t\t * label, and one field per column key.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\trows: { type: Array, default: () => [] },\n\t\t/**\n\t\t * Column declarations.\n\t\t *\n\t\t * @type {Array<{key:string,label?:string,type?:'number'|'string',readOnly?:boolean,formatter?:(value: unknown) => string,aggregate?:'sum'|'avg'|'count'|'none',width?:string}>}\n\t\t */\n\t\tcolumns: { type: Array, default: () => [] },\n\t\t/** Field on each row carrying its id. */\n\t\trowIdKey: { type: String, default: 'id' },\n\t\t/** Field on each row carrying its label (shown in the row header). */\n\t\trowLabelKey: { type: String, default: 'label' },\n\t\t/** Header for the row-label column (empty hides it). */\n\t\trowHeader: { type: String, default: '' },\n\t\t/** Optional title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Optional description. */\n\t\tdescription: { type: String, default: '' },\n\t\t/** Empty-state text when rows[] is empty. */\n\t\temptyLabel: { type: String, default: 'No data.' },\n\t\t/** Disable all cell editing globally. */\n\t\treadOnly: { type: Boolean, default: false },\n\t\t/** Render the per-row totals column. */\n\t\tshowRowTotals: { type: Boolean, default: false },\n\t\t/** Render the per-column totals row at the bottom. */\n\t\tshowColumnTotals: { type: Boolean, default: false },\n\t\t/** Label for the row-totals column header. */\n\t\trowTotalsLabel: { type: String, default: 'Total' },\n\t\t/** Label preceding the column-totals row. */\n\t\tcolumnTotalsLabel: { type: String, default: 'Total' },\n\t},\n\n\temits: ['cell-edit'],\n\tdata() {\n\t\treturn {\n\t\t\tediting: null, // { rowId, colKey }\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Grand total (sum of every numeric cell). Shown in the\n\t\t * bottom-right corner when both totals are enabled.\n\t\t *\n\t\t * @return {number}\n\t\t */\n\t\tgrandTotal() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tfor (const col of this.columns) {\n\t\t\t\t\tif (col.type === 'number') {\n\t\t\t\t\t\tconst v = Number(row[col.key])\n\t\t\t\t\t\tif (Number.isFinite(v)) {\n\t\t\t\t\t\t\ttotal += v\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether the given (row, col) is the active edit cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {boolean}\n\t\t */\n\t\tisEditing(row, col) {\n\t\t\treturn this.editing && this.editing.rowId === row[this.rowIdKey] && this.editing.colKey === col.key\n\t\t},\n\n\t\t/**\n\t\t * Effective read-only flag for a single cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {boolean}\n\t\t */\n\t\tisCellReadOnly(row, col) {\n\t\t\treturn Boolean(col.readOnly)\n\t\t},\n\n\t\t/**\n\t\t * Read the cell value from the row.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {unknown} The raw value.\n\t\t */\n\t\tcellValue(row, col) {\n\t\t\treturn row[col.key]\n\t\t},\n\n\t\t/**\n\t\t * Format a cell value for display. Number columns get a\n\t\t * locale-formatted number; others go through the column's\n\t\t * `formatter` (if any) or are returned verbatim.\n\t\t *\n\t\t * @param {unknown} value The raw value.\n\t\t * @param {object} col Column definition.\n\t\t * @return {string} The display string.\n\t\t */\n\t\tformatCell(value, col) {\n\t\t\tif (value === undefined || value === null || value === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (typeof col.formatter === 'function') {\n\t\t\t\treturn col.formatter(value)\n\t\t\t}\n\t\t\tif (col.type === 'number') {\n\t\t\t\tconst n = Number(value)\n\t\t\t\tif (!Number.isFinite(n)) {\n\t\t\t\t\treturn value\n\t\t\t\t}\n\t\t\t\treturn n.toLocaleString()\n\t\t\t}\n\t\t\treturn value\n\t\t},\n\n\t\t/**\n\t\t * BEM modifier(s) for a cell — currently only the read-only\n\t\t * flag. Consumers extend via the `formatter` if they want\n\t\t * to swap to a styled component.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {object} Class binding.\n\t\t */\n\t\tcellClass(row, col) {\n\t\t\treturn {\n\t\t\t\t'cn-data-matrix__cell--read-only': this.readOnly || this.isCellReadOnly(row, col),\n\t\t\t\t'cn-data-matrix__cell--number': col.type === 'number',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Start editing a cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {void}\n\t\t */\n\t\tstartEdit(row, col) {\n\t\t\tif (this.readOnly || this.isCellReadOnly(row, col)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.editing = { rowId: row[this.rowIdKey], colKey: col.key }\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.$refs.activeInput && this.$refs.activeInput[0]) {\n\t\t\t\t\tthis.$refs.activeInput[0].focus()\n\t\t\t\t\tthis.$refs.activeInput[0].select()\n\t\t\t\t} else if (this.$refs.activeInput && this.$refs.activeInput.focus) {\n\t\t\t\t\tthis.$refs.activeInput.focus()\n\t\t\t\t\tthis.$refs.activeInput.select()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Commit an edit — coerces the value to the column type and\n\t\t * emits `@cell-edit`.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @param {string} raw The raw input string.\n\t\t * @return {void}\n\t\t */\n\t\tcommitEdit(row, col, raw) {\n\t\t\tif (!this.editing) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet value = raw\n\t\t\tif (col.type === 'number') {\n\t\t\t\tconst n = Number(raw)\n\t\t\t\tvalue = Number.isFinite(n) ? n : null\n\t\t\t}\n\t\t\tthis.editing = null\n\t\t\t/**\n\t\t\t * @event cell-edit Emitted on commit (blur or Enter).\n\t\t\t * Payload: `{rowId, colKey, value, row}`.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('cell-edit', { rowId: row[this.rowIdKey], colKey: col.key, value, row })\n\t\t},\n\n\t\t/**\n\t\t * Cancel the active edit (Esc).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcancelEdit() {\n\t\t\tthis.editing = null\n\t\t},\n\n\t\t/**\n\t\t * Per-row total — sum of every numeric column.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @return {number}\n\t\t */\n\t\trowTotal(row) {\n\t\t\tlet total = 0\n\t\t\tfor (const col of this.columns) {\n\t\t\t\tif (col.type === 'number') {\n\t\t\t\t\tconst v = Number(row[col.key])\n\t\t\t\t\tif (Number.isFinite(v)) {\n\t\t\t\t\t\ttotal += v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\t/**\n\t\t * Per-column total — aggregate over all rows using the\n\t\t * column's `aggregate` setting (`sum` by default).\n\t\t *\n\t\t * @param {object} col Column definition.\n\t\t * @return {number}\n\t\t */\n\t\tcolumnTotal(col) {\n\t\t\tif (col.type !== 'number') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst mode = col.aggregate || 'sum'\n\t\t\tif (mode === 'none') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\t// Drop empty cells (null / undefined / '') BEFORE coercing —\n\t\t\t// `Number(null)` is `0`, which would otherwise be counted and\n\t\t\t// summed as a real zero value.\n\t\t\tconst values = this.rows\n\t\t\t\t.map((r) => r[col.key])\n\t\t\t\t.filter((v) => v !== null && v !== undefined && v !== '')\n\t\t\t\t.map((v) => Number(v))\n\t\t\t\t.filter((v) => Number.isFinite(v))\n\t\t\tif (values.length === 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tif (mode === 'count') {\n\t\t\t\treturn values.length\n\t\t\t}\n\t\t\tif (mode === 'avg') {\n\t\t\t\treturn values.reduce((a, b) => a + b, 0) / values.length\n\t\t\t}\n\t\t\t// default: sum\n\t\t\treturn values.reduce((a, b) => a + b, 0)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-data-matrix {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n}\n\n.cn-data-matrix__title {\n\tmargin: 0;\n\tfont-size: 1.1em;\n}\n\n.cn-data-matrix__description {\n\tmargin: 4px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-data-matrix__scroller {\n\toverflow-x: auto;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-data-matrix__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\tfont-size: 0.95em;\n}\n\n.cn-data-matrix__col-header,\n.cn-data-matrix__row-header-corner,\n.cn-data-matrix__total-header {\n\tbackground: var(--color-background-hover);\n\ttext-align: left;\n\tpadding: 8px 10px;\n\tfont-weight: 600;\n\tborder-bottom: 1px solid var(--color-border);\n\tborder-right: 1px solid var(--color-border);\n}\n\n.cn-data-matrix__row-header {\n\tbackground: var(--color-background-hover);\n\ttext-align: left;\n\tpadding: 6px 10px;\n\tfont-weight: 500;\n\tborder-right: 1px solid var(--color-border);\n\tborder-bottom: 1px solid var(--color-border);\n\tmax-width: 200px;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-data-matrix__cell {\n\tpadding: 0;\n\tborder-right: 1px solid var(--color-border);\n\tborder-bottom: 1px solid var(--color-border);\n\tmin-width: 80px;\n}\n\n.cn-data-matrix__cell--number {\n\ttext-align: right;\n}\n\n.cn-data-matrix__cell-value {\n\tdisplay: block;\n\tpadding: 6px 10px;\n\tcursor: cell;\n\tmin-height: 28px;\n}\n\n.cn-data-matrix__cell--read-only .cn-data-matrix__cell-value {\n\tcursor: default;\n\tbackground: var(--color-background-hover);\n}\n\n.cn-data-matrix__cell-input {\n\twidth: 100%;\n\tpadding: 5px 10px;\n\tborder: 1px solid var(--color-primary-element);\n\tbackground: var(--color-main-background);\n\toutline: none;\n\tfont: inherit;\n}\n\n.cn-data-matrix__totals-row .cn-data-matrix__total-cell,\n.cn-data-matrix__total-header {\n\tbackground: var(--color-background-darker, var(--color-background-hover));\n\tfont-weight: 600;\n\ttext-align: right;\n\tpadding: 6px 10px;\n}\n\n.cn-data-matrix__totals-label {\n\tbackground: var(--color-background-darker, var(--color-background-hover));\n\tfont-weight: 600;\n\tpadding: 6px 10px;\n\ttext-align: left;\n}\n\n.cn-data-matrix__grand-total {\n\tbackground: var(--color-primary-element-light);\n}\n\n.cn-data-matrix__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-style: italic;\n\tmargin: 16px 0;\n\ttext-align: center;\n}\n</style>\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CnDataMatrix.vue2.js","sources":["../../../../src/components/CnDataMatrix/CnDataMatrix.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-data-matrix\" data-testid=\"cn-data-matrix\">\n\t\t<header v-if=\"title || description\" class=\"cn-data-matrix__header\">\n\t\t\t<h3 v-if=\"title\" class=\"cn-data-matrix__title\">\n\t\t\t\t{{ title }}\n\t\t\t</h3>\n\t\t\t<p v-if=\"description\" class=\"cn-data-matrix__description\">\n\t\t\t\t{{ description }}\n\t\t\t</p>\n\t\t</header>\n\n\t\t<div class=\"cn-data-matrix__scroller\">\n\t\t\t<table class=\"cn-data-matrix__table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__row-header-corner\">\n\t\t\t\t\t\t\t{{ rowHeader }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__col-header\"\n\t\t\t\t\t\t\t:style=\"col.width ? { width: col.width } : null\">\n\t\t\t\t\t\t\t{{ col.label || col.key }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th v-if=\"showRowTotals\" class=\"cn-data-matrix__total-header\">\n\t\t\t\t\t\t\t{{ rowTotalsLabel }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr v-for=\"row in rows\"\n\t\t\t\t\t\t:key=\"row[rowIdKey]\"\n\t\t\t\t\t\tclass=\"cn-data-matrix__row\">\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__row-header\" :title=\"row[rowLabelKey]\">\n\t\t\t\t\t\t\t{{ row[rowLabelKey] }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<td v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell\"\n\t\t\t\t\t\t\t:class=\"cellClass(row, col)\"\n\t\t\t\t\t\t\t:data-row-id=\"row[rowIdKey]\"\n\t\t\t\t\t\t\t:data-col-key=\"col.key\">\n\t\t\t\t\t\t\t<input v-if=\"isEditing(row, col)\"\n\t\t\t\t\t\t\t\tref=\"activeInput\"\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell-input\"\n\t\t\t\t\t\t\t\t:value=\"cellValue(row, col)\"\n\t\t\t\t\t\t\t\t:disabled=\"readOnly || isCellReadOnly(row, col)\"\n\t\t\t\t\t\t\t\t@blur=\"commitEdit(row, col, $event.target.value)\"\n\t\t\t\t\t\t\t\t@keydown.enter=\"commitEdit(row, col, $event.target.value)\"\n\t\t\t\t\t\t\t\t@keydown.esc=\"cancelEdit\">\n\t\t\t\t\t\t\t<span v-else\n\t\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell-value\"\n\t\t\t\t\t\t\t\t@click=\"startEdit(row, col)\">\n\t\t\t\t\t\t\t\t{{ formatCell(cellValue(row, col), col) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td v-if=\"showRowTotals\" class=\"cn-data-matrix__total-cell\">\n\t\t\t\t\t\t\t{{ formatCell(rowTotal(row), { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr v-if=\"showColumnTotals\" class=\"cn-data-matrix__totals-row\">\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__totals-label\">\n\t\t\t\t\t\t\t{{ columnTotalsLabel }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<td v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__total-cell\">\n\t\t\t\t\t\t\t{{ formatCell(columnTotal(col), { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td v-if=\"showRowTotals\" class=\"cn-data-matrix__total-cell cn-data-matrix__grand-total\">\n\t\t\t\t\t\t\t{{ formatCell(grandTotal, { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\n\t\t<p v-if=\"rows.length === 0\" class=\"cn-data-matrix__empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport { userLocale } from '../../utils/userLocale.js'\n/**\n * CnDataMatrix — Inline-edit data grid for matrix-shaped data\n * (rows × columns). Each cell is click-to-edit; commits emit\n * `@cell-edit({ rowId, colKey, value })` so the parent can persist.\n * Optional row / column / grand totals computed from the live data\n * with the column's `aggregate` setting (`sum` by default).\n *\n * Use for gradebooks, weight matrices, allocation tables, scoring\n * rubrics — the \"spreadsheet\" shape where cells are independently\n * editable values rather than a row/object form.\n *\n * Not a full spreadsheet engine — there's no formula language, no\n * cell-range references, no charts. Consumers wanting that should\n * embed a dedicated library (e.g. handsontable) and use this for\n * the simpler cases.\n *\n * ```vue\n * <CnDataMatrix\n * title=\"Cohort gradebook\"\n * row-header=\"Student\"\n * :rows=\"students\"\n * :columns=\"[\n * { key: 'midterm', label: 'Midterm', type: 'number', aggregate: 'sum' },\n * { key: 'final', label: 'Final', type: 'number', aggregate: 'sum' },\n * { key: 'notes', label: 'Notes', type: 'string', readOnly: true },\n * ]\"\n * :show-row-totals=\"true\"\n * :show-column-totals=\"true\"\n * @cell-edit=\"onCellEdit\" />\n * ```\n */\nexport default {\n\tname: 'CnDataMatrix',\n\tprops: {\n\t\t/**\n\t\t * Row records. Each record holds an `id`, an optional row\n\t\t * label, and one field per column key.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\trows: { type: Array, default: () => [] },\n\t\t/**\n\t\t * Column declarations.\n\t\t *\n\t\t * @type {Array<{key:string,label?:string,type?:'number'|'string',readOnly?:boolean,formatter?:(value: unknown) => string,aggregate?:'sum'|'avg'|'count'|'none',width?:string}>}\n\t\t */\n\t\tcolumns: { type: Array, default: () => [] },\n\t\t/** Field on each row carrying its id. */\n\t\trowIdKey: { type: String, default: 'id' },\n\t\t/** Field on each row carrying its label (shown in the row header). */\n\t\trowLabelKey: { type: String, default: 'label' },\n\t\t/** Header for the row-label column (empty hides it). */\n\t\trowHeader: { type: String, default: '' },\n\t\t/** Optional title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Optional description. */\n\t\tdescription: { type: String, default: '' },\n\t\t/** Empty-state text when rows[] is empty. */\n\t\temptyLabel: { type: String, default: 'No data.' },\n\t\t/** Disable all cell editing globally. */\n\t\treadOnly: { type: Boolean, default: false },\n\t\t/** Render the per-row totals column. */\n\t\tshowRowTotals: { type: Boolean, default: false },\n\t\t/** Render the per-column totals row at the bottom. */\n\t\tshowColumnTotals: { type: Boolean, default: false },\n\t\t/** Label for the row-totals column header. */\n\t\trowTotalsLabel: { type: String, default: 'Total' },\n\t\t/** Label preceding the column-totals row. */\n\t\tcolumnTotalsLabel: { type: String, default: 'Total' },\n\t},\n\n\temits: ['cell-edit'],\n\tdata() {\n\t\treturn {\n\t\t\tediting: null, // { rowId, colKey }\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Grand total (sum of every numeric cell). Shown in the\n\t\t * bottom-right corner when both totals are enabled.\n\t\t *\n\t\t * @return {number}\n\t\t */\n\t\tgrandTotal() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tfor (const col of this.columns) {\n\t\t\t\t\tif (col.type === 'number') {\n\t\t\t\t\t\tconst v = Number(row[col.key])\n\t\t\t\t\t\tif (Number.isFinite(v)) {\n\t\t\t\t\t\t\ttotal += v\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether the given (row, col) is the active edit cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {boolean}\n\t\t */\n\t\tisEditing(row, col) {\n\t\t\treturn this.editing && this.editing.rowId === row[this.rowIdKey] && this.editing.colKey === col.key\n\t\t},\n\n\t\t/**\n\t\t * Effective read-only flag for a single cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {boolean}\n\t\t */\n\t\tisCellReadOnly(row, col) {\n\t\t\treturn Boolean(col.readOnly)\n\t\t},\n\n\t\t/**\n\t\t * Read the cell value from the row.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {unknown} The raw value.\n\t\t */\n\t\tcellValue(row, col) {\n\t\t\treturn row[col.key]\n\t\t},\n\n\t\t/**\n\t\t * Format a cell value for display. Number columns get a\n\t\t * locale-formatted number; others go through the column's\n\t\t * `formatter` (if any) or are returned verbatim.\n\t\t *\n\t\t * @param {unknown} value The raw value.\n\t\t * @param {object} col Column definition.\n\t\t * @return {string} The display string.\n\t\t */\n\t\tformatCell(value, col) {\n\t\t\tif (value === undefined || value === null || value === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (typeof col.formatter === 'function') {\n\t\t\t\treturn col.formatter(value)\n\t\t\t}\n\t\t\tif (col.type === 'number') {\n\t\t\t\tconst n = Number(value)\n\t\t\t\tif (!Number.isFinite(n)) {\n\t\t\t\t\treturn value\n\t\t\t\t}\n\t\t\t\treturn n.toLocaleString(userLocale())\n\t\t\t}\n\t\t\treturn value\n\t\t},\n\n\t\t/**\n\t\t * BEM modifier(s) for a cell — currently only the read-only\n\t\t * flag. Consumers extend via the `formatter` if they want\n\t\t * to swap to a styled component.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {object} Class binding.\n\t\t */\n\t\tcellClass(row, col) {\n\t\t\treturn {\n\t\t\t\t'cn-data-matrix__cell--read-only': this.readOnly || this.isCellReadOnly(row, col),\n\t\t\t\t'cn-data-matrix__cell--number': col.type === 'number',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Start editing a cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {void}\n\t\t */\n\t\tstartEdit(row, col) {\n\t\t\tif (this.readOnly || this.isCellReadOnly(row, col)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.editing = { rowId: row[this.rowIdKey], colKey: col.key }\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.$refs.activeInput && this.$refs.activeInput[0]) {\n\t\t\t\t\tthis.$refs.activeInput[0].focus()\n\t\t\t\t\tthis.$refs.activeInput[0].select()\n\t\t\t\t} else if (this.$refs.activeInput && this.$refs.activeInput.focus) {\n\t\t\t\t\tthis.$refs.activeInput.focus()\n\t\t\t\t\tthis.$refs.activeInput.select()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Commit an edit — coerces the value to the column type and\n\t\t * emits `@cell-edit`.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @param {string} raw The raw input string.\n\t\t * @return {void}\n\t\t */\n\t\tcommitEdit(row, col, raw) {\n\t\t\tif (!this.editing) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet value = raw\n\t\t\tif (col.type === 'number') {\n\t\t\t\tconst n = Number(raw)\n\t\t\t\tvalue = Number.isFinite(n) ? n : null\n\t\t\t}\n\t\t\tthis.editing = null\n\t\t\t/**\n\t\t\t * @event cell-edit Emitted on commit (blur or Enter).\n\t\t\t * Payload: `{rowId, colKey, value, row}`.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('cell-edit', { rowId: row[this.rowIdKey], colKey: col.key, value, row })\n\t\t},\n\n\t\t/**\n\t\t * Cancel the active edit (Esc).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcancelEdit() {\n\t\t\tthis.editing = null\n\t\t},\n\n\t\t/**\n\t\t * Per-row total — sum of every numeric column.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @return {number}\n\t\t */\n\t\trowTotal(row) {\n\t\t\tlet total = 0\n\t\t\tfor (const col of this.columns) {\n\t\t\t\tif (col.type === 'number') {\n\t\t\t\t\tconst v = Number(row[col.key])\n\t\t\t\t\tif (Number.isFinite(v)) {\n\t\t\t\t\t\ttotal += v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\t/**\n\t\t * Per-column total — aggregate over all rows using the\n\t\t * column's `aggregate` setting (`sum` by default).\n\t\t *\n\t\t * @param {object} col Column definition.\n\t\t * @return {number}\n\t\t */\n\t\tcolumnTotal(col) {\n\t\t\tif (col.type !== 'number') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst mode = col.aggregate || 'sum'\n\t\t\tif (mode === 'none') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\t// Drop empty cells (null / undefined / '') BEFORE coercing —\n\t\t\t// `Number(null)` is `0`, which would otherwise be counted and\n\t\t\t// summed as a real zero value.\n\t\t\tconst values = this.rows\n\t\t\t\t.map((r) => r[col.key])\n\t\t\t\t.filter((v) => v !== null && v !== undefined && v !== '')\n\t\t\t\t.map((v) => Number(v))\n\t\t\t\t.filter((v) => Number.isFinite(v))\n\t\t\tif (values.length === 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tif (mode === 'count') {\n\t\t\t\treturn values.length\n\t\t\t}\n\t\t\tif (mode === 'avg') {\n\t\t\t\treturn values.reduce((a, b) => a + b, 0) / values.length\n\t\t\t}\n\t\t\t// default: sum\n\t\t\treturn values.reduce((a, b) => a + b, 0)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-data-matrix {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n}\n\n.cn-data-matrix__title {\n\tmargin: 0;\n\tfont-size: 1.1em;\n}\n\n.cn-data-matrix__description {\n\tmargin: 4px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-data-matrix__scroller {\n\toverflow-x: auto;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-data-matrix__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\tfont-size: 0.95em;\n}\n\n.cn-data-matrix__col-header,\n.cn-data-matrix__row-header-corner,\n.cn-data-matrix__total-header {\n\tbackground: var(--color-background-hover);\n\ttext-align: left;\n\tpadding: 8px 10px;\n\tfont-weight: 600;\n\tborder-bottom: 1px solid var(--color-border);\n\tborder-right: 1px solid var(--color-border);\n}\n\n.cn-data-matrix__row-header {\n\tbackground: var(--color-background-hover);\n\ttext-align: left;\n\tpadding: 6px 10px;\n\tfont-weight: 500;\n\tborder-right: 1px solid var(--color-border);\n\tborder-bottom: 1px solid var(--color-border);\n\tmax-width: 200px;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-data-matrix__cell {\n\tpadding: 0;\n\tborder-right: 1px solid var(--color-border);\n\tborder-bottom: 1px solid var(--color-border);\n\tmin-width: 80px;\n}\n\n.cn-data-matrix__cell--number {\n\ttext-align: right;\n}\n\n.cn-data-matrix__cell-value {\n\tdisplay: block;\n\tpadding: 6px 10px;\n\tcursor: cell;\n\tmin-height: 28px;\n}\n\n.cn-data-matrix__cell--read-only .cn-data-matrix__cell-value {\n\tcursor: default;\n\tbackground: var(--color-background-hover);\n}\n\n.cn-data-matrix__cell-input {\n\twidth: 100%;\n\tpadding: 5px 10px;\n\tborder: 1px solid var(--color-primary-element);\n\tbackground: var(--color-main-background);\n\toutline: none;\n\tfont: inherit;\n}\n\n.cn-data-matrix__totals-row .cn-data-matrix__total-cell,\n.cn-data-matrix__total-header {\n\tbackground: var(--color-background-darker, var(--color-background-hover));\n\tfont-weight: 600;\n\ttext-align: right;\n\tpadding: 6px 10px;\n}\n\n.cn-data-matrix__totals-label {\n\tbackground: var(--color-background-darker, var(--color-background-hover));\n\tfont-weight: 600;\n\tpadding: 6px 10px;\n\ttext-align: left;\n}\n\n.cn-data-matrix__grand-total {\n\tbackground: var(--color-primary-element-light);\n}\n\n.cn-data-matrix__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-style: italic;\n\tmargin: 16px 0;\n\ttext-align: center;\n}\n</style>\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnDataMatrix.vue3.js","sources":["../../../../src/components/CnDataMatrix/CnDataMatrix.vue?vue&type=template&id=21e92577&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-data-matrix\" data-testid=\"cn-data-matrix\">\n\t\t<header v-if=\"title || description\" class=\"cn-data-matrix__header\">\n\t\t\t<h3 v-if=\"title\" class=\"cn-data-matrix__title\">\n\t\t\t\t{{ title }}\n\t\t\t</h3>\n\t\t\t<p v-if=\"description\" class=\"cn-data-matrix__description\">\n\t\t\t\t{{ description }}\n\t\t\t</p>\n\t\t</header>\n\n\t\t<div class=\"cn-data-matrix__scroller\">\n\t\t\t<table class=\"cn-data-matrix__table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__row-header-corner\">\n\t\t\t\t\t\t\t{{ rowHeader }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__col-header\"\n\t\t\t\t\t\t\t:style=\"col.width ? { width: col.width } : null\">\n\t\t\t\t\t\t\t{{ col.label || col.key }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th v-if=\"showRowTotals\" class=\"cn-data-matrix__total-header\">\n\t\t\t\t\t\t\t{{ rowTotalsLabel }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr v-for=\"row in rows\"\n\t\t\t\t\t\t:key=\"row[rowIdKey]\"\n\t\t\t\t\t\tclass=\"cn-data-matrix__row\">\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__row-header\" :title=\"row[rowLabelKey]\">\n\t\t\t\t\t\t\t{{ row[rowLabelKey] }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<td v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell\"\n\t\t\t\t\t\t\t:class=\"cellClass(row, col)\"\n\t\t\t\t\t\t\t:data-row-id=\"row[rowIdKey]\"\n\t\t\t\t\t\t\t:data-col-key=\"col.key\">\n\t\t\t\t\t\t\t<input v-if=\"isEditing(row, col)\"\n\t\t\t\t\t\t\t\tref=\"activeInput\"\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell-input\"\n\t\t\t\t\t\t\t\t:value=\"cellValue(row, col)\"\n\t\t\t\t\t\t\t\t:disabled=\"readOnly || isCellReadOnly(row, col)\"\n\t\t\t\t\t\t\t\t@blur=\"commitEdit(row, col, $event.target.value)\"\n\t\t\t\t\t\t\t\t@keydown.enter=\"commitEdit(row, col, $event.target.value)\"\n\t\t\t\t\t\t\t\t@keydown.esc=\"cancelEdit\">\n\t\t\t\t\t\t\t<span v-else\n\t\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell-value\"\n\t\t\t\t\t\t\t\t@click=\"startEdit(row, col)\">\n\t\t\t\t\t\t\t\t{{ formatCell(cellValue(row, col), col) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td v-if=\"showRowTotals\" class=\"cn-data-matrix__total-cell\">\n\t\t\t\t\t\t\t{{ formatCell(rowTotal(row), { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr v-if=\"showColumnTotals\" class=\"cn-data-matrix__totals-row\">\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__totals-label\">\n\t\t\t\t\t\t\t{{ columnTotalsLabel }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<td v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__total-cell\">\n\t\t\t\t\t\t\t{{ formatCell(columnTotal(col), { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td v-if=\"showRowTotals\" class=\"cn-data-matrix__total-cell cn-data-matrix__grand-total\">\n\t\t\t\t\t\t\t{{ formatCell(grandTotal, { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\n\t\t<p v-if=\"rows.length === 0\" class=\"cn-data-matrix__empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\n/**\n * CnDataMatrix — Inline-edit data grid for matrix-shaped data\n * (rows × columns). Each cell is click-to-edit; commits emit\n * `@cell-edit({ rowId, colKey, value })` so the parent can persist.\n * Optional row / column / grand totals computed from the live data\n * with the column's `aggregate` setting (`sum` by default).\n *\n * Use for gradebooks, weight matrices, allocation tables, scoring\n * rubrics — the \"spreadsheet\" shape where cells are independently\n * editable values rather than a row/object form.\n *\n * Not a full spreadsheet engine — there's no formula language, no\n * cell-range references, no charts. Consumers wanting that should\n * embed a dedicated library (e.g. handsontable) and use this for\n * the simpler cases.\n *\n * ```vue\n * <CnDataMatrix\n * title=\"Cohort gradebook\"\n * row-header=\"Student\"\n * :rows=\"students\"\n * :columns=\"[\n * { key: 'midterm', label: 'Midterm', type: 'number', aggregate: 'sum' },\n * { key: 'final', label: 'Final', type: 'number', aggregate: 'sum' },\n * { key: 'notes', label: 'Notes', type: 'string', readOnly: true },\n * ]\"\n * :show-row-totals=\"true\"\n * :show-column-totals=\"true\"\n * @cell-edit=\"onCellEdit\" />\n * ```\n */\nexport default {\n\tname: 'CnDataMatrix',\n\tprops: {\n\t\t/**\n\t\t * Row records. Each record holds an `id`, an optional row\n\t\t * label, and one field per column key.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\trows: { type: Array, default: () => [] },\n\t\t/**\n\t\t * Column declarations.\n\t\t *\n\t\t * @type {Array<{key:string,label?:string,type?:'number'|'string',readOnly?:boolean,formatter?:(value: unknown) => string,aggregate?:'sum'|'avg'|'count'|'none',width?:string}>}\n\t\t */\n\t\tcolumns: { type: Array, default: () => [] },\n\t\t/** Field on each row carrying its id. */\n\t\trowIdKey: { type: String, default: 'id' },\n\t\t/** Field on each row carrying its label (shown in the row header). */\n\t\trowLabelKey: { type: String, default: 'label' },\n\t\t/** Header for the row-label column (empty hides it). */\n\t\trowHeader: { type: String, default: '' },\n\t\t/** Optional title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Optional description. */\n\t\tdescription: { type: String, default: '' },\n\t\t/** Empty-state text when rows[] is empty. */\n\t\temptyLabel: { type: String, default: 'No data.' },\n\t\t/** Disable all cell editing globally. */\n\t\treadOnly: { type: Boolean, default: false },\n\t\t/** Render the per-row totals column. */\n\t\tshowRowTotals: { type: Boolean, default: false },\n\t\t/** Render the per-column totals row at the bottom. */\n\t\tshowColumnTotals: { type: Boolean, default: false },\n\t\t/** Label for the row-totals column header. */\n\t\trowTotalsLabel: { type: String, default: 'Total' },\n\t\t/** Label preceding the column-totals row. */\n\t\tcolumnTotalsLabel: { type: String, default: 'Total' },\n\t},\n\n\temits: ['cell-edit'],\n\tdata() {\n\t\treturn {\n\t\t\tediting: null, // { rowId, colKey }\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Grand total (sum of every numeric cell). Shown in the\n\t\t * bottom-right corner when both totals are enabled.\n\t\t *\n\t\t * @return {number}\n\t\t */\n\t\tgrandTotal() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tfor (const col of this.columns) {\n\t\t\t\t\tif (col.type === 'number') {\n\t\t\t\t\t\tconst v = Number(row[col.key])\n\t\t\t\t\t\tif (Number.isFinite(v)) {\n\t\t\t\t\t\t\ttotal += v\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether the given (row, col) is the active edit cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {boolean}\n\t\t */\n\t\tisEditing(row, col) {\n\t\t\treturn this.editing && this.editing.rowId === row[this.rowIdKey] && this.editing.colKey === col.key\n\t\t},\n\n\t\t/**\n\t\t * Effective read-only flag for a single cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {boolean}\n\t\t */\n\t\tisCellReadOnly(row, col) {\n\t\t\treturn Boolean(col.readOnly)\n\t\t},\n\n\t\t/**\n\t\t * Read the cell value from the row.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {unknown} The raw value.\n\t\t */\n\t\tcellValue(row, col) {\n\t\t\treturn row[col.key]\n\t\t},\n\n\t\t/**\n\t\t * Format a cell value for display. Number columns get a\n\t\t * locale-formatted number; others go through the column's\n\t\t * `formatter` (if any) or are returned verbatim.\n\t\t *\n\t\t * @param {unknown} value The raw value.\n\t\t * @param {object} col Column definition.\n\t\t * @return {string} The display string.\n\t\t */\n\t\tformatCell(value, col) {\n\t\t\tif (value === undefined || value === null || value === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (typeof col.formatter === 'function') {\n\t\t\t\treturn col.formatter(value)\n\t\t\t}\n\t\t\tif (col.type === 'number') {\n\t\t\t\tconst n = Number(value)\n\t\t\t\tif (!Number.isFinite(n)) {\n\t\t\t\t\treturn value\n\t\t\t\t}\n\t\t\t\treturn n.toLocaleString()\n\t\t\t}\n\t\t\treturn value\n\t\t},\n\n\t\t/**\n\t\t * BEM modifier(s) for a cell — currently only the read-only\n\t\t * flag. Consumers extend via the `formatter` if they want\n\t\t * to swap to a styled component.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {object} Class binding.\n\t\t */\n\t\tcellClass(row, col) {\n\t\t\treturn {\n\t\t\t\t'cn-data-matrix__cell--read-only': this.readOnly || this.isCellReadOnly(row, col),\n\t\t\t\t'cn-data-matrix__cell--number': col.type === 'number',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Start editing a cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {void}\n\t\t */\n\t\tstartEdit(row, col) {\n\t\t\tif (this.readOnly || this.isCellReadOnly(row, col)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.editing = { rowId: row[this.rowIdKey], colKey: col.key }\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.$refs.activeInput && this.$refs.activeInput[0]) {\n\t\t\t\t\tthis.$refs.activeInput[0].focus()\n\t\t\t\t\tthis.$refs.activeInput[0].select()\n\t\t\t\t} else if (this.$refs.activeInput && this.$refs.activeInput.focus) {\n\t\t\t\t\tthis.$refs.activeInput.focus()\n\t\t\t\t\tthis.$refs.activeInput.select()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Commit an edit — coerces the value to the column type and\n\t\t * emits `@cell-edit`.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @param {string} raw The raw input string.\n\t\t * @return {void}\n\t\t */\n\t\tcommitEdit(row, col, raw) {\n\t\t\tif (!this.editing) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet value = raw\n\t\t\tif (col.type === 'number') {\n\t\t\t\tconst n = Number(raw)\n\t\t\t\tvalue = Number.isFinite(n) ? n : null\n\t\t\t}\n\t\t\tthis.editing = null\n\t\t\t/**\n\t\t\t * @event cell-edit Emitted on commit (blur or Enter).\n\t\t\t * Payload: `{rowId, colKey, value, row}`.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('cell-edit', { rowId: row[this.rowIdKey], colKey: col.key, value, row })\n\t\t},\n\n\t\t/**\n\t\t * Cancel the active edit (Esc).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcancelEdit() {\n\t\t\tthis.editing = null\n\t\t},\n\n\t\t/**\n\t\t * Per-row total — sum of every numeric column.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @return {number}\n\t\t */\n\t\trowTotal(row) {\n\t\t\tlet total = 0\n\t\t\tfor (const col of this.columns) {\n\t\t\t\tif (col.type === 'number') {\n\t\t\t\t\tconst v = Number(row[col.key])\n\t\t\t\t\tif (Number.isFinite(v)) {\n\t\t\t\t\t\ttotal += v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\t/**\n\t\t * Per-column total — aggregate over all rows using the\n\t\t * column's `aggregate` setting (`sum` by default).\n\t\t *\n\t\t * @param {object} col Column definition.\n\t\t * @return {number}\n\t\t */\n\t\tcolumnTotal(col) {\n\t\t\tif (col.type !== 'number') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst mode = col.aggregate || 'sum'\n\t\t\tif (mode === 'none') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\t// Drop empty cells (null / undefined / '') BEFORE coercing —\n\t\t\t// `Number(null)` is `0`, which would otherwise be counted and\n\t\t\t// summed as a real zero value.\n\t\t\tconst values = this.rows\n\t\t\t\t.map((r) => r[col.key])\n\t\t\t\t.filter((v) => v !== null && v !== undefined && v !== '')\n\t\t\t\t.map((v) => Number(v))\n\t\t\t\t.filter((v) => Number.isFinite(v))\n\t\t\tif (values.length === 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tif (mode === 'count') {\n\t\t\t\treturn values.length\n\t\t\t}\n\t\t\tif (mode === 'avg') {\n\t\t\t\treturn values.reduce((a, b) => a + b, 0) / values.length\n\t\t\t}\n\t\t\t// default: sum\n\t\t\treturn values.reduce((a, b) => a + b, 0)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-data-matrix {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n}\n\n.cn-data-matrix__title {\n\tmargin: 0;\n\tfont-size: 1.1em;\n}\n\n.cn-data-matrix__description {\n\tmargin: 4px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-data-matrix__scroller {\n\toverflow-x: auto;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-data-matrix__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\tfont-size: 0.95em;\n}\n\n.cn-data-matrix__col-header,\n.cn-data-matrix__row-header-corner,\n.cn-data-matrix__total-header {\n\tbackground: var(--color-background-hover);\n\ttext-align: left;\n\tpadding: 8px 10px;\n\tfont-weight: 600;\n\tborder-bottom: 1px solid var(--color-border);\n\tborder-right: 1px solid var(--color-border);\n}\n\n.cn-data-matrix__row-header {\n\tbackground: var(--color-background-hover);\n\ttext-align: left;\n\tpadding: 6px 10px;\n\tfont-weight: 500;\n\tborder-right: 1px solid var(--color-border);\n\tborder-bottom: 1px solid var(--color-border);\n\tmax-width: 200px;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-data-matrix__cell {\n\tpadding: 0;\n\tborder-right: 1px solid var(--color-border);\n\tborder-bottom: 1px solid var(--color-border);\n\tmin-width: 80px;\n}\n\n.cn-data-matrix__cell--number {\n\ttext-align: right;\n}\n\n.cn-data-matrix__cell-value {\n\tdisplay: block;\n\tpadding: 6px 10px;\n\tcursor: cell;\n\tmin-height: 28px;\n}\n\n.cn-data-matrix__cell--read-only .cn-data-matrix__cell-value {\n\tcursor: default;\n\tbackground: var(--color-background-hover);\n}\n\n.cn-data-matrix__cell-input {\n\twidth: 100%;\n\tpadding: 5px 10px;\n\tborder: 1px solid var(--color-primary-element);\n\tbackground: var(--color-main-background);\n\toutline: none;\n\tfont: inherit;\n}\n\n.cn-data-matrix__totals-row .cn-data-matrix__total-cell,\n.cn-data-matrix__total-header {\n\tbackground: var(--color-background-darker, var(--color-background-hover));\n\tfont-weight: 600;\n\ttext-align: right;\n\tpadding: 6px 10px;\n}\n\n.cn-data-matrix__totals-label {\n\tbackground: var(--color-background-darker, var(--color-background-hover));\n\tfont-weight: 600;\n\tpadding: 6px 10px;\n\ttext-align: left;\n}\n\n.cn-data-matrix__grand-total {\n\tbackground: var(--color-primary-element-light);\n}\n\n.cn-data-matrix__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-style: italic;\n\tmargin: 16px 0;\n\ttext-align: center;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_createElementVNode","_Fragment","_renderList"],"mappings":"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"}
1
+ {"version":3,"file":"CnDataMatrix.vue3.js","sources":["../../../../src/components/CnDataMatrix/CnDataMatrix.vue?vue&type=template&id=b800caca&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-data-matrix\" data-testid=\"cn-data-matrix\">\n\t\t<header v-if=\"title || description\" class=\"cn-data-matrix__header\">\n\t\t\t<h3 v-if=\"title\" class=\"cn-data-matrix__title\">\n\t\t\t\t{{ title }}\n\t\t\t</h3>\n\t\t\t<p v-if=\"description\" class=\"cn-data-matrix__description\">\n\t\t\t\t{{ description }}\n\t\t\t</p>\n\t\t</header>\n\n\t\t<div class=\"cn-data-matrix__scroller\">\n\t\t\t<table class=\"cn-data-matrix__table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__row-header-corner\">\n\t\t\t\t\t\t\t{{ rowHeader }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__col-header\"\n\t\t\t\t\t\t\t:style=\"col.width ? { width: col.width } : null\">\n\t\t\t\t\t\t\t{{ col.label || col.key }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th v-if=\"showRowTotals\" class=\"cn-data-matrix__total-header\">\n\t\t\t\t\t\t\t{{ rowTotalsLabel }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr v-for=\"row in rows\"\n\t\t\t\t\t\t:key=\"row[rowIdKey]\"\n\t\t\t\t\t\tclass=\"cn-data-matrix__row\">\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__row-header\" :title=\"row[rowLabelKey]\">\n\t\t\t\t\t\t\t{{ row[rowLabelKey] }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<td v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell\"\n\t\t\t\t\t\t\t:class=\"cellClass(row, col)\"\n\t\t\t\t\t\t\t:data-row-id=\"row[rowIdKey]\"\n\t\t\t\t\t\t\t:data-col-key=\"col.key\">\n\t\t\t\t\t\t\t<input v-if=\"isEditing(row, col)\"\n\t\t\t\t\t\t\t\tref=\"activeInput\"\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell-input\"\n\t\t\t\t\t\t\t\t:value=\"cellValue(row, col)\"\n\t\t\t\t\t\t\t\t:disabled=\"readOnly || isCellReadOnly(row, col)\"\n\t\t\t\t\t\t\t\t@blur=\"commitEdit(row, col, $event.target.value)\"\n\t\t\t\t\t\t\t\t@keydown.enter=\"commitEdit(row, col, $event.target.value)\"\n\t\t\t\t\t\t\t\t@keydown.esc=\"cancelEdit\">\n\t\t\t\t\t\t\t<span v-else\n\t\t\t\t\t\t\t\tclass=\"cn-data-matrix__cell-value\"\n\t\t\t\t\t\t\t\t@click=\"startEdit(row, col)\">\n\t\t\t\t\t\t\t\t{{ formatCell(cellValue(row, col), col) }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td v-if=\"showRowTotals\" class=\"cn-data-matrix__total-cell\">\n\t\t\t\t\t\t\t{{ formatCell(rowTotal(row), { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr v-if=\"showColumnTotals\" class=\"cn-data-matrix__totals-row\">\n\t\t\t\t\t\t<th v-if=\"rowHeader\" class=\"cn-data-matrix__totals-label\">\n\t\t\t\t\t\t\t{{ columnTotalsLabel }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<td v-for=\"col in columns\"\n\t\t\t\t\t\t\t:key=\"col.key\"\n\t\t\t\t\t\t\tclass=\"cn-data-matrix__total-cell\">\n\t\t\t\t\t\t\t{{ formatCell(columnTotal(col), { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td v-if=\"showRowTotals\" class=\"cn-data-matrix__total-cell cn-data-matrix__grand-total\">\n\t\t\t\t\t\t\t{{ formatCell(grandTotal, { type: 'number' }) }}\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\n\t\t<p v-if=\"rows.length === 0\" class=\"cn-data-matrix__empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport { userLocale } from '../../utils/userLocale.js'\n/**\n * CnDataMatrix — Inline-edit data grid for matrix-shaped data\n * (rows × columns). Each cell is click-to-edit; commits emit\n * `@cell-edit({ rowId, colKey, value })` so the parent can persist.\n * Optional row / column / grand totals computed from the live data\n * with the column's `aggregate` setting (`sum` by default).\n *\n * Use for gradebooks, weight matrices, allocation tables, scoring\n * rubrics — the \"spreadsheet\" shape where cells are independently\n * editable values rather than a row/object form.\n *\n * Not a full spreadsheet engine — there's no formula language, no\n * cell-range references, no charts. Consumers wanting that should\n * embed a dedicated library (e.g. handsontable) and use this for\n * the simpler cases.\n *\n * ```vue\n * <CnDataMatrix\n * title=\"Cohort gradebook\"\n * row-header=\"Student\"\n * :rows=\"students\"\n * :columns=\"[\n * { key: 'midterm', label: 'Midterm', type: 'number', aggregate: 'sum' },\n * { key: 'final', label: 'Final', type: 'number', aggregate: 'sum' },\n * { key: 'notes', label: 'Notes', type: 'string', readOnly: true },\n * ]\"\n * :show-row-totals=\"true\"\n * :show-column-totals=\"true\"\n * @cell-edit=\"onCellEdit\" />\n * ```\n */\nexport default {\n\tname: 'CnDataMatrix',\n\tprops: {\n\t\t/**\n\t\t * Row records. Each record holds an `id`, an optional row\n\t\t * label, and one field per column key.\n\t\t *\n\t\t * @type {Array<object>}\n\t\t */\n\t\trows: { type: Array, default: () => [] },\n\t\t/**\n\t\t * Column declarations.\n\t\t *\n\t\t * @type {Array<{key:string,label?:string,type?:'number'|'string',readOnly?:boolean,formatter?:(value: unknown) => string,aggregate?:'sum'|'avg'|'count'|'none',width?:string}>}\n\t\t */\n\t\tcolumns: { type: Array, default: () => [] },\n\t\t/** Field on each row carrying its id. */\n\t\trowIdKey: { type: String, default: 'id' },\n\t\t/** Field on each row carrying its label (shown in the row header). */\n\t\trowLabelKey: { type: String, default: 'label' },\n\t\t/** Header for the row-label column (empty hides it). */\n\t\trowHeader: { type: String, default: '' },\n\t\t/** Optional title. */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Optional description. */\n\t\tdescription: { type: String, default: '' },\n\t\t/** Empty-state text when rows[] is empty. */\n\t\temptyLabel: { type: String, default: 'No data.' },\n\t\t/** Disable all cell editing globally. */\n\t\treadOnly: { type: Boolean, default: false },\n\t\t/** Render the per-row totals column. */\n\t\tshowRowTotals: { type: Boolean, default: false },\n\t\t/** Render the per-column totals row at the bottom. */\n\t\tshowColumnTotals: { type: Boolean, default: false },\n\t\t/** Label for the row-totals column header. */\n\t\trowTotalsLabel: { type: String, default: 'Total' },\n\t\t/** Label preceding the column-totals row. */\n\t\tcolumnTotalsLabel: { type: String, default: 'Total' },\n\t},\n\n\temits: ['cell-edit'],\n\tdata() {\n\t\treturn {\n\t\t\tediting: null, // { rowId, colKey }\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Grand total (sum of every numeric cell). Shown in the\n\t\t * bottom-right corner when both totals are enabled.\n\t\t *\n\t\t * @return {number}\n\t\t */\n\t\tgrandTotal() {\n\t\t\tlet total = 0\n\t\t\tfor (const row of this.rows) {\n\t\t\t\tfor (const col of this.columns) {\n\t\t\t\t\tif (col.type === 'number') {\n\t\t\t\t\t\tconst v = Number(row[col.key])\n\t\t\t\t\t\tif (Number.isFinite(v)) {\n\t\t\t\t\t\t\ttotal += v\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Whether the given (row, col) is the active edit cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {boolean}\n\t\t */\n\t\tisEditing(row, col) {\n\t\t\treturn this.editing && this.editing.rowId === row[this.rowIdKey] && this.editing.colKey === col.key\n\t\t},\n\n\t\t/**\n\t\t * Effective read-only flag for a single cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {boolean}\n\t\t */\n\t\tisCellReadOnly(row, col) {\n\t\t\treturn Boolean(col.readOnly)\n\t\t},\n\n\t\t/**\n\t\t * Read the cell value from the row.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {unknown} The raw value.\n\t\t */\n\t\tcellValue(row, col) {\n\t\t\treturn row[col.key]\n\t\t},\n\n\t\t/**\n\t\t * Format a cell value for display. Number columns get a\n\t\t * locale-formatted number; others go through the column's\n\t\t * `formatter` (if any) or are returned verbatim.\n\t\t *\n\t\t * @param {unknown} value The raw value.\n\t\t * @param {object} col Column definition.\n\t\t * @return {string} The display string.\n\t\t */\n\t\tformatCell(value, col) {\n\t\t\tif (value === undefined || value === null || value === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (typeof col.formatter === 'function') {\n\t\t\t\treturn col.formatter(value)\n\t\t\t}\n\t\t\tif (col.type === 'number') {\n\t\t\t\tconst n = Number(value)\n\t\t\t\tif (!Number.isFinite(n)) {\n\t\t\t\t\treturn value\n\t\t\t\t}\n\t\t\t\treturn n.toLocaleString(userLocale())\n\t\t\t}\n\t\t\treturn value\n\t\t},\n\n\t\t/**\n\t\t * BEM modifier(s) for a cell — currently only the read-only\n\t\t * flag. Consumers extend via the `formatter` if they want\n\t\t * to swap to a styled component.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {object} Class binding.\n\t\t */\n\t\tcellClass(row, col) {\n\t\t\treturn {\n\t\t\t\t'cn-data-matrix__cell--read-only': this.readOnly || this.isCellReadOnly(row, col),\n\t\t\t\t'cn-data-matrix__cell--number': col.type === 'number',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Start editing a cell.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @return {void}\n\t\t */\n\t\tstartEdit(row, col) {\n\t\t\tif (this.readOnly || this.isCellReadOnly(row, col)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.editing = { rowId: row[this.rowIdKey], colKey: col.key }\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.$refs.activeInput && this.$refs.activeInput[0]) {\n\t\t\t\t\tthis.$refs.activeInput[0].focus()\n\t\t\t\t\tthis.$refs.activeInput[0].select()\n\t\t\t\t} else if (this.$refs.activeInput && this.$refs.activeInput.focus) {\n\t\t\t\t\tthis.$refs.activeInput.focus()\n\t\t\t\t\tthis.$refs.activeInput.select()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Commit an edit — coerces the value to the column type and\n\t\t * emits `@cell-edit`.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @param {object} col Column definition.\n\t\t * @param {string} raw The raw input string.\n\t\t * @return {void}\n\t\t */\n\t\tcommitEdit(row, col, raw) {\n\t\t\tif (!this.editing) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet value = raw\n\t\t\tif (col.type === 'number') {\n\t\t\t\tconst n = Number(raw)\n\t\t\t\tvalue = Number.isFinite(n) ? n : null\n\t\t\t}\n\t\t\tthis.editing = null\n\t\t\t/**\n\t\t\t * @event cell-edit Emitted on commit (blur or Enter).\n\t\t\t * Payload: `{rowId, colKey, value, row}`.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('cell-edit', { rowId: row[this.rowIdKey], colKey: col.key, value, row })\n\t\t},\n\n\t\t/**\n\t\t * Cancel the active edit (Esc).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tcancelEdit() {\n\t\t\tthis.editing = null\n\t\t},\n\n\t\t/**\n\t\t * Per-row total — sum of every numeric column.\n\t\t *\n\t\t * @param {object} row Row record.\n\t\t * @return {number}\n\t\t */\n\t\trowTotal(row) {\n\t\t\tlet total = 0\n\t\t\tfor (const col of this.columns) {\n\t\t\t\tif (col.type === 'number') {\n\t\t\t\t\tconst v = Number(row[col.key])\n\t\t\t\t\tif (Number.isFinite(v)) {\n\t\t\t\t\t\ttotal += v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn total\n\t\t},\n\n\t\t/**\n\t\t * Per-column total — aggregate over all rows using the\n\t\t * column's `aggregate` setting (`sum` by default).\n\t\t *\n\t\t * @param {object} col Column definition.\n\t\t * @return {number}\n\t\t */\n\t\tcolumnTotal(col) {\n\t\t\tif (col.type !== 'number') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst mode = col.aggregate || 'sum'\n\t\t\tif (mode === 'none') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\t// Drop empty cells (null / undefined / '') BEFORE coercing —\n\t\t\t// `Number(null)` is `0`, which would otherwise be counted and\n\t\t\t// summed as a real zero value.\n\t\t\tconst values = this.rows\n\t\t\t\t.map((r) => r[col.key])\n\t\t\t\t.filter((v) => v !== null && v !== undefined && v !== '')\n\t\t\t\t.map((v) => Number(v))\n\t\t\t\t.filter((v) => Number.isFinite(v))\n\t\t\tif (values.length === 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tif (mode === 'count') {\n\t\t\t\treturn values.length\n\t\t\t}\n\t\t\tif (mode === 'avg') {\n\t\t\t\treturn values.reduce((a, b) => a + b, 0) / values.length\n\t\t\t}\n\t\t\t// default: sum\n\t\t\treturn values.reduce((a, b) => a + b, 0)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-data-matrix {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n}\n\n.cn-data-matrix__title {\n\tmargin: 0;\n\tfont-size: 1.1em;\n}\n\n.cn-data-matrix__description {\n\tmargin: 4px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-data-matrix__scroller {\n\toverflow-x: auto;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-data-matrix__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n\tfont-size: 0.95em;\n}\n\n.cn-data-matrix__col-header,\n.cn-data-matrix__row-header-corner,\n.cn-data-matrix__total-header {\n\tbackground: var(--color-background-hover);\n\ttext-align: left;\n\tpadding: 8px 10px;\n\tfont-weight: 600;\n\tborder-bottom: 1px solid var(--color-border);\n\tborder-right: 1px solid var(--color-border);\n}\n\n.cn-data-matrix__row-header {\n\tbackground: var(--color-background-hover);\n\ttext-align: left;\n\tpadding: 6px 10px;\n\tfont-weight: 500;\n\tborder-right: 1px solid var(--color-border);\n\tborder-bottom: 1px solid var(--color-border);\n\tmax-width: 200px;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-data-matrix__cell {\n\tpadding: 0;\n\tborder-right: 1px solid var(--color-border);\n\tborder-bottom: 1px solid var(--color-border);\n\tmin-width: 80px;\n}\n\n.cn-data-matrix__cell--number {\n\ttext-align: right;\n}\n\n.cn-data-matrix__cell-value {\n\tdisplay: block;\n\tpadding: 6px 10px;\n\tcursor: cell;\n\tmin-height: 28px;\n}\n\n.cn-data-matrix__cell--read-only .cn-data-matrix__cell-value {\n\tcursor: default;\n\tbackground: var(--color-background-hover);\n}\n\n.cn-data-matrix__cell-input {\n\twidth: 100%;\n\tpadding: 5px 10px;\n\tborder: 1px solid var(--color-primary-element);\n\tbackground: var(--color-main-background);\n\toutline: none;\n\tfont: inherit;\n}\n\n.cn-data-matrix__totals-row .cn-data-matrix__total-cell,\n.cn-data-matrix__total-header {\n\tbackground: var(--color-background-darker, var(--color-background-hover));\n\tfont-weight: 600;\n\ttext-align: right;\n\tpadding: 6px 10px;\n}\n\n.cn-data-matrix__totals-label {\n\tbackground: var(--color-background-darker, var(--color-background-hover));\n\tfont-weight: 600;\n\tpadding: 6px 10px;\n\ttext-align: left;\n}\n\n.cn-data-matrix__grand-total {\n\tbackground: var(--color-primary-element-light);\n}\n\n.cn-data-matrix__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-style: italic;\n\tmargin: 16px 0;\n\ttext-align: center;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_createElementVNode","_Fragment","_renderList"],"mappings":"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"}
@@ -5,6 +5,7 @@ import { useObjectStore } from '../../store/useObjectStore.js';
5
5
  import { resolveObjectOpType } from '../../utils/actionsDispatcher.js';
6
6
  import { normalizeHeaderFields, isEmptyChipValue, pickRefLabel, variantForColor, isPastDate } from '../../utils/headerFieldChips.js';
7
7
  import { schemaRefSlug } from '../../utils/schemaRefSlug.js';
8
+ import { userLocale } from '../../utils/userLocale.js';
8
9
 
9
10
  const DAY_MS = 86400000;
10
11
 
@@ -186,7 +187,7 @@ var script = {
186
187
  try {
187
188
  return new Intl.RelativeTimeFormat(this.locale(), { numeric: 'auto' }).format(days, 'day')
188
189
  } catch {
189
- return new Date(time).toLocaleDateString()
190
+ return new Date(time).toLocaleDateString(userLocale())
190
191
  }
191
192
  },
192
193
 
@@ -1 +1 @@
1
- {"version":3,"file":"CnDetailHeaderChips.vue2.js","sources":["../../../../src/components/CnDetailPage/CnDetailHeaderChips.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<!--\n CnDetailHeaderChips — the row of field chips under a detail page's title\n (manifest `config.headerFields`). Internal to CnDetailPage.\n\n An empty value renders no chip, and a row with no chips renders nothing. A\n reference shows the referenced object's label and falls back to the raw id\n in mono when it does not resolve. Each chip reads \"Title: value\" to a screen\n reader, and the row prints as plain text.\n\n @spec openspec/changes/detail-header-field-chips/specs/detail-page-header/spec.md\n-->\n\n<template>\n\t<ul\n\t\tv-if=\"chips.length > 0\"\n\t\tclass=\"cn-detail-header-chips\"\n\t\tdata-testid=\"cn-detail-header-chips\">\n\t\t<li\n\t\t\tv-for=\"chip in chips\"\n\t\t\t:key=\"chip.key\"\n\t\t\tclass=\"cn-detail-header-chips__chip\"\n\t\t\t:class=\"[`cn-detail-header-chips__chip--${chip.format}`, `cn-detail-header-chips__chip--${chip.variant}`]\"\n\t\t\t:data-testid=\"`cn-detail-header-chip-${chip.key}`\">\n\t\t\t<span class=\"hidden-visually\">{{ chip.title }}: </span>\n\t\t\t<CnStatusBadge\n\t\t\t\tv-if=\"chip.format === 'badge'\"\n\t\t\t\t:label=\"chip.text\"\n\t\t\t\t:variant=\"chip.variant\"\n\t\t\t\tsize=\"small\" />\n\t\t\t<NcUserBubble\n\t\t\t\tv-else-if=\"chip.format === 'user' && chip.userId\"\n\t\t\t\t:user=\"chip.userId\"\n\t\t\t\t:displayName=\"chip.text\"\n\t\t\t\t:showUserStatus=\"false\" />\n\t\t\t<time\n\t\t\t\tv-else-if=\"chip.format === 'date'\"\n\t\t\t\tclass=\"cn-detail-header-chips__value\"\n\t\t\t\t:datetime=\"chip.iso\"\n\t\t\t\t:title=\"chip.fullDate\">{{ chip.text }}</time>\n\t\t\t<span\n\t\t\t\tv-else\n\t\t\t\tclass=\"cn-detail-header-chips__value\"\n\t\t\t\t:class=\"{ 'cn-detail-header-chips__value--mono': chip.mono }\">{{ chip.text }}</span>\n\t\t</li>\n\t</ul>\n</template>\n\n<script>\nimport { getCanonicalLocale } from '@nextcloud/l10n'\nimport { NcUserBubble } from '@nextcloud/vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { useObjectStore } from '../../store/useObjectStore.js'\nimport { resolveObjectOpType } from '../../utils/actionsDispatcher.js'\nimport { isEmptyChipValue, isPastDate, normalizeHeaderFields, pickRefLabel, variantForColor } from '../../utils/headerFieldChips.js'\nimport { schemaRefSlug } from '../../utils/schemaRefSlug.js'\n\nconst DAY_MS = 86400000\n\n/**\n * CnDetailHeaderChips — the chips row of `CnDetailPage`'s header.\n */\nexport default {\n\tname: 'CnDetailHeaderChips',\n\n\tcomponents: { CnStatusBadge, NcUserBubble },\n\n\tprops: {\n\t\t/**\n\t\t * The declared header fields (strings or `{ key, format, labelField, colorField, warnWhenPast }`).\n\t\t *\n\t\t * @type {Array<string|object>}\n\t\t */\n\t\tfields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The object the chips read their values from.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The JSON Schema of the object, for property titles and `$ref` targets.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The OpenRegister register slug a reference resolves in. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\t// Resolved referenced objects, by \"<schema slug>:<id>\".\n\t\t\trefs: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The normalised entries. */\n\t\tentries() {\n\t\t\treturn normalizeHeaderFields(this.fields)\n\t\t},\n\n\t\t/** The chips that have a value to show, in declared order. */\n\t\tchips() {\n\t\t\tconst obj = this.object\n\t\t\tif (!obj) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst out = []\n\t\t\tfor (const entry of this.entries) {\n\t\t\t\tconst chip = this.buildChip(entry, obj)\n\t\t\t\tif (chip) {\n\t\t\t\t\tout.push(chip)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\t},\n\n\twatch: {\n\t\tobject: { handler: 'resolveRefs', immediate: true },\n\t\tschema: 'resolveRefs',\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The schema property definition of a field.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @return {object|null} The property or null.\n\t\t */\n\t\tpropertyOf(key) {\n\t\t\tconst props = this.schema && this.schema.properties\n\t\t\treturn (props && props[key]) || null\n\t\t},\n\n\t\t/**\n\t\t * The referenced schema slug of a property, or '' when it is not a reference.\n\t\t *\n\t\t * @param {object|null} prop The schema property.\n\t\t * @return {string} The slug.\n\t\t */\n\t\trefSlugOf(prop) {\n\t\t\tconst ref = prop && (prop.$ref || (prop.items && prop.items.$ref))\n\t\t\treturn ref ? String(schemaRefSlug(ref) || '') : ''\n\t\t},\n\n\t\t/**\n\t\t * Build one chip, or null when its value is empty.\n\t\t *\n\t\t * @param {object} entry The normalised entry.\n\t\t * @param {object} obj The object.\n\t\t * @return {object|null} The chip view model.\n\t\t */\n\t\tbuildChip(entry, obj) {\n\t\t\tconst raw = obj[entry.key]\n\t\t\tif (isEmptyChipValue(raw)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst prop = this.propertyOf(entry.key)\n\t\t\tconst slug = this.refSlugOf(prop)\n\t\t\tconst title = (prop && typeof prop.title === 'string' && prop.title) || entry.key\n\t\t\tconst chip = { key: entry.key, title, format: entry.format, variant: 'default', mono: entry.format === 'mono', text: '', userId: '' }\n\n\t\t\t// A reference: an expanded object reads straight off, an id comes from the resolved cache.\n\t\t\tconst isRefValue = (raw && typeof raw === 'object' && !Array.isArray(raw)) || slug !== ''\n\t\t\tif (isRefValue) {\n\t\t\t\tconst refObj = (raw && typeof raw === 'object') ? raw : this.refs[`${slug}:${raw}`]\n\t\t\t\tconst label = pickRefLabel(refObj, entry.labelField)\n\t\t\t\tconst idText = (raw && typeof raw === 'object') ? String(raw.id ?? raw.uuid ?? '') : String(raw)\n\t\t\t\tchip.text = label || idText\n\t\t\t\tchip.mono = label === ''\n\t\t\t\tchip.userId = (refObj && (refObj.uid || refObj.id)) ? String(refObj.uid || refObj.id) : idText\n\t\t\t\tif (entry.format === 'badge' && entry.colorField && refObj) {\n\t\t\t\t\tchip.variant = variantForColor(refObj[entry.colorField])\n\t\t\t\t}\n\t\t\t\treturn chip.text === '' ? null : chip\n\t\t\t}\n\n\t\t\tchip.text = Array.isArray(raw) ? raw.join(', ') : String(raw)\n\t\t\tchip.userId = String(raw)\n\t\t\tif (entry.format === 'date') {\n\t\t\t\tconst time = new Date(raw).getTime()\n\t\t\t\tif (Number.isNaN(time)) {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tchip.iso = new Date(time).toISOString()\n\t\t\t\tchip.fullDate = new Date(time).toLocaleString(this.locale())\n\t\t\t\tchip.text = this.relativeDate(time)\n\t\t\t\tif (entry.warnWhenPast && isPastDate(raw)) {\n\t\t\t\t\tchip.variant = 'error'\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn chip\n\t\t},\n\n\t\t/**\n\t\t * The locale for date formatting.\n\t\t *\n\t\t * @return {string} A BCP 47 locale.\n\t\t */\n\t\tlocale() {\n\t\t\ttry {\n\t\t\t\treturn getCanonicalLocale()\n\t\t\t} catch {\n\t\t\t\treturn 'en'\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A date as relative text (\"in 3 days\", \"yesterday\").\n\t\t *\n\t\t * @param {number} time The time in ms.\n\t\t * @return {string} The relative text.\n\t\t */\n\t\trelativeDate(time) {\n\t\t\tconst startOfDay = (ms) => new Date(ms).setHours(0, 0, 0, 0)\n\t\t\tconst days = Math.round((startOfDay(time) - startOfDay(Date.now())) / DAY_MS)\n\t\t\ttry {\n\t\t\t\treturn new Intl.RelativeTimeFormat(this.locale(), { numeric: 'auto' }).format(days, 'day')\n\t\t\t} catch {\n\t\t\t\treturn new Date(time).toLocaleDateString()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the referenced objects behind id-valued reference fields. A\n\t\t * failed lookup leaves the id showing in mono.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync resolveRefs() {\n\t\t\tif (!this.object || !this.register) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet store = null\n\t\t\ttry {\n\t\t\t\tstore = useObjectStore()\n\t\t\t} catch {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!store) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tawait Promise.all(this.entries.map(async (entry) => {\n\t\t\t\tconst raw = this.object[entry.key]\n\t\t\t\tconst slug = this.refSlugOf(this.propertyOf(entry.key))\n\t\t\t\tif (!slug || isEmptyChipValue(raw) || typeof raw === 'object') {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst cacheKey = `${slug}:${raw}`\n\t\t\t\tif (this.refs[cacheKey]) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst type = resolveObjectOpType(store, { register: this.register, schema: slug }, { exactSchema: true })\n\t\t\t\t\tconst cached = store.objects && store.objects[type] && store.objects[type][raw]\n\t\t\t\t\tconst found = cached || await store.fetchObject(type, String(raw))\n\t\t\t\t\tif (found) {\n\t\t\t\t\t\tthis.refs = { ...this.refs, [cacheKey]: found }\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// The id stays visible in mono.\n\t\t\t\t}\n\t\t\t}))\n\t\t},\n\t},\n}\n</script>\n\n<style>\n.cn-detail-header-chips {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 6px;\n\tmargin: 6px 0 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-detail-header-chips__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tmax-width: 100%;\n\tpadding: 2px 10px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-pill, 16px);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-main-text);\n\tfont-size: 0.9em;\n}\n\n.cn-detail-header-chips__chip--error {\n\tborder-color: var(--color-error);\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-detail-header-chips__chip--badge {\n\tpadding: 0;\n\tborder: none;\n\tbackground: transparent;\n}\n\n.cn-detail-header-chips__value--mono {\n\tfont-family: var(--font-family-monospace, ui-monospace, monospace);\n}\n\n/* Printed, the row is plain text: no pill, border or background. */\n@media print {\n\t.cn-detail-header-chips__chip {\n\t\tpadding: 0;\n\t\tborder: none;\n\t\tbackground: transparent;\n\t\tcolor: inherit;\n\t}\n\n\t.cn-detail-header-chips__chip:not(:last-child)::after {\n\t\tcontent: ' ·';\n\t}\n}\n</style>\n"],"names":["CnStatusBadge"],"mappings":"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"}
1
+ {"version":3,"file":"CnDetailHeaderChips.vue2.js","sources":["../../../../src/components/CnDetailPage/CnDetailHeaderChips.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<!--\n CnDetailHeaderChips — the row of field chips under a detail page's title\n (manifest `config.headerFields`). Internal to CnDetailPage.\n\n An empty value renders no chip, and a row with no chips renders nothing. A\n reference shows the referenced object's label and falls back to the raw id\n in mono when it does not resolve. Each chip reads \"Title: value\" to a screen\n reader, and the row prints as plain text.\n\n @spec openspec/changes/detail-header-field-chips/specs/detail-page-header/spec.md\n-->\n\n<template>\n\t<ul\n\t\tv-if=\"chips.length > 0\"\n\t\tclass=\"cn-detail-header-chips\"\n\t\tdata-testid=\"cn-detail-header-chips\">\n\t\t<li\n\t\t\tv-for=\"chip in chips\"\n\t\t\t:key=\"chip.key\"\n\t\t\tclass=\"cn-detail-header-chips__chip\"\n\t\t\t:class=\"[`cn-detail-header-chips__chip--${chip.format}`, `cn-detail-header-chips__chip--${chip.variant}`]\"\n\t\t\t:data-testid=\"`cn-detail-header-chip-${chip.key}`\">\n\t\t\t<span class=\"hidden-visually\">{{ chip.title }}: </span>\n\t\t\t<CnStatusBadge\n\t\t\t\tv-if=\"chip.format === 'badge'\"\n\t\t\t\t:label=\"chip.text\"\n\t\t\t\t:variant=\"chip.variant\"\n\t\t\t\tsize=\"small\" />\n\t\t\t<NcUserBubble\n\t\t\t\tv-else-if=\"chip.format === 'user' && chip.userId\"\n\t\t\t\t:user=\"chip.userId\"\n\t\t\t\t:displayName=\"chip.text\"\n\t\t\t\t:showUserStatus=\"false\" />\n\t\t\t<time\n\t\t\t\tv-else-if=\"chip.format === 'date'\"\n\t\t\t\tclass=\"cn-detail-header-chips__value\"\n\t\t\t\t:datetime=\"chip.iso\"\n\t\t\t\t:title=\"chip.fullDate\">{{ chip.text }}</time>\n\t\t\t<span\n\t\t\t\tv-else\n\t\t\t\tclass=\"cn-detail-header-chips__value\"\n\t\t\t\t:class=\"{ 'cn-detail-header-chips__value--mono': chip.mono }\">{{ chip.text }}</span>\n\t\t</li>\n\t</ul>\n</template>\n\n<script>\nimport { getCanonicalLocale } from '@nextcloud/l10n'\nimport { NcUserBubble } from '@nextcloud/vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { useObjectStore } from '../../store/useObjectStore.js'\nimport { resolveObjectOpType } from '../../utils/actionsDispatcher.js'\nimport { isEmptyChipValue, isPastDate, normalizeHeaderFields, pickRefLabel, variantForColor } from '../../utils/headerFieldChips.js'\nimport { schemaRefSlug } from '../../utils/schemaRefSlug.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\nconst DAY_MS = 86400000\n\n/**\n * CnDetailHeaderChips — the chips row of `CnDetailPage`'s header.\n */\nexport default {\n\tname: 'CnDetailHeaderChips',\n\n\tcomponents: { CnStatusBadge, NcUserBubble },\n\n\tprops: {\n\t\t/**\n\t\t * The declared header fields (strings or `{ key, format, labelField, colorField, warnWhenPast }`).\n\t\t *\n\t\t * @type {Array<string|object>}\n\t\t */\n\t\tfields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The object the chips read their values from.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The JSON Schema of the object, for property titles and `$ref` targets.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The OpenRegister register slug a reference resolves in. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\t// Resolved referenced objects, by \"<schema slug>:<id>\".\n\t\t\trefs: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The normalised entries. */\n\t\tentries() {\n\t\t\treturn normalizeHeaderFields(this.fields)\n\t\t},\n\n\t\t/** The chips that have a value to show, in declared order. */\n\t\tchips() {\n\t\t\tconst obj = this.object\n\t\t\tif (!obj) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst out = []\n\t\t\tfor (const entry of this.entries) {\n\t\t\t\tconst chip = this.buildChip(entry, obj)\n\t\t\t\tif (chip) {\n\t\t\t\t\tout.push(chip)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\t},\n\n\twatch: {\n\t\tobject: { handler: 'resolveRefs', immediate: true },\n\t\tschema: 'resolveRefs',\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The schema property definition of a field.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @return {object|null} The property or null.\n\t\t */\n\t\tpropertyOf(key) {\n\t\t\tconst props = this.schema && this.schema.properties\n\t\t\treturn (props && props[key]) || null\n\t\t},\n\n\t\t/**\n\t\t * The referenced schema slug of a property, or '' when it is not a reference.\n\t\t *\n\t\t * @param {object|null} prop The schema property.\n\t\t * @return {string} The slug.\n\t\t */\n\t\trefSlugOf(prop) {\n\t\t\tconst ref = prop && (prop.$ref || (prop.items && prop.items.$ref))\n\t\t\treturn ref ? String(schemaRefSlug(ref) || '') : ''\n\t\t},\n\n\t\t/**\n\t\t * Build one chip, or null when its value is empty.\n\t\t *\n\t\t * @param {object} entry The normalised entry.\n\t\t * @param {object} obj The object.\n\t\t * @return {object|null} The chip view model.\n\t\t */\n\t\tbuildChip(entry, obj) {\n\t\t\tconst raw = obj[entry.key]\n\t\t\tif (isEmptyChipValue(raw)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst prop = this.propertyOf(entry.key)\n\t\t\tconst slug = this.refSlugOf(prop)\n\t\t\tconst title = (prop && typeof prop.title === 'string' && prop.title) || entry.key\n\t\t\tconst chip = { key: entry.key, title, format: entry.format, variant: 'default', mono: entry.format === 'mono', text: '', userId: '' }\n\n\t\t\t// A reference: an expanded object reads straight off, an id comes from the resolved cache.\n\t\t\tconst isRefValue = (raw && typeof raw === 'object' && !Array.isArray(raw)) || slug !== ''\n\t\t\tif (isRefValue) {\n\t\t\t\tconst refObj = (raw && typeof raw === 'object') ? raw : this.refs[`${slug}:${raw}`]\n\t\t\t\tconst label = pickRefLabel(refObj, entry.labelField)\n\t\t\t\tconst idText = (raw && typeof raw === 'object') ? String(raw.id ?? raw.uuid ?? '') : String(raw)\n\t\t\t\tchip.text = label || idText\n\t\t\t\tchip.mono = label === ''\n\t\t\t\tchip.userId = (refObj && (refObj.uid || refObj.id)) ? String(refObj.uid || refObj.id) : idText\n\t\t\t\tif (entry.format === 'badge' && entry.colorField && refObj) {\n\t\t\t\t\tchip.variant = variantForColor(refObj[entry.colorField])\n\t\t\t\t}\n\t\t\t\treturn chip.text === '' ? null : chip\n\t\t\t}\n\n\t\t\tchip.text = Array.isArray(raw) ? raw.join(', ') : String(raw)\n\t\t\tchip.userId = String(raw)\n\t\t\tif (entry.format === 'date') {\n\t\t\t\tconst time = new Date(raw).getTime()\n\t\t\t\tif (Number.isNaN(time)) {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tchip.iso = new Date(time).toISOString()\n\t\t\t\tchip.fullDate = new Date(time).toLocaleString(this.locale())\n\t\t\t\tchip.text = this.relativeDate(time)\n\t\t\t\tif (entry.warnWhenPast && isPastDate(raw)) {\n\t\t\t\t\tchip.variant = 'error'\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn chip\n\t\t},\n\n\t\t/**\n\t\t * The locale for date formatting.\n\t\t *\n\t\t * @return {string} A BCP 47 locale.\n\t\t */\n\t\tlocale() {\n\t\t\ttry {\n\t\t\t\treturn getCanonicalLocale()\n\t\t\t} catch {\n\t\t\t\treturn 'en'\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A date as relative text (\"in 3 days\", \"yesterday\").\n\t\t *\n\t\t * @param {number} time The time in ms.\n\t\t * @return {string} The relative text.\n\t\t */\n\t\trelativeDate(time) {\n\t\t\tconst startOfDay = (ms) => new Date(ms).setHours(0, 0, 0, 0)\n\t\t\tconst days = Math.round((startOfDay(time) - startOfDay(Date.now())) / DAY_MS)\n\t\t\ttry {\n\t\t\t\treturn new Intl.RelativeTimeFormat(this.locale(), { numeric: 'auto' }).format(days, 'day')\n\t\t\t} catch {\n\t\t\t\treturn new Date(time).toLocaleDateString(userLocale())\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the referenced objects behind id-valued reference fields. A\n\t\t * failed lookup leaves the id showing in mono.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync resolveRefs() {\n\t\t\tif (!this.object || !this.register) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet store = null\n\t\t\ttry {\n\t\t\t\tstore = useObjectStore()\n\t\t\t} catch {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!store) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tawait Promise.all(this.entries.map(async (entry) => {\n\t\t\t\tconst raw = this.object[entry.key]\n\t\t\t\tconst slug = this.refSlugOf(this.propertyOf(entry.key))\n\t\t\t\tif (!slug || isEmptyChipValue(raw) || typeof raw === 'object') {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst cacheKey = `${slug}:${raw}`\n\t\t\t\tif (this.refs[cacheKey]) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst type = resolveObjectOpType(store, { register: this.register, schema: slug }, { exactSchema: true })\n\t\t\t\t\tconst cached = store.objects && store.objects[type] && store.objects[type][raw]\n\t\t\t\t\tconst found = cached || await store.fetchObject(type, String(raw))\n\t\t\t\t\tif (found) {\n\t\t\t\t\t\tthis.refs = { ...this.refs, [cacheKey]: found }\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// The id stays visible in mono.\n\t\t\t\t}\n\t\t\t}))\n\t\t},\n\t},\n}\n</script>\n\n<style>\n.cn-detail-header-chips {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 6px;\n\tmargin: 6px 0 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-detail-header-chips__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tmax-width: 100%;\n\tpadding: 2px 10px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-pill, 16px);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-main-text);\n\tfont-size: 0.9em;\n}\n\n.cn-detail-header-chips__chip--error {\n\tborder-color: var(--color-error);\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-detail-header-chips__chip--badge {\n\tpadding: 0;\n\tborder: none;\n\tbackground: transparent;\n}\n\n.cn-detail-header-chips__value--mono {\n\tfont-family: var(--font-family-monospace, ui-monospace, monospace);\n}\n\n/* Printed, the row is plain text: no pill, border or background. */\n@media print {\n\t.cn-detail-header-chips__chip {\n\t\tpadding: 0;\n\t\tborder: none;\n\t\tbackground: transparent;\n\t\tcolor: inherit;\n\t}\n\n\t.cn-detail-header-chips__chip:not(:last-child)::after {\n\t\tcontent: ' ·';\n\t}\n}\n</style>\n"],"names":["CnStatusBadge"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnDetailHeaderChips.vue3.js","sources":["../../../../src/components/CnDetailPage/CnDetailHeaderChips.vue?vue&type=template&id=d7f01c18&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<!--\n CnDetailHeaderChips — the row of field chips under a detail page's title\n (manifest `config.headerFields`). Internal to CnDetailPage.\n\n An empty value renders no chip, and a row with no chips renders nothing. A\n reference shows the referenced object's label and falls back to the raw id\n in mono when it does not resolve. Each chip reads \"Title: value\" to a screen\n reader, and the row prints as plain text.\n\n @spec openspec/changes/detail-header-field-chips/specs/detail-page-header/spec.md\n-->\n\n<template>\n\t<ul\n\t\tv-if=\"chips.length > 0\"\n\t\tclass=\"cn-detail-header-chips\"\n\t\tdata-testid=\"cn-detail-header-chips\">\n\t\t<li\n\t\t\tv-for=\"chip in chips\"\n\t\t\t:key=\"chip.key\"\n\t\t\tclass=\"cn-detail-header-chips__chip\"\n\t\t\t:class=\"[`cn-detail-header-chips__chip--${chip.format}`, `cn-detail-header-chips__chip--${chip.variant}`]\"\n\t\t\t:data-testid=\"`cn-detail-header-chip-${chip.key}`\">\n\t\t\t<span class=\"hidden-visually\">{{ chip.title }}: </span>\n\t\t\t<CnStatusBadge\n\t\t\t\tv-if=\"chip.format === 'badge'\"\n\t\t\t\t:label=\"chip.text\"\n\t\t\t\t:variant=\"chip.variant\"\n\t\t\t\tsize=\"small\" />\n\t\t\t<NcUserBubble\n\t\t\t\tv-else-if=\"chip.format === 'user' && chip.userId\"\n\t\t\t\t:user=\"chip.userId\"\n\t\t\t\t:displayName=\"chip.text\"\n\t\t\t\t:showUserStatus=\"false\" />\n\t\t\t<time\n\t\t\t\tv-else-if=\"chip.format === 'date'\"\n\t\t\t\tclass=\"cn-detail-header-chips__value\"\n\t\t\t\t:datetime=\"chip.iso\"\n\t\t\t\t:title=\"chip.fullDate\">{{ chip.text }}</time>\n\t\t\t<span\n\t\t\t\tv-else\n\t\t\t\tclass=\"cn-detail-header-chips__value\"\n\t\t\t\t:class=\"{ 'cn-detail-header-chips__value--mono': chip.mono }\">{{ chip.text }}</span>\n\t\t</li>\n\t</ul>\n</template>\n\n<script>\nimport { getCanonicalLocale } from '@nextcloud/l10n'\nimport { NcUserBubble } from '@nextcloud/vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { useObjectStore } from '../../store/useObjectStore.js'\nimport { resolveObjectOpType } from '../../utils/actionsDispatcher.js'\nimport { isEmptyChipValue, isPastDate, normalizeHeaderFields, pickRefLabel, variantForColor } from '../../utils/headerFieldChips.js'\nimport { schemaRefSlug } from '../../utils/schemaRefSlug.js'\n\nconst DAY_MS = 86400000\n\n/**\n * CnDetailHeaderChips — the chips row of `CnDetailPage`'s header.\n */\nexport default {\n\tname: 'CnDetailHeaderChips',\n\n\tcomponents: { CnStatusBadge, NcUserBubble },\n\n\tprops: {\n\t\t/**\n\t\t * The declared header fields (strings or `{ key, format, labelField, colorField, warnWhenPast }`).\n\t\t *\n\t\t * @type {Array<string|object>}\n\t\t */\n\t\tfields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The object the chips read their values from.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The JSON Schema of the object, for property titles and `$ref` targets.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The OpenRegister register slug a reference resolves in. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\t// Resolved referenced objects, by \"<schema slug>:<id>\".\n\t\t\trefs: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The normalised entries. */\n\t\tentries() {\n\t\t\treturn normalizeHeaderFields(this.fields)\n\t\t},\n\n\t\t/** The chips that have a value to show, in declared order. */\n\t\tchips() {\n\t\t\tconst obj = this.object\n\t\t\tif (!obj) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst out = []\n\t\t\tfor (const entry of this.entries) {\n\t\t\t\tconst chip = this.buildChip(entry, obj)\n\t\t\t\tif (chip) {\n\t\t\t\t\tout.push(chip)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\t},\n\n\twatch: {\n\t\tobject: { handler: 'resolveRefs', immediate: true },\n\t\tschema: 'resolveRefs',\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The schema property definition of a field.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @return {object|null} The property or null.\n\t\t */\n\t\tpropertyOf(key) {\n\t\t\tconst props = this.schema && this.schema.properties\n\t\t\treturn (props && props[key]) || null\n\t\t},\n\n\t\t/**\n\t\t * The referenced schema slug of a property, or '' when it is not a reference.\n\t\t *\n\t\t * @param {object|null} prop The schema property.\n\t\t * @return {string} The slug.\n\t\t */\n\t\trefSlugOf(prop) {\n\t\t\tconst ref = prop && (prop.$ref || (prop.items && prop.items.$ref))\n\t\t\treturn ref ? String(schemaRefSlug(ref) || '') : ''\n\t\t},\n\n\t\t/**\n\t\t * Build one chip, or null when its value is empty.\n\t\t *\n\t\t * @param {object} entry The normalised entry.\n\t\t * @param {object} obj The object.\n\t\t * @return {object|null} The chip view model.\n\t\t */\n\t\tbuildChip(entry, obj) {\n\t\t\tconst raw = obj[entry.key]\n\t\t\tif (isEmptyChipValue(raw)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst prop = this.propertyOf(entry.key)\n\t\t\tconst slug = this.refSlugOf(prop)\n\t\t\tconst title = (prop && typeof prop.title === 'string' && prop.title) || entry.key\n\t\t\tconst chip = { key: entry.key, title, format: entry.format, variant: 'default', mono: entry.format === 'mono', text: '', userId: '' }\n\n\t\t\t// A reference: an expanded object reads straight off, an id comes from the resolved cache.\n\t\t\tconst isRefValue = (raw && typeof raw === 'object' && !Array.isArray(raw)) || slug !== ''\n\t\t\tif (isRefValue) {\n\t\t\t\tconst refObj = (raw && typeof raw === 'object') ? raw : this.refs[`${slug}:${raw}`]\n\t\t\t\tconst label = pickRefLabel(refObj, entry.labelField)\n\t\t\t\tconst idText = (raw && typeof raw === 'object') ? String(raw.id ?? raw.uuid ?? '') : String(raw)\n\t\t\t\tchip.text = label || idText\n\t\t\t\tchip.mono = label === ''\n\t\t\t\tchip.userId = (refObj && (refObj.uid || refObj.id)) ? String(refObj.uid || refObj.id) : idText\n\t\t\t\tif (entry.format === 'badge' && entry.colorField && refObj) {\n\t\t\t\t\tchip.variant = variantForColor(refObj[entry.colorField])\n\t\t\t\t}\n\t\t\t\treturn chip.text === '' ? null : chip\n\t\t\t}\n\n\t\t\tchip.text = Array.isArray(raw) ? raw.join(', ') : String(raw)\n\t\t\tchip.userId = String(raw)\n\t\t\tif (entry.format === 'date') {\n\t\t\t\tconst time = new Date(raw).getTime()\n\t\t\t\tif (Number.isNaN(time)) {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tchip.iso = new Date(time).toISOString()\n\t\t\t\tchip.fullDate = new Date(time).toLocaleString(this.locale())\n\t\t\t\tchip.text = this.relativeDate(time)\n\t\t\t\tif (entry.warnWhenPast && isPastDate(raw)) {\n\t\t\t\t\tchip.variant = 'error'\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn chip\n\t\t},\n\n\t\t/**\n\t\t * The locale for date formatting.\n\t\t *\n\t\t * @return {string} A BCP 47 locale.\n\t\t */\n\t\tlocale() {\n\t\t\ttry {\n\t\t\t\treturn getCanonicalLocale()\n\t\t\t} catch {\n\t\t\t\treturn 'en'\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A date as relative text (\"in 3 days\", \"yesterday\").\n\t\t *\n\t\t * @param {number} time The time in ms.\n\t\t * @return {string} The relative text.\n\t\t */\n\t\trelativeDate(time) {\n\t\t\tconst startOfDay = (ms) => new Date(ms).setHours(0, 0, 0, 0)\n\t\t\tconst days = Math.round((startOfDay(time) - startOfDay(Date.now())) / DAY_MS)\n\t\t\ttry {\n\t\t\t\treturn new Intl.RelativeTimeFormat(this.locale(), { numeric: 'auto' }).format(days, 'day')\n\t\t\t} catch {\n\t\t\t\treturn new Date(time).toLocaleDateString()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the referenced objects behind id-valued reference fields. A\n\t\t * failed lookup leaves the id showing in mono.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync resolveRefs() {\n\t\t\tif (!this.object || !this.register) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet store = null\n\t\t\ttry {\n\t\t\t\tstore = useObjectStore()\n\t\t\t} catch {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!store) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tawait Promise.all(this.entries.map(async (entry) => {\n\t\t\t\tconst raw = this.object[entry.key]\n\t\t\t\tconst slug = this.refSlugOf(this.propertyOf(entry.key))\n\t\t\t\tif (!slug || isEmptyChipValue(raw) || typeof raw === 'object') {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst cacheKey = `${slug}:${raw}`\n\t\t\t\tif (this.refs[cacheKey]) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst type = resolveObjectOpType(store, { register: this.register, schema: slug }, { exactSchema: true })\n\t\t\t\t\tconst cached = store.objects && store.objects[type] && store.objects[type][raw]\n\t\t\t\t\tconst found = cached || await store.fetchObject(type, String(raw))\n\t\t\t\t\tif (found) {\n\t\t\t\t\t\tthis.refs = { ...this.refs, [cacheKey]: found }\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// The id stays visible in mono.\n\t\t\t\t}\n\t\t\t}))\n\t\t},\n\t},\n}\n</script>\n\n<style>\n.cn-detail-header-chips {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 6px;\n\tmargin: 6px 0 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-detail-header-chips__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tmax-width: 100%;\n\tpadding: 2px 10px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-pill, 16px);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-main-text);\n\tfont-size: 0.9em;\n}\n\n.cn-detail-header-chips__chip--error {\n\tborder-color: var(--color-error);\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-detail-header-chips__chip--badge {\n\tpadding: 0;\n\tborder: none;\n\tbackground: transparent;\n}\n\n.cn-detail-header-chips__value--mono {\n\tfont-family: var(--font-family-monospace, ui-monospace, monospace);\n}\n\n/* Printed, the row is plain text: no pill, border or background. */\n@media print {\n\t.cn-detail-header-chips__chip {\n\t\tpadding: 0;\n\t\tborder: none;\n\t\tbackground: transparent;\n\t\tcolor: inherit;\n\t}\n\n\t.cn-detail-header-chips__chip:not(:last-child)::after {\n\t\tcontent: ' ·';\n\t}\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_normalizeClass","_toDisplayString","_createBlock"],"mappings":";;;;OAqCO,EAAC,wBAAmB;eACd,EAAC,wBAAe;;;AAOpB,MAAA,UAAA,GAAA,EAAA,KAAA,EAAA,iBAAA,GAAA;;;;;;;AATD,EAAA,OAAA,CAAA,QAAA,CAAA,KAAK,CAAC,MAAM,GAAA,CAAA;AADhB,OAAAA,SAAA,EAAA,EAAAC,kBAAA,OAAA;wEAKc,QAAK,CAAA,KAAA,EAAA,CAAb,IAAI,KAAA;;YACV,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,KAAI,EAAAC,cAAA,CAAA,CAAC,8BAAc,EACsB,CAAA,CAAA,8BAAA,EAAA,IAAG,CAAA,MAAA,CAAA,CAAA,EAAA,CAAA,8BAAA,EAAA,IAAA,CAAA,OAAA,CAAA,CAAA,CAAA,CAAA,CAAA;YAC5C,aAAG,EAAA,CAAA,sBAAA,EAAA,IAAA,CAAA,GAAA,CAAA,CAAA;;+BACE,kBAAA,EAAAC,eAAA,CAAA,IAAA,CAAA,KAAA,IAAA,IAAA,EAAA,CAAA,YAAA;AAEC,YAAA,CAAA,IAAI,CAAC,MAAM,KAAA,OAAA;8BADjBC;;kBAEC,KAAC,EAAA,IAAA,CAAA,IAAA;kBACD,OAAA,EAAA,IAAA,CAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"CnDetailHeaderChips.vue3.js","sources":["../../../../src/components/CnDetailPage/CnDetailHeaderChips.vue?vue&type=template&id=4f8ee522&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<!--\n CnDetailHeaderChips — the row of field chips under a detail page's title\n (manifest `config.headerFields`). Internal to CnDetailPage.\n\n An empty value renders no chip, and a row with no chips renders nothing. A\n reference shows the referenced object's label and falls back to the raw id\n in mono when it does not resolve. Each chip reads \"Title: value\" to a screen\n reader, and the row prints as plain text.\n\n @spec openspec/changes/detail-header-field-chips/specs/detail-page-header/spec.md\n-->\n\n<template>\n\t<ul\n\t\tv-if=\"chips.length > 0\"\n\t\tclass=\"cn-detail-header-chips\"\n\t\tdata-testid=\"cn-detail-header-chips\">\n\t\t<li\n\t\t\tv-for=\"chip in chips\"\n\t\t\t:key=\"chip.key\"\n\t\t\tclass=\"cn-detail-header-chips__chip\"\n\t\t\t:class=\"[`cn-detail-header-chips__chip--${chip.format}`, `cn-detail-header-chips__chip--${chip.variant}`]\"\n\t\t\t:data-testid=\"`cn-detail-header-chip-${chip.key}`\">\n\t\t\t<span class=\"hidden-visually\">{{ chip.title }}: </span>\n\t\t\t<CnStatusBadge\n\t\t\t\tv-if=\"chip.format === 'badge'\"\n\t\t\t\t:label=\"chip.text\"\n\t\t\t\t:variant=\"chip.variant\"\n\t\t\t\tsize=\"small\" />\n\t\t\t<NcUserBubble\n\t\t\t\tv-else-if=\"chip.format === 'user' && chip.userId\"\n\t\t\t\t:user=\"chip.userId\"\n\t\t\t\t:displayName=\"chip.text\"\n\t\t\t\t:showUserStatus=\"false\" />\n\t\t\t<time\n\t\t\t\tv-else-if=\"chip.format === 'date'\"\n\t\t\t\tclass=\"cn-detail-header-chips__value\"\n\t\t\t\t:datetime=\"chip.iso\"\n\t\t\t\t:title=\"chip.fullDate\">{{ chip.text }}</time>\n\t\t\t<span\n\t\t\t\tv-else\n\t\t\t\tclass=\"cn-detail-header-chips__value\"\n\t\t\t\t:class=\"{ 'cn-detail-header-chips__value--mono': chip.mono }\">{{ chip.text }}</span>\n\t\t</li>\n\t</ul>\n</template>\n\n<script>\nimport { getCanonicalLocale } from '@nextcloud/l10n'\nimport { NcUserBubble } from '@nextcloud/vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { useObjectStore } from '../../store/useObjectStore.js'\nimport { resolveObjectOpType } from '../../utils/actionsDispatcher.js'\nimport { isEmptyChipValue, isPastDate, normalizeHeaderFields, pickRefLabel, variantForColor } from '../../utils/headerFieldChips.js'\nimport { schemaRefSlug } from '../../utils/schemaRefSlug.js'\nimport { userLocale } from '../../utils/userLocale.js'\n\nconst DAY_MS = 86400000\n\n/**\n * CnDetailHeaderChips — the chips row of `CnDetailPage`'s header.\n */\nexport default {\n\tname: 'CnDetailHeaderChips',\n\n\tcomponents: { CnStatusBadge, NcUserBubble },\n\n\tprops: {\n\t\t/**\n\t\t * The declared header fields (strings or `{ key, format, labelField, colorField, warnWhenPast }`).\n\t\t *\n\t\t * @type {Array<string|object>}\n\t\t */\n\t\tfields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The object the chips read their values from.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The JSON Schema of the object, for property titles and `$ref` targets.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The OpenRegister register slug a reference resolves in. */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\t// Resolved referenced objects, by \"<schema slug>:<id>\".\n\t\t\trefs: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The normalised entries. */\n\t\tentries() {\n\t\t\treturn normalizeHeaderFields(this.fields)\n\t\t},\n\n\t\t/** The chips that have a value to show, in declared order. */\n\t\tchips() {\n\t\t\tconst obj = this.object\n\t\t\tif (!obj) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst out = []\n\t\t\tfor (const entry of this.entries) {\n\t\t\t\tconst chip = this.buildChip(entry, obj)\n\t\t\t\tif (chip) {\n\t\t\t\t\tout.push(chip)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn out\n\t\t},\n\t},\n\n\twatch: {\n\t\tobject: { handler: 'resolveRefs', immediate: true },\n\t\tschema: 'resolveRefs',\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The schema property definition of a field.\n\t\t *\n\t\t * @param {string} key The field key.\n\t\t * @return {object|null} The property or null.\n\t\t */\n\t\tpropertyOf(key) {\n\t\t\tconst props = this.schema && this.schema.properties\n\t\t\treturn (props && props[key]) || null\n\t\t},\n\n\t\t/**\n\t\t * The referenced schema slug of a property, or '' when it is not a reference.\n\t\t *\n\t\t * @param {object|null} prop The schema property.\n\t\t * @return {string} The slug.\n\t\t */\n\t\trefSlugOf(prop) {\n\t\t\tconst ref = prop && (prop.$ref || (prop.items && prop.items.$ref))\n\t\t\treturn ref ? String(schemaRefSlug(ref) || '') : ''\n\t\t},\n\n\t\t/**\n\t\t * Build one chip, or null when its value is empty.\n\t\t *\n\t\t * @param {object} entry The normalised entry.\n\t\t * @param {object} obj The object.\n\t\t * @return {object|null} The chip view model.\n\t\t */\n\t\tbuildChip(entry, obj) {\n\t\t\tconst raw = obj[entry.key]\n\t\t\tif (isEmptyChipValue(raw)) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst prop = this.propertyOf(entry.key)\n\t\t\tconst slug = this.refSlugOf(prop)\n\t\t\tconst title = (prop && typeof prop.title === 'string' && prop.title) || entry.key\n\t\t\tconst chip = { key: entry.key, title, format: entry.format, variant: 'default', mono: entry.format === 'mono', text: '', userId: '' }\n\n\t\t\t// A reference: an expanded object reads straight off, an id comes from the resolved cache.\n\t\t\tconst isRefValue = (raw && typeof raw === 'object' && !Array.isArray(raw)) || slug !== ''\n\t\t\tif (isRefValue) {\n\t\t\t\tconst refObj = (raw && typeof raw === 'object') ? raw : this.refs[`${slug}:${raw}`]\n\t\t\t\tconst label = pickRefLabel(refObj, entry.labelField)\n\t\t\t\tconst idText = (raw && typeof raw === 'object') ? String(raw.id ?? raw.uuid ?? '') : String(raw)\n\t\t\t\tchip.text = label || idText\n\t\t\t\tchip.mono = label === ''\n\t\t\t\tchip.userId = (refObj && (refObj.uid || refObj.id)) ? String(refObj.uid || refObj.id) : idText\n\t\t\t\tif (entry.format === 'badge' && entry.colorField && refObj) {\n\t\t\t\t\tchip.variant = variantForColor(refObj[entry.colorField])\n\t\t\t\t}\n\t\t\t\treturn chip.text === '' ? null : chip\n\t\t\t}\n\n\t\t\tchip.text = Array.isArray(raw) ? raw.join(', ') : String(raw)\n\t\t\tchip.userId = String(raw)\n\t\t\tif (entry.format === 'date') {\n\t\t\t\tconst time = new Date(raw).getTime()\n\t\t\t\tif (Number.isNaN(time)) {\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t\tchip.iso = new Date(time).toISOString()\n\t\t\t\tchip.fullDate = new Date(time).toLocaleString(this.locale())\n\t\t\t\tchip.text = this.relativeDate(time)\n\t\t\t\tif (entry.warnWhenPast && isPastDate(raw)) {\n\t\t\t\t\tchip.variant = 'error'\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn chip\n\t\t},\n\n\t\t/**\n\t\t * The locale for date formatting.\n\t\t *\n\t\t * @return {string} A BCP 47 locale.\n\t\t */\n\t\tlocale() {\n\t\t\ttry {\n\t\t\t\treturn getCanonicalLocale()\n\t\t\t} catch {\n\t\t\t\treturn 'en'\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A date as relative text (\"in 3 days\", \"yesterday\").\n\t\t *\n\t\t * @param {number} time The time in ms.\n\t\t * @return {string} The relative text.\n\t\t */\n\t\trelativeDate(time) {\n\t\t\tconst startOfDay = (ms) => new Date(ms).setHours(0, 0, 0, 0)\n\t\t\tconst days = Math.round((startOfDay(time) - startOfDay(Date.now())) / DAY_MS)\n\t\t\ttry {\n\t\t\t\treturn new Intl.RelativeTimeFormat(this.locale(), { numeric: 'auto' }).format(days, 'day')\n\t\t\t} catch {\n\t\t\t\treturn new Date(time).toLocaleDateString(userLocale())\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve the referenced objects behind id-valued reference fields. A\n\t\t * failed lookup leaves the id showing in mono.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync resolveRefs() {\n\t\t\tif (!this.object || !this.register) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet store = null\n\t\t\ttry {\n\t\t\t\tstore = useObjectStore()\n\t\t\t} catch {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (!store) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tawait Promise.all(this.entries.map(async (entry) => {\n\t\t\t\tconst raw = this.object[entry.key]\n\t\t\t\tconst slug = this.refSlugOf(this.propertyOf(entry.key))\n\t\t\t\tif (!slug || isEmptyChipValue(raw) || typeof raw === 'object') {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst cacheKey = `${slug}:${raw}`\n\t\t\t\tif (this.refs[cacheKey]) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst type = resolveObjectOpType(store, { register: this.register, schema: slug }, { exactSchema: true })\n\t\t\t\t\tconst cached = store.objects && store.objects[type] && store.objects[type][raw]\n\t\t\t\t\tconst found = cached || await store.fetchObject(type, String(raw))\n\t\t\t\t\tif (found) {\n\t\t\t\t\t\tthis.refs = { ...this.refs, [cacheKey]: found }\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// The id stays visible in mono.\n\t\t\t\t}\n\t\t\t}))\n\t\t},\n\t},\n}\n</script>\n\n<style>\n.cn-detail-header-chips {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 6px;\n\tmargin: 6px 0 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-detail-header-chips__chip {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tmax-width: 100%;\n\tpadding: 2px 10px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-pill, 16px);\n\tbackground: var(--color-background-hover);\n\tcolor: var(--color-main-text);\n\tfont-size: 0.9em;\n}\n\n.cn-detail-header-chips__chip--error {\n\tborder-color: var(--color-error);\n\tcolor: var(--color-error-text, var(--color-error));\n}\n\n.cn-detail-header-chips__chip--badge {\n\tpadding: 0;\n\tborder: none;\n\tbackground: transparent;\n}\n\n.cn-detail-header-chips__value--mono {\n\tfont-family: var(--font-family-monospace, ui-monospace, monospace);\n}\n\n/* Printed, the row is plain text: no pill, border or background. */\n@media print {\n\t.cn-detail-header-chips__chip {\n\t\tpadding: 0;\n\t\tborder: none;\n\t\tbackground: transparent;\n\t\tcolor: inherit;\n\t}\n\n\t.cn-detail-header-chips__chip:not(:last-child)::after {\n\t\tcontent: ' ·';\n\t}\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_normalizeClass","_toDisplayString","_createBlock"],"mappings":";;;;OAqCO,EAAC,wBAAmB;eACd,EAAC,wBAAe;;;AAOpB,MAAA,UAAA,GAAA,EAAA,KAAA,EAAA,iBAAA,GAAA;;;;;;;AATD,EAAA,OAAA,CAAA,QAAA,CAAA,KAAK,CAAC,MAAM,GAAA,CAAA;AADhB,OAAAA,SAAA,EAAA,EAAAC,kBAAA,OAAA;wEAKc,QAAK,CAAA,KAAA,EAAA,CAAb,IAAI,KAAA;;YACV,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,KAAI,EAAAC,cAAA,CAAA,CAAC,8BAAc,EACsB,CAAA,CAAA,8BAAA,EAAA,IAAG,CAAA,MAAA,CAAA,CAAA,EAAA,CAAA,8BAAA,EAAA,IAAA,CAAA,OAAA,CAAA,CAAA,CAAA,CAAA,CAAA;YAC5C,aAAG,EAAA,CAAA,sBAAA,EAAA,IAAA,CAAA,GAAA,CAAA,CAAA;;+BACE,kBAAA,EAAAC,eAAA,CAAA,IAAA,CAAA,KAAA,IAAA,IAAA,EAAA,CAAA,YAAA;AAEC,YAAA,CAAA,IAAI,CAAC,MAAM,KAAA,OAAA;8BADjBC;;kBAEC,KAAC,EAAA,IAAA,CAAA,IAAA;kBACD,OAAA,EAAA,IAAA,CAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1448,6 +1448,19 @@ var script = {
1448
1448
  type: String,
1449
1449
  default: '',
1450
1450
  },
1451
+
1452
+ /**
1453
+ * The record field the header names the record by (manifest
1454
+ * `config.titleField`, a dotted path such as `number`), as an invoice
1455
+ * page names the invoice number. An empty value falls back to the
1456
+ * display name. Empty (the default) changes nothing.
1457
+ *
1458
+ * @spec openspec/changes/detail-title-field/specs/detail-page/spec.md#requirement-a-detail-page-can-name-the-record-by-a-field
1459
+ */
1460
+ titleField: {
1461
+ type: String,
1462
+ default: '',
1463
+ },
1451
1464
  },
1452
1465
 
1453
1466
  emits: [
@@ -2886,10 +2899,10 @@ var script = {
2886
2899
  },
2887
2900
 
2888
2901
  /**
2889
- * The resolved record's human display name, drawn from the loaded
2890
- * object via the same fallback chain the relation resolver uses:
2891
- * `@self.name` → `name` / `title` / `displayName` → `firstName
2892
- * lastName`. Empty string while the object is still loading or when no
2902
+ * The resolved record's human display name: the `titleField` value
2903
+ * when the page names one and it is filled, else the same fallback
2904
+ * chain the relation resolver uses: `@self.name` → `name` / `title` /
2905
+ * `displayName` → `firstName lastName`. Empty string while the object is still loading or when no
2893
2906
  * name-like field exists (a raw id is never used as a name — ADR-062).
2894
2907
  *
2895
2908
  * @return {string}
@@ -2899,6 +2912,12 @@ var script = {
2899
2912
  if (!obj || typeof obj !== 'object') {
2900
2913
  return ''
2901
2914
  }
2915
+ if (this.titleField) {
2916
+ const named = stageOf(obj, this.titleField);
2917
+ if (named !== '') {
2918
+ return named
2919
+ }
2920
+ }
2902
2921
  const self = obj['@self'] || {};
2903
2922
  const id = this.objectId !== null && this.objectId !== undefined ? String(this.objectId) : '';
2904
2923
  const candidates = [self.name, self.title, obj.name, obj.title, obj.displayName];