@conduction/nextcloud-vue 2.59.0 → 2.61.0

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 (254) hide show
  1. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js +14 -1
  2. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  4. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +80 -1
  5. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  6. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +46 -4
  7. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  8. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +232 -10
  9. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  10. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +79 -25
  11. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  12. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue2.js +42 -3
  13. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue2.js.map +1 -1
  14. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue3.js +39 -6
  15. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue3.js.map +1 -1
  16. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +46 -0
  17. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  18. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +11 -4
  19. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  20. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue.js +10 -0
  21. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue.js.map +1 -0
  22. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js +39 -0
  23. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js.map +1 -0
  24. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js +16 -0
  25. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js.map +1 -0
  26. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +117 -1
  27. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  28. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +147 -89
  29. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  30. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +10 -0
  31. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js.map +1 -0
  32. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js +259 -0
  33. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -0
  34. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +131 -0
  35. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -0
  36. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js +10 -0
  37. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js.map +1 -0
  38. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +116 -0
  39. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -0
  40. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js +24 -0
  41. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -0
  42. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +10 -2
  43. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  44. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  45. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +503 -28
  46. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  47. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +303 -144
  48. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  49. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue.js +10 -0
  50. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue.js.map +1 -0
  51. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue2.js +237 -0
  52. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue2.js.map +1 -0
  53. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue3.js +120 -0
  54. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue3.js.map +1 -0
  55. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue.js +10 -0
  56. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue.js.map +1 -0
  57. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +77 -0
  58. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -0
  59. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js +27 -0
  60. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -0
  61. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +171 -12
  62. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +15 -7
  64. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
  65. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue2.js +37 -3
  66. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue2.js.map +1 -1
  67. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue3.js +44 -16
  68. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue3.js.map +1 -1
  69. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +132 -3
  70. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  71. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +6 -3
  72. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  73. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js +10 -0
  74. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js.map +1 -0
  75. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js +316 -0
  76. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js.map +1 -0
  77. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js +105 -0
  78. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js.map +1 -0
  79. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue.js +10 -0
  80. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue.js.map +1 -0
  81. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js +150 -0
  82. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js.map +1 -0
  83. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js +106 -0
  84. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js.map +1 -0
  85. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue.js +9 -0
  86. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue.js.map +1 -0
  87. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +47 -0
  88. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -0
  89. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue3.js +17 -0
  90. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue3.js.map +1 -0
  91. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +49 -0
  92. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
  93. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
  94. package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue2.js +6 -0
  95. package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue2.js.map +1 -1
  96. package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue3.js.map +1 -1
  97. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue2.js +46 -0
  98. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue2.js.map +1 -1
  99. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue3.js +15 -3
  100. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue3.js.map +1 -1
  101. package/dist/esm/components/CnPageRenderer/pageTypes.js +1 -0
  102. package/dist/esm/components/CnPageRenderer/pageTypes.js.map +1 -1
  103. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +37 -1
  104. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
  105. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js +29 -13
  106. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
  107. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js +50 -3
  108. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
  109. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
  110. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js +10 -0
  111. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js.map +1 -0
  112. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js +186 -0
  113. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -0
  114. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js +46 -0
  115. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -0
  116. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue.js +11 -0
  117. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue.js.map +1 -0
  118. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js +298 -0
  119. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js.map +1 -0
  120. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js +77 -0
  121. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js.map +1 -0
  122. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue.js +10 -0
  123. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue.js.map +1 -0
  124. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue2.js +169 -0
  125. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue2.js.map +1 -0
  126. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue3.js +48 -0
  127. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue3.js.map +1 -0
  128. package/dist/esm/components/CnTabs/CnTab.vue2.js +35 -0
  129. package/dist/esm/components/CnTabs/CnTab.vue2.js.map +1 -1
  130. package/dist/esm/components/CnTabs/CnTab.vue3.js.map +1 -1
  131. package/dist/esm/components/CnTabs/CnTabs.vue2.js +120 -7
  132. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  133. package/dist/esm/components/CnTabs/CnTabs.vue3.js +57 -5
  134. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  135. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +59 -4
  136. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  137. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +6 -2
  138. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  139. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js +10 -0
  140. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js.map +1 -0
  141. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +484 -0
  142. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -0
  143. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js +115 -0
  144. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -0
  145. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue.js +10 -0
  146. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue.js.map +1 -0
  147. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue2.js +189 -0
  148. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue2.js.map +1 -0
  149. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue3.js +74 -0
  150. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue3.js.map +1 -0
  151. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +82 -8
  152. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  153. package/dist/esm/index.js +16 -0
  154. package/dist/esm/index.js.map +1 -1
  155. package/dist/esm/l10n/en.json.js +55 -1
  156. package/dist/esm/l10n/en.json.js.map +1 -1
  157. package/dist/esm/l10n/nl.json.js +55 -1
  158. package/dist/esm/l10n/nl.json.js.map +1 -1
  159. package/dist/esm/nextcloud-vue.css +1036 -19
  160. package/dist/esm/utils/dateVariant.js +124 -0
  161. package/dist/esm/utils/dateVariant.js.map +1 -0
  162. package/dist/esm/utils/detailActionModel.js +260 -0
  163. package/dist/esm/utils/detailActionModel.js.map +1 -0
  164. package/dist/esm/utils/dueRule.js +84 -0
  165. package/dist/esm/utils/dueRule.js.map +1 -0
  166. package/dist/esm/utils/fetchAggregate.js +37 -1
  167. package/dist/esm/utils/fetchAggregate.js.map +1 -1
  168. package/dist/esm/utils/fetchFilterCounts.js +186 -0
  169. package/dist/esm/utils/fetchFilterCounts.js.map +1 -0
  170. package/dist/esm/utils/headers.js +28 -2
  171. package/dist/esm/utils/headers.js.map +1 -1
  172. package/dist/esm/utils/libraryWidgetKeys.js +5 -0
  173. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  174. package/dist/esm/utils/readPath.js +30 -0
  175. package/dist/esm/utils/readPath.js.map +1 -0
  176. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  177. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  178. package/dist/nextcloud-vue.cjs.js +16019 -9528
  179. package/dist/nextcloud-vue.cjs.js.map +2 -2
  180. package/dist/nextcloud-vue.css +1036 -19
  181. package/l10n/en.json +55 -1
  182. package/l10n/nl.json +55 -1
  183. package/package.json +1 -1
  184. package/scripts/.jsdoc-baselines.json +4 -0
  185. package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -1
  186. package/src/components/CnAppNav/CnAppNav.md +26 -0
  187. package/src/components/CnAppNav/CnAppNav.vue +145 -2
  188. package/src/components/CnBannerWidget/CnBannerWidget.vue +373 -11
  189. package/src/components/CnBannerWidgetForm/CnBannerWidgetForm.vue +65 -3
  190. package/src/components/CnBoardView/CnBoardView.vue +77 -0
  191. package/src/components/CnBrandStripe/CnBrandStripe.md +22 -0
  192. package/src/components/CnBrandStripe/CnBrandStripe.vue +100 -0
  193. package/src/components/CnBrandStripe/index.js +4 -0
  194. package/src/components/CnCellRenderer/CnCellRenderer.vue +207 -1
  195. package/src/components/CnConversationThread/CnConversationThread.md +51 -0
  196. package/src/components/CnConversationThread/CnConversationThread.vue +457 -0
  197. package/src/components/CnConversationThread/CnConversationWidget.vue +144 -0
  198. package/src/components/CnConversationThread/index.js +4 -0
  199. package/src/components/CnDashboardPage/CnDashboardPage.vue +10 -2
  200. package/src/components/CnDetailPage/CnDetailPage.md +16 -0
  201. package/src/components/CnDetailPage/CnDetailPage.vue +687 -49
  202. package/src/components/CnDocumentReviewList/CnDocumentReviewList.md +55 -0
  203. package/src/components/CnDocumentReviewList/CnDocumentReviewList.vue +384 -0
  204. package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +108 -0
  205. package/src/components/CnDocumentReviewList/index.js +4 -0
  206. package/src/components/CnHeaderWidget/CnHeaderWidget.vue +191 -14
  207. package/src/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue +53 -3
  208. package/src/components/CnIndexPage/CnIndexPage.vue +135 -3
  209. package/src/components/CnLinkCardsPage/CnLinkCardsPage.vue +484 -0
  210. package/src/components/CnLinkCardsPage/index.js +4 -0
  211. package/src/components/CnNextStepCard/CnNextStepCard.md +40 -0
  212. package/src/components/CnNextStepCard/CnNextStepCard.vue +319 -0
  213. package/src/components/CnNextStepCard/CnNextStepWidget.vue +59 -0
  214. package/src/components/CnNextStepCard/index.js +4 -0
  215. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +49 -0
  216. package/src/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue +6 -0
  217. package/src/components/CnObjectKanban/CnObjectKanban.vue +86 -2
  218. package/src/components/CnPageRenderer/pageTypes.js +1 -0
  219. package/src/components/CnQuickFilterBar/CnQuickFilterBar.md +3 -0
  220. package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +60 -1
  221. package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +50 -3
  222. package/src/components/CnSegmentedControl/CnSegmentedControl.md +46 -0
  223. package/src/components/CnSegmentedControl/CnSegmentedControl.vue +291 -0
  224. package/src/components/CnSegmentedControl/index.js +4 -0
  225. package/src/components/CnStackedBarWidget/CnStackedBarWidget.md +15 -0
  226. package/src/components/CnStackedBarWidget/CnStackedBarWidget.vue +414 -0
  227. package/src/components/CnStackedBarWidget/index.js +14 -0
  228. package/src/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue +224 -0
  229. package/src/components/CnStackedBarWidgetForm/index.js +4 -0
  230. package/src/components/CnTabs/CnTab.vue +35 -0
  231. package/src/components/CnTabs/CnTabs.vue +244 -10
  232. package/src/components/CnTabsWidget/CnTabsWidget.vue +63 -4
  233. package/src/components/CnWeekStripWidget/CnWeekStripWidget.md +31 -0
  234. package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +668 -0
  235. package/src/components/CnWeekStripWidget/index.js +14 -0
  236. package/src/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue +265 -0
  237. package/src/components/CnWeekStripWidgetForm/index.js +4 -0
  238. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +74 -0
  239. package/src/components/index.js +12 -0
  240. package/src/css/detail-page.css +57 -2
  241. package/src/index.js +16 -0
  242. package/src/schemas/app-manifest-v2.schema.json +433 -3
  243. package/src/schemas/app-manifest.schema.json +2 -1
  244. package/src/types/manifest.d.ts +1 -1
  245. package/src/utils/dateVariant.js +120 -0
  246. package/src/utils/detailActionModel.js +256 -0
  247. package/src/utils/dueRule.js +94 -0
  248. package/src/utils/fetchAggregate.js +36 -0
  249. package/src/utils/fetchFilterCounts.js +182 -0
  250. package/src/utils/headers.js +28 -2
  251. package/src/utils/index.js +1 -0
  252. package/src/utils/libraryWidgetKeys.js +5 -0
  253. package/src/utils/readPath.js +27 -0
  254. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -1,5 +1,5 @@
1
1
  import { translate } from '@nextcloud/l10n';
2
- import { NcTextField, NcSelect, NcButton } from '@nextcloud/vue';
2
+ import { NcTextField, NcSelect, NcButton, NcCheckboxRadioSwitch } from '@nextcloud/vue';
3
3
  import __sfc_main from '../CnColorPicker/CnColorPicker.vue.js';
4
4
  import { extractTransportUrl, warnUploadFnDeprecated, readFileAsDataUrl, embedAsDataUrl } from '../../utils/widgetUpload.js';
5
5
 
@@ -43,6 +43,7 @@ var script = {
43
43
  NcTextField,
44
44
  NcSelect,
45
45
  NcButton,
46
+ NcCheckboxRadioSwitch,
46
47
  CnColorPicker: __sfc_main,
47
48
  },
48
49
 
