@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.
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue.js +1 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js +14 -0
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +5 -4
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
- package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue.js +9 -0
- package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue.js.map +1 -0
- package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue2.js +237 -0
- package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue2.js.map +1 -0
- package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue3.js +103 -0
- package/dist/esm/components/CnConditionListEditor/CnConditionListEditor.vue3.js.map +1 -0
- package/dist/esm/components/CnConditionListEditor/conditionOperators.js +121 -0
- package/dist/esm/components/CnConditionListEditor/conditionOperators.js.map +1 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +12 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +8 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue.js +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js +46 -7
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js +59 -3
- package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js +197 -127
- package/dist/esm/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +42 -2
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +17 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +17 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +277 -166
- package/dist/esm/store/plugins/liveUpdates.js +13 -4
- package/dist/esm/store/plugins/liveUpdates.js.map +1 -1
- package/dist/esm/store/useObjectStore.js +28 -5
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/detailActionModel.js +6 -0
- package/dist/esm/utils/detailActionModel.js.map +1 -1
- package/dist/esm/utils/listLenses.js +20 -1
- package/dist/esm/utils/listLenses.js.map +1 -1
- package/dist/esm/utils/objectRows.js +59 -0
- package/dist/esm/utils/objectRows.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +5961 -5127
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +277 -166
- package/l10n/en.json +17 -1
- package/l10n/nl.json +17 -1
- package/package.json +1 -1
- package/src/components/CnActionButtons/CnActionButtons.vue +15 -0
- package/src/components/CnConditionListEditor/CnConditionListEditor.vue +346 -0
- package/src/components/CnConditionListEditor/conditionOperators.js +126 -0
- package/src/components/CnConditionListEditor/index.js +17 -0
- package/src/components/CnDetailPage/CnDetailPage.md +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +13 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +8 -1
- package/src/components/CnNcWidgetWidget/CnNcWidgetWidget.vue +47 -8
- package/src/components/CnObjectPartiesWidget/CnObjectPartiesWidget.vue +139 -2
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +42 -2
- package/src/components/CnTabsWidget/CnTabsWidget.vue +1 -1
- package/src/components/index.js +1 -0
- package/src/css/look-board-index.css +6 -0
- package/src/css/look-board.css +35 -7
- package/src/index.js +2 -0
- package/src/schemas/app-manifest-v2.schema.json +17 -1
- package/src/store/plugins/liveUpdates.js +13 -4
- package/src/store/useObjectStore.js +28 -5
- package/src/utils/detailActionModel.js +6 -0
- package/src/utils/listLenses.js +20 -1
- package/src/utils/objectRows.js +55 -0
- 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
|
-
|
|
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
|
package/src/utils/listLenses.js
CHANGED
|
@@ -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
|
+
}
|