@conduction/nextcloud-vue 2.132.0 → 2.133.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 (86) hide show
  1. package/dist/esm/components/CnActionButtons/CnActionButtons.vue.js +1 -1
  2. package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js +14 -0
  3. package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
  4. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +5 -4
  5. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
  6. package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue.js +9 -0
  7. package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue.js.map +1 -0
  8. package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue2.js +237 -0
  9. package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue2.js.map +1 -0
  10. package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue3.js +103 -0
  11. package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue3.js.map +1 -0
  12. package/dist/esm/components/CnConditionListEditor/conditionOperators.js +121 -0
  13. package/dist/esm/components/CnConditionListEditor/conditionOperators.js.map +1 -0
  14. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +12 -0
  15. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  16. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +1 -1
  17. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  18. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +8 -1
  19. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  20. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  21. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue.js +1 -1
  22. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js +46 -7
  23. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js.map +1 -1
  24. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js +1 -1
  25. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js.map +1 -1
  26. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue.js +1 -1
  27. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js +59 -3
  28. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js.map +1 -1
  29. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js +197 -127
  30. package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js.map +1 -1
  31. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  32. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +42 -2
  33. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  34. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  35. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  36. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +1 -1
  37. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  38. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  39. package/dist/esm/index.js +2 -0
  40. package/dist/esm/index.js.map +1 -1
  41. package/dist/esm/l10n/en.json.js +17 -1
  42. package/dist/esm/l10n/en.json.js.map +1 -1
  43. package/dist/esm/l10n/nl.json.js +17 -1
  44. package/dist/esm/l10n/nl.json.js.map +1 -1
  45. package/dist/esm/library-style.js +1 -1
  46. package/dist/esm/nextcloud-vue.css +277 -166
  47. package/dist/esm/store/plugins/liveUpdates.js +13 -4
  48. package/dist/esm/store/plugins/liveUpdates.js.map +1 -1
  49. package/dist/esm/store/useObjectStore.js +28 -5
  50. package/dist/esm/store/useObjectStore.js.map +1 -1
  51. package/dist/esm/utils/detailActionModel.js +6 -0
  52. package/dist/esm/utils/detailActionModel.js.map +1 -1
  53. package/dist/esm/utils/listLenses.js +20 -1
  54. package/dist/esm/utils/listLenses.js.map +1 -1
  55. package/dist/esm/utils/objectRows.js +59 -0
  56. package/dist/esm/utils/objectRows.js.map +1 -0
  57. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  58. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  59. package/dist/nextcloud-vue.cjs.js +5961 -5127
  60. package/dist/nextcloud-vue.cjs.js.map +2 -2
  61. package/dist/nextcloud-vue.css +277 -166
  62. package/l10n/en.json +17 -1
  63. package/l10n/nl.json +17 -1
  64. package/package.json +1 -1
  65. package/src/components/CnActionButtons/CnActionButtons.vue +15 -0
  66. package/src/components/CnConditionListEditor/CnConditionListEditor.vue +346 -0
  67. package/src/components/CnConditionListEditor/conditionOperators.js +126 -0
  68. package/src/components/CnConditionListEditor/index.js +17 -0
  69. package/src/components/CnDetailPage/CnDetailPage.md +1 -0
  70. package/src/components/CnDetailPage/CnDetailPage.vue +13 -1
  71. package/src/components/CnIndexPage/CnIndexPage.vue +8 -1
  72. package/src/components/CnNcWidgetWidget/CnNcWidgetWidget.vue +47 -8
  73. package/src/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue +139 -2
  74. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +42 -2
  75. package/src/components/CnTabsWidget/CnTabsWidget.vue +1 -1
  76. package/src/components/index.js +1 -0
  77. package/src/css/look-board-index.css +6 -0
  78. package/src/css/look-board.css +35 -7
  79. package/src/index.js +2 -0
  80. package/src/schemas/app-manifest-v2.schema.json +17 -1
  81. package/src/store/plugins/liveUpdates.js +13 -4
  82. package/src/store/useObjectStore.js +28 -5
  83. package/src/utils/detailActionModel.js +6 -0
  84. package/src/utils/listLenses.js +20 -1
  85. package/src/utils/objectRows.js +55 -0
  86. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -714,10 +714,20 @@ const baseActions = {
714
714
  * @param {{error?: object|null}} [options.outcome] Opt-in sink for THIS call's own result: `error` is set to the error it records on `errors[type]`, or `null` on success. Unlike `errors[type]`, which every call for the type shares, a later call cannot overwrite it.
715
715
  * @param {boolean} [options.append] Add the fetched page after the rows already stored instead of replacing them. Dropped when the stored rows are no longer the page before it, or when `options.isCurrent()` returns false (the query changed meanwhile).
716
716
  * @param {() => boolean} [options.isCurrent] With `append`: asked once the page arrives; false drops it.
717
- * @return {Promise<Array>} The fetched collection (also stored in state)
717
+ * @return {Promise<Array>} The fetched collection (also stored in state, unless a newer non-append fetch for the type started meanwhile)
718
+ * @spec openspec/changes/list-keeps-its-newest-query/specs/list-keeps-its-newest-query/spec.md#requirement-the-newest-list-query-decides-what-the-list-shows
718
719
  */
719
720
  async fetchCollection(type, params = {}, options = {}) {
720
721
  const outcome = (options && typeof options.outcome === 'object' && options.outcome) || null
722
+ const append = options?.append === true
723
+ // Responses can arrive out of order; only the newest query for the
724
+ // type may write the list. An append page belongs to the query before it.
725
+ const currentQuery = this.__currentCollectionQuery || (this.__currentCollectionQuery = {})
726
+ if (!append) {
727
+ currentQuery[type] = JSON.stringify(params)
728
+ }
729
+ const query = currentQuery[type]
730
+ const superseded = () => currentQuery[type] !== query
721
731
  this.loading = { ...this.loading, [type]: true }
722
732
  this.errors = { ...this.errors, [type]: null }
723
733
 
@@ -746,10 +756,13 @@ const baseActions = {
746
756
 
747
757
  if (!response.ok) {
748
758
  const failure = await parseResponseError(response, type)
749
- this.errors = { ...this.errors, [type]: failure }
750
759
  if (outcome) {
751
760
  outcome.error = failure
752
761
  }
762
+ if (superseded()) {
763
+ return []
764
+ }
765
+ this.errors = { ...this.errors, [type]: failure }
753
766
  // eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from
754
767
  console.error(`Error fetching ${type} collection:`, failure)
755
768
  return []
@@ -757,8 +770,13 @@ const baseActions = {
757
770
 
758
771
  const data = await response.json()
759
772
  const results = data.results || data
760
- const append = options?.append === true
761
773
 
774
+ if (superseded()) {
775
+ if (outcome) {
776
+ outcome.error = null
777
+ }
778
+ return append ? [] : results
779
+ }
762
780
  const stale = append && (
763
781
  Number(this.pagination[type]?.page || 1) !== Number(params._page || 1) - 1
764
782
  || (typeof options.isCurrent === 'function' && !options.isCurrent())
@@ -797,15 +815,20 @@ const baseActions = {
797
815
  const failure = error.name === 'TypeError'
798
816
  ? networkError(error)
799
817
  : genericError(error)
800
- this.errors = { ...this.errors, [type]: failure }
801
818
  if (outcome) {
802
819
  outcome.error = failure
803
820
  }
821
+ if (superseded()) {
822
+ return []
823
+ }
824
+ this.errors = { ...this.errors, [type]: failure }
804
825
  // eslint-disable-next-line no-console -- diagnostic for a failure this code already degrades from
805
826
  console.error(`Error fetching ${type} collection:`, error)
806
827
  return []
807
828
  } finally {
808
- this.loading = { ...this.loading, [type]: false }
829
+ if (!superseded()) {
830
+ this.loading = { ...this.loading, [type]: false }
831
+ }
809
832
  }
810
833
  },
811
834
 
@@ -17,6 +17,7 @@
17
17
  */
18
18
 
19
19
  import { variantForColor } from './headerFieldChips.js'
20
+ import { isObjectRowsSpec, objectRowsOf } from './objectRows.js'
20
21
  import { readPath } from './readPath.js'
21
22
  import { evaluateVisibleWhenLocal } from './visibleWhen.js'
22
23
 
@@ -335,6 +336,7 @@ export function pillLook(look) {
335
336
  * @param {object|null} object The record.
336
337
  * @return {number|null} The count, or null when the tab declares none.
337
338
  * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-tab-counts-and-overflow
339
+ * @spec openspec/changes/record-list-object-rows/specs/record-list/spec.md#requirement-a-tab-can-count-the-rows-its-record-holds
338
340
  */
339
341
  export function resolveTabCount(tab, object) {
340
342
  if (!tab || typeof tab !== 'object') {
@@ -343,6 +345,10 @@ export function resolveTabCount(tab, object) {
343
345
  if (typeof tab.count === 'number' && Number.isFinite(tab.count)) {
344
346
  return tab.count
345
347
  }
348
+ // `countObjectRows`: the rows the record holds where it points, as a record list's `objectRows`.
349
+ if (isObjectRowsSpec(tab.countObjectRows)) {
350
+ return objectRowsOf(object, tab.countObjectRows).length
351
+ }
346
352
  if (typeof tab.countField !== 'string' || tab.countField === '') {
347
353
  const counts = object && typeof object === 'object' && object['@self'] && object['@self'].unreadCounts
348
354
  const key = tab.unreadKey || tab.id || tab.widgetId
@@ -74,6 +74,12 @@ export function splitViewsIntoTabs(views, tabIds) {
74
74
  * One saved view as a tab for CnQuickFilterBar, so a lens renders through the
75
75
  * tab strip the page already has rather than a second one beside it.
76
76
  *
77
+ * Its filter is the view's own `filters`/`filter`, else what an OpenRegister
78
+ * view (and a default view) keeps in its query: `query.filters` with the
79
+ * locked conditions of `query.overrides.lockedFilters` under them. Reading
80
+ * only the first left a stored view with no filter, so every view in the
81
+ * side panel counted the whole list.
82
+ *
77
83
  * @param {object} view A saved view.
78
84
  * @return {{id: string, label: string, filter: object}}
79
85
  */
@@ -81,10 +87,23 @@ export function viewAsTab(view) {
81
87
  return {
82
88
  id: viewIdOf(view),
83
89
  label: (view && (view.title || view.name)) || viewIdOf(view),
84
- filter: (view && (view.filters || view.filter)) || {},
90
+ filter: (view && (view.filters || view.filter)) || queryFilterOf(view),
85
91
  }
86
92
  }
87
93
 
94
+ /**
95
+ * The conditions a stored view's query applies: its locked filters, then its filters.
96
+ *
97
+ * @param {object} view A saved view.
98
+ * @return {object}
99
+ */
100
+ function queryFilterOf(view) {
101
+ const query = view && view.query && typeof view.query === 'object' ? view.query : {}
102
+ const plain = (value) => (value && typeof value === 'object' && !Array.isArray(value) ? value : {})
103
+ const locked = plain(query.overrides && query.overrides.lockedFilters)
104
+ return { ...locked, ...plain(query.filters) }
105
+ }
106
+
88
107
  /**
89
108
  * The teams this person is treated as having claimed.
90
109
  *
@@ -0,0 +1,55 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ * SPDX-License-Identifier: EUPL-1.2
4
+ *
5
+ * The rows a record holds in one of its fields, as `objectRows` names them:
6
+ * `{ path, where?, valuePath? }`. The list at `path`, narrowed to the entry
7
+ * matching `where` when set (`{ name: "onderzoeksstappen" }` in a case's
8
+ * answers), its `valuePath` taken; a JSON string is decoded. Anything that is
9
+ * not a list of objects is no rows. The record is read as the page read it, so
10
+ * its own read rules already decided what is in it.
11
+ *
12
+ * @spec openspec/changes/record-list-object-rows/specs/record-list/spec.md#requirement-a-record-list-can-list-rows-the-page-record-holds
13
+ */
14
+ import { readPath } from './readPath.js'
15
+
16
+ /**
17
+ * Whether a declaration names rows on a record.
18
+ *
19
+ * @param {unknown} spec The declaration.
20
+ * @return {boolean}
21
+ */
22
+ export function isObjectRowsSpec(spec) {
23
+ return Boolean(spec && typeof spec === 'object' && typeof spec.path === 'string' && spec.path !== '')
24
+ }
25
+
26
+ /**
27
+ * The rows a record holds where `spec` points.
28
+ *
29
+ * @param {object|null} object The record.
30
+ * @param {{path: string, where?: object, valuePath?: string}} spec The declaration.
31
+ * @return {Array<object>} The rows.
32
+ */
33
+ export function objectRowsOf(object, spec) {
34
+ if (!object || typeof object !== 'object' || !isObjectRowsSpec(spec)) {
35
+ return []
36
+ }
37
+ const valuePath = typeof spec.valuePath === 'string' && spec.valuePath !== '' ? spec.valuePath : ''
38
+ let value = readPath(object, spec.path)
39
+ if (spec.where && typeof spec.where === 'object') {
40
+ const entries = Array.isArray(value) ? value : []
41
+ const match = entries.find((entry) => entry && typeof entry === 'object'
42
+ && Object.entries(spec.where).every(([key, wanted]) => entry[key] === wanted))
43
+ value = match && valuePath ? readPath(match, valuePath) : match
44
+ } else if (valuePath) {
45
+ value = readPath(value, valuePath)
46
+ }
47
+ if (typeof value === 'string') {
48
+ try {
49
+ value = JSON.parse(value)
50
+ } catch {
51
+ value = undefined
52
+ }
53
+ }
54
+ return Array.isArray(value) ? value.filter((row) => row && typeof row === 'object' && !Array.isArray(row)) : []
55
+ }