@conduction/nextcloud-vue 2.56.0-beta.30 → 2.56.0-beta.31

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 (173) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +3 -3
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  4. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +80 -4
  5. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  6. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +95 -91
  7. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  8. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +85 -6
  9. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  10. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +1 -1
  11. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  12. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +4 -1
  13. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  14. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  15. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +1 -1
  16. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
  17. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
  18. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +2 -2
  19. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
  20. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
  21. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +40 -3
  22. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  23. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +19 -11
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  27. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +43 -0
  28. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  29. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +1 -1
  30. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  31. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +1 -1
  32. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -1
  33. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -1
  34. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js +7 -7
  35. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js.map +1 -1
  36. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue3.js.map +1 -1
  37. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +125 -0
  38. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  39. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  40. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +3 -2
  41. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
  42. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
  43. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +302 -4
  44. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  45. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +417 -244
  46. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  47. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +101 -1
  48. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  49. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +141 -1
  50. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
  51. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +60 -0
  52. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  53. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +74 -49
  54. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  55. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +29 -1
  56. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  57. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +48 -4
  58. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  59. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +7 -7
  60. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  61. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  62. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +1 -1
  63. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -1
  64. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +1 -1
  65. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  66. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  67. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +63 -1
  68. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  69. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +8 -1
  70. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  71. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +14 -0
  72. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  73. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js +5 -1
  74. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  75. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +13 -6
  76. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  77. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  78. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +84 -1
  79. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
  80. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +85 -41
  81. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
  82. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +7 -2
  83. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  84. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  85. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  86. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js.map +1 -1
  87. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +197 -6
  88. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  89. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +94 -50
  90. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  91. package/dist/esm/composables/useListView.js +4 -4
  92. package/dist/esm/composables/useListView.js.map +1 -1
  93. package/dist/esm/index.js +10 -1
  94. package/dist/esm/index.js.map +1 -1
  95. package/dist/esm/l10n/en.json.js +8 -1
  96. package/dist/esm/l10n/en.json.js.map +1 -1
  97. package/dist/esm/l10n/nl.json.js +54 -1
  98. package/dist/esm/l10n/nl.json.js.map +1 -1
  99. package/dist/esm/nextcloud-vue.css +198 -8
  100. package/dist/esm/utils/facets.js +75 -3
  101. package/dist/esm/utils/facets.js.map +1 -1
  102. package/dist/esm/utils/fillTemplate.js +7 -1
  103. package/dist/esm/utils/fillTemplate.js.map +1 -1
  104. package/dist/esm/utils/pageControls.js +81 -0
  105. package/dist/esm/utils/pageControls.js.map +1 -0
  106. package/dist/esm/utils/personalLenses.js +40 -3
  107. package/dist/esm/utils/personalLenses.js.map +1 -1
  108. package/dist/esm/utils/resolveFilterTokens.js +1 -1
  109. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  110. package/dist/esm/utils/resolveManifestTokens.js +2 -0
  111. package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
  112. package/dist/esm/utils/sentinelTokens.js +19 -1
  113. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  114. package/dist/esm/utils/userGroupsPeek.js +91 -0
  115. package/dist/esm/utils/userGroupsPeek.js.map +1 -0
  116. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  117. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  118. package/dist/esm/utils/visibleWhen.js +57 -5
  119. package/dist/esm/utils/visibleWhen.js.map +1 -1
  120. package/dist/esm/utils/widgetVisibility.js +4 -57
  121. package/dist/esm/utils/widgetVisibility.js.map +1 -1
  122. package/dist/nextcloud-vue.cjs.js +4309 -2233
  123. package/dist/nextcloud-vue.cjs.js.map +2 -2
  124. package/dist/nextcloud-vue.css +198 -8
  125. package/l10n/en.json +8 -1
  126. package/l10n/nl.json +54 -1
  127. package/package.json +2 -1
  128. package/scripts/check-docs-mdx.mjs +108 -0
  129. package/src/components/CnActionsBar/CnActionsBar.vue +6 -1
  130. package/src/components/CnAppRoot/CnAppRoot.md +4 -2
  131. package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
  132. package/src/components/CnBannerWidget/CnBannerWidget.vue +112 -7
  133. package/src/components/CnCellRenderer/CnCellRenderer.vue +4 -1
  134. package/src/components/CnConversationThread/CnConversationWidget.vue +1 -1
  135. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +2 -2
  136. package/src/components/CnDashboardPage/CnDashboardPage.vue +40 -3
  137. package/src/components/CnDataTable/CnDataTable.vue +19 -11
  138. package/src/components/CnDetailPage/CnDetailPage.vue +44 -1
  139. package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
  140. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
  141. package/src/components/CnFactsWidget/CnFactsWidget.vue +125 -0
  142. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +3 -2
  143. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +588 -117
  144. package/src/components/CnFilesBrowser/filesBrowser.js +100 -0
  145. package/src/components/CnFilesBrowser/filesBrowserColumns.js +140 -0
  146. package/src/components/CnFormPage/CnFormPage.vue +81 -0
  147. package/src/components/CnIndexPage/CnIndexPage.vue +56 -3
  148. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +7 -7
  149. package/src/components/CnNextStepCard/CnNextStepWidget.vue +1 -1
  150. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +1 -1
  151. package/src/components/CnObjectSidebar/CnFilesTab.vue +70 -1
  152. package/src/components/CnObjectSidebar/CnNotesTab.vue +16 -0
  153. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +13 -6
  154. package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +180 -3
  155. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
  156. package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  157. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +281 -10
  158. package/src/components/index.js +8 -0
  159. package/src/composables/useListView.js +4 -4
  160. package/src/css/look-board-index.css +16 -8
  161. package/src/index.js +8 -0
  162. package/src/schemas/app-manifest-v2.schema.json +93 -9
  163. package/src/utils/facets.js +74 -2
  164. package/src/utils/fillTemplate.js +7 -1
  165. package/src/utils/pageControls.js +78 -0
  166. package/src/utils/personalLenses.js +39 -2
  167. package/src/utils/resolveFilterTokens.js +3 -1
  168. package/src/utils/resolveManifestTokens.js +2 -0
  169. package/src/utils/sentinelTokens.js +19 -1
  170. package/src/utils/userGroupsPeek.js +87 -0
  171. package/src/utils/validateManifestV2.compiled.js +3 -3
  172. package/src/utils/visibleWhen.js +56 -4
  173. package/src/utils/widgetVisibility.js +7 -56
