@conduction/nextcloud-vue 2.77.0 → 2.79.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 (159) hide show
  1. package/dist/bundled-packages.json +0 -2
  2. package/dist/esm/bundled-packages.json +0 -2
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +113 -104
  5. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  6. package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue2.js +23 -0
  7. package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue2.js.map +1 -1
  8. package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue3.js.map +1 -1
  9. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +12 -2
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  12. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +2 -1
  13. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  14. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  15. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +117 -0
  16. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  17. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +122 -97
  18. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  19. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +37 -0
  20. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  21. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +2 -2
  22. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  23. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +168 -5
  24. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  25. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +140 -92
  26. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  27. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +84 -2
  28. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  29. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +4 -2
  30. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  31. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue.js +1 -1
  32. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue2.js +45 -2
  33. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue2.js.map +1 -1
  34. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue3.js.map +1 -1
  35. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +61 -1
  36. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  37. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  38. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +173 -5
  39. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  40. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +181 -99
  41. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  42. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +29 -11
  43. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  44. package/dist/esm/components/CnFilesBrowser/openRegisterSource.js +252 -0
  45. package/dist/esm/components/CnFilesBrowser/openRegisterSource.js.map +1 -0
  46. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +8 -2
  47. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  48. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  49. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +4 -1
  50. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  51. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  52. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +31 -2
  53. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  54. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +3 -2
  55. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  56. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +100 -3
  57. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  58. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -2
  59. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  60. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +3 -2
  61. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
  62. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
  63. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
  64. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js +1 -1
  65. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
  66. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +36 -0
  67. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  68. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +35 -23
  69. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  70. package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue2.js +6 -2
  71. package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue2.js.map +1 -1
  72. package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue3.js.map +1 -1
  73. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +30 -1
  74. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  75. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  76. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -0
  77. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  78. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  79. package/dist/esm/composables/useAppManifest.js +8 -3
  80. package/dist/esm/composables/useAppManifest.js.map +1 -1
  81. package/dist/esm/composables/useFormDraft.js +11 -1
  82. package/dist/esm/composables/useFormDraft.js.map +1 -1
  83. package/dist/esm/composables/useObjectPresence.js +15 -4
  84. package/dist/esm/composables/useObjectPresence.js.map +1 -1
  85. package/dist/esm/index.js +1 -0
  86. package/dist/esm/index.js.map +1 -1
  87. package/dist/esm/l10n/en.json.js +68 -1
  88. package/dist/esm/l10n/en.json.js.map +1 -1
  89. package/dist/esm/l10n/nl.json.js +113 -2
  90. package/dist/esm/l10n/nl.json.js.map +1 -1
  91. package/dist/esm/nextcloud-vue.css +526 -16
  92. package/dist/esm/utils/badgeVariants.js +32 -0
  93. package/dist/esm/utils/badgeVariants.js.map +1 -0
  94. package/dist/esm/utils/boardDate.js +193 -0
  95. package/dist/esm/utils/boardDate.js.map +1 -0
  96. package/dist/esm/utils/dynamicProperties.js +5 -1
  97. package/dist/esm/utils/dynamicProperties.js.map +1 -1
  98. package/dist/esm/utils/validateManifestV2.compiled.js +57 -6
  99. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  100. package/dist/nextcloud-vue.cjs.js +2569 -811
  101. package/dist/nextcloud-vue.cjs.js.map +2 -2
  102. package/dist/nextcloud-vue.css +526 -16
  103. package/l10n/en.json +68 -1
  104. package/l10n/nl.json +113 -2
  105. package/package.json +1 -1
  106. package/scripts/build-validators.js +26 -0
  107. package/src/components/CnActionsBar/CnActionsBar.vue +48 -39
  108. package/src/components/CnAiCompanion/CnAiCompanion.vue +23 -0
  109. package/src/components/CnAppRoot/CnAppRoot.vue +12 -2
  110. package/src/components/CnBoardView/CnBoardView.vue +2 -1
  111. package/src/components/CnCellRenderer/CnCellRenderer.md +1 -0
  112. package/src/components/CnCellRenderer/CnCellRenderer.vue +169 -10
  113. package/src/components/CnChartWidget/CnChartWidget.vue +39 -2
  114. package/src/components/CnDashboardPage/CnDashboardPage.vue +248 -8
  115. package/src/components/CnDataTable/CnDataTable.md +2 -0
  116. package/src/components/CnDataTable/CnDataTable.vue +88 -4
  117. package/src/components/CnDateRangePicker/CnDateRangePicker.vue +44 -1
  118. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +61 -1
  119. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +232 -11
  120. package/src/components/CnFilesBrowser/filesBrowser.js +43 -2
  121. package/src/components/CnFilesBrowser/index.js +1 -0
  122. package/src/components/CnFilesBrowser/openRegisterSource.js +248 -0
  123. package/src/components/CnFormDialog/CnFormDialog.vue +8 -2
  124. package/src/components/CnFormPage/CnFormPage.vue +4 -1
  125. package/src/components/CnIndexPage/CnIndexPage.vue +33 -3
  126. package/src/components/CnObjectSidebar/CnFilesTab.vue +103 -4
  127. package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +3 -2
  128. package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +1 -1
  129. package/src/components/CnStatWidget/CnStatWidget.vue +52 -2
  130. package/src/components/CnStatusBadge/CnStatusBadge.vue +6 -2
  131. package/src/components/CnTabsWidget/CnTabsWidget.vue +30 -1
  132. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
  133. package/src/components/index.js +1 -1
  134. package/src/composables/__tests__/useObjectPresence.spec.js +24 -0
  135. package/src/composables/useAppManifest.js +8 -3
  136. package/src/composables/useFormDraft.js +11 -1
  137. package/src/composables/useObjectPresence.js +15 -4
  138. package/src/css/badge.css +31 -0
  139. package/src/css/kpi-card.css +51 -0
  140. package/src/css/look-board-detail.css +10 -0
  141. package/src/css/look-board-index.css +128 -14
  142. package/src/css/look-board.css +85 -0
  143. package/src/css/table.css +37 -0
  144. package/src/index.js +4 -0
  145. package/src/schemas/app-manifest-v2.schema.json +50 -5
  146. package/src/utils/badgeVariants.js +29 -0
  147. package/src/utils/boardDate.js +189 -0
  148. package/src/utils/dynamicProperties.js +5 -1
  149. package/src/utils/validateManifestV2.compiled.js +57 -2
  150. package/dist/esm/_virtual/equal.js +0 -4
  151. package/dist/esm/_virtual/equal.js.map +0 -1
  152. package/dist/esm/_virtual/ucs2length.js +0 -4
  153. package/dist/esm/_virtual/ucs2length.js.map +0 -1
  154. package/dist/esm/node_modules/ajv/dist/runtime/equal.js +0 -9
  155. package/dist/esm/node_modules/ajv/dist/runtime/equal.js.map +0 -1
  156. package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js +0 -25
  157. package/dist/esm/node_modules/ajv/dist/runtime/ucs2length.js.map +0 -1
  158. package/dist/esm/node_modules/fast-deep-equal/index.js +0 -47
  159. package/dist/esm/node_modules/fast-deep-equal/index.js.map +0 -1
