@conduction/nextcloud-vue 2.75.0 → 2.77.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 (158) hide show
  1. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +18 -3
  2. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +49 -3
  4. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  5. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +6 -0
  6. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  7. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +3 -2
  8. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  9. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +42 -1
  10. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  11. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +228 -203
  12. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  13. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue.js +9 -0
  14. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue.js.map +1 -0
  15. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue2.js +50 -0
  16. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue2.js.map +1 -0
  17. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue3.js +13 -0
  18. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue3.js.map +1 -0
  19. package/dist/esm/components/CnCredentials/CnCredentials.vue2.js +11 -10
  20. package/dist/esm/components/CnCredentials/CnCredentials.vue2.js.map +1 -1
  21. package/dist/esm/components/CnCredentials/CnCredentials.vue3.js.map +1 -1
  22. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +41 -4
  23. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +90 -51
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  26. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +24 -33
  27. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  28. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +12 -24
  29. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  30. package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue2.js +7 -2
  31. package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue2.js.map +1 -1
  32. package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue3.js.map +1 -1
  33. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +114 -1
  34. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  35. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +115 -62
  36. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  37. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js +89 -7
  38. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js.map +1 -1
  39. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue3.js +62 -30
  40. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue3.js.map +1 -1
  41. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +242 -19
  42. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  43. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +25 -20
  44. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  45. package/dist/esm/components/CnIndexPage/selfModeActions.js +90 -14
  46. package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
  47. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +15 -3
  48. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  49. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +26 -0
  50. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  51. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +26 -9
  52. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  53. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +199 -7
  54. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
  55. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +155 -119
  56. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
  57. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +19 -3
  58. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  59. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  60. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +6 -0
  61. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  62. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -1
  63. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  64. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +153 -38
  65. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
  66. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js +74 -56
  67. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js.map +1 -1
  68. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -0
  69. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  70. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  71. package/dist/esm/composables/useListView.js +4 -0
  72. package/dist/esm/composables/useListView.js.map +1 -1
  73. package/dist/esm/composables/useObjectLock.js +41 -10
  74. package/dist/esm/composables/useObjectLock.js.map +1 -1
  75. package/dist/esm/index.js +3 -1
  76. package/dist/esm/index.js.map +1 -1
  77. package/dist/esm/l10n/en.json.js +31 -1
  78. package/dist/esm/l10n/en.json.js.map +1 -1
  79. package/dist/esm/l10n/nl.json.js +32 -2
  80. package/dist/esm/l10n/nl.json.js.map +1 -1
  81. package/dist/esm/nextcloud-vue.css +376 -75
  82. package/dist/esm/store/plugins/interactions.js +20 -10
  83. package/dist/esm/store/plugins/interactions.js.map +1 -1
  84. package/dist/esm/store/useObjectStore.js +54 -9
  85. package/dist/esm/store/useObjectStore.js.map +1 -1
  86. package/dist/esm/utils/builtInFormatters.js +15 -7
  87. package/dist/esm/utils/builtInFormatters.js.map +1 -1
  88. package/dist/esm/utils/groupAutocomplete.js +63 -1
  89. package/dist/esm/utils/groupAutocomplete.js.map +1 -1
  90. package/dist/esm/utils/lensAvailability.js +90 -0
  91. package/dist/esm/utils/lensAvailability.js.map +1 -0
  92. package/dist/esm/utils/objectSignals.js +44 -0
  93. package/dist/esm/utils/objectSignals.js.map +1 -0
  94. package/dist/esm/utils/personalLenses.js +13 -6
  95. package/dist/esm/utils/personalLenses.js.map +1 -1
  96. package/dist/esm/utils/recordInteractions.js +31 -13
  97. package/dist/esm/utils/recordInteractions.js.map +1 -1
  98. package/dist/esm/utils/resolveFilterTokens.js +46 -3
  99. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  100. package/dist/esm/utils/schema.js +1 -1
  101. package/dist/esm/utils/schema.js.map +1 -1
  102. package/dist/esm/utils/sentinelTokens.js +4 -4
  103. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  104. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  105. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  106. package/dist/esm/utils/widgetVisibility.js +46 -1
  107. package/dist/esm/utils/widgetVisibility.js.map +1 -1
  108. package/dist/nextcloud-vue.cjs.js +4732 -2990
  109. package/dist/nextcloud-vue.cjs.js.map +2 -2
  110. package/dist/nextcloud-vue.css +376 -75
  111. package/l10n/en.json +31 -1
  112. package/l10n/nl.json +32 -2
  113. package/package.json +1 -1
  114. package/src/components/CnAppNav/CnAppNav.vue +44 -5
  115. package/src/components/CnCardGrid/CnCardGrid.md +1 -0
  116. package/src/components/CnCardGrid/CnCardGrid.vue +7 -0
  117. package/src/components/CnCellRenderer/CnCellRenderer.vue +56 -1
  118. package/src/components/CnCellRenderer/CnGroupNameCell.vue +61 -0
  119. package/src/components/CnCredentials/CnCredentials.vue +17 -16
  120. package/src/components/CnDataTable/CnDataTable.md +9 -0
  121. package/src/components/CnDataTable/CnDataTable.vue +80 -20
  122. package/src/components/CnDetailPage/CnDetailPage.vue +18 -32
  123. package/src/components/CnFavouriteToggle/CnFavouriteToggle.vue +7 -2
  124. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +204 -5
  125. package/src/components/CnFollowToggle/CnFollowToggle.vue +118 -13
  126. package/src/components/CnIndexPage/CnIndexPage.md +51 -0
  127. package/src/components/CnIndexPage/CnIndexPage.vue +261 -35
  128. package/src/components/CnIndexPage/selfModeActions.js +89 -13
  129. package/src/components/CnIndexPage/useSelfFetchList.js +15 -3
  130. package/src/components/CnObjectCard/CnObjectCard.md +1 -0
  131. package/src/components/CnObjectCard/CnObjectCard.vue +89 -1
  132. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +226 -2
  133. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +19 -3
  134. package/src/components/CnObjectSidebar/CnFilesTab.vue +9 -0
  135. package/src/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +181 -38
  136. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
  137. package/src/composables/__tests__/useObjectLock.endpoints.spec.js +83 -0
  138. package/src/composables/useListView.js +4 -0
  139. package/src/composables/useObjectLock.js +41 -10
  140. package/src/css/look-board-detail.css +40 -2
  141. package/src/css/look-board-index.css +35 -1
  142. package/src/css/table.css +82 -17
  143. package/src/index.js +3 -1
  144. package/src/schemas/app-manifest-v2.schema.json +11 -7
  145. package/src/store/plugins/interactions.js +20 -10
  146. package/src/store/useObjectStore.js +54 -9
  147. package/src/utils/builtInFormatters.js +15 -7
  148. package/src/utils/groupAutocomplete.js +75 -0
  149. package/src/utils/index.js +2 -1
  150. package/src/utils/lensAvailability.js +86 -0
  151. package/src/utils/objectSignals.js +41 -0
  152. package/src/utils/personalLenses.js +13 -6
  153. package/src/utils/recordInteractions.js +31 -13
  154. package/src/utils/resolveFilterTokens.js +45 -2
  155. package/src/utils/schema.js +1 -1
  156. package/src/utils/sentinelTokens.js +4 -4
  157. package/src/utils/validateManifestV2.compiled.js +2 -2
  158. package/src/utils/widgetVisibility.js +45 -0
