@conduction/nextcloud-vue 2.89.2 → 2.91.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/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +13 -0
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +3 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +18 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +15 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnDialog/CnDialog.vue2.js +1 -1
- package/dist/esm/components/CnDialog/CnDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnDialog/CnDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnDialogHeader/CnDialogHeader.vue2.js +1 -1
- package/dist/esm/components/CnDialogHeader/CnDialogHeader.vue2.js.map +1 -1
- package/dist/esm/components/CnDialogHeader/CnDialogHeader.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +16 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js +6 -3
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue3.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +74 -74
- package/dist/esm/utils/declaredProps.js +106 -0
- package/dist/esm/utils/declaredProps.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +185 -20
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +74 -74
- package/package.json +1 -1
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +14 -1
- package/src/components/CnDashboardPage/CnDashboardPage.vue +20 -2
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +16 -2
- package/src/components/CnDialog/CnDialog.vue +1 -1
- package/src/components/CnDialog/index.js +7 -0
- package/src/components/CnDialogHeader/CnDialogHeader.vue +1 -1
- package/src/components/CnDialogHeader/index.js +7 -0
- package/src/components/CnIndexPage/CnIndexPageWidgets.vue +17 -2
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +6 -3
- package/src/components/index.js +2 -0
- package/src/index.js +2 -0
- package/src/utils/declaredProps.js +115 -0
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
:is="entry.renderer"
|
|
35
35
|
:widgetId="entry.id"
|
|
36
36
|
:content="entry.content"
|
|
37
|
-
v-bind="entry
|
|
37
|
+
v-bind="declaredContent(entry)" />
|
|
38
38
|
</CnWidgetWrapper>
|
|
39
39
|
</div>
|
|
40
40
|
</component>
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
<script>
|
|
44
44
|
import CnKpiGrid from '../CnKpiGrid/CnKpiGrid.vue'
|
|
45
45
|
import CnWidgetWrapper from '../CnWidgetWrapper/CnWidgetWrapper.vue'
|
|
46
|
+
import { pickDeclaredProps } from '../../utils/declaredProps.js'
|
|
46
47
|
import { canonicalWidgetType } from '../../utils/widgetTypeAliases.js'
|
|
47
48
|
import { BUILT_IN_WIDGETS } from '../CnWidgetGrid/builtInWidgets.js'
|
|
48
49
|
import { getWidgetTypeEntry } from '../CnWidgetGrid/dashboardWidgetRegistry.js'
|
|
@@ -89,7 +90,7 @@ export default {
|
|
|
89
90
|
* The renderable entries, in the listed order.
|
|
90
91
|
*
|
|
91
92
|
* @spec openspec/changes/screens-index-stat-row/specs/index-page/spec.md#requirement-an-index-page-can-draw-a-stat-row-above-the-list
|
|
92
|
-
* @return {Array<{id: string, title: string, content: object, headerLink: (object|null), renderer: object}>}
|
|
93
|
+
* @return {Array<{id: string, type: string, title: string, content: object, headerLink: (object|null), renderer: object}>}
|
|
93
94
|
*/
|
|
94
95
|
entries() {
|
|
95
96
|
const list = Array.isArray(this.widgets) ? this.widgets : []
|
|
@@ -109,6 +110,7 @@ export default {
|
|
|
109
110
|
const translate = typeof this.cnTranslate === 'function' ? this.cnTranslate : (key) => key
|
|
110
111
|
out.push({
|
|
111
112
|
id: def.id,
|
|
113
|
+
type: def.type,
|
|
112
114
|
title: typeof def.title === 'string' && def.title !== '' ? translate(def.title) : '',
|
|
113
115
|
content: (def.content && typeof def.content === 'object') ? def.content : {},
|
|
114
116
|
headerLink: (def.headerLink && typeof def.headerLink === 'object') ? def.headerLink : null,
|
|
@@ -120,6 +122,19 @@ export default {
|
|
|
120
122
|
},
|
|
121
123
|
|
|
122
124
|
methods: {
|
|
125
|
+
/**
|
|
126
|
+
* The content keys an entry's renderer declares as props. An undeclared
|
|
127
|
+
* key would fall through onto the renderer's root; the whole content
|
|
128
|
+
* still arrives as `content`.
|
|
129
|
+
*
|
|
130
|
+
* @param {{ renderer: object, content: object, type?: string, id: string }} entry A resolved entry.
|
|
131
|
+
* @return {object} The declared content keys.
|
|
132
|
+
* @spec openspec/changes/declared-props-detail-index-sidebar/specs/widget-hosts/spec.md#requirement-a-widget-host-passes-only-the-keys-a-widget-declares
|
|
133
|
+
*/
|
|
134
|
+
declaredContent(entry) {
|
|
135
|
+
return pickDeclaredProps(entry.renderer, entry.content, entry.type || entry.id)
|
|
136
|
+
},
|
|
137
|
+
|
|
123
138
|
/**
|
|
124
139
|
* Resolve a widget type: the consumer registry, then the dashboard
|
|
125
140
|
* catalog (the type as written, then its alias), then the built-ins.
|
|
@@ -229,6 +229,7 @@ import CnTagsTab from './CnTagsTab.vue'
|
|
|
229
229
|
import CnTasksTab from './CnTasksTab.vue'
|
|
230
230
|
import { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js'
|
|
231
231
|
import { useObjectSubscription } from '../../composables/useObjectSubscription.js'
|
|
232
|
+
import { pickDeclaredProps } from '../../utils/declaredProps.js'
|
|
232
233
|
import { labelLang } from '../../utils/manifestTranslate.js'
|
|
233
234
|
import { CnIcon } from '../CnIcon/index.js'
|
|
234
235
|
import { CnLeafMountHost } from '../CnLeafMountHost/index.js'
|
|
@@ -965,9 +966,11 @@ export default {
|
|
|
965
966
|
* widgets get the shared coordinate context plus the loaded object
|
|
966
967
|
* (`objectData`); the prop-driven `data` built-in also gets the schema
|
|
967
968
|
* OBJECT bound to its `schema` prop (the other tabs use the `schema`
|
|
968
|
-
* slug string, so the override is scoped to `type: 'data'`).
|
|
969
|
-
* own `props
|
|
969
|
+
* slug string, so the override is scoped to `type: 'data'`). Of a
|
|
970
|
+
* widget's own `props`, the ones its component declares win on overlap;
|
|
971
|
+
* an undeclared one would only fall through onto the component's root.
|
|
970
972
|
*
|
|
973
|
+
* @spec openspec/changes/declared-props-detail-index-sidebar/specs/widget-hosts/spec.md#requirement-a-widget-host-passes-only-the-keys-a-widget-declares
|
|
971
974
|
* @param {{ type: string, props?: object }} w Widget spec
|
|
972
975
|
* @return {object} Props to v-bind onto the resolved widget component
|
|
973
976
|
*/
|
|
@@ -980,7 +983,7 @@ export default {
|
|
|
980
983
|
// a String `schema`.
|
|
981
984
|
bindings.schema = this.objectSchema
|
|
982
985
|
}
|
|
983
|
-
return { ...bindings, ...(w.props || {}) }
|
|
986
|
+
return { ...bindings, ...pickDeclaredProps(this.resolveWidgetComponent(w.type), w.props || {}, w.type || '') }
|
|
984
987
|
},
|
|
985
988
|
|
|
986
989
|
/**
|
package/src/components/index.js
CHANGED
|
@@ -43,6 +43,8 @@ export { CnDeleteDialog } from './CnDeleteDialog/index.js'
|
|
|
43
43
|
export { CnCopyDialog } from './CnCopyDialog/index.js'
|
|
44
44
|
export { CnFieldHelper } from './CnFieldHelper/index.js'
|
|
45
45
|
export { CnFormDialog } from './CnFormDialog/index.js'
|
|
46
|
+
export { CnDialog } from './CnDialog/index.js'
|
|
47
|
+
export { CnDialogHeader } from './CnDialogHeader/index.js'
|
|
46
48
|
export { CnFormBuilder } from './CnFormBuilder/index.js'
|
|
47
49
|
export { CnAdvancedFormDialog, CnMetadataTab, CnPropertiesTab, CnPropertyValueCell } from './CnAdvancedFormDialog/index.js'
|
|
48
50
|
export { CnMassDeleteDialog } from './CnMassDeleteDialog/index.js'
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* `process.env.NODE_ENV` is substituted by every consuming app's bundler at
|
|
8
|
+
* build time; see NcSelectTags.js.
|
|
9
|
+
*/
|
|
10
|
+
/* global process */
|
|
11
|
+
|
|
12
|
+
import { camelize } from 'vue'
|
|
13
|
+
|
|
14
|
+
const warned = new Set()
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Add the prop names one component options object declares, following its
|
|
18
|
+
* `extends` and `mixins`.
|
|
19
|
+
*
|
|
20
|
+
* @param {object} options A component options object.
|
|
21
|
+
* @param {Set<string>} names The set to add camelCase prop names to.
|
|
22
|
+
* @param {number} depth Recursion guard.
|
|
23
|
+
*/
|
|
24
|
+
function collect(options, names, depth) {
|
|
25
|
+
if (!options || typeof options !== 'object' || depth > 8) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
const props = options.props
|
|
29
|
+
if (Array.isArray(props)) {
|
|
30
|
+
props.forEach((name) => names.add(camelize(String(name))))
|
|
31
|
+
} else if (props && typeof props === 'object') {
|
|
32
|
+
Object.keys(props).forEach((name) => names.add(camelize(name)))
|
|
33
|
+
}
|
|
34
|
+
collect(options.extends, names, depth + 1)
|
|
35
|
+
if (Array.isArray(options.mixins)) {
|
|
36
|
+
options.mixins.forEach((mixin) => collect(mixin, names, depth + 1))
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The prop names a component declares, or `null` when it takes every
|
|
42
|
+
* attribute itself and so must keep receiving all of them:
|
|
43
|
+
* - `inheritAttrs: false`: the component forwards `$attrs` on purpose;
|
|
44
|
+
* - a functional component without `props`: its attrs are its props;
|
|
45
|
+
* - an async component that has not resolved yet.
|
|
46
|
+
*
|
|
47
|
+
* @spec openspec/changes/dashboard-declared-widget-props/specs/dashboard-page/spec.md#requirement-a-dashboard-widget-receives-only-the-content-keys-it-declares
|
|
48
|
+
* @param {object|((...args: unknown[]) => unknown)|null} component A component definition.
|
|
49
|
+
* @return {Set<string>|null} camelCase prop names, or null for "all".
|
|
50
|
+
*/
|
|
51
|
+
export function declaredPropNames(component) {
|
|
52
|
+
if (!component) {
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
if (typeof component === 'function') {
|
|
56
|
+
if (!component.props) {
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
const names = new Set()
|
|
60
|
+
collect({ props: component.props }, names, 0)
|
|
61
|
+
return names
|
|
62
|
+
}
|
|
63
|
+
if (typeof component !== 'object') {
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
if (component.__asyncLoader) {
|
|
67
|
+
return component.__asyncResolved ? declaredPropNames(component.__asyncResolved) : null
|
|
68
|
+
}
|
|
69
|
+
const chain = [component, component.extends, ...(component.mixins || [])]
|
|
70
|
+
if (chain.some((options) => options && options.inheritAttrs === false)) {
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
const names = new Set()
|
|
74
|
+
collect(component, names, 0)
|
|
75
|
+
return names
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Keep the entries of `bindings` whose key `component` declares as a prop.
|
|
80
|
+
* A component for which {@link declaredPropNames} returns null gets every
|
|
81
|
+
* entry. A dropped key is warned about once outside production when the
|
|
82
|
+
* component has no `content` prop either, so the key reaches nothing.
|
|
83
|
+
*
|
|
84
|
+
* @spec openspec/changes/dashboard-declared-widget-props/specs/dashboard-page/spec.md#requirement-a-dashboard-widget-receives-only-the-content-keys-it-declares
|
|
85
|
+
* @param {object|((...args: unknown[]) => unknown)|null} component The component that will receive the bindings.
|
|
86
|
+
* @param {object} bindings The candidate bindings.
|
|
87
|
+
* @param {string} label Names the component in the warning (for example the widget type).
|
|
88
|
+
* @return {object} The bindings the component declares.
|
|
89
|
+
*/
|
|
90
|
+
export function pickDeclaredProps(component, bindings, label) {
|
|
91
|
+
const declared = declaredPropNames(component)
|
|
92
|
+
if (!declared || !bindings || typeof bindings !== 'object') {
|
|
93
|
+
return bindings
|
|
94
|
+
}
|
|
95
|
+
const out = {}
|
|
96
|
+
for (const [key, value] of Object.entries(bindings)) {
|
|
97
|
+
if (declared.has(camelize(key))) {
|
|
98
|
+
out[key] = value
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
if (process.env.NODE_ENV !== 'production' && !declared.has('content') && !warned.has(`${label}\u0000${key}`)) {
|
|
102
|
+
warned.add(`${label}\u0000${key}`)
|
|
103
|
+
// eslint-disable-next-line no-console
|
|
104
|
+
console.warn(`[nextcloud-vue] content key "${key}" is not passed to widget "${label}": it declares neither that prop nor \`content\`.`)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Forget which keys were warned about. For tests.
|
|
112
|
+
*/
|
|
113
|
+
export function resetDeclaredPropWarnings() {
|
|
114
|
+
warned.clear()
|
|
115
|
+
}
|