@@ -119,6 +120,11 @@ var script = {
119
120
  return {
120
121
  title: typeof initial.title === 'string' ? initial.title : DEFAULT_CONTENT.title,
121
122
  subtitle: typeof initial.subtitle === 'string' ? initial.subtitle : DEFAULT_CONTENT.subtitle,
123
+ greeting: initial.greeting === true || initial.greeting === 'full' || initial.greeting === 'first',
124
+ // `"full"` (whole display name) is a manifest choice; kept as written.
125
+ greetingMode: initial.greeting === 'full' ? 'full' : true,
126
+ showDate: initial.showDate === true,
127
+ plain: initial.plain === true,
122
128
  backgroundImageUrl: typeof initial.backgroundImageUrl === 'string'
123
129
  ? initial.backgroundImageUrl
124
130
  : DEFAULT_CONTENT.backgroundImageUrl,
@@ -231,9 +237,35 @@ var script = {
231
237
  }
232
238
  },
233
239
 
234
- /** The full content blob assembled from the current field values. */
240
+ /**
241
+ * The greeting keys, written only when they are on, so a header that
242
+ * never used them keeps the content blob it always had.
243
+ *
244
+ * @return {object}
245
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
246
+ */
247
+ greetingContent() {
248
+ const content = {};
249
+ if (this.greeting) {
250
+ content.greeting = this.greetingMode;
251
+ }
252
+ if (this.showDate) {
253
+ content.showDate = true;
254
+ }
255
+ if (this.plain) {
256
+ content.plain = true;
257
+ }
258
+ return content
259
+ },
260
+
261
+ /**
262
+ * The full content blob assembled from the current field values.
263
+ *
264
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
265
+ */
235
266
  assembledContent() {
236
267
  return {
268
+ ...this.greetingContent,
237
269
  title: this.title,
238
270
  subtitle: this.subtitle,
239
271
  backgroundImageUrl: this.backgroundImageUrl,
@@ -375,10 +407,12 @@ var script = {
375
407
  * when either CTA field is filled.
376
408
  *
377
409
  * @return {string[]} the validation errors.
410
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
378
411
  */
379
412
  validate() {
380
413
  const errors = [];
381
- if (typeof this.title !== 'string' || this.title.trim() === '') {
414
+ // A greeting is the heading, so a greeting header needs no title.
415
+ if (!this.greeting && (typeof this.title !== 'string' || this.title.trim() === '')) {
382
416
  errors.push(translate('nextcloud-vue', 'Title is required'));
383
417
  }
384
418
  if (typeof this.backgroundImageUrl === 'string'
@@ -1 +1 @@
1
- {"version":3,"file":"CnHeaderWidgetForm.vue2.js","sources":["../../../../src/components/CnHeaderWidgetForm/CnHeaderWidgetForm.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-header-widget-form\">\n\t\t<NcTextField\n\t\t\t:modelValue=\"title\"\n\t\t\t:label=\"t('nextcloud-vue', 'Title')\"\n\t\t\t:placeholder=\"t('nextcloud-vue', 'Header title')\"\n\t\t\trequired\n\t\t\t@update:modelValue=\"updateField('title', $event)\" />\n\n\t\t<NcTextField\n\t\t\t:modelValue=\"subtitle\"\n\t\t\t:label=\"t('nextcloud-vue', 'Subtitle (optional)')\"\n\t\t\t:placeholder=\"t('nextcloud-vue', 'Optional subtitle')\"\n\t\t\t@update:modelValue=\"updateField('subtitle', $event)\" />\n\n\t\t<!-- Pick a background image. Selection does NOT upload — the file is held\n\t\t and only uploaded when the host modal calls commit() on submit, so\n\t\t re-picking or cancelling writes nothing. Uploads are the reliable\n\t\t path: external image URLs are often blocked by the Nextcloud\n\t\t Content-Security-Policy, so only the background colour would show. -->\n\t\t<div class=\"cn-header-widget-form__upload-row\">\n\t\t\t<label class=\"cn-header-widget-form__upload-label\">\n\t\t\t\t<input\n\t\t\t\t\tref=\"fileInput\"\n\t\t\t\t\ttype=\"file\"\n\t\t\t\t\taccept=\"image/*\"\n\t\t\t\t\tclass=\"cn-header-widget-form__file-input\"\n\t\t\t\t\t:disabled=\"uploading\"\n\t\t\t\t\t@change=\"handleFileSelect\">\n\t\t\t\t<span class=\"cn-header-widget-form__upload-button\">\n\t\t\t\t\t{{ pendingFile ? t('nextcloud-vue', 'Change background image') : t('nextcloud-vue', 'Choose background image') }}\n\t\t\t\t</span>\n\t\t\t</label>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"pendingFile\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:disabled=\"uploading\"\n\t\t\t\t@click=\"clearPendingFile\">\n\t\t\t\t{{ t('nextcloud-vue', 'Remove') }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<p v-if=\"pendingFile\" class=\"cn-header-widget-form__pending\">\n\t\t\t{{ t('nextcloud-vue', 'Ready to upload on save: {name}', { name: pendingFile.name }) }}\n\t\t</p>\n\t\t<p v-if=\"uploadError\" class=\"cn-header-widget-form__error\" role=\"alert\">\n\t\t\t{{ uploadError }}\n\t\t</p>\n\n\t\t<NcTextField\n\t\t\t:modelValue=\"backgroundImageUrl\"\n\t\t\t:label=\"t('nextcloud-vue', 'Background image URL')\"\n\t\t\tplaceholder=\"https://example.com/banner.jpg\"\n\t\t\t:disabled=\"!!pendingFile\"\n\t\t\t@update:modelValue=\"updateField('backgroundImageUrl', $event)\" />\n\n\t\t<label class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Background color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"backgroundColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('backgroundColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('backgroundColor', '')\" />\n\t\t</label>\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"overlayMode\"\n\t\t\t:options=\"overlayModeOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Overlay mode')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('overlayMode', $event)\" />\n\n\t\t<label v-if=\"overlayMode !== 'none'\" class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Overlay color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"overlayColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('overlayColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('overlayColor', '')\" />\n\t\t</label>\n\n\t\t<label v-if=\"overlayMode === 'tint'\" class=\"cn-header-widget-form__field\">\n\t\t\t<span class=\"cn-header-widget-form__label\">\n\t\t\t\t{{ t('nextcloud-vue', 'Overlay opacity') }} ({{ overlayOpacity.toFixed(2) }})\n\t\t\t</span>\n\t\t\t<input\n\t\t\t\ttype=\"range\"\n\t\t\t\tmin=\"0\"\n\t\t\t\tmax=\"1\"\n\t\t\t\tstep=\"0.05\"\n\t\t\t\t:value=\"overlayOpacity\"\n\t\t\t\tclass=\"cn-header-widget-form__range\"\n\t\t\t\t@input=\"updateField('overlayOpacity', parseFloat($event.target.value))\">\n\t\t</label>\n\n\t\t<label class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Text color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"textColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('textColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('textColor', '')\" />\n\t\t</label>\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"textAlign\"\n\t\t\t:options=\"textAlignOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Text alignment')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('textAlign', $event)\" />\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"verticalAlign\"\n\t\t\t:options=\"verticalAlignOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Vertical alignment')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('verticalAlign', $event)\" />\n\n\t\t<!-- The banner fills its dashboard grid cell, so a fixed Height preset\n\t\t no longer changes anything — resize the widget on the grid instead.\n\t\t (The `height` field is kept in the saved content for back-compat\n\t\t but is no longer surfaced as a control.) -->\n\n\t\t<fieldset class=\"cn-header-widget-form__fieldset\">\n\t\t\t<legend class=\"cn-header-widget-form__legend\">\n\t\t\t\t{{ t('nextcloud-vue', 'Call-to-action button (optional)') }}\n\t\t\t</legend>\n\n\t\t\t<NcTextField\n\t\t\t\t:modelValue=\"ctaLabel\"\n\t\t\t\t:label=\"t('nextcloud-vue', 'Button text')\"\n\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Sign up')\"\n\t\t\t\t@update:modelValue=\"updateCta('label', $event)\" />\n\n\t\t\t<NcTextField\n\t\t\t\t:modelValue=\"ctaUrl\"\n\t\t\t\t:label=\"t('nextcloud-vue', 'Target URL')\"\n\t\t\t\tplaceholder=\"https://...\"\n\t\t\t\t@update:modelValue=\"updateCta('url', $event)\" />\n\n\t\t\t<NcSelect\n\t\t\t\t:modelValue=\"ctaStyle\"\n\t\t\t\t:options=\"ctaStyleOptions\"\n\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Button style')\"\n\t\t\t\t:reduce=\"(option) => option.value\"\n\t\t\t\tlabel=\"label\"\n\t\t\t\t:clearable=\"false\"\n\t\t\t\t@update:modelValue=\"updateCta('style', $event)\" />\n\t\t</fieldset>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcSelect, NcTextField } from '@nextcloud/vue'\nimport CnColorPicker from '../CnColorPicker/CnColorPicker.vue'\nimport { embedAsDataUrl, extractTransportUrl, readFileAsDataUrl, warnUploadFnDeprecated } from '../../utils/widgetUpload.js'\n\nconst ALLOWED_OVERLAY_MODES = ['none', 'tint', 'gradient-bottom']\nconst ALLOWED_HEIGHTS = ['small', 'medium', 'large', 'xlarge']\nconst ALLOWED_TEXT_ALIGN = ['left', 'center', 'right']\nconst ALLOWED_VERTICAL_ALIGN = ['top', 'middle', 'bottom']\nconst ALLOWED_CTA_STYLES = ['primary', 'secondary', 'ghost']\n\nconst DEFAULT_CONTENT = Object.freeze({\n\ttitle: '',\n\tsubtitle: '',\n\tbackgroundImageUrl: '',\n\tbackgroundImageFileId: null,\n\tbackgroundColor: '',\n\toverlayMode: 'none',\n\toverlayColor: '',\n\toverlayOpacity: 0.4,\n\ttextColor: '',\n\ttextAlign: 'center',\n\tverticalAlign: 'middle',\n\theight: 'medium',\n\tcta: null,\n})\n\n/**\n * CnHeaderWidgetForm — the `CnAddWidgetModal` sub-form for creating or editing\n * a `header` widget placement (renderer: `CnHeaderWidget`).\n *\n * Controls cover every persisted field: title, subtitle, background image URL,\n * background colour, overlay mode/colour/opacity, text colour/alignment/\n * vertical alignment, height preset, and an optional call-to-action with\n * label, URL, and style. `validate()` requires `title`, requires any provided\n * `backgroundImageUrl` to be http(s), and flags a half-filled CTA (label XOR\n * url). Emits `update:content` with the assembled blob on change.\n */\nexport default {\n\tname: 'CnHeaderWidgetForm',\n\n\tcomponents: {\n\t\tNcTextField,\n\t\tNcSelect,\n\t\tNcButton,\n\t\tCnColorPicker,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The placement being edited, or `null` in create mode.\n\t\t *\n\t\t * @type {{content: object}|null}\n\t\t */\n\t\teditingWidget: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Initial content values — used when not editing and the parent\n\t\t * supplies registry defaults.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tvalue: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({ ...DEFAULT_CONTENT }),\n\t\t},\n\n\t\t/**\n\t\t * Optional raw-file upload transport: `async (file: File) => ({ url })`.\n\t\t * Named `fileUploadFn` (not `uploadFn`) to match `CnAddWidgetModal`'s\n\t\t * File-typed sub-form transport. Called by `commit()` on submit (never on\n\t\t * file selection) with the raw picked `File`; the returned hosted URL is\n\t\t * stored as `backgroundImageUrl`. When omitted, the file is embedded as a\n\t\t * data URL on commit instead (same-origin, so it isn't blocked by the CSP\n\t\t * the way external http(s) URLs are) — but only when the raw file is ≤ 1 MB\n\t\t * (~1.37 MB once base64-encoded and stored) so a huge inline blob can't\n\t\t * freeze the tab. Wire a transport for anything larger.\n\t\t *\n\t\t * @type {((file: File) => Promise<{ url: string }>)|null}\n\t\t */\n\t\tfileUploadFn: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Legacy base64 upload transport, superseded by `fileUploadFn`.\n\t\t *\n\t\t * @deprecated Use `fileUploadFn` instead. Legacy base64 transport\n\t\t * `async (dataUrl: string) => ({ url })`, kept for backward compatibility.\n\t\t * When `fileUploadFn` is not set but this is, `commit()` reads the file to\n\t\t * a data URL and hands that to this function (emitting a one-time\n\t\t * console.warn). `fileUploadFn` takes precedence when both are provided.\n\t\t *\n\t\t * @type {((dataUrl: string) => Promise<{ url: string }>)|null}\n\t\t */\n\t\tuploadFn: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted with the assembled content blob on every field change.\n\t\t *\n\t\t * @event update:content\n\t\t * @type {object}\n\t\t */\n\t\t'update:content',\n\t],\n\n\tdata() {\n\t\tconst initial = (this.editingWidget && this.editingWidget.content) || this.value || {}\n\t\tconst cta = (initial.cta && typeof initial.cta === 'object') ? initial.cta : null\n\t\treturn {\n\t\t\ttitle: typeof initial.title === 'string' ? initial.title : DEFAULT_CONTENT.title,\n\t\t\tsubtitle: typeof initial.subtitle === 'string' ? initial.subtitle : DEFAULT_CONTENT.subtitle,\n\t\t\tbackgroundImageUrl: typeof initial.backgroundImageUrl === 'string'\n\t\t\t\t? initial.backgroundImageUrl\n\t\t\t\t: DEFAULT_CONTENT.backgroundImageUrl,\n\n\t\t\tbackgroundImageFileId: (typeof initial.backgroundImageFileId === 'number')\n\t\t\t\t? initial.backgroundImageFileId\n\t\t\t\t: DEFAULT_CONTENT.backgroundImageFileId,\n\n\t\t\tbackgroundColor: typeof initial.backgroundColor === 'string'\n\t\t\t\t? initial.backgroundColor\n\t\t\t\t: DEFAULT_CONTENT.backgroundColor,\n\n\t\t\toverlayMode: ALLOWED_OVERLAY_MODES.includes(initial.overlayMode)\n\t\t\t\t? initial.overlayMode\n\t\t\t\t: DEFAULT_CONTENT.overlayMode,\n\n\t\t\toverlayColor: typeof initial.overlayColor === 'string'\n\t\t\t\t? initial.overlayColor\n\t\t\t\t: DEFAULT_CONTENT.overlayColor,\n\n\t\t\toverlayOpacity: typeof initial.overlayOpacity === 'number'\n\t\t\t\t? initial.overlayOpacity\n\t\t\t\t: DEFAULT_CONTENT.overlayOpacity,\n\n\t\t\ttextColor: typeof initial.textColor === 'string'\n\t\t\t\t? initial.textColor\n\t\t\t\t: DEFAULT_CONTENT.textColor,\n\n\t\t\ttextAlign: ALLOWED_TEXT_ALIGN.includes(initial.textAlign)\n\t\t\t\t? initial.textAlign\n\t\t\t\t: DEFAULT_CONTENT.textAlign,\n\n\t\t\tverticalAlign: ALLOWED_VERTICAL_ALIGN.includes(initial.verticalAlign)\n\t\t\t\t? initial.verticalAlign\n\t\t\t\t: DEFAULT_CONTENT.verticalAlign,\n\n\t\t\theight: ALLOWED_HEIGHTS.includes(initial.height)\n\t\t\t\t? initial.height\n\t\t\t\t: DEFAULT_CONTENT.height,\n\n\t\t\tctaLabel: cta && typeof cta.label === 'string' ? cta.label : '',\n\t\t\tctaUrl: cta && typeof cta.url === 'string' ? cta.url : '',\n\t\t\tctaStyle: (cta && ALLOWED_CTA_STYLES.includes(cta.style)) ? cta.style : 'primary',\n\t\t\tuploading: false,\n\t\t\tuploadError: '',\n\t\t\t// The picked-but-not-yet-uploaded file. Upload is deferred to commit()\n\t\t\t// so nothing is written on selection.\n\t\t\tpendingFile: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Overlay-mode select options. */\n\t\toverlayModeOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'none', label: t('nextcloud-vue', 'None') },\n\t\t\t\t{ value: 'tint', label: t('nextcloud-vue', 'Tinted overlay') },\n\t\t\t\t{ value: 'gradient-bottom', label: t('nextcloud-vue', 'Gradient bottom') },\n\t\t\t]\n\t\t},\n\n\t\t/** Text-alignment select options. */\n\t\ttextAlignOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'left', label: t('nextcloud-vue', 'Left') },\n\t\t\t\t{ value: 'center', label: t('nextcloud-vue', 'Center') },\n\t\t\t\t{ value: 'right', label: t('nextcloud-vue', 'Right') },\n\t\t\t]\n\t\t},\n\n\t\t/** Vertical-alignment select options. */\n\t\tverticalAlignOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'top', label: t('nextcloud-vue', 'Top') },\n\t\t\t\t{ value: 'middle', label: t('nextcloud-vue', 'Middle') },\n\t\t\t\t{ value: 'bottom', label: t('nextcloud-vue', 'Bottom') },\n\t\t\t]\n\t\t},\n\n\t\t/** Height-preset select options. */\n\t\theightOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'small', label: t('nextcloud-vue', 'Small (120px)') },\n\t\t\t\t{ value: 'medium', label: t('nextcloud-vue', 'Medium (200px)') },\n\t\t\t\t{ value: 'large', label: t('nextcloud-vue', 'Large (320px)') },\n\t\t\t\t{ value: 'xlarge', label: t('nextcloud-vue', 'Extra large (480px)') },\n\t\t\t]\n\t\t},\n\n\t\t/** CTA-style select options. */\n\t\tctaStyleOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'primary', label: t('nextcloud-vue', 'Primary') },\n\t\t\t\t{ value: 'secondary', label: t('nextcloud-vue', 'Secondary') },\n\t\t\t\t{ value: 'ghost', label: t('nextcloud-vue', 'Ghost') },\n\t\t\t]\n\t\t},\n\n\t\t/** The assembled CTA object, or null when both fields are empty. */\n\t\tctaPayload() {\n\t\t\tconst label = typeof this.ctaLabel === 'string' ? this.ctaLabel.trim() : ''\n\t\t\tconst url = typeof this.ctaUrl === 'string' ? this.ctaUrl.trim() : ''\n\t\t\tif (label === '' && url === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tlabel,\n\t\t\t\turl,\n\t\t\t\tstyle: this.ctaStyle,\n\t\t\t}\n\t\t},\n\n\t\t/** The full content blob assembled from the current field values. */\n\t\tassembledContent() {\n\t\t\treturn {\n\t\t\t\ttitle: this.title,\n\t\t\t\tsubtitle: this.subtitle,\n\t\t\t\tbackgroundImageUrl: this.backgroundImageUrl,\n\t\t\t\tbackgroundImageFileId: this.backgroundImageFileId,\n\t\t\t\tbackgroundColor: this.backgroundColor,\n\t\t\t\toverlayMode: this.overlayMode,\n\t\t\t\toverlayColor: this.overlayColor,\n\t\t\t\toverlayOpacity: this.overlayOpacity,\n\t\t\t\ttextColor: this.textColor,\n\t\t\t\ttextAlign: this.textAlign,\n\t\t\t\tverticalAlign: this.verticalAlign,\n\t\t\t\theight: this.height,\n\t\t\t\tcta: this.ctaPayload,\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Set a top-level field and re-emit the assembled payload.\n\t\t *\n\t\t * @param {string} field one of the top-level content keys.\n\t\t * @param {unknown} value the new value.\n\t\t * @return {void}\n\t\t */\n\t\tupdateField(field, value) {\n\t\t\tthis[field] = value\n\t\t\tthis.$emit('update:content', this.assembledContent)\n\t\t},\n\n\t\t/**\n\t\t * Hold the picked file for a deferred upload. Does NOT upload — that\n\t\t * happens in commit() when the host modal submits, so re-picking or\n\t\t * cancelling writes nothing.\n\t\t *\n\t\t * @param {Event} event the file-input change event.\n\t\t * @return {void}\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\thandleFileSelect(event) {\n\t\t\tconst file = event.target.files && event.target.files[0]\n\t\t\tif (!file) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.uploadError = ''\n\t\t\tthis.pendingFile = file\n\t\t\tthis.resetFileInput()\n\t\t},\n\n\t\t/**\n\t\t * Upload the pending file (if any) and store it as backgroundImageUrl.\n\t\t * Called by the host modal on submit. A no-op when no file is pending, so\n\t\t * editing without changing the image keeps the existing value.\n\t\t *\n\t\t * @return {Promise<void>} resolves once the URL is set.\n\t\t * @throws {Error} when the upload fails, so the modal can block submit.\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\tasync commit() {\n\t\t\tif (this.pendingFile === null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.uploadError = ''\n\t\t\tthis.uploading = true\n\t\t\ttry {\n\t\t\t\tlet resolvedUrl\n\t\t\t\tif (typeof this.fileUploadFn === 'function') {\n\t\t\t\t\tresolvedUrl = extractTransportUrl(await this.fileUploadFn(this.pendingFile))\n\t\t\t\t} else if (typeof this.uploadFn === 'function') {\n\t\t\t\t\t// Deprecated path: the legacy uploadFn expects a base64 data URL.\n\t\t\t\t\twarnUploadFnDeprecated('CnHeaderWidgetForm')\n\t\t\t\t\tconst dataUrl = await readFileAsDataUrl(this.pendingFile)\n\t\t\t\t\tresolvedUrl = extractTransportUrl(await this.uploadFn(dataUrl))\n\t\t\t\t} else {\n\t\t\t\t\tresolvedUrl = await embedAsDataUrl(this.pendingFile)\n\t\t\t\t}\n\t\t\t\tthis.pendingFile = null\n\t\t\t\tthis.updateField('backgroundImageUrl', resolvedUrl)\n\t\t\t} catch (err) {\n\t\t\t\tthis.uploadError = (err && err.message) || t('nextcloud-vue', 'Failed to upload image')\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('Header image upload failed:', err)\n\t\t\t\tthrow err\n\t\t\t} finally {\n\t\t\t\tthis.uploading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Discard the pending file, re-enabling the URL field. Called by the\n\t\t * Remove button.\n\t\t *\n\t\t * @return {void}\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\tclearPendingFile() {\n\t\t\tthis.pendingFile = null\n\t\t\tthis.uploadError = ''\n\t\t\tthis.resetFileInput()\n\t\t},\n\n\t\t/**\n\t\t * Clear the native file input so re-selecting the same file re-fires.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tresetFileInput() {\n\t\t\tif (this.$refs.fileInput) {\n\t\t\t\tthis.$refs.fileInput.value = ''\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Set a CTA sub-field (label, url, style) and re-emit.\n\t\t *\n\t\t * @param {string} field one of: label, url, style.\n\t\t * @param {string} value the new value.\n\t\t * @return {void}\n\t\t */\n\t\tupdateCta(field, value) {\n\t\t\tif (field === 'label') {\n\t\t\t\tthis.ctaLabel = value\n\t\t\t} else if (field === 'url') {\n\t\t\t\tthis.ctaUrl = value\n\t\t\t} else if (field === 'style') {\n\t\t\t\tthis.ctaStyle = value\n\t\t\t}\n\t\t\tthis.$emit('update:content', this.assembledContent)\n\t\t},\n\n\t\t/**\n\t\t * Validate the form; an empty array means valid. Requires a title,\n\t\t * an http(s) background image URL when provided, and a complete CTA\n\t\t * when either CTA field is filled.\n\t\t *\n\t\t * @return {string[]} the validation errors.\n\t\t */\n\t\tvalidate() {\n\t\t\tconst errors = []\n\t\t\tif (typeof this.title !== 'string' || this.title.trim() === '') {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Title is required'))\n\t\t\t}\n\t\t\tif (typeof this.backgroundImageUrl === 'string'\n\t\t\t\t&& this.backgroundImageUrl.trim() !== ''\n\t\t\t\t&& /^(https?:\\/\\/|data:|blob:|\\/)/i.test(this.backgroundImageUrl.trim()) === false) {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Background image must be a URL or an uploaded image'))\n\t\t\t}\n\t\t\tconst labelEmpty = typeof this.ctaLabel !== 'string' || this.ctaLabel.trim() === ''\n\t\t\tconst urlEmpty = typeof this.ctaUrl !== 'string' || this.ctaUrl.trim() === ''\n\t\t\tif (labelEmpty !== urlEmpty) {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Call-to-action requires both label and URL'))\n\t\t\t}\n\t\t\treturn errors\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-header-widget-form {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-header-widget-form__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__label {\n\tfont-weight: 500;\n}\n\n.cn-header-widget-form__color-label {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__upload-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-header-widget-form__upload-label {\n\tdisplay: inline-flex;\n\tcursor: pointer;\n}\n\n.cn-header-widget-form__pending {\n\tmargin: 0;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-header-widget-form__file-input {\n\tdisplay: none;\n}\n\n.cn-header-widget-form__upload-button {\n\tpadding: 8px 12px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbackground-color: var(--color-background-hover);\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__upload-label:hover .cn-header-widget-form__upload-button {\n\tbackground-color: var(--color-background-dark);\n}\n\n.cn-header-widget-form__error {\n\tmargin: 0;\n\tfont-size: 12px;\n\tcolor: var(--color-error);\n}\n\n.cn-header-widget-form__color {\n\twidth: 48px;\n\theight: 32px;\n\tpadding: 0;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tcursor: pointer;\n\tbackground: transparent;\n}\n\n.cn-header-widget-form__range {\n\twidth: 100%;\n}\n\n.cn-header-widget-form__fieldset {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 12px;\n}\n\n.cn-header-widget-form__legend {\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tpadding: 0 4px;\n}\n</style>\n"],"names":["CnColorPicker","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnHeaderWidgetForm.vue2.js","sources":["../../../../src/components/CnHeaderWidgetForm/CnHeaderWidgetForm.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-header-widget-form\">\n\t\t<NcTextField\n\t\t\t:modelValue=\"title\"\n\t\t\t:label=\"t('nextcloud-vue', 'Title')\"\n\t\t\t:placeholder=\"t('nextcloud-vue', 'Header title')\"\n\t\t\trequired\n\t\t\t@update:modelValue=\"updateField('title', $event)\" />\n\n\t\t<NcTextField\n\t\t\t:modelValue=\"subtitle\"\n\t\t\t:label=\"t('nextcloud-vue', 'Subtitle (optional)')\"\n\t\t\t:placeholder=\"t('nextcloud-vue', 'Optional subtitle')\"\n\t\t\t@update:modelValue=\"updateField('subtitle', $event)\" />\n\n\t\t<NcCheckboxRadioSwitch\n\t\t\t:modelValue=\"greeting\"\n\t\t\t@update:modelValue=\"updateField('greeting', $event)\">\n\t\t\t{{ t('nextcloud-vue', 'Greet the user by name instead of showing the title') }}\n\t\t</NcCheckboxRadioSwitch>\n\t\t<NcCheckboxRadioSwitch\n\t\t\t:modelValue=\"showDate\"\n\t\t\t@update:modelValue=\"updateField('showDate', $event)\">\n\t\t\t{{ t('nextcloud-vue', 'Show the date of today above the heading') }}\n\t\t</NcCheckboxRadioSwitch>\n\t\t<NcCheckboxRadioSwitch\n\t\t\t:modelValue=\"plain\"\n\t\t\t@update:modelValue=\"updateField('plain', $event)\">\n\t\t\t{{ t('nextcloud-vue', 'Plain header without a coloured background') }}\n\t\t</NcCheckboxRadioSwitch>\n\n\t\t<!-- Pick a background image. Selection does NOT upload — the file is held\n\t\t and only uploaded when the host modal calls commit() on submit, so\n\t\t re-picking or cancelling writes nothing. Uploads are the reliable\n\t\t path: external image URLs are often blocked by the Nextcloud\n\t\t Content-Security-Policy, so only the background colour would show. -->\n\t\t<div class=\"cn-header-widget-form__upload-row\">\n\t\t\t<label class=\"cn-header-widget-form__upload-label\">\n\t\t\t\t<input\n\t\t\t\t\tref=\"fileInput\"\n\t\t\t\t\ttype=\"file\"\n\t\t\t\t\taccept=\"image/*\"\n\t\t\t\t\tclass=\"cn-header-widget-form__file-input\"\n\t\t\t\t\t:disabled=\"uploading\"\n\t\t\t\t\t@change=\"handleFileSelect\">\n\t\t\t\t<span class=\"cn-header-widget-form__upload-button\">\n\t\t\t\t\t{{ pendingFile ? t('nextcloud-vue', 'Change background image') : t('nextcloud-vue', 'Choose background image') }}\n\t\t\t\t</span>\n\t\t\t</label>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"pendingFile\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:disabled=\"uploading\"\n\t\t\t\t@click=\"clearPendingFile\">\n\t\t\t\t{{ t('nextcloud-vue', 'Remove') }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<p v-if=\"pendingFile\" class=\"cn-header-widget-form__pending\">\n\t\t\t{{ t('nextcloud-vue', 'Ready to upload on save: {name}', { name: pendingFile.name }) }}\n\t\t</p>\n\t\t<p v-if=\"uploadError\" class=\"cn-header-widget-form__error\" role=\"alert\">\n\t\t\t{{ uploadError }}\n\t\t</p>\n\n\t\t<NcTextField\n\t\t\t:modelValue=\"backgroundImageUrl\"\n\t\t\t:label=\"t('nextcloud-vue', 'Background image URL')\"\n\t\t\tplaceholder=\"https://example.com/banner.jpg\"\n\t\t\t:disabled=\"!!pendingFile\"\n\t\t\t@update:modelValue=\"updateField('backgroundImageUrl', $event)\" />\n\n\t\t<label class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Background color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"backgroundColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('backgroundColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('backgroundColor', '')\" />\n\t\t</label>\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"overlayMode\"\n\t\t\t:options=\"overlayModeOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Overlay mode')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('overlayMode', $event)\" />\n\n\t\t<label v-if=\"overlayMode !== 'none'\" class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Overlay color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"overlayColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('overlayColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('overlayColor', '')\" />\n\t\t</label>\n\n\t\t<label v-if=\"overlayMode === 'tint'\" class=\"cn-header-widget-form__field\">\n\t\t\t<span class=\"cn-header-widget-form__label\">\n\t\t\t\t{{ t('nextcloud-vue', 'Overlay opacity') }} ({{ overlayOpacity.toFixed(2) }})\n\t\t\t</span>\n\t\t\t<input\n\t\t\t\ttype=\"range\"\n\t\t\t\tmin=\"0\"\n\t\t\t\tmax=\"1\"\n\t\t\t\tstep=\"0.05\"\n\t\t\t\t:value=\"overlayOpacity\"\n\t\t\t\tclass=\"cn-header-widget-form__range\"\n\t\t\t\t@input=\"updateField('overlayOpacity', parseFloat($event.target.value))\">\n\t\t</label>\n\n\t\t<label class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Text color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"textColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('textColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('textColor', '')\" />\n\t\t</label>\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"textAlign\"\n\t\t\t:options=\"textAlignOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Text alignment')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('textAlign', $event)\" />\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"verticalAlign\"\n\t\t\t:options=\"verticalAlignOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Vertical alignment')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('verticalAlign', $event)\" />\n\n\t\t<!-- The banner fills its dashboard grid cell, so a fixed Height preset\n\t\t no longer changes anything — resize the widget on the grid instead.\n\t\t (The `height` field is kept in the saved content for back-compat\n\t\t but is no longer surfaced as a control.) -->\n\n\t\t<fieldset class=\"cn-header-widget-form__fieldset\">\n\t\t\t<legend class=\"cn-header-widget-form__legend\">\n\t\t\t\t{{ t('nextcloud-vue', 'Call-to-action button (optional)') }}\n\t\t\t</legend>\n\n\t\t\t<NcTextField\n\t\t\t\t:modelValue=\"ctaLabel\"\n\t\t\t\t:label=\"t('nextcloud-vue', 'Button text')\"\n\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Sign up')\"\n\t\t\t\t@update:modelValue=\"updateCta('label', $event)\" />\n\n\t\t\t<NcTextField\n\t\t\t\t:modelValue=\"ctaUrl\"\n\t\t\t\t:label=\"t('nextcloud-vue', 'Target URL')\"\n\t\t\t\tplaceholder=\"https://...\"\n\t\t\t\t@update:modelValue=\"updateCta('url', $event)\" />\n\n\t\t\t<NcSelect\n\t\t\t\t:modelValue=\"ctaStyle\"\n\t\t\t\t:options=\"ctaStyleOptions\"\n\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Button style')\"\n\t\t\t\t:reduce=\"(option) => option.value\"\n\t\t\t\tlabel=\"label\"\n\t\t\t\t:clearable=\"false\"\n\t\t\t\t@update:modelValue=\"updateCta('style', $event)\" />\n\t\t</fieldset>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcSelect, NcTextField } from '@nextcloud/vue'\nimport CnColorPicker from '../CnColorPicker/CnColorPicker.vue'\nimport { embedAsDataUrl, extractTransportUrl, readFileAsDataUrl, warnUploadFnDeprecated } from '../../utils/widgetUpload.js'\n\nconst ALLOWED_OVERLAY_MODES = ['none', 'tint', 'gradient-bottom']\nconst ALLOWED_HEIGHTS = ['small', 'medium', 'large', 'xlarge']\nconst ALLOWED_TEXT_ALIGN = ['left', 'center', 'right']\nconst ALLOWED_VERTICAL_ALIGN = ['top', 'middle', 'bottom']\nconst ALLOWED_CTA_STYLES = ['primary', 'secondary', 'ghost']\n\nconst DEFAULT_CONTENT = Object.freeze({\n\ttitle: '',\n\tsubtitle: '',\n\tbackgroundImageUrl: '',\n\tbackgroundImageFileId: null,\n\tbackgroundColor: '',\n\toverlayMode: 'none',\n\toverlayColor: '',\n\toverlayOpacity: 0.4,\n\ttextColor: '',\n\ttextAlign: 'center',\n\tverticalAlign: 'middle',\n\theight: 'medium',\n\tcta: null,\n})\n\n/**\n * CnHeaderWidgetForm — the `CnAddWidgetModal` sub-form for creating or editing\n * a `header` widget placement (renderer: `CnHeaderWidget`).\n *\n * Controls cover every persisted field: title, subtitle, background image URL,\n * background colour, overlay mode/colour/opacity, text colour/alignment/\n * vertical alignment, height preset, and an optional call-to-action with\n * label, URL, and style. `validate()` requires `title`, requires any provided\n * `backgroundImageUrl` to be http(s), and flags a half-filled CTA (label XOR\n * url). Emits `update:content` with the assembled blob on change.\n */\nexport default {\n\tname: 'CnHeaderWidgetForm',\n\n\tcomponents: {\n\t\tNcTextField,\n\t\tNcSelect,\n\t\tNcButton,\n\t\tNcCheckboxRadioSwitch,\n\t\tCnColorPicker,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The placement being edited, or `null` in create mode.\n\t\t *\n\t\t * @type {{content: object}|null}\n\t\t */\n\t\teditingWidget: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Initial content values — used when not editing and the parent\n\t\t * supplies registry defaults.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tvalue: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({ ...DEFAULT_CONTENT }),\n\t\t},\n\n\t\t/**\n\t\t * Optional raw-file upload transport: `async (file: File) => ({ url })`.\n\t\t * Named `fileUploadFn` (not `uploadFn`) to match `CnAddWidgetModal`'s\n\t\t * File-typed sub-form transport. Called by `commit()` on submit (never on\n\t\t * file selection) with the raw picked `File`; the returned hosted URL is\n\t\t * stored as `backgroundImageUrl`. When omitted, the file is embedded as a\n\t\t * data URL on commit instead (same-origin, so it isn't blocked by the CSP\n\t\t * the way external http(s) URLs are) — but only when the raw file is ≤ 1 MB\n\t\t * (~1.37 MB once base64-encoded and stored) so a huge inline blob can't\n\t\t * freeze the tab. Wire a transport for anything larger.\n\t\t *\n\t\t * @type {((file: File) => Promise<{ url: string }>)|null}\n\t\t */\n\t\tfileUploadFn: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Legacy base64 upload transport, superseded by `fileUploadFn`.\n\t\t *\n\t\t * @deprecated Use `fileUploadFn` instead. Legacy base64 transport\n\t\t * `async (dataUrl: string) => ({ url })`, kept for backward compatibility.\n\t\t * When `fileUploadFn` is not set but this is, `commit()` reads the file to\n\t\t * a data URL and hands that to this function (emitting a one-time\n\t\t * console.warn). `fileUploadFn` takes precedence when both are provided.\n\t\t *\n\t\t * @type {((dataUrl: string) => Promise<{ url: string }>)|null}\n\t\t */\n\t\tuploadFn: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted with the assembled content blob on every field change.\n\t\t *\n\t\t * @event update:content\n\t\t * @type {object}\n\t\t */\n\t\t'update:content',\n\t],\n\n\tdata() {\n\t\tconst initial = (this.editingWidget && this.editingWidget.content) || this.value || {}\n\t\tconst cta = (initial.cta && typeof initial.cta === 'object') ? initial.cta : null\n\t\treturn {\n\t\t\ttitle: typeof initial.title === 'string' ? initial.title : DEFAULT_CONTENT.title,\n\t\t\tsubtitle: typeof initial.subtitle === 'string' ? initial.subtitle : DEFAULT_CONTENT.subtitle,\n\t\t\tgreeting: initial.greeting === true || initial.greeting === 'full' || initial.greeting === 'first',\n\t\t\t// `\"full\"` (whole display name) is a manifest choice; kept as written.\n\t\t\tgreetingMode: initial.greeting === 'full' ? 'full' : true,\n\t\t\tshowDate: initial.showDate === true,\n\t\t\tplain: initial.plain === true,\n\t\t\tbackgroundImageUrl: typeof initial.backgroundImageUrl === 'string'\n\t\t\t\t? initial.backgroundImageUrl\n\t\t\t\t: DEFAULT_CONTENT.backgroundImageUrl,\n\n\t\t\tbackgroundImageFileId: (typeof initial.backgroundImageFileId === 'number')\n\t\t\t\t? initial.backgroundImageFileId\n\t\t\t\t: DEFAULT_CONTENT.backgroundImageFileId,\n\n\t\t\tbackgroundColor: typeof initial.backgroundColor === 'string'\n\t\t\t\t? initial.backgroundColor\n\t\t\t\t: DEFAULT_CONTENT.backgroundColor,\n\n\t\t\toverlayMode: ALLOWED_OVERLAY_MODES.includes(initial.overlayMode)\n\t\t\t\t? initial.overlayMode\n\t\t\t\t: DEFAULT_CONTENT.overlayMode,\n\n\t\t\toverlayColor: typeof initial.overlayColor === 'string'\n\t\t\t\t? initial.overlayColor\n\t\t\t\t: DEFAULT_CONTENT.overlayColor,\n\n\t\t\toverlayOpacity: typeof initial.overlayOpacity === 'number'\n\t\t\t\t? initial.overlayOpacity\n\t\t\t\t: DEFAULT_CONTENT.overlayOpacity,\n\n\t\t\ttextColor: typeof initial.textColor === 'string'\n\t\t\t\t? initial.textColor\n\t\t\t\t: DEFAULT_CONTENT.textColor,\n\n\t\t\ttextAlign: ALLOWED_TEXT_ALIGN.includes(initial.textAlign)\n\t\t\t\t? initial.textAlign\n\t\t\t\t: DEFAULT_CONTENT.textAlign,\n\n\t\t\tverticalAlign: ALLOWED_VERTICAL_ALIGN.includes(initial.verticalAlign)\n\t\t\t\t? initial.verticalAlign\n\t\t\t\t: DEFAULT_CONTENT.verticalAlign,\n\n\t\t\theight: ALLOWED_HEIGHTS.includes(initial.height)\n\t\t\t\t? initial.height\n\t\t\t\t: DEFAULT_CONTENT.height,\n\n\t\t\tctaLabel: cta && typeof cta.label === 'string' ? cta.label : '',\n\t\t\tctaUrl: cta && typeof cta.url === 'string' ? cta.url : '',\n\t\t\tctaStyle: (cta && ALLOWED_CTA_STYLES.includes(cta.style)) ? cta.style : 'primary',\n\t\t\tuploading: false,\n\t\t\tuploadError: '',\n\t\t\t// The picked-but-not-yet-uploaded file. Upload is deferred to commit()\n\t\t\t// so nothing is written on selection.\n\t\t\tpendingFile: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Overlay-mode select options. */\n\t\toverlayModeOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'none', label: t('nextcloud-vue', 'None') },\n\t\t\t\t{ value: 'tint', label: t('nextcloud-vue', 'Tinted overlay') },\n\t\t\t\t{ value: 'gradient-bottom', label: t('nextcloud-vue', 'Gradient bottom') },\n\t\t\t]\n\t\t},\n\n\t\t/** Text-alignment select options. */\n\t\ttextAlignOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'left', label: t('nextcloud-vue', 'Left') },\n\t\t\t\t{ value: 'center', label: t('nextcloud-vue', 'Center') },\n\t\t\t\t{ value: 'right', label: t('nextcloud-vue', 'Right') },\n\t\t\t]\n\t\t},\n\n\t\t/** Vertical-alignment select options. */\n\t\tverticalAlignOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'top', label: t('nextcloud-vue', 'Top') },\n\t\t\t\t{ value: 'middle', label: t('nextcloud-vue', 'Middle') },\n\t\t\t\t{ value: 'bottom', label: t('nextcloud-vue', 'Bottom') },\n\t\t\t]\n\t\t},\n\n\t\t/** Height-preset select options. */\n\t\theightOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'small', label: t('nextcloud-vue', 'Small (120px)') },\n\t\t\t\t{ value: 'medium', label: t('nextcloud-vue', 'Medium (200px)') },\n\t\t\t\t{ value: 'large', label: t('nextcloud-vue', 'Large (320px)') },\n\t\t\t\t{ value: 'xlarge', label: t('nextcloud-vue', 'Extra large (480px)') },\n\t\t\t]\n\t\t},\n\n\t\t/** CTA-style select options. */\n\t\tctaStyleOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'primary', label: t('nextcloud-vue', 'Primary') },\n\t\t\t\t{ value: 'secondary', label: t('nextcloud-vue', 'Secondary') },\n\t\t\t\t{ value: 'ghost', label: t('nextcloud-vue', 'Ghost') },\n\t\t\t]\n\t\t},\n\n\t\t/** The assembled CTA object, or null when both fields are empty. */\n\t\tctaPayload() {\n\t\t\tconst label = typeof this.ctaLabel === 'string' ? this.ctaLabel.trim() : ''\n\t\t\tconst url = typeof this.ctaUrl === 'string' ? this.ctaUrl.trim() : ''\n\t\t\tif (label === '' && url === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tlabel,\n\t\t\t\turl,\n\t\t\t\tstyle: this.ctaStyle,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The greeting keys, written only when they are on, so a header that\n\t\t * never used them keeps the content blob it always had.\n\t\t *\n\t\t * @return {object}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header\n\t\t */\n\t\tgreetingContent() {\n\t\t\tconst content = {}\n\t\t\tif (this.greeting) {\n\t\t\t\tcontent.greeting = this.greetingMode\n\t\t\t}\n\t\t\tif (this.showDate) {\n\t\t\t\tcontent.showDate = true\n\t\t\t}\n\t\t\tif (this.plain) {\n\t\t\t\tcontent.plain = true\n\t\t\t}\n\t\t\treturn content\n\t\t},\n\n\t\t/**\n\t\t * The full content blob assembled from the current field values.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header\n\t\t */\n\t\tassembledContent() {\n\t\t\treturn {\n\t\t\t\t...this.greetingContent,\n\t\t\t\ttitle: this.title,\n\t\t\t\tsubtitle: this.subtitle,\n\t\t\t\tbackgroundImageUrl: this.backgroundImageUrl,\n\t\t\t\tbackgroundImageFileId: this.backgroundImageFileId,\n\t\t\t\tbackgroundColor: this.backgroundColor,\n\t\t\t\toverlayMode: this.overlayMode,\n\t\t\t\toverlayColor: this.overlayColor,\n\t\t\t\toverlayOpacity: this.overlayOpacity,\n\t\t\t\ttextColor: this.textColor,\n\t\t\t\ttextAlign: this.textAlign,\n\t\t\t\tverticalAlign: this.verticalAlign,\n\t\t\t\theight: this.height,\n\t\t\t\tcta: this.ctaPayload,\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Set a top-level field and re-emit the assembled payload.\n\t\t *\n\t\t * @param {string} field one of the top-level content keys.\n\t\t * @param {unknown} value the new value.\n\t\t * @return {void}\n\t\t */\n\t\tupdateField(field, value) {\n\t\t\tthis[field] = value\n\t\t\tthis.$emit('update:content', this.assembledContent)\n\t\t},\n\n\t\t/**\n\t\t * Hold the picked file for a deferred upload. Does NOT upload — that\n\t\t * happens in commit() when the host modal submits, so re-picking or\n\t\t * cancelling writes nothing.\n\t\t *\n\t\t * @param {Event} event the file-input change event.\n\t\t * @return {void}\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\thandleFileSelect(event) {\n\t\t\tconst file = event.target.files && event.target.files[0]\n\t\t\tif (!file) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.uploadError = ''\n\t\t\tthis.pendingFile = file\n\t\t\tthis.resetFileInput()\n\t\t},\n\n\t\t/**\n\t\t * Upload the pending file (if any) and store it as backgroundImageUrl.\n\t\t * Called by the host modal on submit. A no-op when no file is pending, so\n\t\t * editing without changing the image keeps the existing value.\n\t\t *\n\t\t * @return {Promise<void>} resolves once the URL is set.\n\t\t * @throws {Error} when the upload fails, so the modal can block submit.\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\tasync commit() {\n\t\t\tif (this.pendingFile === null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.uploadError = ''\n\t\t\tthis.uploading = true\n\t\t\ttry {\n\t\t\t\tlet resolvedUrl\n\t\t\t\tif (typeof this.fileUploadFn === 'function') {\n\t\t\t\t\tresolvedUrl = extractTransportUrl(await this.fileUploadFn(this.pendingFile))\n\t\t\t\t} else if (typeof this.uploadFn === 'function') {\n\t\t\t\t\t// Deprecated path: the legacy uploadFn expects a base64 data URL.\n\t\t\t\t\twarnUploadFnDeprecated('CnHeaderWidgetForm')\n\t\t\t\t\tconst dataUrl = await readFileAsDataUrl(this.pendingFile)\n\t\t\t\t\tresolvedUrl = extractTransportUrl(await this.uploadFn(dataUrl))\n\t\t\t\t} else {\n\t\t\t\t\tresolvedUrl = await embedAsDataUrl(this.pendingFile)\n\t\t\t\t}\n\t\t\t\tthis.pendingFile = null\n\t\t\t\tthis.updateField('backgroundImageUrl', resolvedUrl)\n\t\t\t} catch (err) {\n\t\t\t\tthis.uploadError = (err && err.message) || t('nextcloud-vue', 'Failed to upload image')\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('Header image upload failed:', err)\n\t\t\t\tthrow err\n\t\t\t} finally {\n\t\t\t\tthis.uploading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Discard the pending file, re-enabling the URL field. Called by the\n\t\t * Remove button.\n\t\t *\n\t\t * @return {void}\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\tclearPendingFile() {\n\t\t\tthis.pendingFile = null\n\t\t\tthis.uploadError = ''\n\t\t\tthis.resetFileInput()\n\t\t},\n\n\t\t/**\n\t\t * Clear the native file input so re-selecting the same file re-fires.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tresetFileInput() {\n\t\t\tif (this.$refs.fileInput) {\n\t\t\t\tthis.$refs.fileInput.value = ''\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Set a CTA sub-field (label, url, style) and re-emit.\n\t\t *\n\t\t * @param {string} field one of: label, url, style.\n\t\t * @param {string} value the new value.\n\t\t * @return {void}\n\t\t */\n\t\tupdateCta(field, value) {\n\t\t\tif (field === 'label') {\n\t\t\t\tthis.ctaLabel = value\n\t\t\t} else if (field === 'url') {\n\t\t\t\tthis.ctaUrl = value\n\t\t\t} else if (field === 'style') {\n\t\t\t\tthis.ctaStyle = value\n\t\t\t}\n\t\t\tthis.$emit('update:content', this.assembledContent)\n\t\t},\n\n\t\t/**\n\t\t * Validate the form; an empty array means valid. Requires a title,\n\t\t * an http(s) background image URL when provided, and a complete CTA\n\t\t * when either CTA field is filled.\n\t\t *\n\t\t * @return {string[]} the validation errors.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header\n\t\t */\n\t\tvalidate() {\n\t\t\tconst errors = []\n\t\t\t// A greeting is the heading, so a greeting header needs no title.\n\t\t\tif (!this.greeting && (typeof this.title !== 'string' || this.title.trim() === '')) {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Title is required'))\n\t\t\t}\n\t\t\tif (typeof this.backgroundImageUrl === 'string'\n\t\t\t\t&& this.backgroundImageUrl.trim() !== ''\n\t\t\t\t&& /^(https?:\\/\\/|data:|blob:|\\/)/i.test(this.backgroundImageUrl.trim()) === false) {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Background image must be a URL or an uploaded image'))\n\t\t\t}\n\t\t\tconst labelEmpty = typeof this.ctaLabel !== 'string' || this.ctaLabel.trim() === ''\n\t\t\tconst urlEmpty = typeof this.ctaUrl !== 'string' || this.ctaUrl.trim() === ''\n\t\t\tif (labelEmpty !== urlEmpty) {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Call-to-action requires both label and URL'))\n\t\t\t}\n\t\t\treturn errors\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-header-widget-form {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-header-widget-form__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__label {\n\tfont-weight: 500;\n}\n\n.cn-header-widget-form__color-label {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__upload-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-header-widget-form__upload-label {\n\tdisplay: inline-flex;\n\tcursor: pointer;\n}\n\n.cn-header-widget-form__pending {\n\tmargin: 0;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-header-widget-form__file-input {\n\tdisplay: none;\n}\n\n.cn-header-widget-form__upload-button {\n\tpadding: 8px 12px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbackground-color: var(--color-background-hover);\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__upload-label:hover .cn-header-widget-form__upload-button {\n\tbackground-color: var(--color-background-dark);\n}\n\n.cn-header-widget-form__error {\n\tmargin: 0;\n\tfont-size: 12px;\n\tcolor: var(--color-error);\n}\n\n.cn-header-widget-form__color {\n\twidth: 48px;\n\theight: 32px;\n\tpadding: 0;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tcursor: pointer;\n\tbackground: transparent;\n}\n\n.cn-header-widget-form__range {\n\twidth: 100%;\n}\n\n.cn-header-widget-form__fieldset {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 12px;\n}\n\n.cn-header-widget-form__legend {\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tpadding: 0 4px;\n}\n</style>\n"],"names":["CnColorPicker","t"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { resolveComponent, openBlock, createElementBlock, createVNode, createElementVNode, toDisplayString, createBlock, withCtx, createTextVNode, createCommentVNode } from 'vue';
1
+ import { resolveComponent, openBlock, createElementBlock, createVNode, withCtx, createTextVNode, toDisplayString, createElementVNode, createBlock, createCommentVNode } from 'vue';
2
2
 
3
3
  const _hoisted_1 = { class: "cn-header-widget-form" };
4
4
  const _hoisted_2 = { class: "cn-header-widget-form__upload-row" };
@@ -31,6 +31,7 @@ const _hoisted_15 = { class: "cn-header-widget-form__legend" };
31
31
 
32
32
  function render(_ctx, _cache, $props, $setup, $data, $options) {
33
33
  const _component_NcTextField = resolveComponent("NcTextField");
34
+ const _component_NcCheckboxRadioSwitch = resolveComponent("NcCheckboxRadioSwitch");
34
35
  const _component_NcButton = resolveComponent("NcButton");
35
36
  const _component_CnColorPicker = resolveComponent("CnColorPicker");
36
37
  const _component_NcSelect = resolveComponent("NcSelect");
@@ -49,6 +50,33 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
49
50
  placeholder: $options.t('nextcloud-vue', 'Optional subtitle'),
50
51
  "onUpdate:modelValue": _cache[1] || (_cache[1] = $event => ($options.updateField('subtitle', $event)))
51
52
  }, null, 8 /* PROPS */, ["modelValue", "label", "placeholder"]),
53
+ createVNode(_component_NcCheckboxRadioSwitch, {
54
+ modelValue: $data.greeting,
55
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = $event => ($options.updateField('greeting', $event)))
56
+ }, {
57
+ default: withCtx(() => [
58
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Greet the user by name instead of showing the title')), 1 /* TEXT */)
59
+ ]),
60
+ _: 1 /* STABLE */
61
+ }, 8 /* PROPS */, ["modelValue"]),
62
+ createVNode(_component_NcCheckboxRadioSwitch, {
63
+ modelValue: $data.showDate,
64
+ "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => ($options.updateField('showDate', $event)))
65
+ }, {
66
+ default: withCtx(() => [
67
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Show the date of today above the heading')), 1 /* TEXT */)
68
+ ]),
69
+ _: 1 /* STABLE */
70
+ }, 8 /* PROPS */, ["modelValue"]),
71
+ createVNode(_component_NcCheckboxRadioSwitch, {
72
+ modelValue: $data.plain,
73
+ "onUpdate:modelValue": _cache[4] || (_cache[4] = $event => ($options.updateField('plain', $event)))
74
+ }, {
75
+ default: withCtx(() => [
76
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Plain header without a coloured background')), 1 /* TEXT */)
77
+ ]),
78
+ _: 1 /* STABLE */
79
+ }, 8 /* PROPS */, ["modelValue"]),
52
80
  createElementVNode("div", _hoisted_2, [
53
81
  createElementVNode("label", _hoisted_3, [
54
82
  createElementVNode("input", {
@@ -57,7 +85,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
57
85
  accept: "image/*",
58
86
  class: "cn-header-widget-form__file-input",
59
87
  disabled: $data.uploading,
60
- onChange: _cache[2] || (_cache[2] = (...args) => ($options.handleFileSelect && $options.handleFileSelect(...args)))
88
+ onChange: _cache[5] || (_cache[5] = (...args) => ($options.handleFileSelect && $options.handleFileSelect(...args)))
61
89
  }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_4),
62
90
  createElementVNode("span", _hoisted_5, toDisplayString($data.pendingFile ? $options.t('nextcloud-vue', 'Change background image') : $options.t('nextcloud-vue', 'Choose background image')), 1 /* TEXT */)
63
91
  ]),
@@ -86,15 +114,15 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
86
114
  label: $options.t('nextcloud-vue', 'Background image URL'),
87
115
  placeholder: "https://example.com/banner.jpg",
88
116
  disabled: !!$data.pendingFile,
89
- "onUpdate:modelValue": _cache[3] || (_cache[3] = $event => ($options.updateField('backgroundImageUrl', $event)))
117
+ "onUpdate:modelValue": _cache[6] || (_cache[6] = $event => ($options.updateField('backgroundImageUrl', $event)))
90
118
  }, null, 8 /* PROPS */, ["modelValue", "label", "disabled"]),
91
119
  createElementVNode("label", _hoisted_8, [
92
120
  createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Background color')) + " ", 1 /* TEXT */),
93
121
  createVNode(_component_CnColorPicker, {
94
122
  value: $data.backgroundColor,
95
123
  clearable: "",
96
- onInput: _cache[4] || (_cache[4] = $event => ($options.updateField('backgroundColor', $event.hex))),
97
- onClear: _cache[5] || (_cache[5] = $event => ($options.updateField('backgroundColor', '')))
124
+ onInput: _cache[7] || (_cache[7] = $event => ($options.updateField('backgroundColor', $event.hex))),
125
+ onClear: _cache[8] || (_cache[8] = $event => ($options.updateField('backgroundColor', '')))
98
126
  }, null, 8 /* PROPS */, ["value"])
99
127
  ]),
100
128
  createVNode(_component_NcSelect, {
@@ -104,7 +132,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
104
132
  reduce: (option) => option.value,
105
133
  label: "label",
106
134
  clearable: false,
107
- "onUpdate:modelValue": _cache[6] || (_cache[6] = $event => ($options.updateField('overlayMode', $event)))
135
+ "onUpdate:modelValue": _cache[9] || (_cache[9] = $event => ($options.updateField('overlayMode', $event)))
108
136
  }, null, 8 /* PROPS */, ["modelValue", "options", "inputLabel", "reduce"]),
109
137
  ($data.overlayMode !== 'none')
110
138
  ? (openBlock(), createElementBlock("label", _hoisted_9, [
@@ -112,8 +140,8 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
112
140
  createVNode(_component_CnColorPicker, {
113
141
  value: $data.overlayColor,
114
142
  clearable: "",
115
- onInput: _cache[7] || (_cache[7] = $event => ($options.updateField('overlayColor', $event.hex))),
116
- onClear: _cache[8] || (_cache[8] = $event => ($options.updateField('overlayColor', '')))
143
+ onInput: _cache[10] || (_cache[10] = $event => ($options.updateField('overlayColor', $event.hex))),
144
+ onClear: _cache[11] || (_cache[11] = $event => ($options.updateField('overlayColor', '')))
117
145
  }, null, 8 /* PROPS */, ["value"])
118
146
  ]))
119
147
  : createCommentVNode("v-if", true),
@@ -127,7 +155,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
127
155
  step: "0.05",
128
156
  value: $data.overlayOpacity,
129
157
  class: "cn-header-widget-form__range",
130
- onInput: _cache[9] || (_cache[9] = $event => ($options.updateField('overlayOpacity', parseFloat($event.target.value))))
158
+ onInput: _cache[12] || (_cache[12] = $event => ($options.updateField('overlayOpacity', parseFloat($event.target.value))))
131
159
  }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_12)
132
160
  ]))
133
161
  : createCommentVNode("v-if", true),
@@ -136,8 +164,8 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
136
164
  createVNode(_component_CnColorPicker, {
137
165
  value: $data.textColor,
138
166
  clearable: "",
139
- onInput: _cache[10] || (_cache[10] = $event => ($options.updateField('textColor', $event.hex))),
140
- onClear: _cache[11] || (_cache[11] = $event => ($options.updateField('textColor', '')))
167
+ onInput: _cache[13] || (_cache[13] = $event => ($options.updateField('textColor', $event.hex))),
168
+ onClear: _cache[14] || (_cache[14] = $event => ($options.updateField('textColor', '')))
141
169
  }, null, 8 /* PROPS */, ["value"])
142
170
  ]),
143
171
  createVNode(_component_NcSelect, {
@@ -147,7 +175,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
147
175
  reduce: (option) => option.value,
148
176
  label: "label",
149
177
  clearable: false,
150
- "onUpdate:modelValue": _cache[12] || (_cache[12] = $event => ($options.updateField('textAlign', $event)))
178
+ "onUpdate:modelValue": _cache[15] || (_cache[15] = $event => ($options.updateField('textAlign', $event)))
151
179
  }, null, 8 /* PROPS */, ["modelValue", "options", "inputLabel", "reduce"]),
152
180
  createVNode(_component_NcSelect, {
153
181
  modelValue: $data.verticalAlign,
@@ -156,7 +184,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
156
184
  reduce: (option) => option.value,
157
185
  label: "label",
158
186
  clearable: false,
159
- "onUpdate:modelValue": _cache[13] || (_cache[13] = $event => ($options.updateField('verticalAlign', $event)))
187
+ "onUpdate:modelValue": _cache[16] || (_cache[16] = $event => ($options.updateField('verticalAlign', $event)))
160
188
  }, null, 8 /* PROPS */, ["modelValue", "options", "inputLabel", "reduce"]),
161
189
  createElementVNode("fieldset", _hoisted_14, [
162
190
  createElementVNode("legend", _hoisted_15, toDisplayString($options.t('nextcloud-vue', 'Call-to-action button (optional)')), 1 /* TEXT */),
@@ -164,13 +192,13 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
164
192
  modelValue: $data.ctaLabel,
165
193
  label: $options.t('nextcloud-vue', 'Button text'),
166
194
  placeholder: $options.t('nextcloud-vue', 'Sign up'),
167
- "onUpdate:modelValue": _cache[14] || (_cache[14] = $event => ($options.updateCta('label', $event)))
195
+ "onUpdate:modelValue": _cache[17] || (_cache[17] = $event => ($options.updateCta('label', $event)))
168
196
  }, null, 8 /* PROPS */, ["modelValue", "label", "placeholder"]),
169
197
  createVNode(_component_NcTextField, {
170
198
  modelValue: $data.ctaUrl,
171
199
  label: $options.t('nextcloud-vue', 'Target URL'),
172
200
  placeholder: "https://...",
173
- "onUpdate:modelValue": _cache[15] || (_cache[15] = $event => ($options.updateCta('url', $event)))
201
+ "onUpdate:modelValue": _cache[18] || (_cache[18] = $event => ($options.updateCta('url', $event)))
174
202
  }, null, 8 /* PROPS */, ["modelValue", "label"]),
175
203
  createVNode(_component_NcSelect, {
176
204
  modelValue: $data.ctaStyle,
@@ -179,7 +207,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
179
207
  reduce: (option) => option.value,
180
208
  label: "label",
181
209
  clearable: false,
182
- "onUpdate:modelValue": _cache[16] || (_cache[16] = $event => ($options.updateCta('style', $event)))
210
+ "onUpdate:modelValue": _cache[19] || (_cache[19] = $event => ($options.updateCta('style', $event)))
183
211
  }, null, 8 /* PROPS */, ["modelValue", "options", "inputLabel", "reduce"])
184
212
  ])
185
213
  ]))