@@ -430,19 +430,19 @@
430
430
  name="form-dialog"
431
431
  :show="showFormDialogVisible"
432
432
  :item="editItem"
433
- :schema="effectiveSchema"
433
+ :schema="formSchema"
434
434
  :confirm="onFormConfirm"
435
435
  :close="closeFormDialog"
436
436
  :refresh="onRefreshEvent">
437
437
  <CnFormDialog
438
438
  v-if="showFormDialogVisible && !useAdvancedFormDialog"
439
439
  ref="formDialog"
440
- :schema="effectiveSchema"
440
+ :schema="formSchema"
441
441
  :item="editItem"
442
- :register="register"
443
- :excludeFields="excludeFields"
444
- :includeFields="includeFields"
445
- :fieldOverrides="fieldOverrides"
442
+ :register="formRegister"
443
+ :excludeFields="rowFormTarget ? [] : excludeFields"
444
+ :includeFields="rowFormTarget ? null : includeFields"
445
+ :fieldOverrides="rowFormTarget ? {} : fieldOverrides"
446
446
  :nameField="massActionNameField"
447
447
  :size="formSize"
448
448
  :columns="formColumns"
@@ -456,11 +456,11 @@
456
456
  <CnAdvancedFormDialog
457
457
  v-if="showFormDialogVisible && useAdvancedFormDialog"
