@conduction/nextcloud-vue 2.111.0 → 2.113.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 +4 -6
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
- package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +3 -2
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +4 -2
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js +1 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +3 -2
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js +2 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue.js +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +54 -5
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +16 -2
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue.js +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +10 -0
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +2 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +22 -2
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +17 -3
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue.js +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +18 -3
- 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/CnDetailPage/CnDetailPage.vue2.js +52 -13
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +31 -3
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +48 -11
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +160 -97
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue.js +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +2 -2
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +222 -15
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +24 -7
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js +1 -1
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js +21 -1
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js.map +1 -1
- package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue.js +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +36 -2
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +44 -5
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js +1 -0
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js.map +1 -1
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js +22 -2
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js.map +1 -1
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js +36 -16
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue.js +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js +29 -12
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js +3 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue.js +1 -1
- package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue2.js.map +1 -1
- package/dist/esm/components/CnSearchSelect/CnSearchSelect.vue3.js.map +1 -1
- package/dist/esm/composables/useRefLabels.js +26 -6
- package/dist/esm/composables/useRefLabels.js.map +1 -1
- package/dist/esm/integrations/registry.js +4 -0
- package/dist/esm/integrations/registry.js.map +1 -1
- package/dist/esm/l10n/en.json.js +3 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +3 -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 +680 -449
- package/dist/esm/store/plugins/liveUpdates.js +3 -1
- package/dist/esm/store/plugins/liveUpdates.js.map +1 -1
- package/dist/esm/store/plugins/logs.js +2 -1
- package/dist/esm/store/plugins/logs.js.map +1 -1
- package/dist/esm/utils/actionLink.js +104 -1
- package/dist/esm/utils/actionLink.js.map +1 -1
- package/dist/esm/utils/detailActionModel.js +28 -1
- package/dist/esm/utils/detailActionModel.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +13 -5
- package/dist/esm/utils/fillTemplate.js.map +1 -1
- package/dist/esm/utils/headerFieldChips.js +21 -2
- package/dist/esm/utils/headerFieldChips.js.map +1 -1
- package/dist/esm/utils/mapList.js +26 -11
- package/dist/esm/utils/mapList.js.map +1 -1
- package/dist/esm/utils/savedViewMenu.js +12 -5
- package/dist/esm/utils/savedViewMenu.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 +2800 -1933
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +680 -449
- package/l10n/en.json +3 -1
- package/l10n/nl.json +3 -1
- package/package.json +1 -1
- package/src/components/CnActionButtons/CnActionButtons.vue +4 -6
- package/src/components/CnActionDialog/CnActionDialog.vue +32 -9
- package/src/components/CnAssignDialog/CnAssignDialog.vue +4 -2
- package/src/components/CnBoardView/CnBoardView.vue +58 -5
- package/src/components/CnCardGrid/CnCardGrid.md +1 -0
- package/src/components/CnCardGrid/CnCardGrid.vue +11 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +27 -3
- package/src/components/CnChartWidget/CnChartWidget.vue +6 -0
- package/src/components/CnChoiceCards/CnChoiceCards.vue +0 -3
- package/src/components/CnDashboardPage/CnDashboardPage.vue +20 -5
- package/src/components/CnDetailPage/CnDetailPage.vue +62 -14
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +100 -5
- package/src/components/CnFormDialog/CnFormDialog.vue +1 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +233 -18
- package/src/components/CnNcDashboardList/CnNcDashboardList.vue +21 -1
- package/src/components/CnObjectCard/CnObjectCard.md +1 -0
- package/src/components/CnObjectCard/CnObjectCard.vue +51 -5
- package/src/components/CnPartyPicker/CnPartyPicker.vue +62 -16
- package/src/components/CnReportsPage/CnReportsPage.vue +31 -12
- package/src/components/CnSearchSelect/CnSearchSelect.vue +8 -0
- package/src/composables/useRefLabels.js +25 -5
- package/src/css/badge.css +15 -13
- package/src/css/board.css +0 -1
- package/src/css/dialog.css +53 -3
- package/src/css/index-page.css +26 -1
- package/src/css/look-board-detail.css +36 -15
- package/src/css/look-board-index.css +67 -15
- package/src/css/look-board.css +32 -18
- package/src/integrations/registry.js +4 -0
- package/src/schemas/app-manifest-v2.schema.json +61 -13
- package/src/store/plugins/liveUpdates.js +3 -1
- package/src/store/plugins/logs.js +2 -1
- package/src/utils/actionLink.js +103 -0
- package/src/utils/detailActionModel.js +27 -0
- package/src/utils/fillTemplate.js +13 -5
- package/src/utils/headerFieldChips.js +21 -2
- package/src/utils/mapList.js +26 -11
- package/src/utils/savedViewMenu.js +12 -5
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -104,7 +104,11 @@
|
|
|
104
104
|
:colorKey="pill.colorKey"
|
|
105
105
|
:colorMap="pill.colorMap"
|
|
106
106
|
:variant="pill.variant"
|
|
107
|
-
:data-testid="`cn-detail-page-pill-${pill.key}`"
|
|
107
|
+
:data-testid="`cn-detail-page-pill-${pill.key}`">
|
|
108
|
+
<template v-if="pill.icon" #icon>
|
|
109
|
+
<CnIcon :name="pill.icon" :size="14" class="cn-detail-page__pill-icon" />
|
|
110
|
+
</template>
|
|
111
|
+
</CnStatusBadge>
|
|
108
112
|
</div>
|
|
109
113
|
<!-- Type name as a small eyebrow above the record name, so
|
|
110
114
|
the header reads "the record" not "the kind of record"
|
|
@@ -485,7 +489,11 @@
|
|
|
485
489
|
:colorKey="pill.colorKey"
|
|
486
490
|
:colorMap="pill.colorMap"
|
|
487
491
|
:variant="pill.variant"
|
|
488
|
-
:data-testid="`cn-detail-page-pill-${pill.key}`"
|
|
492
|
+
:data-testid="`cn-detail-page-pill-${pill.key}`">
|
|
493
|
+
<template v-if="pill.icon" #icon>
|
|
494
|
+
<CnIcon :name="pill.icon" :size="14" class="cn-detail-page__pill-icon" />
|
|
495
|
+
</template>
|
|
496
|
+
</CnStatusBadge>
|
|
489
497
|
</div>
|
|
490
498
|
<CnBreadcrumbs
|
|
491
499
|
v-if="breadcrumbCrumbs.length > 0"
|
|
@@ -1249,6 +1257,7 @@ import { createRefLabelResolver } from '../../composables/useRefLabels.js'
|
|
|
1249
1257
|
import { gridLayout } from '../../mixins/gridLayout.js'
|
|
1250
1258
|
import { pageViews } from '../../mixins/pageViews.js'
|
|
1251
1259
|
import { useObjectStore } from '../../store/index.js'
|
|
1260
|
+
import { headerActionAsLink } from '../../utils/actionLink.js'
|
|
1252
1261
|
import { isAppInstalled } from '../../utils/appInstalled.js'
|
|
1253
1262
|
import { compactLayoutRows } from '../../utils/dashboardPlacement.js'
|
|
1254
1263
|
import { defaultDetailGrid } from '../../utils/defaultDetailGrid.js'
|
|
@@ -1257,6 +1266,7 @@ import {
|
|
|
1257
1266
|
MAX_QUICK_ACTIONS,
|
|
1258
1267
|
normalisePinnedAction,
|
|
1259
1268
|
pillCountSource,
|
|
1269
|
+
pillLook,
|
|
1260
1270
|
resolveCountPill,
|
|
1261
1271
|
resolveNextStep,
|
|
1262
1272
|
resolvePill,
|
|
@@ -2210,6 +2220,11 @@ export default {
|
|
|
2210
2220
|
* `@objectId` / `@object.<field>` token + local-predicate resolution.
|
|
2211
2221
|
* Empty (the default) renders nothing.
|
|
2212
2222
|
*
|
|
2223
|
+
* An entry with `href` is a link, never dispatched: an absolute
|
|
2224
|
+
* `http(s)`, `mailto:`, `tel:` or `//host/...` URL as written, or an instance path
|
|
2225
|
+
* (leading `/`) through `generateUrl()`, opening in place unless
|
|
2226
|
+
* `target` is `'_blank'`.
|
|
2227
|
+
*
|
|
2213
2228
|
* The menu, not a row of buttons: that is what the manifest schema has
|
|
2214
2229
|
* always said this key is, and what CnIndexPage already does with it.
|
|
2215
2230
|
* The two surfaces had drifted, and on a dossiq case — twelve actions —
|
|
@@ -2497,9 +2512,12 @@ export default {
|
|
|
2497
2512
|
* `field`: `text`, a constant ("Inwoner"), or `countFrom`
|
|
2498
2513
|
* (`{ register, schema, filter? }`) with `countLabel` (`{ one, other }`
|
|
2499
2514
|
* with `{count}`), a count of a related list ("1 lopende zaak") that
|
|
2500
|
-
* shows nothing at 0.
|
|
2515
|
+
* shows nothing at 0. With `resolve: { register, schema, labelField?,
|
|
2516
|
+
* colorField?, iconField? }` the pill shows the referenced record's
|
|
2517
|
+
* label, takes its tone from that record's colour (an NL Design System
|
|
2518
|
+
* hue such as `red`, or a badge role) and shows its icon.
|
|
2501
2519
|
*
|
|
2502
|
-
* @type {{field?: string, text?: string, countFrom?: object, countLabel?: object, colorMap?: object, labels?: object, variant?: string}|null}
|
|
2520
|
+
* @type {{field?: string, text?: string, countFrom?: object, countLabel?: object, colorMap?: object, labels?: object, variant?: string, resolve?: object}|null}
|
|
2503
2521
|
*/
|
|
2504
2522
|
typePill: {
|
|
2505
2523
|
type: Object,
|
|
@@ -2940,6 +2958,8 @@ export default {
|
|
|
2940
2958
|
pillCountSeq: 0,
|
|
2941
2959
|
/** The labels of the records pills with `resolve` point at, by pill key. */
|
|
2942
2960
|
pillRefLabels: {},
|
|
2961
|
+
/** The colour and icon names read off the records pills with `resolve` point at, by pill key. */
|
|
2962
|
+
pillRefLooks: {},
|
|
2943
2963
|
/** Sequence of the last pill label lookup, so a late answer for another record is dropped. */
|
|
2944
2964
|
pillRefSeq: 0,
|
|
2945
2965
|
/** `register/schema/id` the read-state call was last made for, so it is sent once per load. */
|
|
@@ -3488,10 +3508,11 @@ export default {
|
|
|
3488
3508
|
// `resolve`: the referenced record's label replaces the id, and
|
|
3489
3509
|
// keys the colour too ("Woo-verzoek", not a uuid).
|
|
3490
3510
|
const resolved = this.pillRefLabels[key]
|
|
3511
|
+
const look = pillLook(this.pillRefLooks[key])
|
|
3491
3512
|
if (typeof resolved === 'string' && resolved !== '') {
|
|
3492
|
-
return { ...pill, key, label: this.effectiveTranslate(resolved), colorKey: resolved }
|
|
3513
|
+
return { ...pill, ...look, key, label: this.effectiveTranslate(resolved), colorKey: resolved }
|
|
3493
3514
|
}
|
|
3494
|
-
return { ...pill, key, label: this.effectiveTranslate(pill.label) }
|
|
3515
|
+
return { ...pill, ...look, key, label: this.effectiveTranslate(pill.label) }
|
|
3495
3516
|
})
|
|
3496
3517
|
.filter(Boolean)
|
|
3497
3518
|
},
|
|
@@ -3526,7 +3547,8 @@ export default {
|
|
|
3526
3547
|
.filter(([, config]) => config && config.resolve && config.resolve.register && config.resolve.schema && typeof config.field === 'string')
|
|
3527
3548
|
.map(([key, config]) => {
|
|
3528
3549
|
const pill = resolvePill({ field: config.field }, this.resolvedObject)
|
|
3529
|
-
|
|
3550
|
+
const { register, schema, labelField, colorField, iconField } = config.resolve
|
|
3551
|
+
return pill ? [key, register, schema, labelField || '', pill.colorKey, colorField || '', iconField || ''] : null
|
|
3530
3552
|
})
|
|
3531
3553
|
.filter(Boolean)
|
|
3532
3554
|
},
|
|
@@ -3925,6 +3947,7 @@ export default {
|
|
|
3925
3947
|
* in. Edit goes LAST so it reads after the app's own actions in the
|
|
3926
3948
|
* Actions menu — the page's affordance yields to the app's.
|
|
3927
3949
|
*
|
|
3950
|
+
* @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
|
|
3928
3951
|
* @return {Array<object>} The action descriptors.
|
|
3929
3952
|
*/
|
|
3930
3953
|
effectiveHeaderActions() {
|
|
@@ -3933,12 +3956,16 @@ export default {
|
|
|
3933
3956
|
// written out in place) join the list CnActionButtons dispatches,
|
|
3934
3957
|
// so they get the same dialogs, toasts and `visibleWhen` gating as
|
|
3935
3958
|
// any header action. An id that is already declared is not added
|
|
3936
|
-
// twice. With nothing pinned inline
|
|
3959
|
+
// twice. With nothing pinned inline and no `href` entry this is
|
|
3960
|
+
// `headerActions` itself.
|
|
3937
3961
|
const known = new Set(authored.map((action) => action && action.id))
|
|
3938
3962
|
const inline = [this.stagePrimary, ...this.pinnedQuickActions]
|
|
3939
3963
|
.filter((pinned) => pinned && pinned.inline && !known.has(pinned.id))
|
|
3940
3964
|
.map((pinned) => pinned.inline)
|
|
3941
|
-
const
|
|
3965
|
+
const combined = inline.length > 0 ? [...authored, ...inline] : authored
|
|
3966
|
+
// An entry (or child) with `href` becomes a link CnActionButtons renders as an anchor.
|
|
3967
|
+
const linked = combined.map(headerActionAsLink)
|
|
3968
|
+
const declared = linked.some((action, index) => action !== combined[index]) ? linked : combined
|
|
3942
3969
|
if (!(this.foldsEditIntoActions && this.canEditRecord)) {
|
|
3943
3970
|
return declared
|
|
3944
3971
|
}
|
|
@@ -5182,12 +5209,13 @@ export default {
|
|
|
5182
5209
|
*
|
|
5183
5210
|
* A stage's action is dispatched the way a header action is, through
|
|
5184
5211
|
* the `run()` CnActionButtons bound for it, and announced to the host
|
|
5185
|
-
* with its declaration. A link action is left to the browser
|
|
5186
|
-
* a stage action this is the page's
|
|
5187
|
-
* before.
|
|
5212
|
+
* with its declaration. A link action is left to the browser and
|
|
5213
|
+
* announces nothing. Without a stage action this is the page's
|
|
5214
|
+
* `primaryAction`, announced as before.
|
|
5188
5215
|
*
|
|
5189
5216
|
* @return {void}
|
|
5190
5217
|
* @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-primary-action-follows-the-stage
|
|
5218
|
+
* @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
|
|
5191
5219
|
*/
|
|
5192
5220
|
onPrimaryActionClick() {
|
|
5193
5221
|
const entry = this.stagePrimaryEntry
|
|
@@ -5195,6 +5223,9 @@ export default {
|
|
|
5195
5223
|
this.$emit('primary-action', this.primaryAction)
|
|
5196
5224
|
return
|
|
5197
5225
|
}
|
|
5226
|
+
if (entry.href) {
|
|
5227
|
+
return
|
|
5228
|
+
}
|
|
5198
5229
|
this.$emit('primary-action', this.headerActionsById[entry.id] || { id: entry.id, label: entry.label })
|
|
5199
5230
|
this.onPinnedActionClick(entry)
|
|
5200
5231
|
},
|
|
@@ -5340,6 +5371,7 @@ export default {
|
|
|
5340
5371
|
const seq = ++this.pillRefSeq
|
|
5341
5372
|
if (targets.length === 0) {
|
|
5342
5373
|
this.pillRefLabels = {}
|
|
5374
|
+
this.pillRefLooks = {}
|
|
5343
5375
|
return
|
|
5344
5376
|
}
|
|
5345
5377
|
if (!this._pillRefResolver) {
|
|
@@ -5352,14 +5384,30 @@ export default {
|
|
|
5352
5384
|
})
|
|
5353
5385
|
}
|
|
5354
5386
|
const labels = {}
|
|
5355
|
-
|
|
5356
|
-
|
|
5387
|
+
const looks = {}
|
|
5388
|
+
const field = async (register, schema, id, name) => {
|
|
5389
|
+
if (!name) {
|
|
5390
|
+
return null
|
|
5391
|
+
}
|
|
5392
|
+
const found = await this._pillRefResolver.resolve(register, schema, [id], name, { strict: true }).catch(() => ({}))
|
|
5393
|
+
return found && typeof found[id] === 'string' ? found[id] : null
|
|
5394
|
+
}
|
|
5395
|
+
await Promise.all(targets.map(async ([key, register, schema, labelField, id, colorField, iconField]) => {
|
|
5396
|
+
const [found, color, icon] = await Promise.all([
|
|
5397
|
+
this._pillRefResolver.resolve(register, schema, [id], labelField).catch(() => ({})),
|
|
5398
|
+
field(register, schema, id, colorField),
|
|
5399
|
+
field(register, schema, id, iconField),
|
|
5400
|
+
])
|
|
5357
5401
|
if (found && typeof found[id] === 'string') {
|
|
5358
5402
|
labels[key] = found[id]
|
|
5359
5403
|
}
|
|
5404
|
+
if (color || icon) {
|
|
5405
|
+
looks[key] = { color, icon }
|
|
5406
|
+
}
|
|
5360
5407
|
}))
|
|
5361
5408
|
if (seq === this.pillRefSeq) {
|
|
5362
5409
|
this.pillRefLabels = labels
|
|
5410
|
+
this.pillRefLooks = looks
|
|
5363
5411
|
}
|
|
5364
5412
|
},
|
|
5365
5413
|
|
|
@@ -147,6 +147,39 @@
|
|
|
147
147
|
hidden
|
|
148
148
|
data-testid="cn-detail-widget-props-pending" />
|
|
149
149
|
|
|
150
|
+
<!-- A mount-mode leaf whose descriptor asks for the library card
|
|
151
|
+
(`chrome: "card"`): the host draws the card, its title with the
|
|
152
|
+
count the leaf reports, the leaf's actions in the title row and the
|
|
153
|
+
library empty state. The leaf reports them through the `onHeader`
|
|
154
|
+
mount prop and renders only its rows. -->
|
|
155
|
+
<CnWidgetWrapper
|
|
156
|
+
v-else-if="wrapsMountLeaf"
|
|
157
|
+
:title="leafCardTitle"
|
|
158
|
+
titleIconPosition="left"
|
|
159
|
+
:showActions="false"
|
|
160
|
+
:showRefresh="false"
|
|
161
|
+
:showRequestFeature="false"
|
|
162
|
+
:headerLink="cardHeaderLink"
|
|
163
|
+
class="cn-detail-page__leaf-card"
|
|
164
|
+
data-testid="cn-detail-widget-leaf-card">
|
|
165
|
+
<template v-if="leafCardIcon" #title-icon>
|
|
166
|
+
<CnIcon :name="leafCardIcon" :size="20" />
|
|
167
|
+
</template>
|
|
168
|
+
<template v-if="rendererHeader.count !== null" #title-meta>
|
|
169
|
+
<span class="cn-detail-page__catalog-count" data-testid="cn-detail-widget-header-count">{{ rendererHeader.count }}</span>
|
|
170
|
+
</template>
|
|
171
|
+
<template v-if="hasRendererHeaderItems" #actions>
|
|
172
|
+
<CnDetailWidgetHeaderItems :header="rendererHeader" />
|
|
173
|
+
</template>
|
|
174
|
+
<CnWidgetEmptyState
|
|
175
|
+
v-if="rendererHeader.empty"
|
|
176
|
+
:name="rendererHeader.empty.name"
|
|
177
|
+
:description="rendererHeader.empty.description" />
|
|
178
|
+
<CnLeafMountHost
|
|
179
|
+
:provider="integrationProvider"
|
|
180
|
+
:mountProps="integrationMountProps" />
|
|
181
|
+
</CnWidgetWrapper>
|
|
182
|
+
|
|
150
183
|
<CnLeafMountHost
|
|
151
184
|
v-else-if="isMountIntegration"
|
|
152
185
|
:provider="integrationProvider"
|
|
@@ -293,6 +326,7 @@ import CnObjectDataWidget from '../CnObjectDataWidget/CnObjectDataWidget.vue'
|
|
|
293
326
|
import CnObjectFilesWidget from '../CnObjectFilesWidget/CnObjectFilesWidget.vue'
|
|
294
327
|
import CnObjectGeoWidget from '../CnObjectGeoWidget/CnObjectGeoWidget.vue'
|
|
295
328
|
import CnRelatedObjectsWidget from '../CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue'
|
|
329
|
+
import CnWidgetEmptyState from '../CnWidgetEmptyState/CnWidgetEmptyState.vue'
|
|
296
330
|
import CnDetailWidgetHeaderItems from './CnDetailWidgetHeaderItems.vue'
|
|
297
331
|
import { useIntegrationRegistry } from '../../composables/useIntegrationRegistry.js'
|
|
298
332
|
import { normalizeLook } from '../../composables/useLook.js'
|
|
@@ -373,6 +407,7 @@ export default {
|
|
|
373
407
|
CnObjectDataWidget,
|
|
374
408
|
CnObjectGeoWidget,
|
|
375
409
|
CnRelatedObjectsWidget,
|
|
410
|
+
CnWidgetEmptyState,
|
|
376
411
|
CnWidgetWrapper,
|
|
377
412
|
NcActionButton,
|
|
378
413
|
CnEmptyContent,
|
|
@@ -628,7 +663,7 @@ export default {
|
|
|
628
663
|
* `header` event (screens-relation-pages): a link and a note,
|
|
629
664
|
* with the list's total already filled in.
|
|
630
665
|
*/
|
|
631
|
-
rendererHeader: { link: null, note: '', actions: [], pill: null },
|
|
666
|
+
rendererHeader: { link: null, note: '', actions: [], pill: null, count: null, empty: null },
|
|
632
667
|
}
|
|
633
668
|
},
|
|
634
669
|
|
|
@@ -844,6 +879,34 @@ export default {
|
|
|
844
879
|
&& typeof provider.unmount === 'function'
|
|
845
880
|
},
|
|
846
881
|
|
|
882
|
+
/**
|
|
883
|
+
* Whether a mount-mode leaf gets the library card: on a card surface,
|
|
884
|
+
* grid cell included, when its descriptor asks for it (`chrome:
|
|
885
|
+
* "card"`). Opt-in, because a leaf that draws its own card would
|
|
886
|
+
* otherwise sit in two.
|
|
887
|
+
*
|
|
888
|
+
* @return {boolean}
|
|
889
|
+
* @spec openspec/changes/integration-leaf-card/specs/integration-leaf-card/spec.md#requirement-a-mount-mode-leaf-can-take-the-library-card
|
|
890
|
+
*/
|
|
891
|
+
wrapsMountLeaf() {
|
|
892
|
+
return !this.isBare && this.isMountIntegration && this.integrationProvider.chrome === 'card'
|
|
893
|
+
},
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* The leaf card's title: the manifest's, else the descriptor's label.
|
|
897
|
+
*
|
|
898
|
+
* @return {string} The title in the user's language.
|
|
899
|
+
*/
|
|
900
|
+
leafCardTitle() {
|
|
901
|
+
const title = widgetTitleOf(this.widget)
|
|
902
|
+
return title ? this.translateLabel(title) : (this.integrationProvider?.label || '')
|
|
903
|
+
},
|
|
904
|
+
|
|
905
|
+
/** @return {string} The leaf card's icon: the manifest's, else the descriptor's. */
|
|
906
|
+
leafCardIcon() {
|
|
907
|
+
return this.widget?.icon || this.integrationProvider?.icon || ''
|
|
908
|
+
},
|
|
909
|
+
|
|
847
910
|
/**
|
|
848
911
|
* Whether this render is using a provider's widget as its bare surface.
|
|
849
912
|
*
|
|
@@ -1017,6 +1080,8 @@ export default {
|
|
|
1017
1080
|
// one that reads `objectId`.
|
|
1018
1081
|
integrationContext: this.widgetIntegrationContext,
|
|
1019
1082
|
...this.resolvedWidgetProps,
|
|
1083
|
+
// A carded leaf reports its title row and empty state here.
|
|
1084
|
+
...(this.wrapsMountLeaf ? { onHeader: this.onRendererHeader } : {}),
|
|
1020
1085
|
}
|
|
1021
1086
|
},
|
|
1022
1087
|
|
|
@@ -1221,11 +1286,12 @@ export default {
|
|
|
1221
1286
|
|
|
1222
1287
|
methods: {
|
|
1223
1288
|
/**
|
|
1224
|
-
* Take the header a content-only renderer,
|
|
1225
|
-
* titled card, reports.
|
|
1289
|
+
* Take the header a content-only renderer, an app widget in the
|
|
1290
|
+
* titled card, or a carded mount-mode leaf (`onHeader`) reports.
|
|
1226
1291
|
*
|
|
1227
|
-
* @param {{link?: object|null, note?: string, action?: {label: string, icon?: string, run: () => unknown}|null, actions?: Array<{label: string, icon?: string, run: () => unknown}
|
|
1292
|
+
* @param {{link?: object|null, note?: string, action?: {label: string, icon?: string, run: () => unknown}|null, actions?: Array<{label: string, icon?: string, run: () => unknown}>, count?: number, empty?: {name: string, description?: string}|null}} header The header parts.
|
|
1228
1293
|
* @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record
|
|
1294
|
+
* @spec openspec/changes/integration-leaf-card/specs/integration-leaf-card/spec.md#requirement-a-carded-leaf-reports-its-count-actions-and-empty-state
|
|
1229
1295
|
*/
|
|
1230
1296
|
onRendererHeader(header) {
|
|
1231
1297
|
const link = header && header.link && typeof header.link === 'object' ? header.link : null
|
|
@@ -1234,7 +1300,11 @@ export default {
|
|
|
1234
1300
|
// `action` (one) and `actions` (several) both count, `action` first.
|
|
1235
1301
|
const actions = [header && header.action, ...((header && Array.isArray(header.actions)) ? header.actions : [])].filter(usable)
|
|
1236
1302
|
const pill = header && header.pill && typeof header.pill.label === 'string' && header.pill.label !== '' ? header.pill : null
|
|
1237
|
-
|
|
1303
|
+
const count = header && Number.isFinite(header.count) ? header.count : null
|
|
1304
|
+
const empty = header && header.empty && typeof header.empty.name === 'string' && header.empty.name !== ''
|
|
1305
|
+
? { name: header.empty.name, description: typeof header.empty.description === 'string' ? header.empty.description : '' }
|
|
1306
|
+
: null
|
|
1307
|
+
this.rendererHeader = { link, note, actions, pill, count, empty }
|
|
1238
1308
|
},
|
|
1239
1309
|
|
|
1240
1310
|
/**
|
|
@@ -1372,6 +1442,31 @@ export default {
|
|
|
1372
1442
|
white-space: nowrap;
|
|
1373
1443
|
}
|
|
1374
1444
|
|
|
1445
|
+
/* A leaf card in a narrow cell keeps its title and count: the title row
|
|
1446
|
+
wraps and the actions move under it instead of squeezing the title out. */
|
|
1447
|
+
.cn-detail-page__leaf-card :deep(.cn-widget-wrapper__header) {
|
|
1448
|
+
flex-wrap: wrap;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
.cn-detail-page__leaf-card :deep(.cn-widget-wrapper__header-left) {
|
|
1452
|
+
flex: 1 0 auto;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
/* The count beside a card title: the grey 20px badge of the tab strip. */
|
|
1456
|
+
.cn-detail-page__catalog-count {
|
|
1457
|
+
background-color: var(--color-border);
|
|
1458
|
+
border-radius: 10px;
|
|
1459
|
+
box-sizing: border-box;
|
|
1460
|
+
color: var(--color-main-text);
|
|
1461
|
+
display: inline-block;
|
|
1462
|
+
font-size: 12px;
|
|
1463
|
+
font-weight: 600;
|
|
1464
|
+
line-height: 20px;
|
|
1465
|
+
min-width: 20px;
|
|
1466
|
+
padding: 0 6px;
|
|
1467
|
+
text-align: center;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1375
1470
|
.cn-detail-page__catalog-note {
|
|
1376
1471
|
color: var(--color-text-maxcontrast);
|
|
1377
1472
|
font-size: 14px;
|