@@ -1 +1 @@
1
- {"version":3,"file":"CnHeaderWidgetForm.vue3.js","sources":["../../../../src/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue?vue&type=template&id=3741270f&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-header-widget-form\">\n\t\t<NcTextField\n\t\t\t:modelValue=\"title\"\n\t\t\t:label=\"t('nextcloud-vue', 'Title')\"\n\t\t\t:placeholder=\"t('nextcloud-vue', 'Header title')\"\n\t\t\trequired\n\t\t\t@update:modelValue=\"updateField('title', $event)\" />\n\n\t\t<NcTextField\n\t\t\t:modelValue=\"subtitle\"\n\t\t\t:label=\"t('nextcloud-vue', 'Subtitle (optional)')\"\n\t\t\t:placeholder=\"t('nextcloud-vue', 'Optional subtitle')\"\n\t\t\t@update:modelValue=\"updateField('subtitle', $event)\" />\n\n\t\t<!-- Pick a background image. Selection does NOT upload — the file is held\n\t\t and only uploaded when the host modal calls commit() on submit, so\n\t\t re-picking or cancelling writes nothing. Uploads are the reliable\n\t\t path: external image URLs are often blocked by the Nextcloud\n\t\t Content-Security-Policy, so only the background colour would show. -->\n\t\t<div class=\"cn-header-widget-form__upload-row\">\n\t\t\t<label class=\"cn-header-widget-form__upload-label\">\n\t\t\t\t<input\n\t\t\t\t\tref=\"fileInput\"\n\t\t\t\t\ttype=\"file\"\n\t\t\t\t\taccept=\"image/*\"\n\t\t\t\t\tclass=\"cn-header-widget-form__file-input\"\n\t\t\t\t\t:disabled=\"uploading\"\n\t\t\t\t\t@change=\"handleFileSelect\">\n\t\t\t\t<span class=\"cn-header-widget-form__upload-button\">\n\t\t\t\t\t{{ pendingFile ? t('nextcloud-vue', 'Change background image') : t('nextcloud-vue', 'Choose background image') }}\n\t\t\t\t</span>\n\t\t\t</label>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"pendingFile\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:disabled=\"uploading\"\n\t\t\t\t@click=\"clearPendingFile\">\n\t\t\t\t{{ t('nextcloud-vue', 'Remove') }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<p v-if=\"pendingFile\" class=\"cn-header-widget-form__pending\">\n\t\t\t{{ t('nextcloud-vue', 'Ready to upload on save: {name}', { name: pendingFile.name }) }}\n\t\t</p>\n\t\t<p v-if=\"uploadError\" class=\"cn-header-widget-form__error\" role=\"alert\">\n\t\t\t{{ uploadError }}\n\t\t</p>\n\n\t\t<NcTextField\n\t\t\t:modelValue=\"backgroundImageUrl\"\n\t\t\t:label=\"t('nextcloud-vue', 'Background image URL')\"\n\t\t\tplaceholder=\"https://example.com/banner.jpg\"\n\t\t\t:disabled=\"!!pendingFile\"\n\t\t\t@update:modelValue=\"updateField('backgroundImageUrl', $event)\" />\n\n\t\t<label class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Background color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"backgroundColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('backgroundColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('backgroundColor', '')\" />\n\t\t</label>\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"overlayMode\"\n\t\t\t:options=\"overlayModeOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Overlay mode')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('overlayMode', $event)\" />\n\n\t\t<label v-if=\"overlayMode !== 'none'\" class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Overlay color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"overlayColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('overlayColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('overlayColor', '')\" />\n\t\t</label>\n\n\t\t<label v-if=\"overlayMode === 'tint'\" class=\"cn-header-widget-form__field\">\n\t\t\t<span class=\"cn-header-widget-form__label\">\n\t\t\t\t{{ t('nextcloud-vue', 'Overlay opacity') }} ({{ overlayOpacity.toFixed(2) }})\n\t\t\t</span>\n\t\t\t<input\n\t\t\t\ttype=\"range\"\n\t\t\t\tmin=\"0\"\n\t\t\t\tmax=\"1\"\n\t\t\t\tstep=\"0.05\"\n\t\t\t\t:value=\"overlayOpacity\"\n\t\t\t\tclass=\"cn-header-widget-form__range\"\n\t\t\t\t@input=\"updateField('overlayOpacity', parseFloat($event.target.value))\">\n\t\t</label>\n\n\t\t<label class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Text color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"textColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('textColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('textColor', '')\" />\n\t\t</label>\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"textAlign\"\n\t\t\t:options=\"textAlignOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Text alignment')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('textAlign', $event)\" />\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"verticalAlign\"\n\t\t\t:options=\"verticalAlignOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Vertical alignment')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('verticalAlign', $event)\" />\n\n\t\t<!-- The banner fills its dashboard grid cell, so a fixed Height preset\n\t\t no longer changes anything — resize the widget on the grid instead.\n\t\t (The `height` field is kept in the saved content for back-compat\n\t\t but is no longer surfaced as a control.) -->\n\n\t\t<fieldset class=\"cn-header-widget-form__fieldset\">\n\t\t\t<legend class=\"cn-header-widget-form__legend\">\n\t\t\t\t{{ t('nextcloud-vue', 'Call-to-action button (optional)') }}\n\t\t\t</legend>\n\n\t\t\t<NcTextField\n\t\t\t\t:modelValue=\"ctaLabel\"\n\t\t\t\t:label=\"t('nextcloud-vue', 'Button text')\"\n\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Sign up')\"\n\t\t\t\t@update:modelValue=\"updateCta('label', $event)\" />\n\n\t\t\t<NcTextField\n\t\t\t\t:modelValue=\"ctaUrl\"\n\t\t\t\t:label=\"t('nextcloud-vue', 'Target URL')\"\n\t\t\t\tplaceholder=\"https://...\"\n\t\t\t\t@update:modelValue=\"updateCta('url', $event)\" />\n\n\t\t\t<NcSelect\n\t\t\t\t:modelValue=\"ctaStyle\"\n\t\t\t\t:options=\"ctaStyleOptions\"\n\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Button style')\"\n\t\t\t\t:reduce=\"(option) => option.value\"\n\t\t\t\tlabel=\"label\"\n\t\t\t\t:clearable=\"false\"\n\t\t\t\t@update:modelValue=\"updateCta('style', $event)\" />\n\t\t</fieldset>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcSelect, NcTextField } from '@nextcloud/vue'\nimport CnColorPicker from '../CnColorPicker/CnColorPicker.vue'\nimport { embedAsDataUrl, extractTransportUrl, readFileAsDataUrl, warnUploadFnDeprecated } from '../../utils/widgetUpload.js'\n\nconst ALLOWED_OVERLAY_MODES = ['none', 'tint', 'gradient-bottom']\nconst ALLOWED_HEIGHTS = ['small', 'medium', 'large', 'xlarge']\nconst ALLOWED_TEXT_ALIGN = ['left', 'center', 'right']\nconst ALLOWED_VERTICAL_ALIGN = ['top', 'middle', 'bottom']\nconst ALLOWED_CTA_STYLES = ['primary', 'secondary', 'ghost']\n\nconst DEFAULT_CONTENT = Object.freeze({\n\ttitle: '',\n\tsubtitle: '',\n\tbackgroundImageUrl: '',\n\tbackgroundImageFileId: null,\n\tbackgroundColor: '',\n\toverlayMode: 'none',\n\toverlayColor: '',\n\toverlayOpacity: 0.4,\n\ttextColor: '',\n\ttextAlign: 'center',\n\tverticalAlign: 'middle',\n\theight: 'medium',\n\tcta: null,\n})\n\n/**\n * CnHeaderWidgetForm — the `CnAddWidgetModal` sub-form for creating or editing\n * a `header` widget placement (renderer: `CnHeaderWidget`).\n *\n * Controls cover every persisted field: title, subtitle, background image URL,\n * background colour, overlay mode/colour/opacity, text colour/alignment/\n * vertical alignment, height preset, and an optional call-to-action with\n * label, URL, and style. `validate()` requires `title`, requires any provided\n * `backgroundImageUrl` to be http(s), and flags a half-filled CTA (label XOR\n * url). Emits `update:content` with the assembled blob on change.\n */\nexport default {\n\tname: 'CnHeaderWidgetForm',\n\n\tcomponents: {\n\t\tNcTextField,\n\t\tNcSelect,\n\t\tNcButton,\n\t\tCnColorPicker,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The placement being edited, or `null` in create mode.\n\t\t *\n\t\t * @type {{content: object}|null}\n\t\t */\n\t\teditingWidget: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Initial content values — used when not editing and the parent\n\t\t * supplies registry defaults.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tvalue: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({ ...DEFAULT_CONTENT }),\n\t\t},\n\n\t\t/**\n\t\t * Optional raw-file upload transport: `async (file: File) => ({ url })`.\n\t\t * Named `fileUploadFn` (not `uploadFn`) to match `CnAddWidgetModal`'s\n\t\t * File-typed sub-form transport. Called by `commit()` on submit (never on\n\t\t * file selection) with the raw picked `File`; the returned hosted URL is\n\t\t * stored as `backgroundImageUrl`. When omitted, the file is embedded as a\n\t\t * data URL on commit instead (same-origin, so it isn't blocked by the CSP\n\t\t * the way external http(s) URLs are) — but only when the raw file is ≤ 1 MB\n\t\t * (~1.37 MB once base64-encoded and stored) so a huge inline blob can't\n\t\t * freeze the tab. Wire a transport for anything larger.\n\t\t *\n\t\t * @type {((file: File) => Promise<{ url: string }>)|null}\n\t\t */\n\t\tfileUploadFn: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Legacy base64 upload transport, superseded by `fileUploadFn`.\n\t\t *\n\t\t * @deprecated Use `fileUploadFn` instead. Legacy base64 transport\n\t\t * `async (dataUrl: string) => ({ url })`, kept for backward compatibility.\n\t\t * When `fileUploadFn` is not set but this is, `commit()` reads the file to\n\t\t * a data URL and hands that to this function (emitting a one-time\n\t\t * console.warn). `fileUploadFn` takes precedence when both are provided.\n\t\t *\n\t\t * @type {((dataUrl: string) => Promise<{ url: string }>)|null}\n\t\t */\n\t\tuploadFn: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted with the assembled content blob on every field change.\n\t\t *\n\t\t * @event update:content\n\t\t * @type {object}\n\t\t */\n\t\t'update:content',\n\t],\n\n\tdata() {\n\t\tconst initial = (this.editingWidget && this.editingWidget.content) || this.value || {}\n\t\tconst cta = (initial.cta && typeof initial.cta === 'object') ? initial.cta : null\n\t\treturn {\n\t\t\ttitle: typeof initial.title === 'string' ? initial.title : DEFAULT_CONTENT.title,\n\t\t\tsubtitle: typeof initial.subtitle === 'string' ? initial.subtitle : DEFAULT_CONTENT.subtitle,\n\t\t\tbackgroundImageUrl: typeof initial.backgroundImageUrl === 'string'\n\t\t\t\t? initial.backgroundImageUrl\n\t\t\t\t: DEFAULT_CONTENT.backgroundImageUrl,\n\n\t\t\tbackgroundImageFileId: (typeof initial.backgroundImageFileId === 'number')\n\t\t\t\t? initial.backgroundImageFileId\n\t\t\t\t: DEFAULT_CONTENT.backgroundImageFileId,\n\n\t\t\tbackgroundColor: typeof initial.backgroundColor === 'string'\n\t\t\t\t? initial.backgroundColor\n\t\t\t\t: DEFAULT_CONTENT.backgroundColor,\n\n\t\t\toverlayMode: ALLOWED_OVERLAY_MODES.includes(initial.overlayMode)\n\t\t\t\t? initial.overlayMode\n\t\t\t\t: DEFAULT_CONTENT.overlayMode,\n\n\t\t\toverlayColor: typeof initial.overlayColor === 'string'\n\t\t\t\t? initial.overlayColor\n\t\t\t\t: DEFAULT_CONTENT.overlayColor,\n\n\t\t\toverlayOpacity: typeof initial.overlayOpacity === 'number'\n\t\t\t\t? initial.overlayOpacity\n\t\t\t\t: DEFAULT_CONTENT.overlayOpacity,\n\n\t\t\ttextColor: typeof initial.textColor === 'string'\n\t\t\t\t? initial.textColor\n\t\t\t\t: DEFAULT_CONTENT.textColor,\n\n\t\t\ttextAlign: ALLOWED_TEXT_ALIGN.includes(initial.textAlign)\n\t\t\t\t? initial.textAlign\n\t\t\t\t: DEFAULT_CONTENT.textAlign,\n\n\t\t\tverticalAlign: ALLOWED_VERTICAL_ALIGN.includes(initial.verticalAlign)\n\t\t\t\t? initial.verticalAlign\n\t\t\t\t: DEFAULT_CONTENT.verticalAlign,\n\n\t\t\theight: ALLOWED_HEIGHTS.includes(initial.height)\n\t\t\t\t? initial.height\n\t\t\t\t: DEFAULT_CONTENT.height,\n\n\t\t\tctaLabel: cta && typeof cta.label === 'string' ? cta.label : '',\n\t\t\tctaUrl: cta && typeof cta.url === 'string' ? cta.url : '',\n\t\t\tctaStyle: (cta && ALLOWED_CTA_STYLES.includes(cta.style)) ? cta.style : 'primary',\n\t\t\tuploading: false,\n\t\t\tuploadError: '',\n\t\t\t// The picked-but-not-yet-uploaded file. Upload is deferred to commit()\n\t\t\t// so nothing is written on selection.\n\t\t\tpendingFile: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Overlay-mode select options. */\n\t\toverlayModeOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'none', label: t('nextcloud-vue', 'None') },\n\t\t\t\t{ value: 'tint', label: t('nextcloud-vue', 'Tinted overlay') },\n\t\t\t\t{ value: 'gradient-bottom', label: t('nextcloud-vue', 'Gradient bottom') },\n\t\t\t]\n\t\t},\n\n\t\t/** Text-alignment select options. */\n\t\ttextAlignOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'left', label: t('nextcloud-vue', 'Left') },\n\t\t\t\t{ value: 'center', label: t('nextcloud-vue', 'Center') },\n\t\t\t\t{ value: 'right', label: t('nextcloud-vue', 'Right') },\n\t\t\t]\n\t\t},\n\n\t\t/** Vertical-alignment select options. */\n\t\tverticalAlignOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'top', label: t('nextcloud-vue', 'Top') },\n\t\t\t\t{ value: 'middle', label: t('nextcloud-vue', 'Middle') },\n\t\t\t\t{ value: 'bottom', label: t('nextcloud-vue', 'Bottom') },\n\t\t\t]\n\t\t},\n\n\t\t/** Height-preset select options. */\n\t\theightOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'small', label: t('nextcloud-vue', 'Small (120px)') },\n\t\t\t\t{ value: 'medium', label: t('nextcloud-vue', 'Medium (200px)') },\n\t\t\t\t{ value: 'large', label: t('nextcloud-vue', 'Large (320px)') },\n\t\t\t\t{ value: 'xlarge', label: t('nextcloud-vue', 'Extra large (480px)') },\n\t\t\t]\n\t\t},\n\n\t\t/** CTA-style select options. */\n\t\tctaStyleOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'primary', label: t('nextcloud-vue', 'Primary') },\n\t\t\t\t{ value: 'secondary', label: t('nextcloud-vue', 'Secondary') },\n\t\t\t\t{ value: 'ghost', label: t('nextcloud-vue', 'Ghost') },\n\t\t\t]\n\t\t},\n\n\t\t/** The assembled CTA object, or null when both fields are empty. */\n\t\tctaPayload() {\n\t\t\tconst label = typeof this.ctaLabel === 'string' ? this.ctaLabel.trim() : ''\n\t\t\tconst url = typeof this.ctaUrl === 'string' ? this.ctaUrl.trim() : ''\n\t\t\tif (label === '' && url === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tlabel,\n\t\t\t\turl,\n\t\t\t\tstyle: this.ctaStyle,\n\t\t\t}\n\t\t},\n\n\t\t/** The full content blob assembled from the current field values. */\n\t\tassembledContent() {\n\t\t\treturn {\n\t\t\t\ttitle: this.title,\n\t\t\t\tsubtitle: this.subtitle,\n\t\t\t\tbackgroundImageUrl: this.backgroundImageUrl,\n\t\t\t\tbackgroundImageFileId: this.backgroundImageFileId,\n\t\t\t\tbackgroundColor: this.backgroundColor,\n\t\t\t\toverlayMode: this.overlayMode,\n\t\t\t\toverlayColor: this.overlayColor,\n\t\t\t\toverlayOpacity: this.overlayOpacity,\n\t\t\t\ttextColor: this.textColor,\n\t\t\t\ttextAlign: this.textAlign,\n\t\t\t\tverticalAlign: this.verticalAlign,\n\t\t\t\theight: this.height,\n\t\t\t\tcta: this.ctaPayload,\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Set a top-level field and re-emit the assembled payload.\n\t\t *\n\t\t * @param {string} field one of the top-level content keys.\n\t\t * @param {unknown} value the new value.\n\t\t * @return {void}\n\t\t */\n\t\tupdateField(field, value) {\n\t\t\tthis[field] = value\n\t\t\tthis.$emit('update:content', this.assembledContent)\n\t\t},\n\n\t\t/**\n\t\t * Hold the picked file for a deferred upload. Does NOT upload — that\n\t\t * happens in commit() when the host modal submits, so re-picking or\n\t\t * cancelling writes nothing.\n\t\t *\n\t\t * @param {Event} event the file-input change event.\n\t\t * @return {void}\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\thandleFileSelect(event) {\n\t\t\tconst file = event.target.files && event.target.files[0]\n\t\t\tif (!file) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.uploadError = ''\n\t\t\tthis.pendingFile = file\n\t\t\tthis.resetFileInput()\n\t\t},\n\n\t\t/**\n\t\t * Upload the pending file (if any) and store it as backgroundImageUrl.\n\t\t * Called by the host modal on submit. A no-op when no file is pending, so\n\t\t * editing without changing the image keeps the existing value.\n\t\t *\n\t\t * @return {Promise<void>} resolves once the URL is set.\n\t\t * @throws {Error} when the upload fails, so the modal can block submit.\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\tasync commit() {\n\t\t\tif (this.pendingFile === null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.uploadError = ''\n\t\t\tthis.uploading = true\n\t\t\ttry {\n\t\t\t\tlet resolvedUrl\n\t\t\t\tif (typeof this.fileUploadFn === 'function') {\n\t\t\t\t\tresolvedUrl = extractTransportUrl(await this.fileUploadFn(this.pendingFile))\n\t\t\t\t} else if (typeof this.uploadFn === 'function') {\n\t\t\t\t\t// Deprecated path: the legacy uploadFn expects a base64 data URL.\n\t\t\t\t\twarnUploadFnDeprecated('CnHeaderWidgetForm')\n\t\t\t\t\tconst dataUrl = await readFileAsDataUrl(this.pendingFile)\n\t\t\t\t\tresolvedUrl = extractTransportUrl(await this.uploadFn(dataUrl))\n\t\t\t\t} else {\n\t\t\t\t\tresolvedUrl = await embedAsDataUrl(this.pendingFile)\n\t\t\t\t}\n\t\t\t\tthis.pendingFile = null\n\t\t\t\tthis.updateField('backgroundImageUrl', resolvedUrl)\n\t\t\t} catch (err) {\n\t\t\t\tthis.uploadError = (err && err.message) || t('nextcloud-vue', 'Failed to upload image')\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('Header image upload failed:', err)\n\t\t\t\tthrow err\n\t\t\t} finally {\n\t\t\t\tthis.uploading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Discard the pending file, re-enabling the URL field. Called by the\n\t\t * Remove button.\n\t\t *\n\t\t * @return {void}\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\tclearPendingFile() {\n\t\t\tthis.pendingFile = null\n\t\t\tthis.uploadError = ''\n\t\t\tthis.resetFileInput()\n\t\t},\n\n\t\t/**\n\t\t * Clear the native file input so re-selecting the same file re-fires.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tresetFileInput() {\n\t\t\tif (this.$refs.fileInput) {\n\t\t\t\tthis.$refs.fileInput.value = ''\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Set a CTA sub-field (label, url, style) and re-emit.\n\t\t *\n\t\t * @param {string} field one of: label, url, style.\n\t\t * @param {string} value the new value.\n\t\t * @return {void}\n\t\t */\n\t\tupdateCta(field, value) {\n\t\t\tif (field === 'label') {\n\t\t\t\tthis.ctaLabel = value\n\t\t\t} else if (field === 'url') {\n\t\t\t\tthis.ctaUrl = value\n\t\t\t} else if (field === 'style') {\n\t\t\t\tthis.ctaStyle = value\n\t\t\t}\n\t\t\tthis.$emit('update:content', this.assembledContent)\n\t\t},\n\n\t\t/**\n\t\t * Validate the form; an empty array means valid. Requires a title,\n\t\t * an http(s) background image URL when provided, and a complete CTA\n\t\t * when either CTA field is filled.\n\t\t *\n\t\t * @return {string[]} the validation errors.\n\t\t */\n\t\tvalidate() {\n\t\t\tconst errors = []\n\t\t\tif (typeof this.title !== 'string' || this.title.trim() === '') {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Title is required'))\n\t\t\t}\n\t\t\tif (typeof this.backgroundImageUrl === 'string'\n\t\t\t\t&& this.backgroundImageUrl.trim() !== ''\n\t\t\t\t&& /^(https?:\\/\\/|data:|blob:|\\/)/i.test(this.backgroundImageUrl.trim()) === false) {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Background image must be a URL or an uploaded image'))\n\t\t\t}\n\t\t\tconst labelEmpty = typeof this.ctaLabel !== 'string' || this.ctaLabel.trim() === ''\n\t\t\tconst urlEmpty = typeof this.ctaUrl !== 'string' || this.ctaUrl.trim() === ''\n\t\t\tif (labelEmpty !== urlEmpty) {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Call-to-action requires both label and URL'))\n\t\t\t}\n\t\t\treturn errors\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-header-widget-form {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-header-widget-form__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__label {\n\tfont-weight: 500;\n}\n\n.cn-header-widget-form__color-label {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__upload-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-header-widget-form__upload-label {\n\tdisplay: inline-flex;\n\tcursor: pointer;\n}\n\n.cn-header-widget-form__pending {\n\tmargin: 0;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-header-widget-form__file-input {\n\tdisplay: none;\n}\n\n.cn-header-widget-form__upload-button {\n\tpadding: 8px 12px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbackground-color: var(--color-background-hover);\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__upload-label:hover .cn-header-widget-form__upload-button {\n\tbackground-color: var(--color-background-dark);\n}\n\n.cn-header-widget-form__error {\n\tmargin: 0;\n\tfont-size: 12px;\n\tcolor: var(--color-error);\n}\n\n.cn-header-widget-form__color {\n\twidth: 48px;\n\theight: 32px;\n\tpadding: 0;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tcursor: pointer;\n\tbackground: transparent;\n}\n\n.cn-header-widget-form__range {\n\twidth: 100%;\n}\n\n.cn-header-widget-form__fieldset {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 12px;\n}\n\n.cn-header-widget-form__legend {\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tpadding: 0 4px;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createBlock","_createVNode","_createTextVNode"],"mappings":"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"}
1
+ {"version":3,"file":"CnHeaderWidgetForm.vue3.js","sources":["../../../../src/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue?vue&type=template&id=3741270f&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-header-widget-form\">\n\t\t<NcTextField\n\t\t\t:modelValue=\"title\"\n\t\t\t:label=\"t('nextcloud-vue', 'Title')\"\n\t\t\t:placeholder=\"t('nextcloud-vue', 'Header title')\"\n\t\t\trequired\n\t\t\t@update:modelValue=\"updateField('title', $event)\" />\n\n\t\t<NcTextField\n\t\t\t:modelValue=\"subtitle\"\n\t\t\t:label=\"t('nextcloud-vue', 'Subtitle (optional)')\"\n\t\t\t:placeholder=\"t('nextcloud-vue', 'Optional subtitle')\"\n\t\t\t@update:modelValue=\"updateField('subtitle', $event)\" />\n\n\t\t<NcCheckboxRadioSwitch\n\t\t\t:modelValue=\"greeting\"\n\t\t\t@update:modelValue=\"updateField('greeting', $event)\">\n\t\t\t{{ t('nextcloud-vue', 'Greet the user by name instead of showing the title') }}\n\t\t</NcCheckboxRadioSwitch>\n\t\t<NcCheckboxRadioSwitch\n\t\t\t:modelValue=\"showDate\"\n\t\t\t@update:modelValue=\"updateField('showDate', $event)\">\n\t\t\t{{ t('nextcloud-vue', 'Show the date of today above the heading') }}\n\t\t</NcCheckboxRadioSwitch>\n\t\t<NcCheckboxRadioSwitch\n\t\t\t:modelValue=\"plain\"\n\t\t\t@update:modelValue=\"updateField('plain', $event)\">\n\t\t\t{{ t('nextcloud-vue', 'Plain header without a coloured background') }}\n\t\t</NcCheckboxRadioSwitch>\n\n\t\t<!-- Pick a background image. Selection does NOT upload — the file is held\n\t\t and only uploaded when the host modal calls commit() on submit, so\n\t\t re-picking or cancelling writes nothing. Uploads are the reliable\n\t\t path: external image URLs are often blocked by the Nextcloud\n\t\t Content-Security-Policy, so only the background colour would show. -->\n\t\t<div class=\"cn-header-widget-form__upload-row\">\n\t\t\t<label class=\"cn-header-widget-form__upload-label\">\n\t\t\t\t<input\n\t\t\t\t\tref=\"fileInput\"\n\t\t\t\t\ttype=\"file\"\n\t\t\t\t\taccept=\"image/*\"\n\t\t\t\t\tclass=\"cn-header-widget-form__file-input\"\n\t\t\t\t\t:disabled=\"uploading\"\n\t\t\t\t\t@change=\"handleFileSelect\">\n\t\t\t\t<span class=\"cn-header-widget-form__upload-button\">\n\t\t\t\t\t{{ pendingFile ? t('nextcloud-vue', 'Change background image') : t('nextcloud-vue', 'Choose background image') }}\n\t\t\t\t</span>\n\t\t\t</label>\n\t\t\t<NcButton\n\t\t\t\tv-if=\"pendingFile\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t:disabled=\"uploading\"\n\t\t\t\t@click=\"clearPendingFile\">\n\t\t\t\t{{ t('nextcloud-vue', 'Remove') }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<p v-if=\"pendingFile\" class=\"cn-header-widget-form__pending\">\n\t\t\t{{ t('nextcloud-vue', 'Ready to upload on save: {name}', { name: pendingFile.name }) }}\n\t\t</p>\n\t\t<p v-if=\"uploadError\" class=\"cn-header-widget-form__error\" role=\"alert\">\n\t\t\t{{ uploadError }}\n\t\t</p>\n\n\t\t<NcTextField\n\t\t\t:modelValue=\"backgroundImageUrl\"\n\t\t\t:label=\"t('nextcloud-vue', 'Background image URL')\"\n\t\t\tplaceholder=\"https://example.com/banner.jpg\"\n\t\t\t:disabled=\"!!pendingFile\"\n\t\t\t@update:modelValue=\"updateField('backgroundImageUrl', $event)\" />\n\n\t\t<label class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Background color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"backgroundColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('backgroundColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('backgroundColor', '')\" />\n\t\t</label>\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"overlayMode\"\n\t\t\t:options=\"overlayModeOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Overlay mode')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('overlayMode', $event)\" />\n\n\t\t<label v-if=\"overlayMode !== 'none'\" class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Overlay color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"overlayColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('overlayColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('overlayColor', '')\" />\n\t\t</label>\n\n\t\t<label v-if=\"overlayMode === 'tint'\" class=\"cn-header-widget-form__field\">\n\t\t\t<span class=\"cn-header-widget-form__label\">\n\t\t\t\t{{ t('nextcloud-vue', 'Overlay opacity') }} ({{ overlayOpacity.toFixed(2) }})\n\t\t\t</span>\n\t\t\t<input\n\t\t\t\ttype=\"range\"\n\t\t\t\tmin=\"0\"\n\t\t\t\tmax=\"1\"\n\t\t\t\tstep=\"0.05\"\n\t\t\t\t:value=\"overlayOpacity\"\n\t\t\t\tclass=\"cn-header-widget-form__range\"\n\t\t\t\t@input=\"updateField('overlayOpacity', parseFloat($event.target.value))\">\n\t\t</label>\n\n\t\t<label class=\"cn-header-widget-form__color-label\">\n\t\t\t{{ t('nextcloud-vue', 'Text color') }}\n\t\t\t<CnColorPicker\n\t\t\t\t:value=\"textColor\"\n\t\t\t\tclearable\n\t\t\t\t@input=\"updateField('textColor', $event.hex)\"\n\t\t\t\t@clear=\"updateField('textColor', '')\" />\n\t\t</label>\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"textAlign\"\n\t\t\t:options=\"textAlignOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Text alignment')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('textAlign', $event)\" />\n\n\t\t<NcSelect\n\t\t\t:modelValue=\"verticalAlign\"\n\t\t\t:options=\"verticalAlignOptions\"\n\t\t\t:inputLabel=\"t('nextcloud-vue', 'Vertical alignment')\"\n\t\t\t:reduce=\"(option) => option.value\"\n\t\t\tlabel=\"label\"\n\t\t\t:clearable=\"false\"\n\t\t\t@update:modelValue=\"updateField('verticalAlign', $event)\" />\n\n\t\t<!-- The banner fills its dashboard grid cell, so a fixed Height preset\n\t\t no longer changes anything — resize the widget on the grid instead.\n\t\t (The `height` field is kept in the saved content for back-compat\n\t\t but is no longer surfaced as a control.) -->\n\n\t\t<fieldset class=\"cn-header-widget-form__fieldset\">\n\t\t\t<legend class=\"cn-header-widget-form__legend\">\n\t\t\t\t{{ t('nextcloud-vue', 'Call-to-action button (optional)') }}\n\t\t\t</legend>\n\n\t\t\t<NcTextField\n\t\t\t\t:modelValue=\"ctaLabel\"\n\t\t\t\t:label=\"t('nextcloud-vue', 'Button text')\"\n\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Sign up')\"\n\t\t\t\t@update:modelValue=\"updateCta('label', $event)\" />\n\n\t\t\t<NcTextField\n\t\t\t\t:modelValue=\"ctaUrl\"\n\t\t\t\t:label=\"t('nextcloud-vue', 'Target URL')\"\n\t\t\t\tplaceholder=\"https://...\"\n\t\t\t\t@update:modelValue=\"updateCta('url', $event)\" />\n\n\t\t\t<NcSelect\n\t\t\t\t:modelValue=\"ctaStyle\"\n\t\t\t\t:options=\"ctaStyleOptions\"\n\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Button style')\"\n\t\t\t\t:reduce=\"(option) => option.value\"\n\t\t\t\tlabel=\"label\"\n\t\t\t\t:clearable=\"false\"\n\t\t\t\t@update:modelValue=\"updateCta('style', $event)\" />\n\t\t</fieldset>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcCheckboxRadioSwitch, NcSelect, NcTextField } from '@nextcloud/vue'\nimport CnColorPicker from '../CnColorPicker/CnColorPicker.vue'\nimport { embedAsDataUrl, extractTransportUrl, readFileAsDataUrl, warnUploadFnDeprecated } from '../../utils/widgetUpload.js'\n\nconst ALLOWED_OVERLAY_MODES = ['none', 'tint', 'gradient-bottom']\nconst ALLOWED_HEIGHTS = ['small', 'medium', 'large', 'xlarge']\nconst ALLOWED_TEXT_ALIGN = ['left', 'center', 'right']\nconst ALLOWED_VERTICAL_ALIGN = ['top', 'middle', 'bottom']\nconst ALLOWED_CTA_STYLES = ['primary', 'secondary', 'ghost']\n\nconst DEFAULT_CONTENT = Object.freeze({\n\ttitle: '',\n\tsubtitle: '',\n\tbackgroundImageUrl: '',\n\tbackgroundImageFileId: null,\n\tbackgroundColor: '',\n\toverlayMode: 'none',\n\toverlayColor: '',\n\toverlayOpacity: 0.4,\n\ttextColor: '',\n\ttextAlign: 'center',\n\tverticalAlign: 'middle',\n\theight: 'medium',\n\tcta: null,\n})\n\n/**\n * CnHeaderWidgetForm — the `CnAddWidgetModal` sub-form for creating or editing\n * a `header` widget placement (renderer: `CnHeaderWidget`).\n *\n * Controls cover every persisted field: title, subtitle, background image URL,\n * background colour, overlay mode/colour/opacity, text colour/alignment/\n * vertical alignment, height preset, and an optional call-to-action with\n * label, URL, and style. `validate()` requires `title`, requires any provided\n * `backgroundImageUrl` to be http(s), and flags a half-filled CTA (label XOR\n * url). Emits `update:content` with the assembled blob on change.\n */\nexport default {\n\tname: 'CnHeaderWidgetForm',\n\n\tcomponents: {\n\t\tNcTextField,\n\t\tNcSelect,\n\t\tNcButton,\n\t\tNcCheckboxRadioSwitch,\n\t\tCnColorPicker,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The placement being edited, or `null` in create mode.\n\t\t *\n\t\t * @type {{content: object}|null}\n\t\t */\n\t\teditingWidget: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Initial content values — used when not editing and the parent\n\t\t * supplies registry defaults.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tvalue: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({ ...DEFAULT_CONTENT }),\n\t\t},\n\n\t\t/**\n\t\t * Optional raw-file upload transport: `async (file: File) => ({ url })`.\n\t\t * Named `fileUploadFn` (not `uploadFn`) to match `CnAddWidgetModal`'s\n\t\t * File-typed sub-form transport. Called by `commit()` on submit (never on\n\t\t * file selection) with the raw picked `File`; the returned hosted URL is\n\t\t * stored as `backgroundImageUrl`. When omitted, the file is embedded as a\n\t\t * data URL on commit instead (same-origin, so it isn't blocked by the CSP\n\t\t * the way external http(s) URLs are) — but only when the raw file is ≤ 1 MB\n\t\t * (~1.37 MB once base64-encoded and stored) so a huge inline blob can't\n\t\t * freeze the tab. Wire a transport for anything larger.\n\t\t *\n\t\t * @type {((file: File) => Promise<{ url: string }>)|null}\n\t\t */\n\t\tfileUploadFn: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Legacy base64 upload transport, superseded by `fileUploadFn`.\n\t\t *\n\t\t * @deprecated Use `fileUploadFn` instead. Legacy base64 transport\n\t\t * `async (dataUrl: string) => ({ url })`, kept for backward compatibility.\n\t\t * When `fileUploadFn` is not set but this is, `commit()` reads the file to\n\t\t * a data URL and hands that to this function (emitting a one-time\n\t\t * console.warn). `fileUploadFn` takes precedence when both are provided.\n\t\t *\n\t\t * @type {((dataUrl: string) => Promise<{ url: string }>)|null}\n\t\t */\n\t\tuploadFn: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted with the assembled content blob on every field change.\n\t\t *\n\t\t * @event update:content\n\t\t * @type {object}\n\t\t */\n\t\t'update:content',\n\t],\n\n\tdata() {\n\t\tconst initial = (this.editingWidget && this.editingWidget.content) || this.value || {}\n\t\tconst cta = (initial.cta && typeof initial.cta === 'object') ? initial.cta : null\n\t\treturn {\n\t\t\ttitle: typeof initial.title === 'string' ? initial.title : DEFAULT_CONTENT.title,\n\t\t\tsubtitle: typeof initial.subtitle === 'string' ? initial.subtitle : DEFAULT_CONTENT.subtitle,\n\t\t\tgreeting: initial.greeting === true || initial.greeting === 'full' || initial.greeting === 'first',\n\t\t\t// `\"full\"` (whole display name) is a manifest choice; kept as written.\n\t\t\tgreetingMode: initial.greeting === 'full' ? 'full' : true,\n\t\t\tshowDate: initial.showDate === true,\n\t\t\tplain: initial.plain === true,\n\t\t\tbackgroundImageUrl: typeof initial.backgroundImageUrl === 'string'\n\t\t\t\t? initial.backgroundImageUrl\n\t\t\t\t: DEFAULT_CONTENT.backgroundImageUrl,\n\n\t\t\tbackgroundImageFileId: (typeof initial.backgroundImageFileId === 'number')\n\t\t\t\t? initial.backgroundImageFileId\n\t\t\t\t: DEFAULT_CONTENT.backgroundImageFileId,\n\n\t\t\tbackgroundColor: typeof initial.backgroundColor === 'string'\n\t\t\t\t? initial.backgroundColor\n\t\t\t\t: DEFAULT_CONTENT.backgroundColor,\n\n\t\t\toverlayMode: ALLOWED_OVERLAY_MODES.includes(initial.overlayMode)\n\t\t\t\t? initial.overlayMode\n\t\t\t\t: DEFAULT_CONTENT.overlayMode,\n\n\t\t\toverlayColor: typeof initial.overlayColor === 'string'\n\t\t\t\t? initial.overlayColor\n\t\t\t\t: DEFAULT_CONTENT.overlayColor,\n\n\t\t\toverlayOpacity: typeof initial.overlayOpacity === 'number'\n\t\t\t\t? initial.overlayOpacity\n\t\t\t\t: DEFAULT_CONTENT.overlayOpacity,\n\n\t\t\ttextColor: typeof initial.textColor === 'string'\n\t\t\t\t? initial.textColor\n\t\t\t\t: DEFAULT_CONTENT.textColor,\n\n\t\t\ttextAlign: ALLOWED_TEXT_ALIGN.includes(initial.textAlign)\n\t\t\t\t? initial.textAlign\n\t\t\t\t: DEFAULT_CONTENT.textAlign,\n\n\t\t\tverticalAlign: ALLOWED_VERTICAL_ALIGN.includes(initial.verticalAlign)\n\t\t\t\t? initial.verticalAlign\n\t\t\t\t: DEFAULT_CONTENT.verticalAlign,\n\n\t\t\theight: ALLOWED_HEIGHTS.includes(initial.height)\n\t\t\t\t? initial.height\n\t\t\t\t: DEFAULT_CONTENT.height,\n\n\t\t\tctaLabel: cta && typeof cta.label === 'string' ? cta.label : '',\n\t\t\tctaUrl: cta && typeof cta.url === 'string' ? cta.url : '',\n\t\t\tctaStyle: (cta && ALLOWED_CTA_STYLES.includes(cta.style)) ? cta.style : 'primary',\n\t\t\tuploading: false,\n\t\t\tuploadError: '',\n\t\t\t// The picked-but-not-yet-uploaded file. Upload is deferred to commit()\n\t\t\t// so nothing is written on selection.\n\t\t\tpendingFile: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Overlay-mode select options. */\n\t\toverlayModeOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'none', label: t('nextcloud-vue', 'None') },\n\t\t\t\t{ value: 'tint', label: t('nextcloud-vue', 'Tinted overlay') },\n\t\t\t\t{ value: 'gradient-bottom', label: t('nextcloud-vue', 'Gradient bottom') },\n\t\t\t]\n\t\t},\n\n\t\t/** Text-alignment select options. */\n\t\ttextAlignOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'left', label: t('nextcloud-vue', 'Left') },\n\t\t\t\t{ value: 'center', label: t('nextcloud-vue', 'Center') },\n\t\t\t\t{ value: 'right', label: t('nextcloud-vue', 'Right') },\n\t\t\t]\n\t\t},\n\n\t\t/** Vertical-alignment select options. */\n\t\tverticalAlignOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'top', label: t('nextcloud-vue', 'Top') },\n\t\t\t\t{ value: 'middle', label: t('nextcloud-vue', 'Middle') },\n\t\t\t\t{ value: 'bottom', label: t('nextcloud-vue', 'Bottom') },\n\t\t\t]\n\t\t},\n\n\t\t/** Height-preset select options. */\n\t\theightOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'small', label: t('nextcloud-vue', 'Small (120px)') },\n\t\t\t\t{ value: 'medium', label: t('nextcloud-vue', 'Medium (200px)') },\n\t\t\t\t{ value: 'large', label: t('nextcloud-vue', 'Large (320px)') },\n\t\t\t\t{ value: 'xlarge', label: t('nextcloud-vue', 'Extra large (480px)') },\n\t\t\t]\n\t\t},\n\n\t\t/** CTA-style select options. */\n\t\tctaStyleOptions() {\n\t\t\treturn [\n\t\t\t\t{ value: 'primary', label: t('nextcloud-vue', 'Primary') },\n\t\t\t\t{ value: 'secondary', label: t('nextcloud-vue', 'Secondary') },\n\t\t\t\t{ value: 'ghost', label: t('nextcloud-vue', 'Ghost') },\n\t\t\t]\n\t\t},\n\n\t\t/** The assembled CTA object, or null when both fields are empty. */\n\t\tctaPayload() {\n\t\t\tconst label = typeof this.ctaLabel === 'string' ? this.ctaLabel.trim() : ''\n\t\t\tconst url = typeof this.ctaUrl === 'string' ? this.ctaUrl.trim() : ''\n\t\t\tif (label === '' && url === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tlabel,\n\t\t\t\turl,\n\t\t\t\tstyle: this.ctaStyle,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The greeting keys, written only when they are on, so a header that\n\t\t * never used them keeps the content blob it always had.\n\t\t *\n\t\t * @return {object}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header\n\t\t */\n\t\tgreetingContent() {\n\t\t\tconst content = {}\n\t\t\tif (this.greeting) {\n\t\t\t\tcontent.greeting = this.greetingMode\n\t\t\t}\n\t\t\tif (this.showDate) {\n\t\t\t\tcontent.showDate = true\n\t\t\t}\n\t\t\tif (this.plain) {\n\t\t\t\tcontent.plain = true\n\t\t\t}\n\t\t\treturn content\n\t\t},\n\n\t\t/**\n\t\t * The full content blob assembled from the current field values.\n\t\t *\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header\n\t\t */\n\t\tassembledContent() {\n\t\t\treturn {\n\t\t\t\t...this.greetingContent,\n\t\t\t\ttitle: this.title,\n\t\t\t\tsubtitle: this.subtitle,\n\t\t\t\tbackgroundImageUrl: this.backgroundImageUrl,\n\t\t\t\tbackgroundImageFileId: this.backgroundImageFileId,\n\t\t\t\tbackgroundColor: this.backgroundColor,\n\t\t\t\toverlayMode: this.overlayMode,\n\t\t\t\toverlayColor: this.overlayColor,\n\t\t\t\toverlayOpacity: this.overlayOpacity,\n\t\t\t\ttextColor: this.textColor,\n\t\t\t\ttextAlign: this.textAlign,\n\t\t\t\tverticalAlign: this.verticalAlign,\n\t\t\t\theight: this.height,\n\t\t\t\tcta: this.ctaPayload,\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Set a top-level field and re-emit the assembled payload.\n\t\t *\n\t\t * @param {string} field one of the top-level content keys.\n\t\t * @param {unknown} value the new value.\n\t\t * @return {void}\n\t\t */\n\t\tupdateField(field, value) {\n\t\t\tthis[field] = value\n\t\t\tthis.$emit('update:content', this.assembledContent)\n\t\t},\n\n\t\t/**\n\t\t * Hold the picked file for a deferred upload. Does NOT upload — that\n\t\t * happens in commit() when the host modal submits, so re-picking or\n\t\t * cancelling writes nothing.\n\t\t *\n\t\t * @param {Event} event the file-input change event.\n\t\t * @return {void}\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\thandleFileSelect(event) {\n\t\t\tconst file = event.target.files && event.target.files[0]\n\t\t\tif (!file) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.uploadError = ''\n\t\t\tthis.pendingFile = file\n\t\t\tthis.resetFileInput()\n\t\t},\n\n\t\t/**\n\t\t * Upload the pending file (if any) and store it as backgroundImageUrl.\n\t\t * Called by the host modal on submit. A no-op when no file is pending, so\n\t\t * editing without changing the image keeps the existing value.\n\t\t *\n\t\t * @return {Promise<void>} resolves once the URL is set.\n\t\t * @throws {Error} when the upload fails, so the modal can block submit.\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\tasync commit() {\n\t\t\tif (this.pendingFile === null) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.uploadError = ''\n\t\t\tthis.uploading = true\n\t\t\ttry {\n\t\t\t\tlet resolvedUrl\n\t\t\t\tif (typeof this.fileUploadFn === 'function') {\n\t\t\t\t\tresolvedUrl = extractTransportUrl(await this.fileUploadFn(this.pendingFile))\n\t\t\t\t} else if (typeof this.uploadFn === 'function') {\n\t\t\t\t\t// Deprecated path: the legacy uploadFn expects a base64 data URL.\n\t\t\t\t\twarnUploadFnDeprecated('CnHeaderWidgetForm')\n\t\t\t\t\tconst dataUrl = await readFileAsDataUrl(this.pendingFile)\n\t\t\t\t\tresolvedUrl = extractTransportUrl(await this.uploadFn(dataUrl))\n\t\t\t\t} else {\n\t\t\t\t\tresolvedUrl = await embedAsDataUrl(this.pendingFile)\n\t\t\t\t}\n\t\t\t\tthis.pendingFile = null\n\t\t\t\tthis.updateField('backgroundImageUrl', resolvedUrl)\n\t\t\t} catch (err) {\n\t\t\t\tthis.uploadError = (err && err.message) || t('nextcloud-vue', 'Failed to upload image')\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.error('Header image upload failed:', err)\n\t\t\t\tthrow err\n\t\t\t} finally {\n\t\t\t\tthis.uploading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Discard the pending file, re-enabling the URL field. Called by the\n\t\t * Remove button.\n\t\t *\n\t\t * @return {void}\n\t\t *\n\t\t * @spec openspec/changes/cn-widget-library/specs/cn-widget-library/spec.md\n\t\t */\n\t\tclearPendingFile() {\n\t\t\tthis.pendingFile = null\n\t\t\tthis.uploadError = ''\n\t\t\tthis.resetFileInput()\n\t\t},\n\n\t\t/**\n\t\t * Clear the native file input so re-selecting the same file re-fires.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tresetFileInput() {\n\t\t\tif (this.$refs.fileInput) {\n\t\t\t\tthis.$refs.fileInput.value = ''\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Set a CTA sub-field (label, url, style) and re-emit.\n\t\t *\n\t\t * @param {string} field one of: label, url, style.\n\t\t * @param {string} value the new value.\n\t\t * @return {void}\n\t\t */\n\t\tupdateCta(field, value) {\n\t\t\tif (field === 'label') {\n\t\t\t\tthis.ctaLabel = value\n\t\t\t} else if (field === 'url') {\n\t\t\t\tthis.ctaUrl = value\n\t\t\t} else if (field === 'style') {\n\t\t\t\tthis.ctaStyle = value\n\t\t\t}\n\t\t\tthis.$emit('update:content', this.assembledContent)\n\t\t},\n\n\t\t/**\n\t\t * Validate the form; an empty array means valid. Requires a title,\n\t\t * an http(s) background image URL when provided, and a complete CTA\n\t\t * when either CTA field is filled.\n\t\t *\n\t\t * @return {string[]} the validation errors.\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header\n\t\t */\n\t\tvalidate() {\n\t\t\tconst errors = []\n\t\t\t// A greeting is the heading, so a greeting header needs no title.\n\t\t\tif (!this.greeting && (typeof this.title !== 'string' || this.title.trim() === '')) {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Title is required'))\n\t\t\t}\n\t\t\tif (typeof this.backgroundImageUrl === 'string'\n\t\t\t\t&& this.backgroundImageUrl.trim() !== ''\n\t\t\t\t&& /^(https?:\\/\\/|data:|blob:|\\/)/i.test(this.backgroundImageUrl.trim()) === false) {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Background image must be a URL or an uploaded image'))\n\t\t\t}\n\t\t\tconst labelEmpty = typeof this.ctaLabel !== 'string' || this.ctaLabel.trim() === ''\n\t\t\tconst urlEmpty = typeof this.ctaUrl !== 'string' || this.ctaUrl.trim() === ''\n\t\t\tif (labelEmpty !== urlEmpty) {\n\t\t\t\terrors.push(t('nextcloud-vue', 'Call-to-action requires both label and URL'))\n\t\t\t}\n\t\t\treturn errors\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-header-widget-form {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n}\n\n.cn-header-widget-form__field {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__label {\n\tfont-weight: 500;\n}\n\n.cn-header-widget-form__color-label {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 12px;\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__upload-row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-header-widget-form__upload-label {\n\tdisplay: inline-flex;\n\tcursor: pointer;\n}\n\n.cn-header-widget-form__pending {\n\tmargin: 0;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-header-widget-form__file-input {\n\tdisplay: none;\n}\n\n.cn-header-widget-form__upload-button {\n\tpadding: 8px 12px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tbackground-color: var(--color-background-hover);\n\tfont-size: 14px;\n}\n\n.cn-header-widget-form__upload-label:hover .cn-header-widget-form__upload-button {\n\tbackground-color: var(--color-background-dark);\n}\n\n.cn-header-widget-form__error {\n\tmargin: 0;\n\tfont-size: 12px;\n\tcolor: var(--color-error);\n}\n\n.cn-header-widget-form__color {\n\twidth: 48px;\n\theight: 32px;\n\tpadding: 0;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tcursor: pointer;\n\tbackground: transparent;\n}\n\n.cn-header-widget-form__range {\n\twidth: 100%;\n}\n\n.cn-header-widget-form__fieldset {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 12px;\n}\n\n.cn-header-widget-form__legend {\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tpadding: 0 4px;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_createElementVNode","_toDisplayString","_createBlock","_createTextVNode"],"mappings":"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"}