@@ -1,4 +1,5 @@
1
- import { translate } from '@nextcloud/l10n';
1
+ import { getCurrentUser } from '@nextcloud/auth';
2
+ import { translate, getCanonicalLocale } from '@nextcloud/l10n';
2
3
  import { NcActions, NcActionButton, NcActionInput, NcActionSeparator, NcButton, NcLoadingIcon, NcSelect } from '@nextcloud/vue';
3
4
  import { ref, provide, watch } from 'vue';
4
5
  import CalendarRange from 'vue-material-design-icons/CalendarRange.vue';
@@ -19,6 +20,7 @@ import __sfc_main$g from '../CnKpiGrid/CnKpiGrid.vue.js';
19
20
  import __sfc_main$f from '../CnSegmentedControl/CnSegmentedControl.vue.js';
20
21
  import __sfc_main$6 from '../CnStatsBlockWidget/CnStatsBlockWidget.vue.js';
21
22
  import __sfc_main$4 from '../CnTileWidget/CnTileWidget.vue.js';
23
+ import __sfc_main$h from '../CnWidgetGrid/CnWidgetIcon.vue.js';
22
24
  import __sfc_main$7 from '../CnWidgetRefItem/CnWidgetRefItem.vue.js';
23
25
  import __sfc_main$3 from '../CnWidgetRenderer/CnWidgetRenderer.vue.js';