@@ -1 +1 @@
1
- {"version":3,"file":"CnNotesTab.vue2.js","sources":["../../../../src/components/CnObjectSidebar/CnNotesTab.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-sidebar-tab\">\n\t\t<!-- Add / Edit note -->\n\t\t<div class=\"cn-sidebar-tab__action\">\n\t\t\t<!-- Replying: the composer says to whom, and a reply to a reply goes to the top of its thread. -->\n\t\t\t<p\n\t\t\t\tv-if=\"replyTo\"\n\t\t\t\tclass=\"cn-sidebar-tab__replying\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-notes-replying\">\n\t\t\t\t{{ replyingLabel }}\n\t\t\t\t<NcButton variant=\"tertiary\" @click=\"cancelReply\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</p>\n\t\t\t<CnNoteComposer\n\t\t\t\tclass=\"cn-sidebar-tab__composer\"\n\t\t\t\t:modelValue=\"newNoteText\"\n\t\t\t\t:placeholder=\"addNotePlaceholder\"\n\t\t\t\t:register=\"register\"\n\t\t\t\t:schema=\"schema\"\n\t\t\t\t:objectId=\"objectId\"\n\t\t\t\t:apiBase=\"apiBase\"\n\t\t\t\t@update:modelValue=\"newNoteText = $event\"\n\t\t\t\t@submit=\"submitComposer\" />\n\t\t\t<div class=\"cn-sidebar-tab__action--row\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"editingNoteId\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t@click=\"cancelEdit\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"!newNoteText.trim() || saving\"\n\t\t\t\t\t@click=\"editingNoteId ? saveEdit() : addNote()\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<Send :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ editingNoteId ? saveLabel : addNoteLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Notes list -->\n\t\t<!-- Standalone spinner: give it an accessible name so screen readers\n\t\t announce the loading state (WCAG 1.1.1 / 4.1.2 — a bare\n\t\t NcLoadingIcon renders an unlabelled role=\"img\"). -->\n\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t<div v-else-if=\"notes.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noNotesLabel }}\n\t\t</div>\n\t\t<!-- <ul>, not <div>: NcListItem renders an <li>, which WCAG 1.3.1\n\t\t requires to be contained in a <ul>/<ol> (axe \"listitem\"). -->\n\t\t<ul v-else class=\"cn-sidebar-tab__list\">\n\t\t\t<!-- One <li> per thread; inside it the note first, then its replies (oldest first), as a nested list named after the note. -->\n\t\t\t<li v-for=\"thread in threads\" :key=\"thread.note.id\" class=\"cn-notes-tab__thread\">\n\t\t\t\t<ul class=\"cn-notes-tab__thread-list\" :aria-label=\"threadLabel(thread.note)\">\n\t\t\t\t\t<NcListItem\n\t\t\t\t\t\tv-for=\"row in threadRows(thread)\"\n\t\t\t\t\t\t:key=\"row.note.id\"\n\t\t\t\t\t\t:class=\"{ 'cn-notes-tab__reply': row.isReply }\"\n\t\t\t\t\t\t:name=\"row.note.actorDisplayName || row.note.author || 'Unknown'\"\n\t\t\t\t\t\t:bold=\"false\"\n\t\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<CommentTextOutline :size=\"32\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t\t<span class=\"cn-sidebar-tab__message\">\n\t\t\t\t\t\t\t\t<span v-if=\"row.quote\" class=\"cn-notes-tab__quote\" data-testid=\"cn-note-quote\">{{ row.quote }}</span>\n\t\t\t\t\t\t\t\t<CnNoteBody :message=\"row.note.message || row.note.content || ''\" :record=\"record\" :names=\"mentionNames\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #details>\n\t\t\t\t\t\t\t{{ formatDate(row.note.creationDateTime || row.note.created) }}\n\t\t\t\t\t\t\t<!-- An edited note says so where its time is, because that\n\t\t\t\t\t\t\t is where a reader already looks to date what they are\n\t\t\t\t\t\t\t reading. The line names the editor, so nobody has to\n\t\t\t\t\t\t\t open the history to learn who changed it. -->\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"wasEdited(row.note)\"\n\t\t\t\t\t\t\t\tclass=\"cn-notes-tab__edited\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-edited\">{{ editedLine(row.note) }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"canReply || canDelete(row.note) || wasEdited(row.note) || noteActions.length > 0\" #actions>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"canReply\"\n\t\t\t\t\t\t\t\t:aria-label=\"replyAria(row.note)\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-reply-action\"\n\t\t\t\t\t\t\t\t@click=\"startReply(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Reply :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ replyLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton v-if=\"canDelete(row.note)\" @click=\"startEdit(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Pencil :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ editLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"wasEdited(row.note)\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-history-action\"\n\t\t\t\t\t\t\t\t@click=\"openHistory(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<HistoryIcon :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ historyLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton v-if=\"canDelete(row.note)\" @click=\"deleteNote(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<!-- The consuming app's own per-note actions, after the\n\t\t\t\t\t\t\t library's. They render inside this NcActions and emit\n\t\t\t\t\t\t\t `note-action`; nothing about what they DO lives here. -->\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-for=\"action in noteActions\"\n\t\t\t\t\t\t\t\t:key=\"`${row.note.id}-${action.id}`\"\n\t\t\t\t\t\t\t\t:data-testid=\"`cn-note-action-${action.id}`\"\n\t\t\t\t\t\t\t\t@click=\"$emit('note-action', { action: action.id, note: row.note })\">\n\t\t\t\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t\t\t\t<component :is=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcListItem>\n\t\t\t\t</ul>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<CnNoteHistoryDialog\n\t\t\tv-if=\"historyNoteId\"\n\t\t\t:open=\"true\"\n\t\t\t:noteId=\"historyNoteId\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@update:open=\"historyNoteId = null\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport CommentTextOutline from 'vue-material-design-icons/CommentTextOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport HistoryIcon from 'vue-material-design-icons/History.vue'\nimport Pencil from 'vue-material-design-icons/Pencil.vue'\nimport Reply from 'vue-material-design-icons/Reply.vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnNoteHistoryDialog from '../../dialogs/CnNoteHistoryDialog.vue'\nimport CnNoteBody from '../CnNoteBody/CnNoteBody.vue'\nimport CnNoteComposer from '../CnNoteComposer/CnNoteComposer.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { extractMentionedGroupIds, extractMentionedIds } from '../../utils/mentions.js'\nimport { quoteFor, supportsReplies, threadNotes, threadRootId } from '../../utils/noteThreads.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\nexport default {\n\tname: 'CnNotesTab',\n\n\tcomponents: { NcButton, NcListItem, NcActionButton, NcLoadingIcon, CnNoteBody, CnNoteComposer, CnNoteHistoryDialog, CommentTextOutline, Send, Pencil, Delete, HistoryIcon, Reply },\n\n\tprops: {\n\t\t/** ID of the object this tab belongs to */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register slug */\n\t\tregister: { type: String, default: '' },\n\t\t/** JSON Schema definition for the object */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base URL for the OpenRegister API */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Label for the add note button */\n\t\taddNoteLabel: { type: String, default: () => t('nextcloud-vue', 'Add note') },\n\t\t/** Placeholder text for the note input field */\n\t\taddNotePlaceholder: { type: String, default: () => t('nextcloud-vue', 'Write a note…') },\n\t\t/** Label for the edit action */\n\t\teditLabel: { type: String, default: () => t('nextcloud-vue', 'Edit') },\n\t\t/** Label for the save action */\n\t\tsaveLabel: { type: String, default: () => t('nextcloud-vue', 'Save') },\n\t\t/** Label for the cancel button */\n\t\tcancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },\n\t\t/** Label for the delete action */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete') },\n\t\t/** Text shown when there are no notes */\n\t\tnoNotesLabel: { type: String, default: () => t('nextcloud-vue', 'No notes yet') },\n\t\t/** Text shown while the notes are being fetched */\n\t\tloadingLabel: { type: String, default: () => t('nextcloud-vue', 'Loading notes…') },\n\t\t/** Label for the Reply action (shown only when the backend supports replies) */\n\t\treplyLabel: { type: String, default: () => t('nextcloud-vue', 'Reply') },\n\t\t/** Label for the action that opens a note's earlier versions */\n\t\thistoryLabel: { type: String, default: () => t('nextcloud-vue', 'Show earlier versions') },\n\t\t// WHY THIS PROP EXISTS, since a seam nobody asked for is the kind that\n\t\t// rots. An app-specific act on ONE note had nowhere to live: this\n\t\t// component rendered edit, history and delete and offered no fourth\n\t\t// place. Dossiq sends a single case note to a neighbouring ZGW\n\t\t// register, and its endpoint, guard, route and tests were all in place\n\t\t// while no page could call any of them. Its own change wrote that down\n\t\t// as a blocker rather than declaring a prop nothing read.\n\t\t//\n\t\t// `label` stays the app's to write and translate, so nc-vue phrases\n\t\t// nothing for somebody else's domain and adds no catalogue string here.\n\t\t/**\n\t\t * Per-note actions the consuming app owns, `[{ id, label, icon? }]`.\n\t\t * Each entry renders one NcActionButton in every note's action menu,\n\t\t * after the library's own, and clicking it emits `note-action`. Empty\n\t\t * by default, and an empty list changes nothing. `icon` is optional\n\t\t * and takes an icon component.\n\t\t */\n\t\tnoteActions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (actions) => actions.every((action) => action\n\t\t\t\t&& typeof action.id === 'string' && action.id !== ''\n\t\t\t\t&& typeof action.label === 'string' && action.label !== ''),\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted after a note containing at least one `@mention` was\n\t\t * successfully created or edited, with payload\n\t\t * `{ objectId, register, schema, noteId, mentionedUserIds }`, plus\n\t\t * `mentionedGroupIds` when the note mentions a group (`@\"group/<gid>\"`).\n\t\t * The event also fires for a note that mentions only groups. Expanding a\n\t\t * group to its members is the listener's job.\n\t\t * nc-vue never dispatches server-side notifications itself —\n\t\t * consuming apps listen to this event and notify from their own\n\t\t * backend.\n\t\t */\n\t\t'mention',\n\t\t/**\n\t\t * Emitted when one of the consuming app's `noteActions` is clicked,\n\t\t * with payload `{ action, note }`: the entry's `id` and the whole note\n\t\t * as the backend answered it. The library does the rendering and\n\t\t * nothing else, so what the action means stays in the app.\n\t\t */\n\t\t'note-action',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tnotes: [],\n\t\t\tloading: false,\n\t\t\tnewNoteText: '',\n\t\t\tsaving: false,\n\t\t\teditingNoteId: null,\n\t\t\t/** The note whose earlier versions are open, or null. */\n\t\t\thistoryNoteId: null,\n\t\t\t/**\n\t\t\t * Per-instance cache of mentioned-user display names, keyed by\n\t\t\t * user id. `null` marks an id that could not be resolved (unknown\n\t\t\t * or deleted user) so it is only looked up once.\n\t\t\t */\n\t\t\tmentionNames: {},\n\t\t\t/** The note a reply is being written to, or null. */\n\t\t\treplyTo: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {{apiBase: string, register: string, schema: string, objectId: string}} Where this record's files live, for pasted images. */\n\t\trecord() {\n\t\t\treturn { apiBase: this.apiBase, register: this.register, schema: this.schema, objectId: this.objectId }\n\t\t},\n\n\t\t/** @return {boolean} Whether the backend supports replies: its notes carry a `parentId` key. */\n\t\tcanReply() {\n\t\t\treturn supportsReplies(this.notes)\n\t\t},\n\n\t\t/** @return {Array<{note: object, replies: object[]}>} The notes as threads, in the order they came. */\n\t\tthreads() {\n\t\t\treturn threadNotes(this.notes)\n\t\t},\n\n\t\t/** @return {string} The line above the composer while replying. */\n\t\treplyingLabel() {\n\t\t\treturn this.replyTo ? t('nextcloud-vue', 'Replying to {author}', { author: this.authorOf(this.replyTo) }) : ''\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tasync fetchNotes() {\n\t\t\tif (!this.register || !this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.notes = data.results || data || []\n\t\t\t\t\tthis.resolveMentionNames()\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to fetch notes', err)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve display names for every mentioned id across the current\n\t\t * notes list. Each id is looked up at most once per component\n\t\t * instance; unresolvable ids are cached as `null`.\n\t\t */\n\t\tasync resolveMentionNames() {\n\t\t\tconst ids = new Set()\n\t\t\tfor (const note of this.notes) {\n\t\t\t\tfor (const id of extractMentionedIds(note.message || note.content || '')) {\n\t\t\t\t\tids.add(id)\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait Promise.all([...ids]\n\t\t\t\t.filter((id) => !(id in this.mentionNames))\n\t\t\t\t.map(async (id) => {\n\t\t\t\t\t// Mark as pending (null) first so concurrent calls skip it.\n\t\t\t\t\tthis.mentionNames[id] = null\n\t\t\t\t\tconst results = await searchNextcloudUsers(id)\n\t\t\t\t\tconst match = results.find((user) => user.id === id)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tthis.mentionNames[id] = match.label\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Emit the `mention` notification hook for a successfully saved note.\n\t\t *\n\t\t * @param {string} savedText The note text that was persisted.\n\t\t * @param {string|null} noteId The created/edited note's id.\n\t\t */\n\t\temitMentionEvent(savedText, noteId) {\n\t\t\tconst mentionedUserIds = extractMentionedIds(savedText)\n\t\t\tconst mentionedGroupIds = extractMentionedGroupIds(savedText)\n\t\t\tif (mentionedUserIds.length === 0 && mentionedGroupIds.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('mention', {\n\t\t\t\tobjectId: this.objectId,\n\t\t\t\tregister: this.register,\n\t\t\t\tschema: this.schema,\n\t\t\t\tnoteId,\n\t\t\t\tmentionedUserIds,\n\t\t\t\t// Only when a group is mentioned: a note that names none keeps the payload it always had.\n\t\t\t\t...(mentionedGroupIds.length > 0 ? { mentionedGroupIds } : {}),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Save from the composer (Ctrl/Cmd+Enter): the same as the button.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tsubmitComposer() {\n\t\t\tif (!this.newNoteText.trim() || this.saving) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.editingNoteId) {\n\t\t\t\tthis.saveEdit()\n\t\t\t} else {\n\t\t\t\tthis.addNote()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note A note.\n\t\t * @return {string} Its author's name.\n\t\t */\n\t\tauthorOf(note) {\n\t\t\treturn note.actorDisplayName || note.author || 'Unknown'\n\t\t},\n\n\t\t/**\n\t\t * Write a reply to a note. A reply to a reply goes to the top of its thread.\n\t\t *\n\t\t * @param {object} note The note answered.\n\t\t * @return {void}\n\t\t */\n\t\tstartReply(note) {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.replyTo = note\n\t\t},\n\n\t\tcancelReply() {\n\t\t\tthis.replyTo = null\n\t\t},\n\n\t\t/**\n\t\t * The rows of one thread: the note, then its replies. A reply whose parent\n\t\t * is far up the list carries a one-line quote of it.\n\t\t *\n\t\t * @param {{note: object, replies: object[]}} thread The thread.\n\t\t * @return {Array<{note: object, isReply: boolean, quote: string}>} The rows.\n\t\t */\n\t\tthreadRows(thread) {\n\t\t\tconst flat = this.threads.flatMap((t2) => [t2.note, ...t2.replies])\n\t\t\treturn [\n\t\t\t\t{ note: thread.note, isReply: false, quote: '' },\n\t\t\t\t...thread.replies.map((reply) => ({ note: reply, isReply: true, quote: quoteFor(reply, flat) })),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note A top-level note.\n\t\t * @return {string} The name of its thread for assistive technology.\n\t\t */\n\t\tthreadLabel(note) {\n\t\t\treturn t('nextcloud-vue', 'Note by {author} and its replies', { author: this.authorOf(note) })\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note The note a Reply button answers.\n\t\t * @return {string} The button's accessible name, naming the author.\n\t\t */\n\t\treplyAria(note) {\n\t\t\treturn t('nextcloud-vue', 'Reply to {author}', { author: this.authorOf(note) })\n\t\t},\n\n\t\tasync addNote() {\n\t\t\tif (!this.newNoteText.trim()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify(this.replyTo\n\t\t\t\t\t\t\t? { message: savedText, parentId: threadRootId(this.replyTo, this.notes) }\n\t\t\t\t\t\t\t: { message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tlet noteId = null\n\t\t\t\ttry {\n\t\t\t\t\tconst created = await response.json()\n\t\t\t\t\tnoteId = (created && (created.id || (created.results && created.results.id))) || null\n\t\t\t\t} catch {\n\t\t\t\t\t// Body not JSON — the event still fires with noteId null.\n\t\t\t\t}\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tthis.replyTo = null\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to add note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tstartEdit(note) {\n\t\t\tthis.replyTo = null\n\t\t\tthis.editingNoteId = note.id\n\t\t\tthis.newNoteText = note.message || note.content || ''\n\t\t},\n\n\t\tcancelEdit() {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.newNoteText = ''\n\t\t},\n\n\t\tasync saveEdit() {\n\t\t\tif (!this.newNoteText.trim() || !this.editingNoteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\tconst noteId = this.editingNoteId\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${noteId}`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'PUT',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.editingNoteId = null\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to update note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tcanDelete(note) {\n\t\t\treturn note.actorId === OC?.currentUser || note.author === OC?.currentUser\n\t\t},\n\n\t\t/**\n\t\t * Whether this note carries a history to show.\n\t\t *\n\t\t * Read off `versionCount` rather than off `editedAt`: a note edited by\n\t\t * a since-deleted account still has its prior texts, and a marker that\n\t\t * depended on the editor still existing would quietly vanish.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {boolean} True when at least one earlier version exists.\n\t\t */\n\t\twasEdited(note) {\n\t\t\treturn Number(note.versionCount || 0) > 0\n\t\t},\n\n\t\t/**\n\t\t * The \"edited\" line beside a note's time.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {string} The rendered line.\n\t\t */\n\t\teditedLine(note) {\n\t\t\tconst editor = note.editedByDisplayName || note.editedBy\n\t\t\tif (!editor) {\n\t\t\t\treturn t('nextcloud-vue', 'Edited')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Edited by {editor}', { editor })\n\t\t},\n\n\t\t/**\n\t\t * Open the earlier versions of one note.\n\t\t *\n\t\t * @param {object} note The note to open.\n\t\t */\n\t\topenHistory(note) {\n\t\t\tthis.historyNoteId = note.id\n\t\t},\n\n\t\tasync deleteNote(note) {\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${note.id}`),\n\t\t\t\t\t{ method: 'DELETE', headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tthis.notes = this.notes.filter((n) => n.id !== note.id)\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to delete note', err)\n\t\t\t}\n\t\t},\n\n\t\tformatDate(dateStr) {\n\t\t\tif (!dateStr) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(dateStr).toLocaleString(undefined, {\n\t\t\t\t\tyear: 'numeric',\n\t\t\t\t\tmonth: 'short',\n\t\t\t\t\tday: 'numeric',\n\t\t\t\t\thour: '2-digit',\n\t\t\t\t\tminute: '2-digit',\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\treturn dateStr\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab { padding: 12px; }\n\n.cn-sidebar-tab__action { margin-bottom: 16px; }\n\n.cn-sidebar-tab__action--row { display: flex; gap: 8px; align-items: flex-end; margin-top: 8px; }\n\n.cn-sidebar-tab__composer {\n\twidth: 100%;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__list { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__thread { list-style: none; }\n\n.cn-notes-tab__thread-list { margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__reply { margin-inline-start: 24px; }\n\n.cn-notes-tab__quote {\n\tdisplay: block;\n\tmargin-bottom: 2px;\n\tpadding-inline-start: 8px;\n\tborder-inline-start: 2px solid var(--color-border-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-sidebar-tab__replying {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tmargin: 0 0 4px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-notes-tab__edited {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-inline-start: 6px;\n\tfont-style: italic;\n}\n\n</style>\n"],"names":["CnNoteBody","CnNoteComposer","CnNoteHistoryDialog","HistoryIcon","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnNotesTab.vue2.js","sources":["../../../../src/components/CnObjectSidebar/CnNotesTab.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-sidebar-tab\">\n\t\t<!-- Add / Edit note -->\n\t\t<div class=\"cn-sidebar-tab__action\">\n\t\t\t<!-- Replying: the composer says to whom, and a reply to a reply goes to the top of its thread. -->\n\t\t\t<p\n\t\t\t\tv-if=\"replyTo\"\n\t\t\t\tclass=\"cn-sidebar-tab__replying\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-notes-replying\">\n\t\t\t\t{{ replyingLabel }}\n\t\t\t\t<NcButton variant=\"tertiary\" @click=\"cancelReply\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</p>\n\t\t\t<!-- @slot composer-before An app's own control above the composer, e.g. a template picker. Scope: `{ setText, text }`; `setText(text)` fills the composer, `text` is what it holds now. -->\n\t\t\t<slot name=\"composer-before\" :setText=\"setComposerText\" :text=\"newNoteText\" />\n\t\t\t<CnNoteComposer\n\t\t\t\tclass=\"cn-sidebar-tab__composer\"\n\t\t\t\t:modelValue=\"newNoteText\"\n\t\t\t\t:placeholder=\"addNotePlaceholder\"\n\t\t\t\t:register=\"register\"\n\t\t\t\t:schema=\"schema\"\n\t\t\t\t:objectId=\"objectId\"\n\t\t\t\t:apiBase=\"apiBase\"\n\t\t\t\t@update:modelValue=\"newNoteText = $event\"\n\t\t\t\t@submit=\"submitComposer\" />\n\t\t\t<div class=\"cn-sidebar-tab__action--row\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"editingNoteId\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t@click=\"cancelEdit\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"!newNoteText.trim() || saving\"\n\t\t\t\t\t@click=\"editingNoteId ? saveEdit() : addNote()\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<Send :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ editingNoteId ? saveLabel : addNoteLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Notes list -->\n\t\t<!-- Standalone spinner: give it an accessible name so screen readers\n\t\t announce the loading state (WCAG 1.1.1 / 4.1.2 — a bare\n\t\t NcLoadingIcon renders an unlabelled role=\"img\"). -->\n\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t<div v-else-if=\"notes.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noNotesLabel }}\n\t\t</div>\n\t\t<!-- <ul>, not <div>: NcListItem renders an <li>, which WCAG 1.3.1\n\t\t requires to be contained in a <ul>/<ol> (axe \"listitem\"). -->\n\t\t<ul v-else class=\"cn-sidebar-tab__list\">\n\t\t\t<!-- One <li> per thread; inside it the note first, then its replies (oldest first), as a nested list named after the note. -->\n\t\t\t<li v-for=\"thread in threads\" :key=\"thread.note.id\" class=\"cn-notes-tab__thread\">\n\t\t\t\t<ul class=\"cn-notes-tab__thread-list\" :aria-label=\"threadLabel(thread.note)\">\n\t\t\t\t\t<NcListItem\n\t\t\t\t\t\tv-for=\"row in threadRows(thread)\"\n\t\t\t\t\t\t:key=\"row.note.id\"\n\t\t\t\t\t\t:class=\"{ 'cn-notes-tab__reply': row.isReply }\"\n\t\t\t\t\t\t:name=\"row.note.actorDisplayName || row.note.author || 'Unknown'\"\n\t\t\t\t\t\t:bold=\"false\"\n\t\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<CommentTextOutline :size=\"32\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t\t<span class=\"cn-sidebar-tab__message\">\n\t\t\t\t\t\t\t\t<span v-if=\"row.quote\" class=\"cn-notes-tab__quote\" data-testid=\"cn-note-quote\">{{ row.quote }}</span>\n\t\t\t\t\t\t\t\t<CnNoteBody :message=\"row.note.message || row.note.content || ''\" :record=\"record\" :names=\"mentionNames\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #details>\n\t\t\t\t\t\t\t{{ formatDate(row.note.creationDateTime || row.note.created) }}\n\t\t\t\t\t\t\t<!-- An edited note says so where its time is, because that\n\t\t\t\t\t\t\t is where a reader already looks to date what they are\n\t\t\t\t\t\t\t reading. The line names the editor, so nobody has to\n\t\t\t\t\t\t\t open the history to learn who changed it. -->\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"wasEdited(row.note)\"\n\t\t\t\t\t\t\t\tclass=\"cn-notes-tab__edited\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-edited\">{{ editedLine(row.note) }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"canReply || canDelete(row.note) || wasEdited(row.note) || noteActions.length > 0\" #actions>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"canReply\"\n\t\t\t\t\t\t\t\t:aria-label=\"replyAria(row.note)\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-reply-action\"\n\t\t\t\t\t\t\t\t@click=\"startReply(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Reply :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ replyLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton v-if=\"canDelete(row.note)\" @click=\"startEdit(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Pencil :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ editLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"wasEdited(row.note)\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-history-action\"\n\t\t\t\t\t\t\t\t@click=\"openHistory(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<HistoryIcon :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ historyLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton v-if=\"canDelete(row.note)\" @click=\"deleteNote(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<!-- The consuming app's own per-note actions, after the\n\t\t\t\t\t\t\t library's. They render inside this NcActions and emit\n\t\t\t\t\t\t\t `note-action`; nothing about what they DO lives here. -->\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-for=\"action in noteActions\"\n\t\t\t\t\t\t\t\t:key=\"`${row.note.id}-${action.id}`\"\n\t\t\t\t\t\t\t\t:data-testid=\"`cn-note-action-${action.id}`\"\n\t\t\t\t\t\t\t\t@click=\"$emit('note-action', { action: action.id, note: row.note })\">\n\t\t\t\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t\t\t\t<component :is=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcListItem>\n\t\t\t\t</ul>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<CnNoteHistoryDialog\n\t\t\tv-if=\"historyNoteId\"\n\t\t\t:open=\"true\"\n\t\t\t:noteId=\"historyNoteId\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@update:open=\"historyNoteId = null\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport CommentTextOutline from 'vue-material-design-icons/CommentTextOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport HistoryIcon from 'vue-material-design-icons/History.vue'\nimport Pencil from 'vue-material-design-icons/Pencil.vue'\nimport Reply from 'vue-material-design-icons/Reply.vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnNoteHistoryDialog from '../../dialogs/CnNoteHistoryDialog.vue'\nimport CnNoteBody from '../CnNoteBody/CnNoteBody.vue'\nimport CnNoteComposer from '../CnNoteComposer/CnNoteComposer.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { extractMentionedGroupIds, extractMentionedIds } from '../../utils/mentions.js'\nimport { quoteFor, supportsReplies, threadNotes, threadRootId } from '../../utils/noteThreads.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\nexport default {\n\tname: 'CnNotesTab',\n\n\tcomponents: { NcButton, NcListItem, NcActionButton, NcLoadingIcon, CnNoteBody, CnNoteComposer, CnNoteHistoryDialog, CommentTextOutline, Send, Pencil, Delete, HistoryIcon, Reply },\n\n\tprops: {\n\t\t/** ID of the object this tab belongs to */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register slug */\n\t\tregister: { type: String, default: '' },\n\t\t/** JSON Schema definition for the object */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base URL for the OpenRegister API */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Label for the add note button */\n\t\taddNoteLabel: { type: String, default: () => t('nextcloud-vue', 'Add note') },\n\t\t/** Placeholder text for the note input field */\n\t\taddNotePlaceholder: { type: String, default: () => t('nextcloud-vue', 'Write a note…') },\n\t\t/** Label for the edit action */\n\t\teditLabel: { type: String, default: () => t('nextcloud-vue', 'Edit') },\n\t\t/** Label for the save action */\n\t\tsaveLabel: { type: String, default: () => t('nextcloud-vue', 'Save') },\n\t\t/** Label for the cancel button */\n\t\tcancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },\n\t\t/** Label for the delete action */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete') },\n\t\t/** Text shown when there are no notes */\n\t\tnoNotesLabel: { type: String, default: () => t('nextcloud-vue', 'No notes yet') },\n\t\t/** Text shown while the notes are being fetched */\n\t\tloadingLabel: { type: String, default: () => t('nextcloud-vue', 'Loading notes…') },\n\t\t/** Label for the Reply action (shown only when the backend supports replies) */\n\t\treplyLabel: { type: String, default: () => t('nextcloud-vue', 'Reply') },\n\t\t/** Label for the action that opens a note's earlier versions */\n\t\thistoryLabel: { type: String, default: () => t('nextcloud-vue', 'Show earlier versions') },\n\t\t// WHY THIS PROP EXISTS, since a seam nobody asked for is the kind that\n\t\t// rots. An app-specific act on ONE note had nowhere to live: this\n\t\t// component rendered edit, history and delete and offered no fourth\n\t\t// place. Dossiq sends a single case note to a neighbouring ZGW\n\t\t// register, and its endpoint, guard, route and tests were all in place\n\t\t// while no page could call any of them. Its own change wrote that down\n\t\t// as a blocker rather than declaring a prop nothing read.\n\t\t//\n\t\t// `label` stays the app's to write and translate, so nc-vue phrases\n\t\t// nothing for somebody else's domain and adds no catalogue string here.\n\t\t/**\n\t\t * Per-note actions the consuming app owns, `[{ id, label, icon? }]`.\n\t\t * Each entry renders one NcActionButton in every note's action menu,\n\t\t * after the library's own, and clicking it emits `note-action`. Empty\n\t\t * by default, and an empty list changes nothing. `icon` is optional\n\t\t * and takes an icon component.\n\t\t */\n\t\tnoteActions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (actions) => actions.every((action) => action\n\t\t\t\t&& typeof action.id === 'string' && action.id !== ''\n\t\t\t\t&& typeof action.label === 'string' && action.label !== ''),\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted after a note containing at least one `@mention` was\n\t\t * successfully created or edited, with payload\n\t\t * `{ objectId, register, schema, noteId, mentionedUserIds }`, plus\n\t\t * `mentionedGroupIds` when the note mentions a group (`@\"group/<gid>\"`).\n\t\t * The event also fires for a note that mentions only groups. Expanding a\n\t\t * group to its members is the listener's job.\n\t\t * nc-vue never dispatches server-side notifications itself —\n\t\t * consuming apps listen to this event and notify from their own\n\t\t * backend.\n\t\t */\n\t\t'mention',\n\t\t/**\n\t\t * Emitted when one of the consuming app's `noteActions` is clicked,\n\t\t * with payload `{ action, note }`: the entry's `id` and the whole note\n\t\t * as the backend answered it. The library does the rendering and\n\t\t * nothing else, so what the action means stays in the app.\n\t\t */\n\t\t'note-action',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tnotes: [],\n\t\t\tloading: false,\n\t\t\tnewNoteText: '',\n\t\t\tsaving: false,\n\t\t\teditingNoteId: null,\n\t\t\t/** The note whose earlier versions are open, or null. */\n\t\t\thistoryNoteId: null,\n\t\t\t/**\n\t\t\t * Per-instance cache of mentioned-user display names, keyed by\n\t\t\t * user id. `null` marks an id that could not be resolved (unknown\n\t\t\t * or deleted user) so it is only looked up once.\n\t\t\t */\n\t\t\tmentionNames: {},\n\t\t\t/** The note a reply is being written to, or null. */\n\t\t\treplyTo: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {{apiBase: string, register: string, schema: string, objectId: string}} Where this record's files live, for pasted images. */\n\t\trecord() {\n\t\t\treturn { apiBase: this.apiBase, register: this.register, schema: this.schema, objectId: this.objectId }\n\t\t},\n\n\t\t/** @return {boolean} Whether the backend supports replies: its notes carry a `parentId` key. */\n\t\tcanReply() {\n\t\t\treturn supportsReplies(this.notes)\n\t\t},\n\n\t\t/** @return {Array<{note: object, replies: object[]}>} The notes as threads, in the order they came. */\n\t\tthreads() {\n\t\t\treturn threadNotes(this.notes)\n\t\t},\n\n\t\t/** @return {string} The line above the composer while replying. */\n\t\treplyingLabel() {\n\t\t\treturn this.replyTo ? t('nextcloud-vue', 'Replying to {author}', { author: this.authorOf(this.replyTo) }) : ''\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tasync fetchNotes() {\n\t\t\tif (!this.register || !this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.notes = data.results || data || []\n\t\t\t\t\tthis.resolveMentionNames()\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to fetch notes', err)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve display names for every mentioned id across the current\n\t\t * notes list. Each id is looked up at most once per component\n\t\t * instance; unresolvable ids are cached as `null`.\n\t\t */\n\t\tasync resolveMentionNames() {\n\t\t\tconst ids = new Set()\n\t\t\tfor (const note of this.notes) {\n\t\t\t\tfor (const id of extractMentionedIds(note.message || note.content || '')) {\n\t\t\t\t\tids.add(id)\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait Promise.all([...ids]\n\t\t\t\t.filter((id) => !(id in this.mentionNames))\n\t\t\t\t.map(async (id) => {\n\t\t\t\t\t// Mark as pending (null) first so concurrent calls skip it.\n\t\t\t\t\tthis.mentionNames[id] = null\n\t\t\t\t\tconst results = await searchNextcloudUsers(id)\n\t\t\t\t\tconst match = results.find((user) => user.id === id)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tthis.mentionNames[id] = match.label\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Emit the `mention` notification hook for a successfully saved note.\n\t\t *\n\t\t * @param {string} savedText The note text that was persisted.\n\t\t * @param {string|null} noteId The created/edited note's id.\n\t\t */\n\t\temitMentionEvent(savedText, noteId) {\n\t\t\tconst mentionedUserIds = extractMentionedIds(savedText)\n\t\t\tconst mentionedGroupIds = extractMentionedGroupIds(savedText)\n\t\t\tif (mentionedUserIds.length === 0 && mentionedGroupIds.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('mention', {\n\t\t\t\tobjectId: this.objectId,\n\t\t\t\tregister: this.register,\n\t\t\t\tschema: this.schema,\n\t\t\t\tnoteId,\n\t\t\t\tmentionedUserIds,\n\t\t\t\t// Only when a group is mentioned: a note that names none keeps the payload it always had.\n\t\t\t\t...(mentionedGroupIds.length > 0 ? { mentionedGroupIds } : {}),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Save from the composer (Ctrl/Cmd+Enter): the same as the button.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tsubmitComposer() {\n\t\t\tif (!this.newNoteText.trim() || this.saving) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.editingNoteId) {\n\t\t\t\tthis.saveEdit()\n\t\t\t} else {\n\t\t\t\tthis.addNote()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note A note.\n\t\t * @return {string} Its author's name.\n\t\t */\n\t\tauthorOf(note) {\n\t\t\treturn note.actorDisplayName || note.author || 'Unknown'\n\t\t},\n\n\t\t/**\n\t\t * Write a reply to a note. A reply to a reply goes to the top of its thread.\n\t\t *\n\t\t * @param {object} note The note answered.\n\t\t * @return {void}\n\t\t */\n\t\tstartReply(note) {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.replyTo = note\n\t\t},\n\n\t\tcancelReply() {\n\t\t\tthis.replyTo = null\n\t\t},\n\n\t\t/**\n\t\t * The rows of one thread: the note, then its replies. A reply whose parent\n\t\t * is far up the list carries a one-line quote of it.\n\t\t *\n\t\t * @param {{note: object, replies: object[]}} thread The thread.\n\t\t * @return {Array<{note: object, isReply: boolean, quote: string}>} The rows.\n\t\t */\n\t\tthreadRows(thread) {\n\t\t\tconst flat = this.threads.flatMap((t2) => [t2.note, ...t2.replies])\n\t\t\treturn [\n\t\t\t\t{ note: thread.note, isReply: false, quote: '' },\n\t\t\t\t...thread.replies.map((reply) => ({ note: reply, isReply: true, quote: quoteFor(reply, flat) })),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note A top-level note.\n\t\t * @return {string} The name of its thread for assistive technology.\n\t\t */\n\t\tthreadLabel(note) {\n\t\t\treturn t('nextcloud-vue', 'Note by {author} and its replies', { author: this.authorOf(note) })\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note The note a Reply button answers.\n\t\t * @return {string} The button's accessible name, naming the author.\n\t\t */\n\t\treplyAria(note) {\n\t\t\treturn t('nextcloud-vue', 'Reply to {author}', { author: this.authorOf(note) })\n\t\t},\n\n\t\tasync addNote() {\n\t\t\tif (!this.newNoteText.trim()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify(this.replyTo\n\t\t\t\t\t\t\t? { message: savedText, parentId: threadRootId(this.replyTo, this.notes) }\n\t\t\t\t\t\t\t: { message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tlet noteId = null\n\t\t\t\ttry {\n\t\t\t\t\tconst created = await response.json()\n\t\t\t\t\tnoteId = (created && (created.id || (created.results && created.results.id))) || null\n\t\t\t\t} catch {\n\t\t\t\t\t// Body not JSON — the event still fires with noteId null.\n\t\t\t\t}\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tthis.replyTo = null\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to add note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fill the composer from outside, for the `composer-before` slot.\n\t\t *\n\t\t * The app decides what goes in (a template body, a quoted line); the\n\t\t * composer stays the one place the text lives, so sending, editing and\n\t\t * the mention picker all see it exactly as if it had been typed.\n\t\t *\n\t\t * @param {string} text The text the composer should hold.\n\t\t * @return {void}\n\t\t */\n\t\tsetComposerText(text) {\n\t\t\tthis.newNoteText = (text === null || text === undefined) ? '' : String(text)\n\t\t},\n\n\t\tstartEdit(note) {\n\t\t\tthis.replyTo = null\n\t\t\tthis.editingNoteId = note.id\n\t\t\tthis.newNoteText = note.message || note.content || ''\n\t\t},\n\n\t\tcancelEdit() {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.newNoteText = ''\n\t\t},\n\n\t\tasync saveEdit() {\n\t\t\tif (!this.newNoteText.trim() || !this.editingNoteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\tconst noteId = this.editingNoteId\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${noteId}`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'PUT',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.editingNoteId = null\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to update note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tcanDelete(note) {\n\t\t\treturn note.actorId === OC?.currentUser || note.author === OC?.currentUser\n\t\t},\n\n\t\t/**\n\t\t * Whether this note carries a history to show.\n\t\t *\n\t\t * Read off `versionCount` rather than off `editedAt`: a note edited by\n\t\t * a since-deleted account still has its prior texts, and a marker that\n\t\t * depended on the editor still existing would quietly vanish.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {boolean} True when at least one earlier version exists.\n\t\t */\n\t\twasEdited(note) {\n\t\t\treturn Number(note.versionCount || 0) > 0\n\t\t},\n\n\t\t/**\n\t\t * The \"edited\" line beside a note's time.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {string} The rendered line.\n\t\t */\n\t\teditedLine(note) {\n\t\t\tconst editor = note.editedByDisplayName || note.editedBy\n\t\t\tif (!editor) {\n\t\t\t\treturn t('nextcloud-vue', 'Edited')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Edited by {editor}', { editor })\n\t\t},\n\n\t\t/**\n\t\t * Open the earlier versions of one note.\n\t\t *\n\t\t * @param {object} note The note to open.\n\t\t */\n\t\topenHistory(note) {\n\t\t\tthis.historyNoteId = note.id\n\t\t},\n\n\t\tasync deleteNote(note) {\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${note.id}`),\n\t\t\t\t\t{ method: 'DELETE', headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tthis.notes = this.notes.filter((n) => n.id !== note.id)\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to delete note', err)\n\t\t\t}\n\t\t},\n\n\t\tformatDate(dateStr) {\n\t\t\tif (!dateStr) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(dateStr).toLocaleString(undefined, {\n\t\t\t\t\tyear: 'numeric',\n\t\t\t\t\tmonth: 'short',\n\t\t\t\t\tday: 'numeric',\n\t\t\t\t\thour: '2-digit',\n\t\t\t\t\tminute: '2-digit',\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\treturn dateStr\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab { padding: 12px; }\n\n.cn-sidebar-tab__action { margin-bottom: 16px; }\n\n.cn-sidebar-tab__action--row { display: flex; gap: 8px; align-items: flex-end; margin-top: 8px; }\n\n.cn-sidebar-tab__composer {\n\twidth: 100%;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__list { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__thread { list-style: none; }\n\n.cn-notes-tab__thread-list { margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__reply { margin-inline-start: 24px; }\n\n.cn-notes-tab__quote {\n\tdisplay: block;\n\tmargin-bottom: 2px;\n\tpadding-inline-start: 8px;\n\tborder-inline-start: 2px solid var(--color-border-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-sidebar-tab__replying {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tmargin: 0 0 4px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-notes-tab__edited {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-inline-start: 6px;\n\tfont-style: italic;\n}\n\n</style>\n"],"names":["CnNoteBody","CnNoteComposer","CnNoteHistoryDialog","HistoryIcon","t"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { resolveComponent, openBlock, createElementBlock, createElementVNode, createTextVNode, toDisplayString, createVNode, withCtx, createCommentVNode, createBlock, Fragment, renderList, normalizeClass, createSlots, resolveDynamicComponent } from 'vue';
1
+ import { resolveComponent, openBlock, createElementBlock, createElementVNode, createTextVNode, toDisplayString, createVNode, withCtx, createCommentVNode, renderSlot, createBlock, Fragment, renderList, normalizeClass, createSlots, resolveDynamicComponent } from 'vue';
2
2
 
3
3
  const _hoisted_1 = { class: "cn-sidebar-tab" };
4
4
  const _hoisted_2 = { class: "cn-sidebar-tab__action" };
@@ -61,6 +61,10 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
61
61
  }, 8 /* PROPS */, ["onClick"])
62
62
  ]))