458
458
  ref="formDialog"
459
- :schema="effectiveSchema"
459
+ :schema="formSchema"
460
460
  :item="editItem"
461
- :excludeFields="excludeFields"
462
- :includeFields="includeFields"
463
- :fieldOverrides="fieldOverrides"
461
+ :excludeFields="rowFormTarget ? [] : excludeFields"
462
+ :includeFields="rowFormTarget ? null : includeFields"
463
+ :fieldOverrides="rowFormTarget ? {} : fieldOverrides"
464
464
  :nameField="massActionNameField"
465
465
  :initialValues="resolvedCreateDefaults"
466
466
  @confirm="onFormConfirm"
@@ -533,7 +533,8 @@
533
533
  <!-- Empty state -->
534
534
  <div v-else-if="effectiveObjects.length === 0" class="cn-index-page__empty">
535
535
  <slot name="empty">
536
- <CnEmptyContent :name="resolvedEmptyText">
536
+ <CnEmptyContent :name="lensReasonText || resolvedEmptyText"
537
+ :data-lens-unavailable="lensReasonText ? 'true' : null">
537
538
  <template #icon>
538
539
  <CnIcon v-if="resolvedIcon" :name="resolvedIcon" :size="64" />
539
540
  <DatabaseSearch v-else :size="64" />
@@ -573,14 +574,15 @@
573
574
  @rowClick="onRowClick"
574
575
  @rowAuxClick="onRowAuxClick"
575
576
  @rowContextMenu="onRowContextMenu">
576
- <!-- Star column (showFavouriteColumn) -->
577
- <template v-if="showFavouriteColumn" #column-__favourite="{ row }">
578
- <CnFavouriteToggle
579
- v-if="row && row['@self'] && typeof row['@self'].favourite === 'boolean'"
577
+ <!-- Follow column (showFollowColumn, or the deprecated showFavouriteColumn) -->
578
+ <template v-if="followColumnOn" #column-__follow="{ row }">
579
+ <CnFollowToggle
580
+ v-if="row && row['@self'] && typeof row['@self'].watching === 'boolean'"
581
+ compact
580
582
  :register="typeof register === 'string' ? register : ''"
581
583
  :schema="favouriteSchemaSlug"
582
584
  :objectId="String(row['@self'].id || row.id || '')"
583
- :favourite="row['@self'].favourite === true" />
585
+ :watching="row['@self'].watching === true" />
584
586
  </template>
585
587
 
586
588
  <!-- Pass through column slots -->
@@ -788,6 +790,7 @@
788
790
  :selectable="selectable"
789
791
  :clickToView="rowClickOpens"
790
792
  :cardFields="resolvedCardFields"
793
+ :accentOf="cardAccent"
791
794
  :selectedIds="internalSelectedIds"
792
795
  :rowKey="rowKey"
793
796
  :emptyText="emptyText"
@@ -954,7 +957,7 @@ import CnConfirmDialog from '../../dialogs/CnConfirmDialog.vue'
954
957
  import CnQuickEditDialog from '../../dialogs/CnQuickEditDialog.vue'
955
958
  import CnBuildiqEditButton from '../CnBuildiqEditButton/CnBuildiqEditButton.vue'
956
959
  import CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'
957
- import CnFavouriteToggle from '../CnFavouriteToggle/CnFavouriteToggle.vue'
960
+ import CnFollowToggle from '../CnFollowToggle/CnFollowToggle.vue'
958
961
  import { useContextMenu } from '../../composables/index.js'
959
962
  import { useLook } from '../../composables/useLook.js'
960
963
  import { copyKindsOf } from '../../composables/useObjectCopy.js'
@@ -967,10 +970,12 @@ import { buildOnSuccessRoute, resolveRegisteredHandler } from '../../utils/actio
967
970
  import { reportBindingProblems } from '../../utils/diagnostics.js'