24
26
  import __sfc_main$2 from '../CnWidgetWrapper/CnWidgetWrapper.vue.js';
@@ -35,7 +37,7 @@ import { BUILT_IN_WIDGETS } from '../CnWidgetGrid/builtInWidgets.js';
35
37
  import { getWidgetTypeEntry } from '../CnWidgetGrid/dashboardWidgetRegistry.js';
36
38
  import '../CnWidgetGrid/registerDashboardWidgets.js';
37
39
  import __sfc_main$a from '../CnActionsMenu/CnActionsMenu.vue.js';
38
- import { DEFAULT_DATE_RANGE_PRESETS, resolvePresetWindow } from '../CnDateRangePicker/CnDateRangePicker.vue2.js';
40
+ import { DEFAULT_DATE_RANGE_PRESETS, translatePresetLabel, resolvePresetWindow } from '../CnDateRangePicker/CnDateRangePicker.vue2.js';
39
41
 
40
42
  /** Surfaces understood by the pluggable integration registry (AD-19). */
41
43
  const INTEGRATION_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity'];
@@ -184,6 +186,7 @@ var script = {
184
186
  CnLeafMountHost: __sfc_main$e,
185
187
  CnSegmentedControl: __sfc_main$f,
186
188
  CnKpiGrid: __sfc_main$g,
189
+ CnWidgetIcon: __sfc_main$h,
187
190
  },
188
191
 
189
192
  mixins: [pageViews],
@@ -284,6 +287,50 @@ var script = {
284
287
  default: () => [],
285
288
  },
286
289
 