63
63
  : createCommentVNode("v-if", true),
64
+ renderSlot(_ctx.$slots, "composer-before", {
65
+ setText: $options.setComposerText,
66
+ text: $data.newNoteText
67
+ }),
64
68
  createVNode(_component_CnNoteComposer, {
65
69
  class: "cn-sidebar-tab__composer",
66
70
  modelValue: $data.newNoteText,
@@ -1 +1 @@
1
- {"version":3,"file":"CnNotesTab.vue3.js","sources":["../../../../src/components/CnObjectSidebar/CnNotesTab.vue?vue&type=template&id=6e7356a8&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-sidebar-tab\">\n\t\t<!-- Add / Edit note -->\n\t\t<div class=\"cn-sidebar-tab__action\">\n\t\t\t<!-- Replying: the composer says to whom, and a reply to a reply goes to the top of its thread. -->\n\t\t\t<p\n\t\t\t\tv-if=\"replyTo\"\n\t\t\t\tclass=\"cn-sidebar-tab__replying\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-notes-replying\">\n\t\t\t\t{{ replyingLabel }}\n\t\t\t\t<NcButton variant=\"tertiary\" @click=\"cancelReply\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</p>\n\t\t\t<CnNoteComposer\n\t\t\t\tclass=\"cn-sidebar-tab__composer\"\n\t\t\t\t:modelValue=\"newNoteText\"\n\t\t\t\t:placeholder=\"addNotePlaceholder\"\n\t\t\t\t:register=\"register\"\n\t\t\t\t:schema=\"schema\"\n\t\t\t\t:objectId=\"objectId\"\n\t\t\t\t:apiBase=\"apiBase\"\n\t\t\t\t@update:modelValue=\"newNoteText = $event\"\n\t\t\t\t@submit=\"submitComposer\" />\n\t\t\t<div class=\"cn-sidebar-tab__action--row\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"editingNoteId\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t@click=\"cancelEdit\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"!newNoteText.trim() || saving\"\n\t\t\t\t\t@click=\"editingNoteId ? saveEdit() : addNote()\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<Send :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ editingNoteId ? saveLabel : addNoteLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Notes list -->\n\t\t<!-- Standalone spinner: give it an accessible name so screen readers\n\t\t announce the loading state (WCAG 1.1.1 / 4.1.2 — a bare\n\t\t NcLoadingIcon renders an unlabelled role=\"img\"). -->\n\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t<div v-else-if=\"notes.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noNotesLabel }}\n\t\t</div>\n\t\t<!-- <ul>, not <div>: NcListItem renders an <li>, which WCAG 1.3.1\n\t\t requires to be contained in a <ul>/<ol> (axe \"listitem\"). -->\n\t\t<ul v-else class=\"cn-sidebar-tab__list\">\n\t\t\t<!-- One <li> per thread; inside it the note first, then its replies (oldest first), as a nested list named after the note. -->\n\t\t\t<li v-for=\"thread in threads\" :key=\"thread.note.id\" class=\"cn-notes-tab__thread\">\n\t\t\t\t<ul class=\"cn-notes-tab__thread-list\" :aria-label=\"threadLabel(thread.note)\">\n\t\t\t\t\t<NcListItem\n\t\t\t\t\t\tv-for=\"row in threadRows(thread)\"\n\t\t\t\t\t\t:key=\"row.note.id\"\n\t\t\t\t\t\t:class=\"{ 'cn-notes-tab__reply': row.isReply }\"\n\t\t\t\t\t\t:name=\"row.note.actorDisplayName || row.note.author || 'Unknown'\"\n\t\t\t\t\t\t:bold=\"false\"\n\t\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<CommentTextOutline :size=\"32\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t\t<span class=\"cn-sidebar-tab__message\">\n\t\t\t\t\t\t\t\t<span v-if=\"row.quote\" class=\"cn-notes-tab__quote\" data-testid=\"cn-note-quote\">{{ row.quote }}</span>\n\t\t\t\t\t\t\t\t<CnNoteBody :message=\"row.note.message || row.note.content || ''\" :record=\"record\" :names=\"mentionNames\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #details>\n\t\t\t\t\t\t\t{{ formatDate(row.note.creationDateTime || row.note.created) }}\n\t\t\t\t\t\t\t<!-- An edited note says so where its time is, because that\n\t\t\t\t\t\t\t is where a reader already looks to date what they are\n\t\t\t\t\t\t\t reading. The line names the editor, so nobody has to\n\t\t\t\t\t\t\t open the history to learn who changed it. -->\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"wasEdited(row.note)\"\n\t\t\t\t\t\t\t\tclass=\"cn-notes-tab__edited\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-edited\">{{ editedLine(row.note) }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"canReply || canDelete(row.note) || wasEdited(row.note) || noteActions.length > 0\" #actions>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"canReply\"\n\t\t\t\t\t\t\t\t:aria-label=\"replyAria(row.note)\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-reply-action\"\n\t\t\t\t\t\t\t\t@click=\"startReply(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Reply :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ replyLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton v-if=\"canDelete(row.note)\" @click=\"startEdit(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Pencil :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ editLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"wasEdited(row.note)\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-history-action\"\n\t\t\t\t\t\t\t\t@click=\"openHistory(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<HistoryIcon :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ historyLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton v-if=\"canDelete(row.note)\" @click=\"deleteNote(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<!-- The consuming app's own per-note actions, after the\n\t\t\t\t\t\t\t library's. They render inside this NcActions and emit\n\t\t\t\t\t\t\t `note-action`; nothing about what they DO lives here. -->\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-for=\"action in noteActions\"\n\t\t\t\t\t\t\t\t:key=\"`${row.note.id}-${action.id}`\"\n\t\t\t\t\t\t\t\t:data-testid=\"`cn-note-action-${action.id}`\"\n\t\t\t\t\t\t\t\t@click=\"$emit('note-action', { action: action.id, note: row.note })\">\n\t\t\t\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t\t\t\t<component :is=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcListItem>\n\t\t\t\t</ul>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<CnNoteHistoryDialog\n\t\t\tv-if=\"historyNoteId\"\n\t\t\t:open=\"true\"\n\t\t\t:noteId=\"historyNoteId\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@update:open=\"historyNoteId = null\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport CommentTextOutline from 'vue-material-design-icons/CommentTextOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport HistoryIcon from 'vue-material-design-icons/History.vue'\nimport Pencil from 'vue-material-design-icons/Pencil.vue'\nimport Reply from 'vue-material-design-icons/Reply.vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnNoteHistoryDialog from '../../dialogs/CnNoteHistoryDialog.vue'\nimport CnNoteBody from '../CnNoteBody/CnNoteBody.vue'\nimport CnNoteComposer from '../CnNoteComposer/CnNoteComposer.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { extractMentionedGroupIds, extractMentionedIds } from '../../utils/mentions.js'\nimport { quoteFor, supportsReplies, threadNotes, threadRootId } from '../../utils/noteThreads.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\nexport default {\n\tname: 'CnNotesTab',\n\n\tcomponents: { NcButton, NcListItem, NcActionButton, NcLoadingIcon, CnNoteBody, CnNoteComposer, CnNoteHistoryDialog, CommentTextOutline, Send, Pencil, Delete, HistoryIcon, Reply },\n\n\tprops: {\n\t\t/** ID of the object this tab belongs to */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register slug */\n\t\tregister: { type: String, default: '' },\n\t\t/** JSON Schema definition for the object */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base URL for the OpenRegister API */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Label for the add note button */\n\t\taddNoteLabel: { type: String, default: () => t('nextcloud-vue', 'Add note') },\n\t\t/** Placeholder text for the note input field */\n\t\taddNotePlaceholder: { type: String, default: () => t('nextcloud-vue', 'Write a note…') },\n\t\t/** Label for the edit action */\n\t\teditLabel: { type: String, default: () => t('nextcloud-vue', 'Edit') },\n\t\t/** Label for the save action */\n\t\tsaveLabel: { type: String, default: () => t('nextcloud-vue', 'Save') },\n\t\t/** Label for the cancel button */\n\t\tcancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },\n\t\t/** Label for the delete action */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete') },\n\t\t/** Text shown when there are no notes */\n\t\tnoNotesLabel: { type: String, default: () => t('nextcloud-vue', 'No notes yet') },\n\t\t/** Text shown while the notes are being fetched */\n\t\tloadingLabel: { type: String, default: () => t('nextcloud-vue', 'Loading notes…') },\n\t\t/** Label for the Reply action (shown only when the backend supports replies) */\n\t\treplyLabel: { type: String, default: () => t('nextcloud-vue', 'Reply') },\n\t\t/** Label for the action that opens a note's earlier versions */\n\t\thistoryLabel: { type: String, default: () => t('nextcloud-vue', 'Show earlier versions') },\n\t\t// WHY THIS PROP EXISTS, since a seam nobody asked for is the kind that\n\t\t// rots. An app-specific act on ONE note had nowhere to live: this\n\t\t// component rendered edit, history and delete and offered no fourth\n\t\t// place. Dossiq sends a single case note to a neighbouring ZGW\n\t\t// register, and its endpoint, guard, route and tests were all in place\n\t\t// while no page could call any of them. Its own change wrote that down\n\t\t// as a blocker rather than declaring a prop nothing read.\n\t\t//\n\t\t// `label` stays the app's to write and translate, so nc-vue phrases\n\t\t// nothing for somebody else's domain and adds no catalogue string here.\n\t\t/**\n\t\t * Per-note actions the consuming app owns, `[{ id, label, icon? }]`.\n\t\t * Each entry renders one NcActionButton in every note's action menu,\n\t\t * after the library's own, and clicking it emits `note-action`. Empty\n\t\t * by default, and an empty list changes nothing. `icon` is optional\n\t\t * and takes an icon component.\n\t\t */\n\t\tnoteActions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (actions) => actions.every((action) => action\n\t\t\t\t&& typeof action.id === 'string' && action.id !== ''\n\t\t\t\t&& typeof action.label === 'string' && action.label !== ''),\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted after a note containing at least one `@mention` was\n\t\t * successfully created or edited, with payload\n\t\t * `{ objectId, register, schema, noteId, mentionedUserIds }`, plus\n\t\t * `mentionedGroupIds` when the note mentions a group (`@\"group/<gid>\"`).\n\t\t * The event also fires for a note that mentions only groups. Expanding a\n\t\t * group to its members is the listener's job.\n\t\t * nc-vue never dispatches server-side notifications itself —\n\t\t * consuming apps listen to this event and notify from their own\n\t\t * backend.\n\t\t */\n\t\t'mention',\n\t\t/**\n\t\t * Emitted when one of the consuming app's `noteActions` is clicked,\n\t\t * with payload `{ action, note }`: the entry's `id` and the whole note\n\t\t * as the backend answered it. The library does the rendering and\n\t\t * nothing else, so what the action means stays in the app.\n\t\t */\n\t\t'note-action',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tnotes: [],\n\t\t\tloading: false,\n\t\t\tnewNoteText: '',\n\t\t\tsaving: false,\n\t\t\teditingNoteId: null,\n\t\t\t/** The note whose earlier versions are open, or null. */\n\t\t\thistoryNoteId: null,\n\t\t\t/**\n\t\t\t * Per-instance cache of mentioned-user display names, keyed by\n\t\t\t * user id. `null` marks an id that could not be resolved (unknown\n\t\t\t * or deleted user) so it is only looked up once.\n\t\t\t */\n\t\t\tmentionNames: {},\n\t\t\t/** The note a reply is being written to, or null. */\n\t\t\treplyTo: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {{apiBase: string, register: string, schema: string, objectId: string}} Where this record's files live, for pasted images. */\n\t\trecord() {\n\t\t\treturn { apiBase: this.apiBase, register: this.register, schema: this.schema, objectId: this.objectId }\n\t\t},\n\n\t\t/** @return {boolean} Whether the backend supports replies: its notes carry a `parentId` key. */\n\t\tcanReply() {\n\t\t\treturn supportsReplies(this.notes)\n\t\t},\n\n\t\t/** @return {Array<{note: object, replies: object[]}>} The notes as threads, in the order they came. */\n\t\tthreads() {\n\t\t\treturn threadNotes(this.notes)\n\t\t},\n\n\t\t/** @return {string} The line above the composer while replying. */\n\t\treplyingLabel() {\n\t\t\treturn this.replyTo ? t('nextcloud-vue', 'Replying to {author}', { author: this.authorOf(this.replyTo) }) : ''\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tasync fetchNotes() {\n\t\t\tif (!this.register || !this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.notes = data.results || data || []\n\t\t\t\t\tthis.resolveMentionNames()\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to fetch notes', err)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve display names for every mentioned id across the current\n\t\t * notes list. Each id is looked up at most once per component\n\t\t * instance; unresolvable ids are cached as `null`.\n\t\t */\n\t\tasync resolveMentionNames() {\n\t\t\tconst ids = new Set()\n\t\t\tfor (const note of this.notes) {\n\t\t\t\tfor (const id of extractMentionedIds(note.message || note.content || '')) {\n\t\t\t\t\tids.add(id)\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait Promise.all([...ids]\n\t\t\t\t.filter((id) => !(id in this.mentionNames))\n\t\t\t\t.map(async (id) => {\n\t\t\t\t\t// Mark as pending (null) first so concurrent calls skip it.\n\t\t\t\t\tthis.mentionNames[id] = null\n\t\t\t\t\tconst results = await searchNextcloudUsers(id)\n\t\t\t\t\tconst match = results.find((user) => user.id === id)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tthis.mentionNames[id] = match.label\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Emit the `mention` notification hook for a successfully saved note.\n\t\t *\n\t\t * @param {string} savedText The note text that was persisted.\n\t\t * @param {string|null} noteId The created/edited note's id.\n\t\t */\n\t\temitMentionEvent(savedText, noteId) {\n\t\t\tconst mentionedUserIds = extractMentionedIds(savedText)\n\t\t\tconst mentionedGroupIds = extractMentionedGroupIds(savedText)\n\t\t\tif (mentionedUserIds.length === 0 && mentionedGroupIds.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('mention', {\n\t\t\t\tobjectId: this.objectId,\n\t\t\t\tregister: this.register,\n\t\t\t\tschema: this.schema,\n\t\t\t\tnoteId,\n\t\t\t\tmentionedUserIds,\n\t\t\t\t// Only when a group is mentioned: a note that names none keeps the payload it always had.\n\t\t\t\t...(mentionedGroupIds.length > 0 ? { mentionedGroupIds } : {}),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Save from the composer (Ctrl/Cmd+Enter): the same as the button.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tsubmitComposer() {\n\t\t\tif (!this.newNoteText.trim() || this.saving) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.editingNoteId) {\n\t\t\t\tthis.saveEdit()\n\t\t\t} else {\n\t\t\t\tthis.addNote()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note A note.\n\t\t * @return {string} Its author's name.\n\t\t */\n\t\tauthorOf(note) {\n\t\t\treturn note.actorDisplayName || note.author || 'Unknown'\n\t\t},\n\n\t\t/**\n\t\t * Write a reply to a note. A reply to a reply goes to the top of its thread.\n\t\t *\n\t\t * @param {object} note The note answered.\n\t\t * @return {void}\n\t\t */\n\t\tstartReply(note) {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.replyTo = note\n\t\t},\n\n\t\tcancelReply() {\n\t\t\tthis.replyTo = null\n\t\t},\n\n\t\t/**\n\t\t * The rows of one thread: the note, then its replies. A reply whose parent\n\t\t * is far up the list carries a one-line quote of it.\n\t\t *\n\t\t * @param {{note: object, replies: object[]}} thread The thread.\n\t\t * @return {Array<{note: object, isReply: boolean, quote: string}>} The rows.\n\t\t */\n\t\tthreadRows(thread) {\n\t\t\tconst flat = this.threads.flatMap((t2) => [t2.note, ...t2.replies])\n\t\t\treturn [\n\t\t\t\t{ note: thread.note, isReply: false, quote: '' },\n\t\t\t\t...thread.replies.map((reply) => ({ note: reply, isReply: true, quote: quoteFor(reply, flat) })),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note A top-level note.\n\t\t * @return {string} The name of its thread for assistive technology.\n\t\t */\n\t\tthreadLabel(note) {\n\t\t\treturn t('nextcloud-vue', 'Note by {author} and its replies', { author: this.authorOf(note) })\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note The note a Reply button answers.\n\t\t * @return {string} The button's accessible name, naming the author.\n\t\t */\n\t\treplyAria(note) {\n\t\t\treturn t('nextcloud-vue', 'Reply to {author}', { author: this.authorOf(note) })\n\t\t},\n\n\t\tasync addNote() {\n\t\t\tif (!this.newNoteText.trim()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify(this.replyTo\n\t\t\t\t\t\t\t? { message: savedText, parentId: threadRootId(this.replyTo, this.notes) }\n\t\t\t\t\t\t\t: { message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tlet noteId = null\n\t\t\t\ttry {\n\t\t\t\t\tconst created = await response.json()\n\t\t\t\t\tnoteId = (created && (created.id || (created.results && created.results.id))) || null\n\t\t\t\t} catch {\n\t\t\t\t\t// Body not JSON — the event still fires with noteId null.\n\t\t\t\t}\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tthis.replyTo = null\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to add note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tstartEdit(note) {\n\t\t\tthis.replyTo = null\n\t\t\tthis.editingNoteId = note.id\n\t\t\tthis.newNoteText = note.message || note.content || ''\n\t\t},\n\n\t\tcancelEdit() {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.newNoteText = ''\n\t\t},\n\n\t\tasync saveEdit() {\n\t\t\tif (!this.newNoteText.trim() || !this.editingNoteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\tconst noteId = this.editingNoteId\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${noteId}`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'PUT',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.editingNoteId = null\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to update note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tcanDelete(note) {\n\t\t\treturn note.actorId === OC?.currentUser || note.author === OC?.currentUser\n\t\t},\n\n\t\t/**\n\t\t * Whether this note carries a history to show.\n\t\t *\n\t\t * Read off `versionCount` rather than off `editedAt`: a note edited by\n\t\t * a since-deleted account still has its prior texts, and a marker that\n\t\t * depended on the editor still existing would quietly vanish.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {boolean} True when at least one earlier version exists.\n\t\t */\n\t\twasEdited(note) {\n\t\t\treturn Number(note.versionCount || 0) > 0\n\t\t},\n\n\t\t/**\n\t\t * The \"edited\" line beside a note's time.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {string} The rendered line.\n\t\t */\n\t\teditedLine(note) {\n\t\t\tconst editor = note.editedByDisplayName || note.editedBy\n\t\t\tif (!editor) {\n\t\t\t\treturn t('nextcloud-vue', 'Edited')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Edited by {editor}', { editor })\n\t\t},\n\n\t\t/**\n\t\t * Open the earlier versions of one note.\n\t\t *\n\t\t * @param {object} note The note to open.\n\t\t */\n\t\topenHistory(note) {\n\t\t\tthis.historyNoteId = note.id\n\t\t},\n\n\t\tasync deleteNote(note) {\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${note.id}`),\n\t\t\t\t\t{ method: 'DELETE', headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tthis.notes = this.notes.filter((n) => n.id !== note.id)\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to delete note', err)\n\t\t\t}\n\t\t},\n\n\t\tformatDate(dateStr) {\n\t\t\tif (!dateStr) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(dateStr).toLocaleString(undefined, {\n\t\t\t\t\tyear: 'numeric',\n\t\t\t\t\tmonth: 'short',\n\t\t\t\t\tday: 'numeric',\n\t\t\t\t\thour: '2-digit',\n\t\t\t\t\tminute: '2-digit',\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\treturn dateStr\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab { padding: 12px; }\n\n.cn-sidebar-tab__action { margin-bottom: 16px; }\n\n.cn-sidebar-tab__action--row { display: flex; gap: 8px; align-items: flex-end; margin-top: 8px; }\n\n.cn-sidebar-tab__composer {\n\twidth: 100%;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__list { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__thread { list-style: none; }\n\n.cn-notes-tab__thread-list { margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__reply { margin-inline-start: 24px; }\n\n.cn-notes-tab__quote {\n\tdisplay: block;\n\tmargin-bottom: 2px;\n\tpadding-inline-start: 8px;\n\tborder-inline-start: 2px solid var(--color-border-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-sidebar-tab__replying {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tmargin: 0 0 4px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-notes-tab__edited {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-inline-start: 6px;\n\tfont-style: italic;\n}\n\n</style>\n"],"names":["_openBlock","_createElementBlock","_createTextVNode","_toDisplayString","_createVNode","_createElementVNode","_createBlock","_Fragment","_renderList","_normalizeClass","_resolveDynamicComponent"],"mappings":"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"}
1
+ {"version":3,"file":"CnNotesTab.vue3.js","sources":["../../../../src/components/CnObjectSidebar/CnNotesTab.vue?vue&type=template&id=6e7356a8&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-sidebar-tab\">\n\t\t<!-- Add / Edit note -->\n\t\t<div class=\"cn-sidebar-tab__action\">\n\t\t\t<!-- Replying: the composer says to whom, and a reply to a reply goes to the top of its thread. -->\n\t\t\t<p\n\t\t\t\tv-if=\"replyTo\"\n\t\t\t\tclass=\"cn-sidebar-tab__replying\"\n\t\t\t\trole=\"status\"\n\t\t\t\tdata-testid=\"cn-notes-replying\">\n\t\t\t\t{{ replyingLabel }}\n\t\t\t\t<NcButton variant=\"tertiary\" @click=\"cancelReply\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</p>\n\t\t\t<!-- @slot composer-before An app's own control above the composer, e.g. a template picker. Scope: `{ setText, text }`; `setText(text)` fills the composer, `text` is what it holds now. -->\n\t\t\t<slot name=\"composer-before\" :setText=\"setComposerText\" :text=\"newNoteText\" />\n\t\t\t<CnNoteComposer\n\t\t\t\tclass=\"cn-sidebar-tab__composer\"\n\t\t\t\t:modelValue=\"newNoteText\"\n\t\t\t\t:placeholder=\"addNotePlaceholder\"\n\t\t\t\t:register=\"register\"\n\t\t\t\t:schema=\"schema\"\n\t\t\t\t:objectId=\"objectId\"\n\t\t\t\t:apiBase=\"apiBase\"\n\t\t\t\t@update:modelValue=\"newNoteText = $event\"\n\t\t\t\t@submit=\"submitComposer\" />\n\t\t\t<div class=\"cn-sidebar-tab__action--row\">\n\t\t\t\t<NcButton\n\t\t\t\t\tv-if=\"editingNoteId\"\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t@click=\"cancelEdit\">\n\t\t\t\t\t{{ cancelLabel }}\n\t\t\t\t</NcButton>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t:disabled=\"!newNoteText.trim() || saving\"\n\t\t\t\t\t@click=\"editingNoteId ? saveEdit() : addNote()\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<Send :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ editingNoteId ? saveLabel : addNoteLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Notes list -->\n\t\t<!-- Standalone spinner: give it an accessible name so screen readers\n\t\t announce the loading state (WCAG 1.1.1 / 4.1.2 — a bare\n\t\t NcLoadingIcon renders an unlabelled role=\"img\"). -->\n\t\t<NcLoadingIcon v-if=\"loading\" :name=\"loadingLabel\" />\n\t\t<div v-else-if=\"notes.length === 0\" class=\"cn-sidebar-tab__empty\">\n\t\t\t{{ noNotesLabel }}\n\t\t</div>\n\t\t<!-- <ul>, not <div>: NcListItem renders an <li>, which WCAG 1.3.1\n\t\t requires to be contained in a <ul>/<ol> (axe \"listitem\"). -->\n\t\t<ul v-else class=\"cn-sidebar-tab__list\">\n\t\t\t<!-- One <li> per thread; inside it the note first, then its replies (oldest first), as a nested list named after the note. -->\n\t\t\t<li v-for=\"thread in threads\" :key=\"thread.note.id\" class=\"cn-notes-tab__thread\">\n\t\t\t\t<ul class=\"cn-notes-tab__thread-list\" :aria-label=\"threadLabel(thread.note)\">\n\t\t\t\t\t<NcListItem\n\t\t\t\t\t\tv-for=\"row in threadRows(thread)\"\n\t\t\t\t\t\t:key=\"row.note.id\"\n\t\t\t\t\t\t:class=\"{ 'cn-notes-tab__reply': row.isReply }\"\n\t\t\t\t\t\t:name=\"row.note.actorDisplayName || row.note.author || 'Unknown'\"\n\t\t\t\t\t\t:bold=\"false\"\n\t\t\t\t\t\t:forceDisplayActions=\"true\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<CommentTextOutline :size=\"32\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #subname>\n\t\t\t\t\t\t\t<span class=\"cn-sidebar-tab__message\">\n\t\t\t\t\t\t\t\t<span v-if=\"row.quote\" class=\"cn-notes-tab__quote\" data-testid=\"cn-note-quote\">{{ row.quote }}</span>\n\t\t\t\t\t\t\t\t<CnNoteBody :message=\"row.note.message || row.note.content || ''\" :record=\"record\" :names=\"mentionNames\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template #details>\n\t\t\t\t\t\t\t{{ formatDate(row.note.creationDateTime || row.note.created) }}\n\t\t\t\t\t\t\t<!-- An edited note says so where its time is, because that\n\t\t\t\t\t\t\t is where a reader already looks to date what they are\n\t\t\t\t\t\t\t reading. The line names the editor, so nobody has to\n\t\t\t\t\t\t\t open the history to learn who changed it. -->\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"wasEdited(row.note)\"\n\t\t\t\t\t\t\t\tclass=\"cn-notes-tab__edited\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-edited\">{{ editedLine(row.note) }}</span>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"canReply || canDelete(row.note) || wasEdited(row.note) || noteActions.length > 0\" #actions>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"canReply\"\n\t\t\t\t\t\t\t\t:aria-label=\"replyAria(row.note)\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-reply-action\"\n\t\t\t\t\t\t\t\t@click=\"startReply(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Reply :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ replyLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton v-if=\"canDelete(row.note)\" @click=\"startEdit(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Pencil :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ editLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-if=\"wasEdited(row.note)\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-note-history-action\"\n\t\t\t\t\t\t\t\t@click=\"openHistory(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<HistoryIcon :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ historyLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<NcActionButton v-if=\"canDelete(row.note)\" @click=\"deleteNote(row.note)\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<Delete :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ deleteLabel }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t<!-- The consuming app's own per-note actions, after the\n\t\t\t\t\t\t\t library's. They render inside this NcActions and emit\n\t\t\t\t\t\t\t `note-action`; nothing about what they DO lives here. -->\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-for=\"action in noteActions\"\n\t\t\t\t\t\t\t\t:key=\"`${row.note.id}-${action.id}`\"\n\t\t\t\t\t\t\t\t:data-testid=\"`cn-note-action-${action.id}`\"\n\t\t\t\t\t\t\t\t@click=\"$emit('note-action', { action: action.id, note: row.note })\">\n\t\t\t\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t\t\t\t<component :is=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcListItem>\n\t\t\t\t</ul>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<CnNoteHistoryDialog\n\t\t\tv-if=\"historyNoteId\"\n\t\t\t:open=\"true\"\n\t\t\t:noteId=\"historyNoteId\"\n\t\t\t:objectId=\"objectId\"\n\t\t\t:register=\"register\"\n\t\t\t:schema=\"schema\"\n\t\t\t:apiBase=\"apiBase\"\n\t\t\t@update:open=\"historyNoteId = null\" />\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcActionButton, NcButton, NcListItem, NcLoadingIcon } from '@nextcloud/vue'\nimport CommentTextOutline from 'vue-material-design-icons/CommentTextOutline.vue'\nimport Delete from 'vue-material-design-icons/Delete.vue'\nimport HistoryIcon from 'vue-material-design-icons/History.vue'\nimport Pencil from 'vue-material-design-icons/Pencil.vue'\nimport Reply from 'vue-material-design-icons/Reply.vue'\nimport Send from 'vue-material-design-icons/Send.vue'\nimport CnNoteHistoryDialog from '../../dialogs/CnNoteHistoryDialog.vue'\nimport CnNoteBody from '../CnNoteBody/CnNoteBody.vue'\nimport CnNoteComposer from '../CnNoteComposer/CnNoteComposer.vue'\nimport { buildHeaders, prefixUrl } from '../../utils/index.js'\nimport { extractMentionedGroupIds, extractMentionedIds } from '../../utils/mentions.js'\nimport { quoteFor, supportsReplies, threadNotes, threadRootId } from '../../utils/noteThreads.js'\nimport { searchNextcloudUsers } from '../../utils/userAutocomplete.js'\n\nexport default {\n\tname: 'CnNotesTab',\n\n\tcomponents: { NcButton, NcListItem, NcActionButton, NcLoadingIcon, CnNoteBody, CnNoteComposer, CnNoteHistoryDialog, CommentTextOutline, Send, Pencil, Delete, HistoryIcon, Reply },\n\n\tprops: {\n\t\t/** ID of the object this tab belongs to */\n\t\tobjectId: { type: String, required: true },\n\t\t/** OpenRegister register slug */\n\t\tregister: { type: String, default: '' },\n\t\t/** JSON Schema definition for the object */\n\t\tschema: { type: String, default: '' },\n\t\t/** Base URL for the OpenRegister API */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/** Label for the add note button */\n\t\taddNoteLabel: { type: String, default: () => t('nextcloud-vue', 'Add note') },\n\t\t/** Placeholder text for the note input field */\n\t\taddNotePlaceholder: { type: String, default: () => t('nextcloud-vue', 'Write a note…') },\n\t\t/** Label for the edit action */\n\t\teditLabel: { type: String, default: () => t('nextcloud-vue', 'Edit') },\n\t\t/** Label for the save action */\n\t\tsaveLabel: { type: String, default: () => t('nextcloud-vue', 'Save') },\n\t\t/** Label for the cancel button */\n\t\tcancelLabel: { type: String, default: () => t('nextcloud-vue', 'Cancel') },\n\t\t/** Label for the delete action */\n\t\tdeleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete') },\n\t\t/** Text shown when there are no notes */\n\t\tnoNotesLabel: { type: String, default: () => t('nextcloud-vue', 'No notes yet') },\n\t\t/** Text shown while the notes are being fetched */\n\t\tloadingLabel: { type: String, default: () => t('nextcloud-vue', 'Loading notes…') },\n\t\t/** Label for the Reply action (shown only when the backend supports replies) */\n\t\treplyLabel: { type: String, default: () => t('nextcloud-vue', 'Reply') },\n\t\t/** Label for the action that opens a note's earlier versions */\n\t\thistoryLabel: { type: String, default: () => t('nextcloud-vue', 'Show earlier versions') },\n\t\t// WHY THIS PROP EXISTS, since a seam nobody asked for is the kind that\n\t\t// rots. An app-specific act on ONE note had nowhere to live: this\n\t\t// component rendered edit, history and delete and offered no fourth\n\t\t// place. Dossiq sends a single case note to a neighbouring ZGW\n\t\t// register, and its endpoint, guard, route and tests were all in place\n\t\t// while no page could call any of them. Its own change wrote that down\n\t\t// as a blocker rather than declaring a prop nothing read.\n\t\t//\n\t\t// `label` stays the app's to write and translate, so nc-vue phrases\n\t\t// nothing for somebody else's domain and adds no catalogue string here.\n\t\t/**\n\t\t * Per-note actions the consuming app owns, `[{ id, label, icon? }]`.\n\t\t * Each entry renders one NcActionButton in every note's action menu,\n\t\t * after the library's own, and clicking it emits `note-action`. Empty\n\t\t * by default, and an empty list changes nothing. `icon` is optional\n\t\t * and takes an icon component.\n\t\t */\n\t\tnoteActions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t\tvalidator: (actions) => actions.every((action) => action\n\t\t\t\t&& typeof action.id === 'string' && action.id !== ''\n\t\t\t\t&& typeof action.label === 'string' && action.label !== ''),\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted after a note containing at least one `@mention` was\n\t\t * successfully created or edited, with payload\n\t\t * `{ objectId, register, schema, noteId, mentionedUserIds }`, plus\n\t\t * `mentionedGroupIds` when the note mentions a group (`@\"group/<gid>\"`).\n\t\t * The event also fires for a note that mentions only groups. Expanding a\n\t\t * group to its members is the listener's job.\n\t\t * nc-vue never dispatches server-side notifications itself —\n\t\t * consuming apps listen to this event and notify from their own\n\t\t * backend.\n\t\t */\n\t\t'mention',\n\t\t/**\n\t\t * Emitted when one of the consuming app's `noteActions` is clicked,\n\t\t * with payload `{ action, note }`: the entry's `id` and the whole note\n\t\t * as the backend answered it. The library does the rendering and\n\t\t * nothing else, so what the action means stays in the app.\n\t\t */\n\t\t'note-action',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tnotes: [],\n\t\t\tloading: false,\n\t\t\tnewNoteText: '',\n\t\t\tsaving: false,\n\t\t\teditingNoteId: null,\n\t\t\t/** The note whose earlier versions are open, or null. */\n\t\t\thistoryNoteId: null,\n\t\t\t/**\n\t\t\t * Per-instance cache of mentioned-user display names, keyed by\n\t\t\t * user id. `null` marks an id that could not be resolved (unknown\n\t\t\t * or deleted user) so it is only looked up once.\n\t\t\t */\n\t\t\tmentionNames: {},\n\t\t\t/** The note a reply is being written to, or null. */\n\t\t\treplyTo: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {{apiBase: string, register: string, schema: string, objectId: string}} Where this record's files live, for pasted images. */\n\t\trecord() {\n\t\t\treturn { apiBase: this.apiBase, register: this.register, schema: this.schema, objectId: this.objectId }\n\t\t},\n\n\t\t/** @return {boolean} Whether the backend supports replies: its notes carry a `parentId` key. */\n\t\tcanReply() {\n\t\t\treturn supportsReplies(this.notes)\n\t\t},\n\n\t\t/** @return {Array<{note: object, replies: object[]}>} The notes as threads, in the order they came. */\n\t\tthreads() {\n\t\t\treturn threadNotes(this.notes)\n\t\t},\n\n\t\t/** @return {string} The line above the composer while replying. */\n\t\treplyingLabel() {\n\t\t\treturn this.replyTo ? t('nextcloud-vue', 'Replying to {author}', { author: this.authorOf(this.replyTo) }) : ''\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectId: {\n\t\t\timmediate: true,\n\t\t\thandler(id) {\n\t\t\t\tif (id) {\n\t\t\t\t\tthis.fetchNotes()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\tasync fetchNotes() {\n\t\t\tif (!this.register || !this.schema) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{ headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = await response.json()\n\t\t\t\t\tthis.notes = data.results || data || []\n\t\t\t\t\tthis.resolveMentionNames()\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to fetch notes', err)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Resolve display names for every mentioned id across the current\n\t\t * notes list. Each id is looked up at most once per component\n\t\t * instance; unresolvable ids are cached as `null`.\n\t\t */\n\t\tasync resolveMentionNames() {\n\t\t\tconst ids = new Set()\n\t\t\tfor (const note of this.notes) {\n\t\t\t\tfor (const id of extractMentionedIds(note.message || note.content || '')) {\n\t\t\t\t\tids.add(id)\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait Promise.all([...ids]\n\t\t\t\t.filter((id) => !(id in this.mentionNames))\n\t\t\t\t.map(async (id) => {\n\t\t\t\t\t// Mark as pending (null) first so concurrent calls skip it.\n\t\t\t\t\tthis.mentionNames[id] = null\n\t\t\t\t\tconst results = await searchNextcloudUsers(id)\n\t\t\t\t\tconst match = results.find((user) => user.id === id)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tthis.mentionNames[id] = match.label\n\t\t\t\t\t}\n\t\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * Emit the `mention` notification hook for a successfully saved note.\n\t\t *\n\t\t * @param {string} savedText The note text that was persisted.\n\t\t * @param {string|null} noteId The created/edited note's id.\n\t\t */\n\t\temitMentionEvent(savedText, noteId) {\n\t\t\tconst mentionedUserIds = extractMentionedIds(savedText)\n\t\t\tconst mentionedGroupIds = extractMentionedGroupIds(savedText)\n\t\t\tif (mentionedUserIds.length === 0 && mentionedGroupIds.length === 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('mention', {\n\t\t\t\tobjectId: this.objectId,\n\t\t\t\tregister: this.register,\n\t\t\t\tschema: this.schema,\n\t\t\t\tnoteId,\n\t\t\t\tmentionedUserIds,\n\t\t\t\t// Only when a group is mentioned: a note that names none keeps the payload it always had.\n\t\t\t\t...(mentionedGroupIds.length > 0 ? { mentionedGroupIds } : {}),\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Save from the composer (Ctrl/Cmd+Enter): the same as the button.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tsubmitComposer() {\n\t\t\tif (!this.newNoteText.trim() || this.saving) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.editingNoteId) {\n\t\t\t\tthis.saveEdit()\n\t\t\t} else {\n\t\t\t\tthis.addNote()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note A note.\n\t\t * @return {string} Its author's name.\n\t\t */\n\t\tauthorOf(note) {\n\t\t\treturn note.actorDisplayName || note.author || 'Unknown'\n\t\t},\n\n\t\t/**\n\t\t * Write a reply to a note. A reply to a reply goes to the top of its thread.\n\t\t *\n\t\t * @param {object} note The note answered.\n\t\t * @return {void}\n\t\t */\n\t\tstartReply(note) {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.replyTo = note\n\t\t},\n\n\t\tcancelReply() {\n\t\t\tthis.replyTo = null\n\t\t},\n\n\t\t/**\n\t\t * The rows of one thread: the note, then its replies. A reply whose parent\n\t\t * is far up the list carries a one-line quote of it.\n\t\t *\n\t\t * @param {{note: object, replies: object[]}} thread The thread.\n\t\t * @return {Array<{note: object, isReply: boolean, quote: string}>} The rows.\n\t\t */\n\t\tthreadRows(thread) {\n\t\t\tconst flat = this.threads.flatMap((t2) => [t2.note, ...t2.replies])\n\t\t\treturn [\n\t\t\t\t{ note: thread.note, isReply: false, quote: '' },\n\t\t\t\t...thread.replies.map((reply) => ({ note: reply, isReply: true, quote: quoteFor(reply, flat) })),\n\t\t\t]\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note A top-level note.\n\t\t * @return {string} The name of its thread for assistive technology.\n\t\t */\n\t\tthreadLabel(note) {\n\t\t\treturn t('nextcloud-vue', 'Note by {author} and its replies', { author: this.authorOf(note) })\n\t\t},\n\n\t\t/**\n\t\t * @param {object} note The note a Reply button answers.\n\t\t * @return {string} The button's accessible name, naming the author.\n\t\t */\n\t\treplyAria(note) {\n\t\t\treturn t('nextcloud-vue', 'Reply to {author}', { author: this.authorOf(note) })\n\t\t},\n\n\t\tasync addNote() {\n\t\t\tif (!this.newNoteText.trim()) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify(this.replyTo\n\t\t\t\t\t\t\t? { message: savedText, parentId: threadRootId(this.replyTo, this.notes) }\n\t\t\t\t\t\t\t: { message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tlet noteId = null\n\t\t\t\ttry {\n\t\t\t\t\tconst created = await response.json()\n\t\t\t\t\tnoteId = (created && (created.id || (created.results && created.results.id))) || null\n\t\t\t\t} catch {\n\t\t\t\t\t// Body not JSON — the event still fires with noteId null.\n\t\t\t\t}\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tthis.replyTo = null\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to add note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Fill the composer from outside, for the `composer-before` slot.\n\t\t *\n\t\t * The app decides what goes in (a template body, a quoted line); the\n\t\t * composer stays the one place the text lives, so sending, editing and\n\t\t * the mention picker all see it exactly as if it had been typed.\n\t\t *\n\t\t * @param {string} text The text the composer should hold.\n\t\t * @return {void}\n\t\t */\n\t\tsetComposerText(text) {\n\t\t\tthis.newNoteText = (text === null || text === undefined) ? '' : String(text)\n\t\t},\n\n\t\tstartEdit(note) {\n\t\t\tthis.replyTo = null\n\t\t\tthis.editingNoteId = note.id\n\t\t\tthis.newNoteText = note.message || note.content || ''\n\t\t},\n\n\t\tcancelEdit() {\n\t\t\tthis.editingNoteId = null\n\t\t\tthis.newNoteText = ''\n\t\t},\n\n\t\tasync saveEdit() {\n\t\t\tif (!this.newNoteText.trim() || !this.editingNoteId) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.saving = true\n\t\t\tconst savedText = this.newNoteText.trim()\n\t\t\tconst noteId = this.editingNoteId\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${noteId}`),\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: 'PUT',\n\t\t\t\t\t\theaders: buildHeaders(),\n\t\t\t\t\t\tbody: JSON.stringify({ message: savedText }),\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t\t\tthis.emitMentionEvent(savedText, noteId)\n\t\t\t\tthis.editingNoteId = null\n\t\t\t\tthis.newNoteText = ''\n\t\t\t\tawait this.fetchNotes()\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to update note', err)\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\n\t\tcanDelete(note) {\n\t\t\treturn note.actorId === OC?.currentUser || note.author === OC?.currentUser\n\t\t},\n\n\t\t/**\n\t\t * Whether this note carries a history to show.\n\t\t *\n\t\t * Read off `versionCount` rather than off `editedAt`: a note edited by\n\t\t * a since-deleted account still has its prior texts, and a marker that\n\t\t * depended on the editor still existing would quietly vanish.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {boolean} True when at least one earlier version exists.\n\t\t */\n\t\twasEdited(note) {\n\t\t\treturn Number(note.versionCount || 0) > 0\n\t\t},\n\n\t\t/**\n\t\t * The \"edited\" line beside a note's time.\n\t\t *\n\t\t * @param {object} note The note from the backend.\n\t\t * @return {string} The rendered line.\n\t\t */\n\t\teditedLine(note) {\n\t\t\tconst editor = note.editedByDisplayName || note.editedBy\n\t\t\tif (!editor) {\n\t\t\t\treturn t('nextcloud-vue', 'Edited')\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', 'Edited by {editor}', { editor })\n\t\t},\n\n\t\t/**\n\t\t * Open the earlier versions of one note.\n\t\t *\n\t\t * @param {object} note The note to open.\n\t\t */\n\t\topenHistory(note) {\n\t\t\tthis.historyNoteId = note.id\n\t\t},\n\n\t\tasync deleteNote(note) {\n\t\t\ttry {\n\t\t\t\tawait fetch(\n\t\t\t\t\tprefixUrl(`${this.apiBase}/objects/${this.register}/${this.schema}/${this.objectId}/notes/${note.id}`),\n\t\t\t\t\t{ method: 'DELETE', headers: buildHeaders() },\n\t\t\t\t)\n\t\t\t\tthis.notes = this.notes.filter((n) => n.id !== note.id)\n\t\t\t} catch (err) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('CnNotesTab: Failed to delete note', err)\n\t\t\t}\n\t\t},\n\n\t\tformatDate(dateStr) {\n\t\t\tif (!dateStr) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Date(dateStr).toLocaleString(undefined, {\n\t\t\t\t\tyear: 'numeric',\n\t\t\t\t\tmonth: 'short',\n\t\t\t\t\tday: 'numeric',\n\t\t\t\t\thour: '2-digit',\n\t\t\t\t\tminute: '2-digit',\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\treturn dateStr\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-sidebar-tab { padding: 12px; }\n\n.cn-sidebar-tab__action { margin-bottom: 16px; }\n\n.cn-sidebar-tab__action--row { display: flex; gap: 8px; align-items: flex-end; margin-top: 8px; }\n\n.cn-sidebar-tab__composer {\n\twidth: 100%;\n}\n\n.cn-sidebar-tab__empty {\n\ttext-align: center;\n\tpadding: 24px 12px;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-sidebar-tab__list { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__thread { list-style: none; }\n\n.cn-notes-tab__thread-list { margin: 0; padding: 0; list-style: none; }\n\n.cn-notes-tab__reply { margin-inline-start: 24px; }\n\n.cn-notes-tab__quote {\n\tdisplay: block;\n\tmargin-bottom: 2px;\n\tpadding-inline-start: 8px;\n\tborder-inline-start: 2px solid var(--color-border-dark);\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n}\n\n.cn-sidebar-tab__replying {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tmargin: 0 0 4px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-notes-tab__edited {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin-inline-start: 6px;\n\tfont-style: italic;\n}\n\n</style>\n"],"names":["_openBlock","_createElementBlock","_createTextVNode","_toDisplayString","_createVNode","_renderSlot","_createElementVNode","_createBlock","_Fragment","_renderList","_normalizeClass","_resolveDynamicComponent"],"mappings":"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"}
@@ -6,6 +6,7 @@ import __sfc_main from '../CnIcon/CnIcon.vue.js';
6
6
  import __sfc_main$1 from '../CnStatusBadge/CnStatusBadge.vue.js';
7
7
  import { isBadgeVariant } from '../../utils/badgeVariants.js';
8
8
  import { firstFilled, fillTemplate } from '../../utils/fillTemplate.js';
9
+ import { pageFilter, pageSort } from '../../utils/pageControls.js';
9
10
  import { readPath } from '../../utils/readPath.js';
10
11
  import { dropOptionalUnresolved, resolveFilterTokens, hasUnresolvedTokens } from '../../utils/resolveFilterTokens.js';
11
12
 
@@ -33,7 +34,7 @@ function unwrap(value) {
33
34
  * objects filtered on a reference to this record. The source keys are those of
34
35
  * `object-list` (`register`, `schema`, `filter` with the `@objectId` /
35
36
  * `@object.<field>` / `@workspace.<key>` tokens, `sort`, `limit`, `extend`,
36
- * `rowRoute`); what differs is the drawing: `presentation` is `rows`, `feed`
37
+ * `rowRoute`, `pageControls`); what differs is the drawing: `presentation` is `rows`, `feed`
37
38
  * or `inline`, and each line is a template (`"{identifier} · {caseTypeName}"`,
38
39
  * `"Termijn {deadline|date}"`).
39
40
  *
@@ -113,8 +114,8 @@ var script = {
113
114
 
114
115
  /** @return {object} The filter with its tokens resolved. */
115
116
  resolvedFilter() {
116
- const filter = this.content?.filter;
117
- return dropOptionalUnresolved(resolveFilterTokens(filter && typeof filter === 'object' ? filter : {}, this.tokenCtx))
117
+ const filter = pageFilter(this.content?.pageControls, this.tokenCtx.workspace, this.content?.filter);
118
+ return dropOptionalUnresolved(resolveFilterTokens(filter, this.tokenCtx))
118
119
  },
119
120
 
120
121
  /** @return {boolean} A required token (the record id, say) has no value yet. */
@@ -122,6 +123,11 @@ var script = {
122
123
  return hasUnresolvedTokens(this.resolvedFilter)
123
124
  },
124
125
 
126
+ /** @return {object|null} The sort: the page's sort control when the widget listens to one, else its own. */
127
+ effectiveSort() {
128
+ return pageSort(this.content?.pageControls, this.tokenCtx.workspace, this.content?.sort)
129
+ },
130
+
125
131
  /** @return {number} The fetch cap. */
126
132
  limit() {
127
133
  const value = Number(this.content?.limit);
@@ -131,7 +137,7 @@ var script = {
131
137
  /** @return {string} What a fetch depends on; a change fetches again. */
132
138
  requestKey() {
133
139
  const c = this.content || {};
134
- return JSON.stringify([c.register, c.schema, this.resolvedFilter, c.sort, this.limit, c.extend, this.waiting])
140
+ return JSON.stringify([c.register, c.schema, this.resolvedFilter, this.effectiveSort, this.limit, c.extend, this.waiting])
135
141
  },
136
142
 
137
143
  /** @return {Array<object>} The rows ready to draw. */
@@ -347,8 +353,9 @@ var script = {
347
353
  ]);
348
354
  const url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: c.register, schema: c.schema });
349
355
  const params = { _limit: this.limit };
350
- if (c.sort && typeof c.sort === 'object' && c.sort.field) {
351
- params[`_order[${c.sort.field}]`] = c.sort.dir === 'desc' ? 'desc' : 'asc';
356
+ const sort = this.effectiveSort;
357
+ if (sort && typeof sort === 'object' && sort.field) {
358
+ params[`_order[${sort.field}]`] = sort.dir === 'desc' ? 'desc' : 'asc';
352
359
  }
353
360
  if (Array.isArray(c.extend)) {
354
361
  const extend = c.extend.filter((e) => typeof e === 'string' && e !== '');
@@ -1 +1 @@
1
- {"version":3,"file":"CnRecordListWidget.vue2.js","sources":["../../../../src/components/CnRecordListWidget/CnRecordListWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tclass=\"cn-record-list\"\n\t\t:class=\"`cn-record-list--${presentation}`\"\n\t\tdata-testid=\"cn-record-list\">\n\t\t<p v-if=\"error\" class=\"cn-record-list__error\" data-testid=\"cn-record-list-error\">\n\t\t\t{{ errorLabel }}\n\t\t</p>\n\t\t<div v-else-if=\"loading && rows.length === 0\" class=\"cn-record-list__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" :name=\"loadingLabel\" />\n\t\t</div>\n\t\t<p v-else-if=\"rows.length === 0\" class=\"cn-record-list__empty\" data-testid=\"cn-record-list-empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t\t<ul v-else class=\"cn-record-list__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"item in items\"\n\t\t\t\t:key=\"item.key\"\n\t\t\t\tclass=\"cn-record-list__item\"\n\t\t\t\tdata-testid=\"cn-record-list-item\">\n\t\t\t\t<!-- rows: title and meta, then the pill, the date and a chevron. -->\n\t\t\t\t<component\n\t\t\t\t\t:is=\"item.tag\"\n\t\t\t\t\tv-if=\"presentation === 'rows'\"\n\t\t\t\t\tclass=\"cn-record-list__row\"\n\t\t\t\t\t:class=\"{ 'cn-record-list__row--link': item.tag !== 'div' }\"\n\t\t\t\t\tv-bind=\"item.attrs\">\n\t\t\t\t\t<span class=\"cn-record-list__main\">\n\t\t\t\t\t\t<span v-if=\"item.title\" class=\"cn-record-list__title\">{{ item.title }}</span>\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"item.pill || item.trailing\" class=\"cn-record-list__aside\">\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-if=\"item.pill\"\n\t\t\t\t\t\t\t:label=\"item.pill.label\"\n\t\t\t\t\t\t\t:variant=\"item.pill.variant\"\n\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t<span v-if=\"item.trailing\" class=\"cn-record-list__trailing\">{{ item.trailing }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ChevronRight\n\t\t\t\t\t\tv-if=\"item.tag !== 'div'\"\n\t\t\t\t\t\tclass=\"cn-record-list__chevron\"\n\t\t\t\t\t\t:size=\"18\"\n\t\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t</component>\n\n\t\t\t\t<!-- feed: a round icon, a meta line and the text. -->\n\t\t\t\t<div v-else-if=\"presentation === 'feed'\" class=\"cn-record-list__feed-row\">\n\t\t\t\t\t<span class=\"cn-record-list__feed-icon\" aria-hidden=\"true\">\n\t\t\t\t\t\t<CnIcon :name=\"item.icon\" :size=\"18\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<component :is=\"item.tag\" class=\"cn-record-list__main\" v-bind=\"item.attrs\">\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t\t<span v-if=\"item.text\" class=\"cn-record-list__text\">{{ item.text }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- inline: a label and the title, the title a link. -->\n\t\t\t\t<div v-else class=\"cn-record-list__inline-row\">\n\t\t\t\t\t<span v-if=\"item.label\" class=\"cn-record-list__label\">{{ item.label }}</span>\n\t\t\t\t\t<component :is=\"item.tag\" class=\"cn-record-list__inline-title\" v-bind=\"item.attrs\">\n\t\t\t\t\t\t{{ item.title }}\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { isBadgeVariant } from '../../utils/badgeVariants.js'\nimport { fillTemplate, firstFilled } from '../../utils/fillTemplate.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\n\nconst PAGE_REFRESH_CHANNEL = 'cn:page:refresh'\nconst PRESENTATIONS = ['rows', 'feed', 'inline']\n\n/**\n * Unwrap a value that may be a ref.\n *\n * @param {unknown} value The value.\n * @return {unknown} The plain value.\n */\nfunction unwrap(value) {\n\treturn value && typeof value === 'object' && 'value' in value ? value.value : value\n}\n\n/**\n * CnRecordListWidget — the objects of another schema that point at this\n * record, drawn as the boards draw them (screens-relation-pages).\n *\n * A person page lists its open cases (\"Verlichting fietspad Lindelaan,\n * 2026-0082 · Woo-verzoek, In behandeling, Termijn 1 nov\"), its latest contact\n * moments (an icon by channel, a meta line, the summary) and its relations\n * (\"Secretaris van Buurtvereniging Oost\"). Each is a list of another schema's\n * objects filtered on a reference to this record. The source keys are those of\n * `object-list` (`register`, `schema`, `filter` with the `@objectId` /\n * `@object.<field>` / `@workspace.<key>` tokens, `sort`, `limit`, `extend`,\n * `rowRoute`); what differs is the drawing: `presentation` is `rows`, `feed`\n * or `inline`, and each line is a template (`\"{identifier} · {caseTypeName}\"`,\n * `\"Termijn {deadline|date}\"`).\n *\n * A content-only catalog type: on a card surface the detail host draws the\n * titled card. The widget emits `header` with `{ link, note }` (the\n * `headerLink` and `headerNote` with `{total}` filled in) for that card's\n * header.\n */\nexport default {\n\tname: 'CnRecordListWidget',\n\n\tcomponents: { ChevronRight, CnIcon, CnStatusBadge, NcLoadingIcon },\n\n\tinject: {\n\t\t/** Detail-page object context (`{ objectId, object, register, schema }`). */\n\t\tcnObjectContext: { default: null },\n\t\t/** Page workspace bag, for `@workspace.<key>` tokens. */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/** The host app's translate function. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The list config: `{ register, schema, filter?, sort?, limit?, extend?,\n\t\t * presentation?, title?, meta?, text?, label?, pill?, trailing?, icon?,\n\t\t * rowRoute?, rowIdField?, headerLink?, headerNote?, emptyText?, errorText? }`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The current record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The current record, for `@object.<field>` tokens. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['header'],\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\ttotal: 0,\n\t\t\tloading: false,\n\t\t\terror: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} `rows`, `feed` or `inline`. */\n\t\tpresentation() {\n\t\t\tconst value = this.content?.presentation\n\t\t\treturn PRESENTATIONS.includes(value) ? value : 'rows'\n\t\t},\n\n\t\t/** @return {object} The token context: record, id and workspace. */\n\t\ttokenCtx() {\n\t\t\tconst injected = unwrap(this.cnObjectContext) || {}\n\t\t\treturn {\n\t\t\t\t...injected,\n\t\t\t\tobjectId: this.objectId ?? injected.objectId ?? this.objectData?.id ?? null,\n\t\t\t\tobject: this.objectData || injected.object || null,\n\t\t\t\tworkspace: unwrap(this.cnWorkspaceContext) || {},\n\t\t\t}\n\t\t},\n\n\t\t/** @return {object} The filter with its tokens resolved. */\n\t\tresolvedFilter() {\n\t\t\tconst filter = this.content?.filter\n\t\t\treturn dropOptionalUnresolved(resolveFilterTokens(filter && typeof filter === 'object' ? filter : {}, this.tokenCtx))\n\t\t},\n\n\t\t/** @return {boolean} A required token (the record id, say) has no value yet. */\n\t\twaiting() {\n\t\t\treturn hasUnresolvedTokens(this.resolvedFilter)\n\t\t},\n\n\t\t/** @return {number} The fetch cap. */\n\t\tlimit() {\n\t\t\tconst value = Number(this.content?.limit)\n\t\t\treturn Number.isFinite(value) && value > 0 ? Math.floor(value) : 5\n\t\t},\n\n\t\t/** @return {string} What a fetch depends on; a change fetches again. */\n\t\trequestKey() {\n\t\t\tconst c = this.content || {}\n\t\t\treturn JSON.stringify([c.register, c.schema, this.resolvedFilter, c.sort, this.limit, c.extend, this.waiting])\n\t\t},\n\n\t\t/** @return {Array<object>} The rows ready to draw. */\n\t\titems() {\n\t\t\tconst c = this.content || {}\n\t\t\treturn this.rows.slice(0, this.limit).map((row, index) => {\n\t\t\t\tconst link = this.rowLink(row)\n\t\t\t\treturn {\n\t\t\t\t\tkey: (row && (row.id || row['@self']?.id)) || `row-${index}`,\n\t\t\t\t\ttag: link ? 'router-link' : 'div',\n\t\t\t\t\tattrs: link ? { to: link } : {},\n\t\t\t\t\ttitle: this.fill(c.title, row),\n\t\t\t\t\tmeta: this.fill(c.meta, row),\n\t\t\t\t\ttext: this.fill(c.text, row),\n\t\t\t\t\tlabel: this.fill(c.label, row),\n\t\t\t\t\ttrailing: c.trailing ? firstFilled(c.trailing, row, this.translate) : '',\n\t\t\t\t\tpill: this.pillOf(row),\n\t\t\t\t\ticon: this.iconOf(row),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {object|null} The header link with `{total}` filled and tokens resolved. */\n\t\theaderLink() {\n\t\t\tconst link = this.content?.headerLink\n\t\t\tif (!link || typeof link !== 'object' || typeof link.label !== 'string' || link.label === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst resolved = { label: this.withTotal(link.label) }\n\t\t\tif (link.route) {\n\t\t\t\tresolved.route = link.route\n\t\t\t\tif (link.params && typeof link.params === 'object') {\n\t\t\t\t\tresolved.params = resolveFilterTokens(link.params, this.tokenCtx)\n\t\t\t\t}\n\t\t\t\tif (link.query && typeof link.query === 'object') {\n\t\t\t\t\tresolved.query = resolveFilterTokens(link.query, this.tokenCtx)\n\t\t\t\t}\n\t\t\t} else if (typeof link.href === 'string') {\n\t\t\t\tresolved.href = link.href\n\t\t\t}\n\t\t\treturn resolved\n\t\t},\n\n\t\t/** @return {string} The header note with `{total}` filled. */\n\t\theaderNote() {\n\t\t\tconst note = this.content?.headerNote\n\t\t\treturn typeof note === 'string' && note !== '' ? this.withTotal(note) : ''\n\t\t},\n\n\t\t/** @return {string} The empty line. */\n\t\temptyLabel() {\n\t\t\treturn this.content?.emptyText ? this.translate(this.content.emptyText) : t('nextcloud-vue', 'Nothing here yet')\n\t\t},\n\n\t\t/** @return {string} The error line. */\n\t\terrorLabel() {\n\t\t\treturn this.content?.errorText ? this.translate(this.content.errorText) : t('nextcloud-vue', 'This list could not be loaded.')\n\t\t},\n\n\t\t/** @return {string} The spinner's accessible name. */\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\t},\n\n\twatch: {\n\t\trequestKey() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.onRefresh = () => this.fetchRows()\n\t\tsubscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)\n\t\tthis.fetchRows()\n\t},\n\n\tbeforeUnmount() {\n\t\tunsubscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Translate a manifest string through the host app.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation.\n\t\t */\n\t\ttranslate(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * Fill a template from a row, after translating it.\n\t\t *\n\t\t * @param {string} template The template.\n\t\t * @param {object} row The row.\n\t\t * @return {string} The text, '' when a token is empty.\n\t\t */\n\t\tfill(template, row) {\n\t\t\treturn typeof template === 'string' && template !== '' ? fillTemplate(this.translate(template), row) : ''\n\t\t},\n\n\t\t/**\n\t\t * Replace `{total}` in a translated label.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t * @return {string} The text.\n\t\t */\n\t\twithTotal(label) {\n\t\t\treturn this.translate(label).replace(/\\{total\\}/g, String(this.total))\n\t\t},\n\n\t\t/**\n\t\t * The route a row opens: `rowRoute` with the id at `rowIdField`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {object|null} The router location, or null.\n\t\t */\n\t\trowLink(row) {\n\t\t\tconst route = this.content?.rowRoute\n\t\t\tif (!route || !this.$router || !row) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst field = this.content?.rowIdField\n\t\t\tconst id = field ? readPath(row, field) : (row.id || row['@self']?.id)\n\t\t\tif (id === null || id === undefined || id === '' || typeof id === 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { name: route, params: { id: String(id) } }\n\t\t},\n\n\t\t/**\n\t\t * The row's pill: `{ field, labels?, colorMap?, variant? }`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {{label: string, variant: string}|null} The pill, or null.\n\t\t */\n\t\tpillOf(row) {\n\t\t\tconst pill = this.content?.pill\n\t\t\tif (!pill || typeof pill !== 'object' || !pill.field) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = readPath(row, pill.field)\n\t\t\tif (raw === null || raw === undefined || raw === '' || typeof raw === 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst key = String(raw)\n\t\t\tconst labels = pill.labels && typeof pill.labels === 'object' ? pill.labels : {}\n\t\t\tconst map = pill.colorMap && typeof pill.colorMap === 'object' ? pill.colorMap : {}\n\t\t\tconst found = Object.keys(map).find((k) => k.toLowerCase() === key.toLowerCase())\n\t\t\tconst variant = found ? map[found] : pill.variant\n\t\t\treturn {\n\t\t\t\tlabel: labels[key] ? this.translate(labels[key]) : key,\n\t\t\t\tvariant: isBadgeVariant(variant) ? variant : 'default',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The feed icon: an icon name, or `{ field, map, default }`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string} The MDI icon name.\n\t\t */\n\t\ticonOf(row) {\n\t\t\tconst icon = this.content?.icon\n\t\t\tif (typeof icon === 'string' && icon !== '') {\n\t\t\t\treturn icon\n\t\t\t}\n\t\t\tif (icon && typeof icon === 'object') {\n\t\t\t\tconst raw = icon.field ? readPath(row, icon.field) : undefined\n\t\t\t\tconst map = icon.map && typeof icon.map === 'object' ? icon.map : {}\n\t\t\t\tif (raw !== null && raw !== undefined && map[String(raw)]) {\n\t\t\t\t\treturn map[String(raw)]\n\t\t\t\t}\n\t\t\t\tif (typeof icon.default === 'string' && icon.default !== '') {\n\t\t\t\t\treturn icon.default\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 'CircleSmall'\n\t\t},\n\n\t\t/** Tell the card around this list what its header shows. */\n\t\temitHeader() {\n\t\t\t/**\n\t\t\t * @event header Emitted after every read with the card header's parts.\n\t\t\t * @type {{link: (object|null), note: string}} The header link (resolved, with `{total}` filled) and the note.\n\t\t\t */\n\t\t\tthis.$emit('header', { link: this.headerLink, note: this.headerNote })\n\t\t},\n\n\t\t/**\n\t\t * Fetch the list. Sends nothing while a required token is unresolved\n\t\t * or the source is incomplete.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchRows() {\n\t\t\tconst c = this.content || {}\n\t\t\tif (!c.register || !c.schema || this.waiting) {\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.total = 0\n\t\t\t\tthis.error = false\n\t\t\t\tthis.emitHeader()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = false\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: c.register, schema: c.schema })\n\t\t\t\tconst params = { _limit: this.limit }\n\t\t\t\tif (c.sort && typeof c.sort === 'object' && c.sort.field) {\n\t\t\t\t\tparams[`_order[${c.sort.field}]`] = c.sort.dir === 'desc' ? 'desc' : 'asc'\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(c.extend)) {\n\t\t\t\t\tconst extend = c.extend.filter((e) => typeof e === 'string' && e !== '')\n\t\t\t\t\tif (extend.length > 0) {\n\t\t\t\t\t\tparams._extend = extend\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const [key, value] of Object.entries(this.resolvedFilter)) {\n\t\t\t\t\tif (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\t\t\t\t\tfor (const [op, operand] of Object.entries(value)) {\n\t\t\t\t\t\t\tparams[`${key}[${op}]`] = operand\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (value !== '' && value !== null && value !== undefined) {\n\t\t\t\t\t\tparams[key] = value\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(url, { params })\n\t\t\t\tconst data = response && response.data ? response.data : {}\n\t\t\t\tthis.rows = Array.isArray(data.results) ? data.results : []\n\t\t\t\tthis.total = typeof data.total === 'number' ? data.total : this.rows.length\n\t\t\t} catch (e) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnRecordListWidget] failed to load objects:', e)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.total = 0\n\t\t\t\tthis.error = true\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t\tthis.emitHeader()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-record-list__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-record-list__empty,\n.cn-record-list__error {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n}\n\n.cn-record-list__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 8px 0;\n}\n\n/* rows */\n.cn-record-list--rows .cn-record-list__item + .cn-record-list__item {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-list__row {\n\talign-items: center;\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tgap: 16px;\n\tpadding: 12px 0;\n\ttext-decoration: none;\n}\n\n.cn-record-list__row--link:hover .cn-record-list__title,\n.cn-record-list__row--link:focus-visible .cn-record-list__title {\n\ttext-decoration: underline;\n}\n\n.cn-record-list__main {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-record-list__title {\n\tfont-size: 15px;\n\tfont-weight: 700;\n}\n\n.cn-record-list__meta {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-record-list__aside {\n\talign-items: center;\n\tdisplay: flex;\n\tflex: none;\n\tgap: 16px;\n}\n\n.cn-record-list__trailing {\n\tfont-size: 14px;\n\twhite-space: nowrap;\n}\n\n.cn-record-list__chevron {\n\tcolor: var(--color-text-maxcontrast);\n\tflex: none;\n}\n\n/* feed */\n.cn-record-list--feed .cn-record-list__item + .cn-record-list__item {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-list__feed-row {\n\talign-items: flex-start;\n\tdisplay: flex;\n\tgap: 14px;\n\tpadding: 12px 0;\n}\n\n.cn-record-list__feed-icon {\n\talign-items: center;\n\tbackground: var(--color-primary-element-light);\n\tborder-radius: 50%;\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n\tdisplay: flex;\n\tflex: none;\n\theight: 36px;\n\tjustify-content: center;\n\twidth: 36px;\n}\n\n.cn-record-list__text {\n\tfont-size: 15px;\n}\n\n.cn-record-list--feed .cn-record-list__main {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n/* inline */\n.cn-record-list__inline-row {\n\tpadding: 4px 0;\n}\n\n.cn-record-list__label {\n\tmargin-inline-end: 4px;\n}\n\n.cn-record-list__inline-title {\n\tcolor: var(--color-main-text);\n}\n\na.cn-record-list__inline-title {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n</style>\n\n<style>\n/* In a narrow side column a row stacks: title, meta, then pill and date. */\n.cn-detail-page__side .cn-record-list__row {\n\talign-items: flex-start;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-detail-page__side .cn-record-list__row .cn-record-list__title {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n\n.cn-detail-page__side .cn-record-list__chevron {\n\tdisplay: none;\n}\n</style>\n"],"names":["CnIcon","CnStatusBadge","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnRecordListWidget.vue2.js","sources":["../../../../src/components/CnRecordListWidget/CnRecordListWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div\n\t\tclass=\"cn-record-list\"\n\t\t:class=\"`cn-record-list--${presentation}`\"\n\t\tdata-testid=\"cn-record-list\">\n\t\t<p v-if=\"error\" class=\"cn-record-list__error\" data-testid=\"cn-record-list-error\">\n\t\t\t{{ errorLabel }}\n\t\t</p>\n\t\t<div v-else-if=\"loading && rows.length === 0\" class=\"cn-record-list__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" :name=\"loadingLabel\" />\n\t\t</div>\n\t\t<p v-else-if=\"rows.length === 0\" class=\"cn-record-list__empty\" data-testid=\"cn-record-list-empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t\t<ul v-else class=\"cn-record-list__list\">\n\t\t\t<li\n\t\t\t\tv-for=\"item in items\"\n\t\t\t\t:key=\"item.key\"\n\t\t\t\tclass=\"cn-record-list__item\"\n\t\t\t\tdata-testid=\"cn-record-list-item\">\n\t\t\t\t<!-- rows: title and meta, then the pill, the date and a chevron. -->\n\t\t\t\t<component\n\t\t\t\t\t:is=\"item.tag\"\n\t\t\t\t\tv-if=\"presentation === 'rows'\"\n\t\t\t\t\tclass=\"cn-record-list__row\"\n\t\t\t\t\t:class=\"{ 'cn-record-list__row--link': item.tag !== 'div' }\"\n\t\t\t\t\tv-bind=\"item.attrs\">\n\t\t\t\t\t<span class=\"cn-record-list__main\">\n\t\t\t\t\t\t<span v-if=\"item.title\" class=\"cn-record-list__title\">{{ item.title }}</span>\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span v-if=\"item.pill || item.trailing\" class=\"cn-record-list__aside\">\n\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\tv-if=\"item.pill\"\n\t\t\t\t\t\t\t:label=\"item.pill.label\"\n\t\t\t\t\t\t\t:variant=\"item.pill.variant\"\n\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t<span v-if=\"item.trailing\" class=\"cn-record-list__trailing\">{{ item.trailing }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<ChevronRight\n\t\t\t\t\t\tv-if=\"item.tag !== 'div'\"\n\t\t\t\t\t\tclass=\"cn-record-list__chevron\"\n\t\t\t\t\t\t:size=\"18\"\n\t\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t</component>\n\n\t\t\t\t<!-- feed: a round icon, a meta line and the text. -->\n\t\t\t\t<div v-else-if=\"presentation === 'feed'\" class=\"cn-record-list__feed-row\">\n\t\t\t\t\t<span class=\"cn-record-list__feed-icon\" aria-hidden=\"true\">\n\t\t\t\t\t\t<CnIcon :name=\"item.icon\" :size=\"18\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<component :is=\"item.tag\" class=\"cn-record-list__main\" v-bind=\"item.attrs\">\n\t\t\t\t\t\t<span v-if=\"item.meta\" class=\"cn-record-list__meta\">{{ item.meta }}</span>\n\t\t\t\t\t\t<span v-if=\"item.text\" class=\"cn-record-list__text\">{{ item.text }}</span>\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\n\t\t\t\t<!-- inline: a label and the title, the title a link. -->\n\t\t\t\t<div v-else class=\"cn-record-list__inline-row\">\n\t\t\t\t\t<span v-if=\"item.label\" class=\"cn-record-list__label\">{{ item.label }}</span>\n\t\t\t\t\t<component :is=\"item.tag\" class=\"cn-record-list__inline-title\" v-bind=\"item.attrs\">\n\t\t\t\t\t\t{{ item.title }}\n\t\t\t\t\t</component>\n\t\t\t\t</div>\n\t\t\t</li>\n\t\t</ul>\n\t</div>\n</template>\n\n<script>\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ChevronRight from 'vue-material-design-icons/ChevronRight.vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { isBadgeVariant } from '../../utils/badgeVariants.js'\nimport { fillTemplate, firstFilled } from '../../utils/fillTemplate.js'\nimport { pageFilter, pageSort } from '../../utils/pageControls.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'\n\nconst PAGE_REFRESH_CHANNEL = 'cn:page:refresh'\nconst PRESENTATIONS = ['rows', 'feed', 'inline']\n\n/**\n * Unwrap a value that may be a ref.\n *\n * @param {unknown} value The value.\n * @return {unknown} The plain value.\n */\nfunction unwrap(value) {\n\treturn value && typeof value === 'object' && 'value' in value ? value.value : value\n}\n\n/**\n * CnRecordListWidget — the objects of another schema that point at this\n * record, drawn as the boards draw them (screens-relation-pages).\n *\n * A person page lists its open cases (\"Verlichting fietspad Lindelaan,\n * 2026-0082 · Woo-verzoek, In behandeling, Termijn 1 nov\"), its latest contact\n * moments (an icon by channel, a meta line, the summary) and its relations\n * (\"Secretaris van Buurtvereniging Oost\"). Each is a list of another schema's\n * objects filtered on a reference to this record. The source keys are those of\n * `object-list` (`register`, `schema`, `filter` with the `@objectId` /\n * `@object.<field>` / `@workspace.<key>` tokens, `sort`, `limit`, `extend`,\n * `rowRoute`, `pageControls`); what differs is the drawing: `presentation` is `rows`, `feed`\n * or `inline`, and each line is a template (`\"{identifier} · {caseTypeName}\"`,\n * `\"Termijn {deadline|date}\"`).\n *\n * A content-only catalog type: on a card surface the detail host draws the\n * titled card. The widget emits `header` with `{ link, note }` (the\n * `headerLink` and `headerNote` with `{total}` filled in) for that card's\n * header.\n */\nexport default {\n\tname: 'CnRecordListWidget',\n\n\tcomponents: { ChevronRight, CnIcon, CnStatusBadge, NcLoadingIcon },\n\n\tinject: {\n\t\t/** Detail-page object context (`{ objectId, object, register, schema }`). */\n\t\tcnObjectContext: { default: null },\n\t\t/** Page workspace bag, for `@workspace.<key>` tokens. */\n\t\tcnWorkspaceContext: { default: null },\n\t\t/** The host app's translate function. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The list config: `{ register, schema, filter?, sort?, limit?, extend?,\n\t\t * presentation?, title?, meta?, text?, label?, pill?, trailing?, icon?,\n\t\t * rowRoute?, rowIdField?, headerLink?, headerNote?, emptyText?, errorText? }`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The current record's id, for the `@objectId` token. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The current record, for `@object.<field>` tokens. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['header'],\n\n\tdata() {\n\t\treturn {\n\t\t\trows: [],\n\t\t\ttotal: 0,\n\t\t\tloading: false,\n\t\t\terror: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {string} `rows`, `feed` or `inline`. */\n\t\tpresentation() {\n\t\t\tconst value = this.content?.presentation\n\t\t\treturn PRESENTATIONS.includes(value) ? value : 'rows'\n\t\t},\n\n\t\t/** @return {object} The token context: record, id and workspace. */\n\t\ttokenCtx() {\n\t\t\tconst injected = unwrap(this.cnObjectContext) || {}\n\t\t\treturn {\n\t\t\t\t...injected,\n\t\t\t\tobjectId: this.objectId ?? injected.objectId ?? this.objectData?.id ?? null,\n\t\t\t\tobject: this.objectData || injected.object || null,\n\t\t\t\tworkspace: unwrap(this.cnWorkspaceContext) || {},\n\t\t\t}\n\t\t},\n\n\t\t/** @return {object} The filter with its tokens resolved. */\n\t\tresolvedFilter() {\n\t\t\tconst filter = pageFilter(this.content?.pageControls, this.tokenCtx.workspace, this.content?.filter)\n\t\t\treturn dropOptionalUnresolved(resolveFilterTokens(filter, this.tokenCtx))\n\t\t},\n\n\t\t/** @return {boolean} A required token (the record id, say) has no value yet. */\n\t\twaiting() {\n\t\t\treturn hasUnresolvedTokens(this.resolvedFilter)\n\t\t},\n\n\t\t/** @return {object|null} The sort: the page's sort control when the widget listens to one, else its own. */\n\t\teffectiveSort() {\n\t\t\treturn pageSort(this.content?.pageControls, this.tokenCtx.workspace, this.content?.sort)\n\t\t},\n\n\t\t/** @return {number} The fetch cap. */\n\t\tlimit() {\n\t\t\tconst value = Number(this.content?.limit)\n\t\t\treturn Number.isFinite(value) && value > 0 ? Math.floor(value) : 5\n\t\t},\n\n\t\t/** @return {string} What a fetch depends on; a change fetches again. */\n\t\trequestKey() {\n\t\t\tconst c = this.content || {}\n\t\t\treturn JSON.stringify([c.register, c.schema, this.resolvedFilter, this.effectiveSort, this.limit, c.extend, this.waiting])\n\t\t},\n\n\t\t/** @return {Array<object>} The rows ready to draw. */\n\t\titems() {\n\t\t\tconst c = this.content || {}\n\t\t\treturn this.rows.slice(0, this.limit).map((row, index) => {\n\t\t\t\tconst link = this.rowLink(row)\n\t\t\t\treturn {\n\t\t\t\t\tkey: (row && (row.id || row['@self']?.id)) || `row-${index}`,\n\t\t\t\t\ttag: link ? 'router-link' : 'div',\n\t\t\t\t\tattrs: link ? { to: link } : {},\n\t\t\t\t\ttitle: this.fill(c.title, row),\n\t\t\t\t\tmeta: this.fill(c.meta, row),\n\t\t\t\t\ttext: this.fill(c.text, row),\n\t\t\t\t\tlabel: this.fill(c.label, row),\n\t\t\t\t\ttrailing: c.trailing ? firstFilled(c.trailing, row, this.translate) : '',\n\t\t\t\t\tpill: this.pillOf(row),\n\t\t\t\t\ticon: this.iconOf(row),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** @return {object|null} The header link with `{total}` filled and tokens resolved. */\n\t\theaderLink() {\n\t\t\tconst link = this.content?.headerLink\n\t\t\tif (!link || typeof link !== 'object' || typeof link.label !== 'string' || link.label === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst resolved = { label: this.withTotal(link.label) }\n\t\t\tif (link.route) {\n\t\t\t\tresolved.route = link.route\n\t\t\t\tif (link.params && typeof link.params === 'object') {\n\t\t\t\t\tresolved.params = resolveFilterTokens(link.params, this.tokenCtx)\n\t\t\t\t}\n\t\t\t\tif (link.query && typeof link.query === 'object') {\n\t\t\t\t\tresolved.query = resolveFilterTokens(link.query, this.tokenCtx)\n\t\t\t\t}\n\t\t\t} else if (typeof link.href === 'string') {\n\t\t\t\tresolved.href = link.href\n\t\t\t}\n\t\t\treturn resolved\n\t\t},\n\n\t\t/** @return {string} The header note with `{total}` filled. */\n\t\theaderNote() {\n\t\t\tconst note = this.content?.headerNote\n\t\t\treturn typeof note === 'string' && note !== '' ? this.withTotal(note) : ''\n\t\t},\n\n\t\t/** @return {string} The empty line. */\n\t\temptyLabel() {\n\t\t\treturn this.content?.emptyText ? this.translate(this.content.emptyText) : t('nextcloud-vue', 'Nothing here yet')\n\t\t},\n\n\t\t/** @return {string} The error line. */\n\t\terrorLabel() {\n\t\t\treturn this.content?.errorText ? this.translate(this.content.errorText) : t('nextcloud-vue', 'This list could not be loaded.')\n\t\t},\n\n\t\t/** @return {string} The spinner's accessible name. */\n\t\tloadingLabel() {\n\t\t\treturn t('nextcloud-vue', 'Loading …')\n\t\t},\n\t},\n\n\twatch: {\n\t\trequestKey() {\n\t\t\tthis.fetchRows()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.onRefresh = () => this.fetchRows()\n\t\tsubscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)\n\t\tthis.fetchRows()\n\t},\n\n\tbeforeUnmount() {\n\t\tunsubscribe(PAGE_REFRESH_CHANNEL, this.onRefresh)\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Translate a manifest string through the host app.\n\t\t *\n\t\t * @param {string} text The text.\n\t\t * @return {string} The translation.\n\t\t */\n\t\ttranslate(text) {\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * Fill a template from a row, after translating it.\n\t\t *\n\t\t * @param {string} template The template.\n\t\t * @param {object} row The row.\n\t\t * @return {string} The text, '' when a token is empty.\n\t\t */\n\t\tfill(template, row) {\n\t\t\treturn typeof template === 'string' && template !== '' ? fillTemplate(this.translate(template), row) : ''\n\t\t},\n\n\t\t/**\n\t\t * Replace `{total}` in a translated label.\n\t\t *\n\t\t * @param {string} label The label.\n\t\t * @return {string} The text.\n\t\t */\n\t\twithTotal(label) {\n\t\t\treturn this.translate(label).replace(/\\{total\\}/g, String(this.total))\n\t\t},\n\n\t\t/**\n\t\t * The route a row opens: `rowRoute` with the id at `rowIdField`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {object|null} The router location, or null.\n\t\t */\n\t\trowLink(row) {\n\t\t\tconst route = this.content?.rowRoute\n\t\t\tif (!route || !this.$router || !row) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst field = this.content?.rowIdField\n\t\t\tconst id = field ? readPath(row, field) : (row.id || row['@self']?.id)\n\t\t\tif (id === null || id === undefined || id === '' || typeof id === 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { name: route, params: { id: String(id) } }\n\t\t},\n\n\t\t/**\n\t\t * The row's pill: `{ field, labels?, colorMap?, variant? }`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {{label: string, variant: string}|null} The pill, or null.\n\t\t */\n\t\tpillOf(row) {\n\t\t\tconst pill = this.content?.pill\n\t\t\tif (!pill || typeof pill !== 'object' || !pill.field) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = readPath(row, pill.field)\n\t\t\tif (raw === null || raw === undefined || raw === '' || typeof raw === 'object') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst key = String(raw)\n\t\t\tconst labels = pill.labels && typeof pill.labels === 'object' ? pill.labels : {}\n\t\t\tconst map = pill.colorMap && typeof pill.colorMap === 'object' ? pill.colorMap : {}\n\t\t\tconst found = Object.keys(map).find((k) => k.toLowerCase() === key.toLowerCase())\n\t\t\tconst variant = found ? map[found] : pill.variant\n\t\t\treturn {\n\t\t\t\tlabel: labels[key] ? this.translate(labels[key]) : key,\n\t\t\t\tvariant: isBadgeVariant(variant) ? variant : 'default',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The feed icon: an icon name, or `{ field, map, default }`.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {string} The MDI icon name.\n\t\t */\n\t\ticonOf(row) {\n\t\t\tconst icon = this.content?.icon\n\t\t\tif (typeof icon === 'string' && icon !== '') {\n\t\t\t\treturn icon\n\t\t\t}\n\t\t\tif (icon && typeof icon === 'object') {\n\t\t\t\tconst raw = icon.field ? readPath(row, icon.field) : undefined\n\t\t\t\tconst map = icon.map && typeof icon.map === 'object' ? icon.map : {}\n\t\t\t\tif (raw !== null && raw !== undefined && map[String(raw)]) {\n\t\t\t\t\treturn map[String(raw)]\n\t\t\t\t}\n\t\t\t\tif (typeof icon.default === 'string' && icon.default !== '') {\n\t\t\t\t\treturn icon.default\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 'CircleSmall'\n\t\t},\n\n\t\t/** Tell the card around this list what its header shows. */\n\t\temitHeader() {\n\t\t\t/**\n\t\t\t * @event header Emitted after every read with the card header's parts.\n\t\t\t * @type {{link: (object|null), note: string}} The header link (resolved, with `{total}` filled) and the note.\n\t\t\t */\n\t\t\tthis.$emit('header', { link: this.headerLink, note: this.headerNote })\n\t\t},\n\n\t\t/**\n\t\t * Fetch the list. Sends nothing while a required token is unresolved\n\t\t * or the source is incomplete.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync fetchRows() {\n\t\t\tconst c = this.content || {}\n\t\t\tif (!c.register || !c.schema || this.waiting) {\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.total = 0\n\t\t\t\tthis.error = false\n\t\t\t\tthis.emitHeader()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.loading = true\n\t\t\tthis.error = false\n\t\t\ttry {\n\t\t\t\tconst [{ default: axios }, { generateUrl }] = await Promise.all([\n\t\t\t\t\timport('@nextcloud/axios'),\n\t\t\t\t\timport('@nextcloud/router'),\n\t\t\t\t])\n\t\t\t\tconst url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: c.register, schema: c.schema })\n\t\t\t\tconst params = { _limit: this.limit }\n\t\t\t\tconst sort = this.effectiveSort\n\t\t\t\tif (sort && typeof sort === 'object' && sort.field) {\n\t\t\t\t\tparams[`_order[${sort.field}]`] = sort.dir === 'desc' ? 'desc' : 'asc'\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(c.extend)) {\n\t\t\t\t\tconst extend = c.extend.filter((e) => typeof e === 'string' && e !== '')\n\t\t\t\t\tif (extend.length > 0) {\n\t\t\t\t\t\tparams._extend = extend\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (const [key, value] of Object.entries(this.resolvedFilter)) {\n\t\t\t\t\tif (value && typeof value === 'object' && !Array.isArray(value)) {\n\t\t\t\t\t\tfor (const [op, operand] of Object.entries(value)) {\n\t\t\t\t\t\t\tparams[`${key}[${op}]`] = operand\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (value !== '' && value !== null && value !== undefined) {\n\t\t\t\t\t\tparams[key] = value\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst response = await axios.get(url, { params })\n\t\t\t\tconst data = response && response.data ? response.data : {}\n\t\t\t\tthis.rows = Array.isArray(data.results) ? data.results : []\n\t\t\t\tthis.total = typeof data.total === 'number' ? data.total : this.rows.length\n\t\t\t} catch (e) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnRecordListWidget] failed to load objects:', e)\n\t\t\t\tthis.rows = []\n\t\t\t\tthis.total = 0\n\t\t\t\tthis.error = true\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t\tthis.emitHeader()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-record-list__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-record-list__empty,\n.cn-record-list__error {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n}\n\n.cn-record-list__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 8px 0;\n}\n\n/* rows */\n.cn-record-list--rows .cn-record-list__item + .cn-record-list__item {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-list__row {\n\talign-items: center;\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tgap: 16px;\n\tpadding: 12px 0;\n\ttext-decoration: none;\n}\n\n.cn-record-list__row--link:hover .cn-record-list__title,\n.cn-record-list__row--link:focus-visible .cn-record-list__title {\n\ttext-decoration: underline;\n}\n\n.cn-record-list__main {\n\tdisplay: flex;\n\tflex: 1 1 auto;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-record-list__title {\n\tfont-size: 15px;\n\tfont-weight: 700;\n}\n\n.cn-record-list__meta {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-record-list__aside {\n\talign-items: center;\n\tdisplay: flex;\n\tflex: none;\n\tgap: 16px;\n}\n\n.cn-record-list__trailing {\n\tfont-size: 14px;\n\twhite-space: nowrap;\n}\n\n.cn-record-list__chevron {\n\tcolor: var(--color-text-maxcontrast);\n\tflex: none;\n}\n\n/* feed */\n.cn-record-list--feed .cn-record-list__item + .cn-record-list__item {\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-record-list__feed-row {\n\talign-items: flex-start;\n\tdisplay: flex;\n\tgap: 14px;\n\tpadding: 12px 0;\n}\n\n.cn-record-list__feed-icon {\n\talign-items: center;\n\tbackground: var(--color-primary-element-light);\n\tborder-radius: 50%;\n\tcolor: var(--color-primary-element-light-text, var(--color-primary-element));\n\tdisplay: flex;\n\tflex: none;\n\theight: 36px;\n\tjustify-content: center;\n\twidth: 36px;\n}\n\n.cn-record-list__text {\n\tfont-size: 15px;\n}\n\n.cn-record-list--feed .cn-record-list__main {\n\tcolor: var(--color-main-text);\n\ttext-decoration: none;\n}\n\n/* inline */\n.cn-record-list__inline-row {\n\tpadding: 4px 0;\n}\n\n.cn-record-list__label {\n\tmargin-inline-end: 4px;\n}\n\n.cn-record-list__inline-title {\n\tcolor: var(--color-main-text);\n}\n\na.cn-record-list__inline-title {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n</style>\n\n<style>\n/* In a narrow side column a row stacks: title, meta, then pill and date. */\n.cn-detail-page__side .cn-record-list__row {\n\talign-items: flex-start;\n\tflex-direction: column;\n\tgap: 6px;\n}\n\n.cn-detail-page__side .cn-record-list__row .cn-record-list__title {\n\tcolor: var(--color-primary-element);\n\ttext-decoration: underline;\n}\n\n.cn-detail-page__side .cn-record-list__chevron {\n\tdisplay: none;\n}\n</style>\n"],"names":["CnIcon","CnStatusBadge","t"],"mappings":"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"}