968
971
  import { fetchFilterCounts } from '../../utils/fetchFilterCounts.js'
969
972
  import { buildExportUrl } from '../../utils/indexExportHelpers.js'
973
+ import { lensUnavailableText } from '../../utils/lensAvailability.js'
970
974
  import { openRowTarget } from '../../utils/linkNavigation.js'
971
975
  import { resolveClaimedTeams, resolveClaimTokens, splitViewsIntoTabs, viewAsTab, viewIdOf } from '../../utils/listLenses.js'
972
976
  import { LIST_SHORTCUTS, listPaletteCommands, shortcutFor } from '../../utils/listShortcuts.js'
973
977
  import { multiKeySort } from '../../utils/multiKeySort.js'
978
+ import { OBJECTS_CHANGED_EVENT } from '../../utils/objectSignals.js'
974
979
  import { withPersonalLenses } from '../../utils/personalLenses.js'
975
980
  import { resolveDeepTokens, resolveFilterValue } from '../../utils/resolveFilterTokens.js'
976
981
  import { resolveRowActions } from '../../utils/resolveRowActions.js'
@@ -1016,7 +1021,7 @@ import { buildDefaultActions } from './defaultActions.js'
1016
1021
  import { dispatchAction } from './manifestActionDispatch.js'
1017
1022
  import { applyManualOrder, dropInOrder, manualOrderKey, moveInOrder, visibleIdsOf } from './manualOrder.js'
1018
1023
  import { orderColumns, personalColumnsKey, reconcilePersonalColumns } from './personalColumns.js'
1019
- import { createSelfModeActions } from './selfModeActions.js'
1024
+ import { createSelfModeActions, resolveRowTarget } from './selfModeActions.js'
1020
1025
  import { applyRowPatches, normalisePaneWidth, rowIdOf, splitLayoutFor } from './splitView.js'
1021
1026
  import { useNamedSource } from './useNamedSource.js'
1022
1027
  import { useSelfFetchList } from './useSelfFetchList.js'
@@ -1035,8 +1040,8 @@ const CALENDAR_PAGE_SIZE = 200
1035
1040
  */
1036
1041
  const RANGE_SEPARATOR = '..'
1037
1042
 
1038
- /** Key of the synthetic star column (`showFavouriteColumn`). */
1039
- const FAVOURITE_COLUMN_KEY = '__favourite'
1043
+ /** Key of the synthetic follow column (`showFollowColumn`). */
1044
+ const FOLLOW_COLUMN_KEY = '__follow'
1040
1045
 
1041
1046
  /**
1042
1047
  * Whether a schema property wants a from/to pair rather than a value list.
@@ -1238,7 +1243,7 @@ export default {
1238
1243
 
1239
1244
  components: {
1240
1245
  CnBuildiqEditButton,
1241
- CnFavouriteToggle,
1246
+ CnFollowToggle,
1242
1247
  NcLoadingIcon,
1243
1248
  CnEmptyContent,
1244
1249
  NcActions,
@@ -1475,6 +1480,17 @@ export default {
1475
1480
  default: null,
1476
1481
  },
1477
1482
 
1483
+ /**
1484
+ * Gives each card in the card view a status accent: a colored start
1485
+ * border and icon. See CnObjectCard `accent`.
1486
+ *
1487
+ * @type {((object: object) => ({variant: string, icon: string, label: string}|null))|null}
1488
+ */
1489
+ cardAccent: {
1490
+ type: Function,
1491
+ default: null,
1492
+ },
1493
+
1478
1494
  /**
1479
1495
  * Board look: free text after the footer's count ("8 of 48 · click a
1480
1496
  * column header to sort"). Manifest key `config.footerNote`.
@@ -1563,9 +1579,10 @@ export default {
1563
1579
  },
1564
1580
 
1565
1581
  /**
1566
- * Personal lenses appended to the quick filters: any of `favourite`
1567
- * (`_favourite`), `recent` (`_recent`), `watching` (`_watching`) and
1568
- * `unread` (`_unread`).
1582
+ * Personal lenses appended to the quick filters: any of `watching`
1583
+ * (`_watching`, labelled Following), `recent` (`_recent`) and `unread`
1584
+ * (`_unread`). `favourite` is a deprecated alias of `watching`: a
1585
+ * favourite is a follow now, and asking for both gives one tab.
1569
1586
  * They combine with every other filter. While Recent is active column
1570
1587
  * sorting is off, because the lens owns the order. Manifest
1571
1588
  * `config.personalLenses`.
@@ -1578,9 +1595,20 @@ export default {
1578
1595
  },
1579
1596
 
1580
1597
  /**
1581
- * Add a first column with a star per row (`CnFavouriteToggle`), bound to
1582
- * each row's `@self.favourite`. Clicking it does not open the row.
1583
- * Manifest `config.showFavouriteColumn`.
1598
+ * Add a first column with a follow toggle per row (`CnFollowToggle`,
1599
+ * compact), bound to each row's `@self.watching`. Clicking it does not
1600
+ * open the row. Manifest `config.showFollowColumn`.
1601
+ */
1602
+ showFollowColumn: {
1603
+ type: Boolean,
1604
+ default: false,
1605
+ },
1606
+
1607
+ /**
1608
+ * Deprecated alias of `showFollowColumn`: the star column became the
1609
+ * follow column (OpenRegister `merge-follow-and-favourites`).
1610
+ *
1611
+ * @deprecated Use `showFollowColumn`.
1584
1612
  */
