@conduction/nextcloud-vue 2.127.0 → 2.128.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/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +3 -113
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +12 -12
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +34 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +16 -8
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +153 -8
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +169 -58
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnTileWidget/CnTileWidget.vue.js +1 -1
- package/dist/esm/components/CnTileWidget/CnTileWidget.vue2.js +11 -0
- package/dist/esm/components/CnTileWidget/CnTileWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTileWidget/CnTileWidget.vue3.js +14 -4
- package/dist/esm/components/CnTileWidget/CnTileWidget.vue3.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/mixins/pageFilters.js +142 -0
- package/dist/esm/mixins/pageFilters.js.map +1 -0
- package/dist/esm/nextcloud-vue.css +269 -74
- package/dist/esm/utils/actionLink.js +9 -3
- package/dist/esm/utils/actionLink.js.map +1 -1
- 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 +1176 -828
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +269 -74
- package/package.json +1 -1
- package/src/components/CnDashboardPage/CnDashboardPage.vue +3 -113
- package/src/components/CnDataTable/CnDataTable.vue +40 -1
- package/src/components/CnDetailPage/CnDetailPage.md +2 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +237 -8
- package/src/components/CnTileWidget/CnTileWidget.vue +30 -2
- package/src/css/detail-page.css +42 -0
- package/src/css/dialog.css +13 -0
- package/src/css/look-board-detail.css +1 -1
- package/src/css/look-board-index.css +8 -0
- package/src/css/look-board.css +97 -0
- package/src/css/table.css +12 -0
- package/src/mixins/pageFilters.js +138 -0
- package/src/schemas/app-manifest-v2.schema.json +41 -3
- package/src/utils/actionLink.js +9 -3
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getCurrentUser } from '@nextcloud/auth';
|
|
2
2
|
import { subscribe, unsubscribe } from '@nextcloud/event-bus';
|
|
3
3
|
import { translate } from '@nextcloud/l10n';
|
|
4
|
-
import { NcActionButton, NcActionCaption, NcActionLink, NcActionSeparator, NcButton, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue';
|
|
4
|
+
import { NcActionButton, NcActionCaption, NcActionLink, NcActionSeparator, NcButton, NcEmptyContent, NcLoadingIcon, NcSelect } from '@nextcloud/vue';
|
|
5
5
|
import { computed, ref, provide, watch, markRaw } from 'vue';
|
|
6
6
|
import AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue';
|
|
7
7
|
import ArrowLeft from 'vue-material-design-icons/ArrowLeft.vue';
|
|
@@ -45,8 +45,9 @@ import { useObjectPresence } from '../../composables/useObjectPresence.js';
|
|
|
45
45
|
import { useObjectSubscription } from '../../composables/useObjectSubscription.js';
|
|
46
46
|
import { createRefLabelResolver } from '../../composables/useRefLabels.js';
|
|
47
47
|
import { gridLayout } from '../../mixins/gridLayout.js';
|
|
48
|
+
import { pageFilters } from '../../mixins/pageFilters.js';
|
|
48
49
|
import { pageViews } from '../../mixins/pageViews.js';
|
|
49
|
-
import { headerActionAsLink } from '../../utils/actionLink.js';
|
|
50
|
+
import { resolveManifestHref, routeHref, headerActionAsLink } from '../../utils/actionLink.js';
|
|
50
51
|
import { isAppInstalled } from '../../utils/appInstalled.js';
|
|
51
52
|
import { compactLayoutRows } from '../../utils/dashboardPlacement.js';
|
|
52
53
|
import { defaultDetailGrid } from '../../utils/defaultDetailGrid.js';
|
|
@@ -58,6 +59,8 @@ import { firstFilled } from '../../utils/fillTemplate.js';
|
|
|
58
59
|
import { cnGridCellStyle, hasGridRow } from '../../utils/grid.js';
|
|
59
60
|
import { resolveHeaderMeta, isActivityWidget } from '../../utils/headerMeta.js';
|
|
60
61
|
import { prefixUrl } from '../../utils/headers.js';
|
|
62
|
+
import { interpolateUrlTokens } from '../../utils/interpolateUrlTokens.js';
|
|
63
|
+
import { followLinkClick } from '../../utils/linkNavigation.js';
|
|
61
64
|
import { patchStoredSelf } from '../../utils/patchStoredSelf.js';
|
|
62
65
|
import { setReadState } from '../../utils/recordInteractions.js';
|
|
63
66
|
import { saveEdited } from '../../utils/saveEdited.js';
|
|
@@ -266,6 +269,7 @@ var script = {
|
|
|
266
269
|
CnFormDialog: __sfc_main$8,
|
|
267
270
|
CnDashboardGrid: __sfc_main$9,
|
|
268
271
|
CnSegmentedControl: __sfc_main$a,
|
|
272
|
+
NcSelect,
|
|
269
273
|
ViewDashboardOutline,
|
|
270
274
|
CnLifecycleActions: __sfc_main$b,
|
|
271
275
|
CnActionButtons: __sfc_main$c,
|
|
@@ -288,7 +292,7 @@ var script = {
|
|
|
288
292
|
Restore,
|
|
289
293
|
},
|
|
290
294
|
|
|
291
|
-
mixins: [gridLayout, pageViews],
|
|
295
|
+
mixins: [gridLayout, pageFilters, pageViews],
|
|
292
296
|
|
|
293
297
|
inject: {
|
|
294
298
|
/**
|
|
@@ -1481,6 +1485,39 @@ var script = {
|
|
|
1481
1485
|
default: () => [],
|
|
1482
1486
|
},
|
|
1483
1487
|
|
|
1488
|
+
/**
|
|
1489
|
+
* A row of widgets across the whole page, above the body and the side
|
|
1490
|
+
* column (manifest `config.band`). Each entry is the id of a widget
|
|
1491
|
+
* declared in `widgets`, a widget definition, or `{ widgetId, span,
|
|
1492
|
+
* chrome }`; `span` is the entry's share of the row in twelfths
|
|
1493
|
+
* (default 12) and the row wraps; `chrome` is `card` or `bare` (a
|
|
1494
|
+
* banner defaults to bare, anything else to card, so a widget that
|
|
1495
|
+
* draws its own card, such as a KPI, asks for bare). Each entry
|
|
1496
|
+
* follows its `visibleWhen`. Empty (the default) draws no band.
|
|
1497
|
+
*
|
|
1498
|
+
* @type {Array<string|object>}
|
|
1499
|
+
* @spec openspec/changes/detail-band/specs/detail-page/spec.md#requirement-a-detail-page-can-draw-a-band-across-the-page-above-the-body-and-the-side-column
|
|
1500
|
+
*/
|
|
1501
|
+
band: {
|
|
1502
|
+
type: Array,
|
|
1503
|
+
default: () => [],
|
|
1504
|
+
},
|
|
1505
|
+
|
|
1506
|
+
/**
|
|
1507
|
+
* Page-level filters (manifest `config.pageFilters`), the same a
|
|
1508
|
+
* dashboard takes: each writes its choice into the page's workspace
|
|
1509
|
+
* context, which every widget on the page reads through
|
|
1510
|
+
* `@page.<key>` / `@workspace.<key>`. Drawn in the band, above its
|
|
1511
|
+
* widgets (PtPortal "Periode").
|
|
1512
|
+
*
|
|
1513
|
+
* @type {Array<{key: string, label?: string, type?: string, control?: ('select'|'segmented'), options?: Array<{value: (string|number), label: string}>, optionsFrom?: object, clearable?: boolean, placeholder?: string, default?: (string|number)}>}
|
|
1514
|
+
* @spec openspec/changes/detail-page-filters/specs/detail-page/spec.md#requirement-a-detail-page-can-draw-page-filters-that-its-widgets-read
|
|
1515
|
+
*/
|
|
1516
|
+
pageFilters: {
|
|
1517
|
+
type: Array,
|
|
1518
|
+
default: () => [],
|
|
1519
|
+
},
|
|
1520
|
+
|
|
1484
1521
|
/**
|
|
1485
1522
|
* A field path whose value picks the side column and its order for
|
|
1486
1523
|
* this record (manifest `config.sideColumnFrom`), read like a
|
|
@@ -1813,10 +1850,16 @@ var script = {
|
|
|
1813
1850
|
// which no register answers.
|
|
1814
1851
|
{ schemaSlug: () => props.schema || sidebarSchema() },
|
|
1815
1852
|
);
|
|
1853
|
+
// The page's workspace context, as a dashboard provides it: page
|
|
1854
|
+
// filters write their choice here and a widget reads it through
|
|
1855
|
+
// `@page.<key>` / `@workspace.<key>`.
|
|
1856
|
+
const workspaceContext = ref({});
|
|
1857
|
+
provide('cnWorkspaceContext', workspaceContext);
|
|
1816
1858
|
return {
|
|
1817
1859
|
...registryExposed,
|
|
1818
1860
|
subscriptionStatus: subscription.status,
|
|
1819
1861
|
lockState: lock,
|
|
1862
|
+
workspaceContext,
|
|
1820
1863
|
}
|
|
1821
1864
|
},
|
|
1822
1865
|
|
|
@@ -2249,7 +2292,46 @@ var script = {
|
|
|
2249
2292
|
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
2250
2293
|
*/
|
|
2251
2294
|
primaryActionHref() {
|
|
2252
|
-
|
|
2295
|
+
if (this.stagePrimaryEntry) {
|
|
2296
|
+
return this.stagePrimaryEntry.href || ''
|
|
2297
|
+
}
|
|
2298
|
+
return this.primaryActionLink?.href || ''
|
|
2299
|
+
},
|
|
2300
|
+
|
|
2301
|
+
/**
|
|
2302
|
+
* The page's `primaryAction` as a link, when no stage action stands in
|
|
2303
|
+
* for it: `href` (its record tokens resolved) opens in a new tab, as the
|
|
2304
|
+
* schema says; `route` (with `query`) is an in-app link a plain click
|
|
2305
|
+
* routes to (PtPortal "Nieuwe pagina" opens the page create flow).
|
|
2306
|
+
*
|
|
2307
|
+
* @return {{href: string, to: object|null, target: string}|null} The link, or null.
|
|
2308
|
+
* @spec openspec/changes/detail-band/specs/detail-page/spec.md#requirement-a-detail-pages-own-primary-action-is-a-link
|
|
2309
|
+
*/
|
|
2310
|
+
primaryActionLink() {
|
|
2311
|
+
const action = this.primaryAction;
|
|
2312
|
+
if (!action || typeof action !== 'object') {
|
|
2313
|
+
return null
|
|
2314
|
+
}
|
|
2315
|
+
if (typeof action.href === 'string' && action.href !== '') {
|
|
2316
|
+
const href = resolveManifestHref(interpolateUrlTokens(action.href, this.headerLinkTokenCtx));
|
|
2317
|
+
return href ? { href, to: null, target: '_blank' } : null
|
|
2318
|
+
}
|
|
2319
|
+
if (typeof action.route === 'string' && action.route !== '') {
|
|
2320
|
+
const to = { name: action.route, ...(action.query && typeof action.query === 'object' ? { query: action.query } : {}) };
|
|
2321
|
+
const href = routeHref(to, this.$router);
|
|
2322
|
+
return href ? { href, to, target: '_self' } : null
|
|
2323
|
+
}
|
|
2324
|
+
return null
|
|
2325
|
+
},
|
|
2326
|
+
|
|
2327
|
+
/**
|
|
2328
|
+
* The token context a header link resolves its URL against: this record.
|
|
2329
|
+
*
|
|
2330
|
+
* @return {{objectId: (string|number), object: object}} The context.
|
|
2331
|
+
* @spec openspec/changes/detail-band/specs/detail-page/spec.md#requirement-a-detail-header-link-builds-its-url-from-the-record
|
|
2332
|
+
*/
|
|
2333
|
+
headerLinkTokenCtx() {
|
|
2334
|
+
return { objectId: this.objectId, object: this.resolvedObject || {} }
|
|
2253
2335
|
},
|
|
2254
2336
|
|
|
2255
2337
|
/**
|
|
@@ -2259,7 +2341,10 @@ var script = {
|
|
|
2259
2341
|
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
2260
2342
|
*/
|
|
2261
2343
|
primaryActionTarget() {
|
|
2262
|
-
|
|
2344
|
+
if (this.stagePrimaryEntry) {
|
|
2345
|
+
return this.stagePrimaryEntry.linkTarget || undefined
|
|
2346
|
+
}
|
|
2347
|
+
return this.primaryActionLink?.target || undefined
|
|
2263
2348
|
},
|
|
2264
2349
|
|
|
2265
2350
|
/**
|
|
@@ -2655,6 +2740,57 @@ var script = {
|
|
|
2655
2740
|
* @return {boolean} True when it has widgets and the body is showing.
|
|
2656
2741
|
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-side-column
|
|
2657
2742
|
*/
|
|
2743
|
+
/**
|
|
2744
|
+
* The band's widgets, resolved and visible, each with its span.
|
|
2745
|
+
*
|
|
2746
|
+
* @return {Array<{widget: object, span: number, chrome: string}>}
|
|
2747
|
+
* @spec openspec/changes/detail-band/specs/detail-page/spec.md#requirement-a-detail-page-can-draw-a-band-across-the-page-above-the-body-and-the-side-column
|
|
2748
|
+
*/
|
|
2749
|
+
bandWidgets() {
|
|
2750
|
+
const declared = Array.isArray(this.widgets) ? this.widgets : [];
|
|
2751
|
+
return (Array.isArray(this.band) ? this.band : [])
|
|
2752
|
+
.map((entry, index) => {
|
|
2753
|
+
let widget = null;
|
|
2754
|
+
let span = 12;
|
|
2755
|
+
if (typeof entry === 'string') {
|
|
2756
|
+
widget = declared.find((candidate) => candidate && candidate.id === entry) || null;
|
|
2757
|
+
} else if (entry && typeof entry === 'object' && !Array.isArray(entry)) {
|
|
2758
|
+
span = Number.isInteger(entry.span) ? Math.min(12, Math.max(1, entry.span)) : 12;
|
|
2759
|
+
if (typeof entry.widgetId === 'string') {
|
|
2760
|
+
widget = declared.find((candidate) => candidate && candidate.id === entry.widgetId) || null;
|
|
2761
|
+
} else if (typeof entry.type === 'string') {
|
|
2762
|
+
widget = entry.id ? entry : { ...entry, id: `cn-band-${index}` };
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
if (!widget) {
|
|
2766
|
+
return null
|
|
2767
|
+
}
|
|
2768
|
+
const chrome = ['card', 'bare'].includes(entry?.chrome) ? entry.chrome : (widget.type === 'banner' ? 'bare' : 'card');
|
|
2769
|
+
return { widget, span, chrome }
|
|
2770
|
+
})
|
|
2771
|
+
.filter((entry) => entry && this.sideWidgetVisible(entry.widget))
|
|
2772
|
+
},
|
|
2773
|
+
|
|
2774
|
+
/**
|
|
2775
|
+
* Whether the band renders: it has a visible widget and the record shows.
|
|
2776
|
+
*
|
|
2777
|
+
* @return {boolean}
|
|
2778
|
+
* @spec openspec/changes/detail-band/specs/detail-page/spec.md#requirement-a-detail-page-can-draw-a-band-across-the-page-above-the-body-and-the-side-column
|
|
2779
|
+
*/
|
|
2780
|
+
showsBand() {
|
|
2781
|
+
return (this.bandWidgets.length > 0 || this.detailPageFilters.length > 0) && this.showsBody
|
|
2782
|
+
},
|
|
2783
|
+
|
|
2784
|
+
/**
|
|
2785
|
+
* The declared page filters that carry a key.
|
|
2786
|
+
*
|
|
2787
|
+
* @return {Array<object>}
|
|
2788
|
+
* @spec openspec/changes/detail-page-filters/specs/detail-page/spec.md#requirement-a-detail-page-can-draw-page-filters-that-its-widgets-read
|
|
2789
|
+
*/
|
|
2790
|
+
detailPageFilters() {
|
|
2791
|
+
return (Array.isArray(this.pageFilters) ? this.pageFilters : []).filter((pf) => pf && pf.key)
|
|
2792
|
+
},
|
|
2793
|
+
|
|
2658
2794
|
showsSideColumn() {
|
|
2659
2795
|
return this.sideColumnWidgets.length > 0 && this.showsBody
|
|
2660
2796
|
},
|
|
@@ -3020,7 +3156,7 @@ var script = {
|
|
|
3020
3156
|
.map((pinned) => pinned.inline);
|
|
3021
3157
|
const combined = inline.length > 0 ? [...authored, ...inline] : authored;
|
|
3022
3158
|
// An entry (or child) with `href` becomes a link CnActionButtons renders as an anchor.
|
|
3023
|
-
const linked = combined.map(headerActionAsLink);
|
|
3159
|
+
const linked = combined.map((action) => headerActionAsLink(action, this.headerLinkTokenCtx));
|
|
3024
3160
|
const declared = linked.some((action, index) => action !== combined[index]) ? linked : combined;
|
|
3025
3161
|
if (!(this.foldsEditIntoActions && this.canEditRecord)) {
|
|
3026
3162
|
return declared
|
|
@@ -4120,6 +4256,10 @@ var script = {
|
|
|
4120
4256
|
|
|
4121
4257
|
created() {
|
|
4122
4258
|
this.pushAiContext();
|
|
4259
|
+
// Seeded before the first render, so a widget's first request already
|
|
4260
|
+
// carries the page filter's value.
|
|
4261
|
+
this.initPageFilters();
|
|
4262
|
+
this.loadPageFilterOptions();
|
|
4123
4263
|
},
|
|
4124
4264
|
|
|
4125
4265
|
mounted() {
|
|
@@ -4362,16 +4502,21 @@ var script = {
|
|
|
4362
4502
|
* the `run()` CnActionButtons bound for it, and announced to the host
|
|
4363
4503
|
* with its declaration. A link action is left to the browser and
|
|
4364
4504
|
* announces nothing. Without a stage action this is the page's
|
|
4365
|
-
* `primaryAction`, announced as before
|
|
4505
|
+
* `primaryAction`, announced as before, and when it is an in-app link
|
|
4506
|
+
* a plain click routes there.
|
|
4366
4507
|
*
|
|
4508
|
+
* @param {MouseEvent} [event] The click.
|
|
4367
4509
|
* @return {void}
|
|
4368
4510
|
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
4369
4511
|
* @spec openspec/changes/header-action-href/specs/header-action-href/spec.md#requirement-a-header-action-with-href-is-a-link-that-runs-and-emits-nothing
|
|
4370
4512
|
*/
|
|
4371
|
-
onPrimaryActionClick() {
|
|
4513
|
+
onPrimaryActionClick(event) {
|
|
4372
4514
|
const entry = this.stagePrimaryEntry;
|
|
4373
4515
|
if (!entry) {
|
|
4374
4516
|
this.$emit('primary-action', this.primaryAction);
|
|
4517
|
+
if (this.primaryActionLink?.to) {
|
|
4518
|
+
followLinkClick(event, this.primaryActionLink.to, this.$router);
|
|
4519
|
+
}
|
|
4375
4520
|
return
|
|
4376
4521
|
}
|
|
4377
4522
|
if (entry.href) {
|