290
+ /**
291
+ * Greet the reader in the header subtitle (manifest `config.greeting`):
292
+ * `true` (or `"first"`) writes "Good afternoon, Pieter · Monday 5
293
+ * October 2026", `"full"` uses the whole display name. The page
294
+ * `description`, when set, follows after another middle dot. Off by
295
+ * default: the subtitle is the description alone, as before.
296
+ *
297
+ * @spec openspec/changes/screens-dashboard-greeting-header/specs/dashboard-page/spec.md#requirement-the-header-subtitle-can-greet-the-reader
298
+ * @type {boolean|string}
299
+ */
300
+ greeting: {
301
+ type: [Boolean, String],
302
+ default: false,
303
+ },
304
+
305
+ /**
306
+ * Links drawn as small pills on the switch row under the header
307
+ * (manifest `config.viewLinks`), as DqMijnWerk's "Uw wachtrij / Aan
308
+ * mij toegewezen / Dag afsluiten". Each `{ label, icon?, route?,
309
+ * href? }`: `route` is a route name or a `{ name, params?, query? }`
310
+ * location, `href` a plain URL; `label` goes through the host
311
+ * translate function. An entry without a label or a target is skipped.
312
+ *
313
+ * @spec openspec/changes/screens-dashboard-greeting-header/specs/dashboard-page/spec.md#requirement-a-switch-row-carries-the-view-switch-and-link-pills
314
+ * @type {Array<object>}
315
+ */
316
+ viewLinks: {
317
+ type: Array,
318
+ default: () => [],
319
+ },
320
+
321
+ /**
322
+ * Whether the header draws the page Actions menu (manifest
323
+ * `config.showActionsMenu`). `false` removes it, for a board page
324
+ * whose header carries only the buildiq square and the primary.
325
+ *
326
+ * @spec openspec/changes/screens-dashboard-greeting-header/specs/dashboard-page/spec.md#requirement-the-header-can-drop-the-page-actions-menu
327
+ * @type {boolean}
328
+ */
329
+ showActionsMenu: {
330
+ type: Boolean,
331
+ default: true,
332
+ },
333
+
287
334
  /**
288
335
  * Whether a widget draws its overflow Actions menu when neither its
289
336
  * placement nor its definition says (`showActions`). `false` (manifest
@@ -976,6 +1023,97 @@ var script = {
976
1023
  return typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key
977
1024
  },
978
1025
 
1026
+ /**
1027
+ * The greeting for the time of day, with the reader's first (or
1028
+ * full) name when known, or '' when `greeting` is off.
1029
+ *
1030
+ * @spec openspec/changes/screens-dashboard-greeting-header/specs/dashboard-page/spec.md#requirement-the-header-subtitle-can-greet-the-reader
1031
+ * @return {string}
1032
+ */
1033
+ greetingLine() {
1034
+ if (this.greeting !== true && this.greeting !== 'first' && this.greeting !== 'full') {
1035
+ return ''
1036
+ }
1037
+ let user;
1038
+ try {
1039
+ user = getCurrentUser();
1040
+ } catch {
1041
+ user = null;
1042
+ }
1043
+ const displayName = (user && typeof user.displayName === 'string') ? user.displayName.trim() : '';
1044
+ const name = this.greeting === 'full' ? displayName : displayName.split(/\s+/)[0];
1045
+ const hour = new Date().getHours();
1046
+ if (hour < 12) {
1047
+ return name ? translate('nextcloud-vue', 'Good morning, {name}', { name }) : translate('nextcloud-vue', 'Good morning')
1048
+ }
1049
+ if (hour < 18) {
1050
+ return name ? translate('nextcloud-vue', 'Good afternoon, {name}', { name }) : translate('nextcloud-vue', 'Good afternoon')
1051
+ }
1052
+ return name ? translate('nextcloud-vue', 'Good evening, {name}', { name }) : translate('nextcloud-vue', 'Good evening')
1053
+ },
1054
+
1055
+ /**
1056
+ * The header subtitle: with `greeting` on, the greeting, today's date
1057
+ * written out and the description, joined by a middle dot; else the
1058
+ * description alone, as before.
1059
+ *
1060
+ * @spec openspec/changes/screens-dashboard-greeting-header/specs/dashboard-page/spec.md#requirement-the-header-subtitle-can-greet-the-reader
1061
+ * @return {string}
1062
+ */
1063
+ subtitleText() {
1064
+ const description = this.description ? this.resolvedDescription : '';
1065
+ const greeting = this.greetingLine;
1066
+ if (greeting === '') {
1067
+ return description
1068
+ }
1069
+ const options = { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' };
1070
+ let date;
1071
+ try {
1072
+ date = new Intl.DateTimeFormat(getCanonicalLocale() || undefined, options).format(new Date());
1073
+ } catch {
1074
+ date = new Intl.DateTimeFormat(undefined, options).format(new Date());
1075
+ }
1076
+ return [greeting, date, description].filter((part) => part !== '').join(' · ')
1077
+ },
1078
+
1079
+ /**
1080
+ * Whether the page's view switch sits on the switch row under the
1081
+ * header (board look) rather than among the header actions.
1082
+ *
1083
+ * @spec openspec/changes/screens-dashboard-greeting-header/specs/dashboard-page/spec.md#requirement-a-switch-row-carries-the-view-switch-and-link-pills
1084
+ * @return {boolean}
1085
+ */
1086
+ switchInRow() {
1087
+ return this.isBoardLook && this.showsPageViewSwitch
1088
+ },
1089
+
1090
+ /**
1091
+ * `viewLinks` as render descriptors: a router link for a route, an
1092
+ * anchor for an href, the label translated. Entries without a label
1093
+ * or a target are dropped.
1094
+ *
1095
+ * @spec openspec/changes/screens-dashboard-greeting-header/specs/dashboard-page/spec.md#requirement-a-switch-row-carries-the-view-switch-and-link-pills
1096
+ * @return {Array<{key: string, tag: string, attrs: object, label: string, icon: string}>}
1097
+ */
1098
+ resolvedViewLinks() {
1099
+ const links = Array.isArray(this.viewLinks) ? this.viewLinks : [];
1100
+ const out = [];
1101
+ links.forEach((link, index) => {
1102
+ if (!link || typeof link.label !== 'string' || link.label === '') {
1103
+ return
1104
+ }
1105
+ const icon = typeof link.icon === 'string' ? link.icon : '';
1106
+ const label = this.effectiveTranslate(link.label);
1107
+ if (link.route && this.$router) {
1108
+ const to = typeof link.route === 'string' ? { name: link.route } : link.route;
1109
+ out.push({ key: `route-${index}`, tag: 'router-link', attrs: { to }, label, icon });
1110
+ } else if (typeof link.href === 'string' && link.href !== '') {
1111
+ out.push({ key: `href-${index}`, tag: 'a', attrs: { href: link.href }, label, icon });
1112
+ }
1113
+ });
1114
+ return out
1115
+ },
1116
+
979
1117
  /**
980
1118
  * The page title run through the host translate function so a
981
1119
  * manifest-authored source string localises.
@@ -1137,9 +1275,12 @@ var script = {
1137
1275
  * doesn't supply one.
1138
1276
  */
1139
1277
  effectivePresets() {
1140
- return Array.isArray(this.dateRange?.presets) && this.dateRange.presets.length > 0
1278
+ const presets = Array.isArray(this.dateRange?.presets) && this.dateRange.presets.length > 0
1141
1279
  ? this.dateRange.presets
1142
- : DEFAULT_DATE_RANGE_PRESETS.map((p) => ({ ...p }))
1280
+ : DEFAULT_DATE_RANGE_PRESETS;
1281
+ // Labels in the user's language (screens-dashboard-i18n): the app's
1282
+ // catalogue first, then the library's own for the default labels.
1283
+ return presets.map((p) => ({ ...p, label: translatePresetLabel(p.label, this.effectiveTranslate) }))
1143
1284
  },
1144
1285
 
1145
1286
  /**
@@ -2482,7 +2623,7 @@ var script = {
2482
2623
  * @return {object}
2483
2624
  */
2484
2625
  registryWidgetBindings(item) {
2485
- const content = this.getWidgetContent(item);
2626
+ const content = this.collectionContent(this.getWidgetContent(item));
2486
2627
  const def = this.getWidgetDef(item.widgetId);
2487
2628
  if (!this.isBannerDef(def)) {
2488
2629
  return content
@@ -2491,6 +2632,28 @@ var script = {
2491
2632
  return outcome ? { ...content, conditionOutcome: outcome } : content
2492
2633
  },
2493
2634
 
2635
+ /**
2636
+ * A widget's content with the board's quiet collection footer: in the
2637
+ * board look a dashboard that drops the widget Actions menu
2638
+ * (`showWidgetActions: false`) also drops the Add footer of its list
2639
+ * widgets, whose Add is the menu's create entry at the bottom of the
2640
+ * card. A widget that says `content.allowCreate` keeps its own value.
2641
+ * Otherwise the content is returned as is.
2642
+ *
2643
+ * @spec openspec/changes/screens-dashboard-legacy-widgets/specs/dashboard-page/spec.md#requirement-a-board-dashboard-without-widget-actions-drops-the-add-footer
2644
+ * @param {object} content The widget's stored content.
2645
+ * @return {object}
2646
+ */
2647
+ collectionContent(content) {
2648
+ if (!this.isBoardLook || this.showWidgetActions !== false) {
2649
+ return content
2650
+ }
2651
+ if (content && Object.hasOwn(content, 'allowCreate')) {
2652
+ return content
2653
+ }
2654
+ return { ...content, allowCreate: false }
2655
+ },
2656
+
2494
2657
  /**
2495
2658
  * Re-compact a filtered layout upward, display-only. GridStack runs
2496
2659
  * `float: true`, so removing an item does not close its row — this