1585
1613
  showFavouriteColumn: {
1586
1614
  type: Boolean,
@@ -1646,6 +1674,21 @@ export default {
1646
1674
  default: () => [],
1647
1675
  },
1648
1676
 
1677
+ /**
1678
+ * Self-fetch only: list from this endpoint instead of
1679
+ * `/api/objects/{register}/{schema}`, e.g. an app endpoint that searches
1680
+ * several register/schema pairs at once. `register` and `schema` stay the
1681
+ * page's own pair (columns, create, export, import). Rows of another pair,
1682
+ * read from their `@self.register` / `@self.schema`, are edited, copied and
1683
+ * deleted against that pair, with its own form. The endpoint takes
1684
+ * `_page`, `_limit`, `_search` and `_order[key]=dir` and answers
1685
+ * `{ results, total, page, pages }`.
1686
+ */
1687
+ collectionUrl: {
1688
+ type: String,
1689
+ default: '',
1690
+ },
1691
+
1649
1692
  /**
1650
1693
  * Offer an "Also search inside files" switch beside the search box. On,
1651
1694
  * a search also matches words inside attached files (OpenRegister
@@ -2060,6 +2103,34 @@ export default {
2060
2103
  default: 'No items found',
2061
2104
  },
2062
2105
 
2106
+ /**
2107
+ * Personal-lens report of the list on screen, for a host that fetches
2108
+ * itself: the response's `@self.lenses` (`{ recent: { available,
2109
+ * reason } }`). In self-fetch mode the page reads it from the store
2110
+ * and this prop is ignored. When a lens reports `available: false`
2111
+ * the empty state explains why instead of showing `emptyText`.
2112
+ *
2113
+ * @type {object|null}
2114
+ * @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-an-unavailable-lens-explains-its-empty-page
2115
+ */
2116
+ lenses: {
2117
+ type: Object,
2118
+ default: null,
2119
+ },
2120
+
2121
+ /**
2122
+ * App wording for an unavailable lens, keyed `<lens>.<reason>`
2123
+ * (`recent.audit-trail-disabled`) or `<reason>`; the specific key
2124
+ * wins, and both win over the library's object-neutral text.
2125
+ *
2126
+ * @type {{[key: string]: string}|null}
2127
+ * @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-an-unavailable-lens-explains-its-empty-page
2128
+ */
2129
+ lensReasonTexts: {
2130
+ type: Object,
2131
+ default: null,
2132
+ },
2133
+
2063
2134
  /** Accessible label for the loading spinner (NcLoadingIcon aria-label) */
2064
2135
  loadingText: {
2065
2136
  type: String,
@@ -3153,6 +3224,8 @@ export default {
3153
3224
  */
3154
3225
  persistedFilterKeys: [...(this.initialQueryFilterKeys || [])],
3155
3226
  internalSelectedIds: [...this.selectedIds],
3227
+ // Rows seen in a collectionUrl list, so a selection kept across pages still resolves its own pair.
3228
+ collectionRowsById: markRaw(new Map()),
3156
3229
  // Folder-sidebar state: selected folder id + the register-fetched list.
3157
3230
  selectedFolderId: null,
3158
3231
  /**
@@ -3190,6 +3263,9 @@ export default {
3190
3263
  // Dialog targets
3191
3264
  actionTargetItem: null,
3192
3265
  editItem: null,
3266
+ // The pair and schema of a row edited outside the page's own pair (collectionUrl).
3267
+ rowFormTarget: null,
3268
+ rowFormSchema: null,
3193
3269
  // Drives the Actions-menu Refresh spinner during a self-fetch
3194
3270
  // refresh, where the host has no promise to bind `:refreshing` to.
3195
3271
  internalRefreshing: false,
@@ -3246,6 +3322,40 @@ export default {
3246
3322
  return this.emptyText ? fn(this.emptyText) : this.emptyText
3247
3323
  },
3248
3324
 
3325
+ /**
3326
+ * The personal-lens reports of the list on screen: the store's copy
3327
+ * of the latest response in self-fetch mode, else the `lenses` prop.
3328
+ *
3329
+ * @return {object}
3330
+ * @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-a-list-response-carries-the-report-of-the-lenses-it-was-asked-for
3331
+ */
3332
+ effectiveLenses() {
3333
+ if (this.isNamedSource) {
3334
+ return {}
3335
+ }
3336
+ if (this.isSelfFetchMode) {
3337
+ return (this.list.lenses && this.list.lenses.value) || {}
3338
+ }
3339
+ return this.lenses || {}
3340
+ },
3341
+
3342
+ /**
3343
+ * Why the list is empty when a personal lens could not answer (the
3344
+ * audit trail is off, nobody is logged in, the history could not be
3345
+ * read), or `''` to keep the generic empty text.
3346
+ *
3347
+ * @return {string}
3348
+ * @spec openspec/changes/lens-says-why-it-is-empty/specs/personal-lens-availability/spec.md#requirement-an-unavailable-lens-explains-its-empty-page
3349
+ */
3350
+ lensReasonText() {
3351
+ const text = lensUnavailableText(this.effectiveLenses, this.lensReasonTexts)
3352
+ if (!text) {
3353
+ return ''
3354
+ }
3355
+ const fn = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k
3356
+ return fn(text)
3357
+ },
3358
+
3249
3359
  /**
3250
3360
  * Whether the host manifest editor is in edit mode (unwraps the injected
3251
3361
  * `cnEditingBody`, which may be a Vue ref or a plain boolean). Drives the
@@ -4139,12 +4249,22 @@ export default {
4139
4249
  if (this.recentLensActive) {
4140
4250
  cols = cols.map((col) => (col && typeof col === 'object' ? { ...col, sortable: false } : col))
4141
4251
  }
4142
- if (this.showFavouriteColumn && this.favouriteSchemaSlug !== '') {
4143
- cols = [{ key: FAVOURITE_COLUMN_KEY, label: '', sortable: false, width: '48px' }, ...cols]
4252
+ if (this.followColumnOn && this.favouriteSchemaSlug !== '') {
4253
+ cols = [{ key: FOLLOW_COLUMN_KEY, label: '', sortable: false, width: '48px' }, ...cols]
4144
4254
  }
4145
4255
  return cols
4146
4256
  },
4147
4257
 
4258
+ /**
4259
+ * Whether the follow column renders (`showFollowColumn`, or the deprecated `showFavouriteColumn`).
4260
+ *
4261
+ * @return {boolean}
4262
+ * @spec openspec/changes/one-follow-control/specs/record-follow/spec.md#requirement-index-pages-offer-one-following-lens-and-a-follow-column
4263
+ */
4264
+ followColumnOn() {
4265
+ return this.showFollowColumn || this.showFavouriteColumn
4266
+ },
4267
+
4148
4268
  /**
4149
4269
  * Whether the active quick filter is the Recent lens.
4150
4270
  *
@@ -4159,7 +4279,7 @@ export default {
4159
4279
  },
4160
4280
 
4161
4281
  /**
4162
- * Schema slug the star column calls with: the page's own slug in self-fetch
4282
+ * Schema slug the follow column calls with: the page's own slug in self-fetch
4163
4283
  * mode, else the schema object's `slug`. Empty when neither is known.
4164
4284
  *
4165
4285
  * @return {string}
@@ -4471,6 +4591,16 @@ export default {
4471
4591
  return (this.schema && typeof this.schema === 'object') ? this.schema : null
4472
4592
  },
4473
4593
 
4594
+ /** The form dialog's schema: the edited row's own when it is of another pair. */
4595
+ formSchema() {
4596
+ return this.rowFormTarget ? this.rowFormSchema : this.effectiveSchema
4597
+ },
4598
+
4599
+ /** The form dialog's register, following `formSchema`. */
4600
+ formRegister() {
4601
+ return this.rowFormTarget ? this.rowFormTarget.register : this.register
4602
+ },
4603
+
4474
4604
  /**
4475
4605
  * Schema slug for the export-leaf URL — the `schema` prop directly
4476
4606
  * when it's a string (self-fetch mode's precondition), else the
@@ -4857,8 +4987,7 @@ export default {
4857
4987
  this.$emit('edit-open', row)
4858
4988
  return
4859
4989
  }
4860
- this.editItem = row
4861
- this.showFormDialogVisible = true
4990
+ this.openFormFor(row)
4862
4991
  },
4863
4992
  onCopy: (row) => {
4864
4993
  this.actionTargetItem = row
@@ -5367,6 +5496,21 @@ export default {
5367
5496
  },
5368
5497
 
5369
5498
  watch: {
5499
+ effectiveObjects: {
5500
+ immediate: true,
5501
+ handler(rows) {
5502
+ if (!this.collectionUrl || !this.isSelfFetchMode || !Array.isArray(rows)) {
5503
+ return
5504
+ }
5505
+ for (const row of rows) {
5506
+ const id = row?.id ?? row?.['@self']?.id
5507
+ if (id !== undefined && id !== null) {
5508
+ this.collectionRowsById.set(id, row)
5509
+ }
5510
+ }
5511
+ },
5512
+ },
5513
+
5370
5514
  // A column or form field bound to a property the schema lacks is told to the host once.
5371
5515
  effectiveSchema: {
5372
5516
  immediate: true,
@@ -5559,6 +5703,9 @@ export default {
5559
5703
  if (this.splitViewEnabled && typeof window !== 'undefined') {
5560
5704
  window.addEventListener('resize', this.measureSplitViewport)
5561
5705
  }
5706
+ if (typeof window !== 'undefined') {
5707
+ window.addEventListener(OBJECTS_CHANGED_EVENT, this.onObjectsChanged)
5708
+ }
5562
5709
  this.loadManualOrder()
5563
5710
  this.loadPersonalColumns()
5564
5711
  this.publishHoistedSidebar()
@@ -5583,6 +5730,8 @@ export default {
5583
5730
  this.selfActions = createSelfModeActions({
5584
5731
  isSelfFetchMode: () => this.isSelfFetchMode,
5585
5732
  selfObjectStore: () => this.selfObjectStore,
5733
+ rowTarget: (row) => this.rowTypeTarget(row),
5734
+ knownRow: (id) => this.collectionRowsById.get(id),
5586
5735
  selfObjectType: () => this.selfObjectType,
5587
5736
  list: () => this.list,
5588
5737
  selectedIds: () => this.internalSelectedIds,
@@ -5608,13 +5757,14 @@ export default {
5608
5757
  },
5609
5758
 
5610
5759
  /**
5611
- * Drop the resize listener and the hoisted sidebar.
5760
+ * Drop the window listeners and the hoisted sidebar.
5612
5761
  *
5613
5762
  * @spec openspec/changes/case-page-and-list-as-a-place/specs/index-page/spec.md
5614
5763
  */
5615
5764
  beforeUnmount() {
5616
5765
  if (typeof window !== 'undefined') {
5617
5766
  window.removeEventListener('resize', this.measureSplitViewport)
5767
+ window.removeEventListener(OBJECTS_CHANGED_EVENT, this.onObjectsChanged)
5618
5768
  }
5619
5769
  // Clear the holder so the hoisted sidebar disappears when
5620
5770
  // the user navigates away from the index page.
@@ -7076,6 +7226,33 @@ export default {
7076
7226
  }
7077
7227
  },
7078
7228
 
7229
+ /**
7230
+ * Refresh the list when `dispatchObjectsChanged` names this page's
7231
+ * register and schema, by slug or id. A part missing on either side matches.
7232
+ *
7233
+ * @param {CustomEvent} event The objects-changed event.
7234
+ * @return {void}
7235
+ */
7236
+ onObjectsChanged(event) {
7237
+ // A collection list can hold rows of any pair, so any change may show in it.
7238
+ if (this.isSelfFetchMode && this.collectionUrl) {
7239
+ this.onRefreshEvent()
7240
+ return
7241
+ }
7242
+ const detail = (event && event.detail) || {}
7243
+ const schema = this.effectiveSchema
7244
+ const schemaIds = [this.schema, schema && schema.id, schema && schema.slug]
7245
+ .filter((v) => v !== undefined && v !== null && v !== '' && typeof v !== 'object')
7246
+ .map(String)
7247
+ if (detail.register && this.register && detail.register !== String(this.register)) {
7248
+ return
7249
+ }
7250
+ if (detail.schema && schemaIds.length > 0 && !schemaIds.includes(detail.schema)) {
7251
+ return
7252
+ }
7253
+ this.onRefreshEvent()
7254
+ },
7255
+
7079
7256
  /** @return {Promise<void>} */
7080
7257
  async onRefreshEvent() {
7081
7258
  this.$emit('refresh')
@@ -8218,6 +8395,8 @@ export default {
8218
8395
  closeFormDialog() {
8219
8396
  this.showFormDialogVisible = false
8220
8397
  this.editItem = null
8398
+ this.rowFormTarget = null
8399
+ this.rowFormSchema = null
8221
8400
  },
8222
8401
 
8223
8402
  /**
@@ -8278,8 +8457,55 @@ export default {
8278
8457
  * @public
8279
8458
  */
8280
8459
  openFormDialog(item = null) {
8281
- this.editItem = item
8282
- this.showFormDialogVisible = true
8460
+ return this.openFormFor(item)
8461
+ },
8462
+
8463
+ /**
8464
+ * Open the form dialog for an item. A row of another pair (collectionUrl)
8465
+ * opens with its own schema; everything else opens at once, as before.
8466
+ *
8467
+ * @param {object|null} item The row to edit, or null to create.
8468
+ * @return {Promise<void>|void}
8469
+ */
8470
+ openFormFor(item) {
8471
+ const target = item ? this.rowTypeTarget(item) : null
8472
+ if (!target) {
8473
+ this.rowFormTarget = null
8474
+ this.rowFormSchema = null
8475
+ this.editItem = item
8476
+ this.showFormDialogVisible = true
8477
+ return
8478
+ }
8479
+ const store = this.selfObjectStore
8480
+ return Promise.resolve(store.getSchema?.(target.type) || store.fetchSchema?.(target.type))
8481
+ .catch(() => null)
8482
+ .then((schema) => {
8483
+ if (!schema) {
8484
+ this.toastSavedView('error', t('nextcloud-vue', 'Could not load the form for this item.'))
8485
+ return
8486
+ }
8487
+ this.rowFormTarget = target
8488
+ this.rowFormSchema = schema
8489
+ this.editItem = item
8490
+ this.showFormDialogVisible = true
8491
+ })
8492
+ },
8493
+
8494
+ /**
8495
+ * The row's own pair when it differs from the page's in a collectionUrl list.
8496
+ *
8497
+ * @param {object} row The row.
8498
+ * @return {{register: string, schema: string, type: string}|null} The pair, or null.
8499
+ */
8500
+ rowTypeTarget(row) {
8501
+ return resolveRowTarget({
8502
+ collectionUrl: this.isSelfFetchMode ? this.collectionUrl : '',
8503
+ register: this.register,
8504
+ schema: this.schema,
8505
+ primarySchema: this.effectiveSchema,
8506
+ primaryRegister: this.selfObjectStore?.getRegister?.(this.selfObjectType) || null,
8507
+ store: this.selfObjectStore,
8508
+ }, row)
8283
8509
  },
8284
8510
 
8285
8511
  /**