@conduction/nextcloud-vue 2.75.0 → 2.76.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/CnCardGrid/CnCardGrid.vue2.js +6 -0
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +3 -2
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnCredentials/CnCredentials.vue2.js +11 -10
- package/dist/esm/components/CnCredentials/CnCredentials.vue2.js.map +1 -1
- package/dist/esm/components/CnCredentials/CnCredentials.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +8 -5
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +144 -6
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +12 -11
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/selfModeActions.js +90 -14
- package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js +15 -3
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +26 -0
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +26 -9
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +19 -3
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +153 -38
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js +74 -56
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js.map +1 -1
- package/dist/esm/composables/useObjectLock.js +41 -10
- package/dist/esm/composables/useObjectLock.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +7 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +8 -2
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +88 -1
- package/dist/esm/store/useObjectStore.js +37 -9
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/objectSignals.js +44 -0
- package/dist/esm/utils/objectSignals.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +871 -328
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +88 -1
- package/l10n/en.json +7 -1
- package/l10n/nl.json +8 -2
- package/package.json +1 -1
- package/src/components/CnCardGrid/CnCardGrid.md +1 -0
- package/src/components/CnCardGrid/CnCardGrid.vue +7 -0
- package/src/components/CnCredentials/CnCredentials.vue +17 -16
- package/src/components/CnDetailPage/CnDetailPage.vue +8 -5
- package/src/components/CnIndexPage/CnIndexPage.md +15 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +155 -16
- package/src/components/CnIndexPage/selfModeActions.js +89 -13
- package/src/components/CnIndexPage/useSelfFetchList.js +15 -3
- package/src/components/CnObjectCard/CnObjectCard.md +1 -0
- package/src/components/CnObjectCard/CnObjectCard.vue +89 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +19 -3
- package/src/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +181 -38
- package/src/composables/__tests__/useObjectLock.endpoints.spec.js +83 -0
- package/src/composables/useObjectLock.js +41 -10
- package/src/css/look-board-index.css +35 -1
- package/src/index.js +1 -0
- package/src/store/useObjectStore.js +37 -9
- package/src/utils/index.js +1 -0
- package/src/utils/objectSignals.js +41 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnObjectCard.vue2.js","sources":["../../../../src/components/CnObjectCard/CnObjectCard.vue"],"sourcesContent":["<template>\n\t<div\n\t\tclass=\"cn-object-card\"\n\t\t:class=\"{ 'cn-object-card--selected': selected, 'cn-object-card--board': isBoardLook }\"\n\t\t@mousedown=\"onCardMouseDown\"\n\t\t@click=\"onCardClick($event)\"\n\t\t@auxclick=\"onCardAuxClick($event)\">\n\t\t<!-- Selection checkbox -->\n\t\t<div v-if=\"selectable\"\n\t\t\tclass=\"cn-object-card__checkbox\"\n\t\t\t@click.stop\n\t\t\t@auxclick.stop>\n\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t:modelValue=\"selected\"\n\t\t\t\t@update:modelValue=\"$emit('select', object)\" />\n\t\t</div>\n\n\t\t<!-- Card content -->\n\t\t<div class=\"cn-object-card__content\">\n\t\t\t<!-- Header: image + title -->\n\t\t\t<div class=\"cn-object-card__header\">\n\t\t\t\t<!-- Board look: the leading element, initials in a circle or an icon on a tint. -->\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"isBoardLook && leadingKind\"\n\t\t\t\t\tclass=\"cn-object-card__leading\"\n\t\t\t\t\t:class=\"`cn-object-card__leading--${leadingKind}`\"\n\t\t\t\t\tdata-testid=\"cn-object-card-leading\"\n\t\t\t\t\taria-hidden=\"true\">\n\t\t\t\t\t<template v-if=\"leadingKind === 'initials'\">{{ leading.initials }}</template>\n\t\t\t\t\t<CnIcon v-else :name=\"leading.icon\" :size=\"20\" />\n\t\t\t\t</span>\n\t\t\t\t<img\n\t\t\t\t\tv-if=\"imageUrl\"\n\t\t\t\t\t:src=\"imageUrl\"\n\t\t\t\t\t:alt=\"title\"\n\t\t\t\t\tclass=\"cn-object-card__image\">\n\n\t\t\t\t<div class=\"cn-object-card__title-area\">\n\t\t\t\t\t<h3 class=\"cn-object-card__title\">\n\t\t\t\t\t\t<!-- Beside the title, not in the badges row: the badges\n\t\t\t\t\t\t slot is the consumer's, and a lock is the library's\n\t\t\t\t\t\t to report. A card whose consumer passes no badges\n\t\t\t\t\t\t would otherwise show no padlock at all. -->\n\t\t\t\t\t\t<CnLockIndicator :object=\"object\" :size=\"16\" />\n\t\t\t\t\t\t{{ title }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<p v-if=\"description\" class=\"cn-object-card__description\">\n\t\t\t\t\t\t{{ truncatedDescription }}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<!-- Board look, end of the head: the status pill, or else the row menu. -->\n\t\t\t\t<template v-if=\"isBoardLook\">\n\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\tv-if=\"statusPill\"\n\t\t\t\t\t\tclass=\"cn-object-card__status\"\n\t\t\t\t\t\t:label=\"statusPill.label\"\n\t\t\t\t\t\t:variant=\"statusPill.variant\"\n\t\t\t\t\t\tdata-testid=\"cn-object-card-status\" />\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-else-if=\"$slots.actions\"\n\t\t\t\t\t\tclass=\"cn-object-card__menu\"\n\t\t\t\t\t\t@click.stop\n\t\t\t\t\t\t@auxclick.stop>\n\t\t\t\t\t\t<slot name=\"actions\" :object=\"object\" />\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</div>\n\n\t\t\t<!-- Badges slot -->\n\t\t\t<div v-if=\"$slots.badges\" class=\"cn-object-card__badges\">\n\t\t\t\t<slot name=\"badges\" :object=\"object\" />\n\t\t\t</div>\n\n\t\t\t<!-- Metadata: visible properties as label:value pairs -->\n\t\t\t<div v-if=\"metadataFields.length > 0\" class=\"cn-object-card__metadata\" :class=\"{ 'cn-object-card__metadata--facts': isBoardLook }\">\n\t\t\t\t<slot name=\"metadata\" :object=\"object\" :fields=\"metadataFields\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-for=\"field in metadataFields\"\n\t\t\t\t\t\t:key=\"field.key\"\n\t\t\t\t\t\tclass=\"cn-object-card__meta-item\">\n\t\t\t\t\t\t<span class=\"cn-object-card__meta-label\">{{ field.label }}</span>\n\t\t\t\t\t\t<CnCellRenderer\n\t\t\t\t\t\t\t:value=\"field.value\"\n\t\t\t\t\t\t\t:property=\"field.property\"\n\t\t\t\t\t\t\t:truncate=\"60\" />\n\t\t\t\t\t</div>\n\t\t\t\t</slot>\n\t\t\t</div>\n\n\t\t\t<!-- Board look: one footer action, with a muted meta text at the start. -->\n\t\t\t<div\n\t\t\t\tv-if=\"isBoardLook && footerAction\"\n\t\t\t\tclass=\"cn-object-card__footer\"\n\t\t\t\tdata-testid=\"cn-object-card-footer\"\n\t\t\t\t@click.stop\n\t\t\t\t@auxclick.stop>\n\t\t\t\t<span v-if=\"footerAction.meta\" class=\"cn-object-card__footer-meta\">{{ footerAction.meta }}</span>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\tclass=\"cn-object-card__footer-action\"\n\t\t\t\t\tdata-testid=\"cn-object-card-footer-action\"\n\t\t\t\t\t:aria-label=\"footerAction.ariaLabel || `${footerAction.label} ${title}`\"\n\t\t\t\t\t@click=\"$emit('footer-action', object, footerAction)\">\n\t\t\t\t\t{{ footerAction.label }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Actions slot -->\n\t\t<div v-if=\"$slots.actions && !isBoardLook\"\n\t\t\tclass=\"cn-object-card__actions\"\n\t\t\t@click.stop\n\t\t\t@auxclick.stop>\n\t\t\t<slot name=\"actions\" :object=\"object\" />\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { NcButton, NcCheckboxRadioSwitch } from '@nextcloud/vue'\nimport { useClickDragGuard } from '../../composables/useClickDragGuard.js'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { resolveImageUrl } from '../../utils/resolveImageUrl.js'\nimport { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'\nimport { formatValue } from '../../utils/schema.js'\nimport { CnCellRenderer } from '../CnCellRenderer/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\nimport { CnLockIndicator } from '../CnLockIndicator/index.js'\nimport { CnStatusBadge } from '../CnStatusBadge/index.js'\n\n/**\n * CnObjectCard — Schema-configuration-driven card for object display.\n *\n * Uses `schema.configuration` to determine which fields map to the card title,\n * description, and image. Remaining visible properties are shown as metadata.\n *\n * ```vue\n * <CnObjectCard :object=\"publication\" :schema=\"pubSchema\">\n * <template #actions=\"{ object }\">\n * <NcActions><NcActionButton @click=\"edit(object)\">Edit</NcActionButton></NcActions>\n * </template>\n * </CnObjectCard>\n * ```\n */\nexport default {\n\tname: 'CnObjectCard',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcCheckboxRadioSwitch,\n\t\tCnCellRenderer,\n\t\tCnIcon,\n\t\tCnLockIndicator,\n\t\tCnStatusBadge,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Consumer translation function, provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). Metadata\n\t\t * labels come from schema property titles, authored in English as the\n\t\t * canonical source; the visible label is resolved through this function\n\t\t * so it follows the user's language. Defaults to identity when used\n\t\t * standalone (no CnAppRoot ancestor).\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The look CnAppRoot provides; `board` draws the record card of the screens. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/** The object data */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Schema definition with properties and configuration */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Whether this card is selected */\n\t\tselected: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether to show selection checkbox */\n\t\tselectable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * When true, a body click on a SELECTABLE card emits `click`\n\t\t * (navigation) instead of toggling selection — selection then happens\n\t\t * via the checkbox only. The card counterpart of CnIndexPage's\n\t\t * `rowClickToView` (a table row click already navigates in that mode).\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tclickToView: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the status pill at the end of the head row, as a label\n\t\t * (`\"Open\"`) or `{ label, variant }` with a CnStatusBadge variant\n\t\t * (`success`, `warning`, `error`, `info`, `primary`, `default`).\n\t\t * Without it the head row ends in the row menu, when there is one.\n\t\t *\n\t\t * @type {(string|{label: string, variant?: string})}\n\t\t * @spec openspec/changes/screens-card-parity/specs/card-board-look/spec.md#requirement-a-record-card-has-a-head-facts-and-one-action\n\t\t */\n\t\tstatus: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the leading element of the head row. `{ initials }`\n\t\t * draws the initials in a 36px circle; `{ icon }` a schema icon in a\n\t\t * 36px rounded square on a tint.\n\t\t *\n\t\t * @type {{initials?: string, icon?: string}}\n\t\t */\n\t\tleading: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the card's one footer action. `{ label, meta?,\n\t\t * ariaLabel? }`; `meta` is the 13px muted text at the start of the\n\t\t * footer. Clicking emits `footer-action`.\n\t\t *\n\t\t * @type {{label: string, meta?: string, ariaLabel?: string}}\n\t\t */\n\t\tfooterAction: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The property keys the facts list shows, in order (manifest key\n\t\t * `config.cardFields`). Without it the card shows the first\n\t\t * `maxMetadata` visible properties, as before.\n\t\t *\n\t\t * @type {Array<string>}\n\t\t */\n\t\tcardFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Maximum number of metadata fields to show */\n\t\tmaxMetadata: {\n\t\t\ttype: Number,\n\t\t\tdefault: 4,\n\t\t},\n\t},\n\n\temits: ['click', 'select', 'aux-click', 'footer-action'],\n\n\tsetup() {\n\t\t// Tell a deliberate card click apart from a text-selection drag.\n\t\treturn useClickDragGuard()\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the record card of the board look is drawn.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/**\n\t\t * `initials`, `icon` or '' for the head row's leading element.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tleadingKind() {\n\t\t\tif (this.leading && typeof this.leading.initials === 'string' && this.leading.initials !== '') {\n\t\t\t\treturn 'initials'\n\t\t\t}\n\t\t\treturn this.leading && typeof this.leading.icon === 'string' && this.leading.icon !== '' ? 'icon' : ''\n\t\t},\n\n\t\t/**\n\t\t * The status as `{ label, variant }`, or null.\n\t\t *\n\t\t * @return {?{label: string, variant: string}}\n\t\t */\n\t\tstatusPill() {\n\t\t\tif (typeof this.status === 'string' && this.status !== '') {\n\t\t\t\treturn { label: this.status, variant: 'default' }\n\t\t\t}\n\t\t\tif (this.status && typeof this.status.label === 'string' && this.status.label !== '') {\n\t\t\t\treturn { label: this.status.label, variant: this.status.variant || 'default' }\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\tconfig() {\n\t\t\treturn this.schema?.configuration || {}\n\t\t},\n\n\t\ttitle() {\n\t\t\tconst field = this.config.objectNameField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn String(this.object[field])\n\t\t\t}\n\t\t\treturn this.object.title || this.object.name || this.object.id || '—'\n\t\t},\n\n\t\tdescription() {\n\t\t\tconst field = this.config.objectDescriptionField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn String(this.object[field])\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\ttruncatedDescription() {\n\t\t\tif (!this.description) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (this.description.length > 120) {\n\t\t\t\treturn this.description.substring(0, 120) + '...'\n\t\t\t}\n\t\t\treturn this.description\n\t\t},\n\n\t\timageUrl() {\n\t\t\tconst field = this.config.objectImageField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn resolveImageUrl(this.object[field])\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/** Fields excluded from metadata (already shown as title/desc/image) */\n\t\tconfigFields() {\n\t\t\treturn [\n\t\t\t\tthis.config.objectNameField,\n\t\t\t\tthis.config.objectDescriptionField,\n\t\t\t\tthis.config.objectSummaryField,\n\t\t\t\tthis.config.objectImageField,\n\t\t\t].filter(Boolean)\n\t\t},\n\n\t\t/** Remaining visible properties for the metadata section */\n\t\tmetadataFields() {\n\t\t\tif (!this.schema?.properties) {\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\t// The page names the facts: those properties, in that order.\n\t\t\tif (Array.isArray(this.cardFields)) {\n\t\t\t\treturn this.cardFields\n\t\t\t\t\t.filter((key) => typeof key === 'string' && this.schema.properties[key] && !this.configFields.includes(key))\n\t\t\t\t\t.map((key) => ({\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: this.cnTranslate(this.schema.properties[key].title || key),\n\t\t\t\t\t\tvalue: this.object[key],\n\t\t\t\t\t\tproperty: this.schema.properties[key],\n\t\t\t\t\t}))\n\t\t\t}\n\n\t\t\treturn Object.entries(this.schema.properties)\n\t\t\t\t.filter(([key, prop]) => {\n\t\t\t\t\tif (this.configFields.includes(key)) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.visible === false) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.type === 'object') {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.format === 'markdown') {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t})\n\t\t\t\t.sort(([, a], [, b]) => {\n\t\t\t\t\tconst orderA = typeof a.order === 'number' ? a.order : Infinity\n\t\t\t\t\tconst orderB = typeof b.order === 'number' ? b.order : Infinity\n\t\t\t\t\treturn orderA - orderB\n\t\t\t\t})\n\t\t\t\t.slice(0, this.maxMetadata)\n\t\t\t\t.map(([key, prop]) => ({\n\t\t\t\t\tkey,\n\t\t\t\t\tlabel: this.cnTranslate(prop.title || key),\n\t\t\t\t\tvalue: this.object[key],\n\t\t\t\t\tproperty: prop,\n\t\t\t\t}))\n\t\t},\n\t},\n\n\tmethods: {\n\t\tformatValue,\n\n\t\t/**\n\t\t * Card-body click: emits `select` when `selectable` (ignoring drags),\n\t\t * otherwise emits `click` for navigation. With `clickToView`, a\n\t\t * selectable card's body click navigates too — the checkbox is the\n\t\t * only selection surface, mirroring the table's rowClickToView split.\n\t\t *\n\t\t * @param {MouseEvent} [event] The originating click event.\n\t\t */\n\t\tonCardClick(event) {\n\t\t\tif (this.selectable && !this.clickToView) {\n\t\t\t\tif (this.wasDrag(event)) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event select Emitted when the card toggles selection (clicking the body of a selectable card, or its checkbox).\n\t\t\t\t * @type {object} The card's object.\n\t\t\t\t */\n\t\t\t\tthis.$emit('select', this.object)\n\t\t\t\t// Deprecation: selectable cards used to emit `click`. Keep emitting it\n\t\t\t\t// for listeners that still rely on it, but warn them to migrate to `select`.\n\t\t\t\t// `$.vnode.props`, not `$attrs`: `click` is a declared emit, and\n\t\t\t\t// Vue keeps declared emits out of `$attrs`.\n\t\t\t\tif (this.$.vnode.props?.onClick) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.warn('[CnObjectCard] @click on selectable cards is deprecated; use @select instead.')\n\t\t\t\t\tthis.$emit('click', this.object)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event click Emitted when a non-selectable (or `clickToView`) card is clicked. Payload: `(object, event)` — the card's object and the native click event, for opening it in a new tab on a ctrl/cmd/shift click. A middle click emits `aux-click` instead. Selectable cards emit `select`; they also emit `click` (deprecated) when a `click` listener is present, so migrate selectable consumers to `@select`.\n\t\t\t * @type {object} The card's object.\n\t\t\t */\n\t\t\tthis.$emit('click', this.object, event)\n\t\t},\n\n\t\t/**\n\t\t * Card mousedown: record the press for the drag guard, and on a card a\n\t\t * middle click opens, keep the browser from starting autoscroll.\n\t\t *\n\t\t * @param {MouseEvent} event The mousedown event.\n\t\t */\n\t\tonCardMouseDown(event) {\n\t\t\tthis.onPointerDown(event)\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tpreventMiddleClickAutoscroll(event)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Card-body middle click: emits `aux-click`, not `click`, so an existing\n\t\t * `click` listener that navigates never moves the current tab away.\n\t\t * Ignored on a select-on-click card, on nested controls and on drags.\n\t\t *\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t */\n\t\tonCardAuxClick(event) {\n\t\t\tif (!isRowMiddleClick(event) || this.wasDrag(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.selectable && !this.clickToView) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event aux-click Emitted when a non-selectable (or `clickToView`) card is middle-clicked, for opening it in a new tab (see `openRowTarget`). Payload: `(object, event)` — the card's object and the native auxclick event.\n\t\t\t * @type {object} The card's object.\n\t\t\t */\n\t\t\tthis.$emit('aux-click', this.object, event)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-object-card {\n\tdisplay: flex;\n\tgap: 12px;\n\tpadding: 16px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 10px);\n\tcursor: pointer;\n\ttransition: box-shadow 0.2s ease, border-color 0.2s ease;\n}\n\n.cn-object-card:hover {\n\tborder-color: var(--color-primary-element);\n\tbox-shadow: 0 2px 8px var(--color-box-shadow);\n}\n\n.cn-object-card--selected {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element-light);\n}\n\n.cn-object-card__checkbox {\n\tflex-shrink: 0;\n\tpadding-top: 2px;\n}\n\n.cn-object-card__content {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-object-card__header {\n\tdisplay: flex;\n\tgap: 12px;\n\talign-items: flex-start;\n}\n\n.cn-object-card__image {\n\twidth: 48px;\n\theight: 48px;\n\tborder-radius: var(--border-radius);\n\tobject-fit: cover;\n\tflex-shrink: 0;\n}\n\n.cn-object-card__title-area {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-object-card__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: 600;\n\tline-height: 1.3;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-object-card__description {\n\tmargin: 4px 0 0;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n\tline-height: 1.4;\n}\n\n.cn-object-card__badges {\n\tmargin-top: 8px;\n}\n\n.cn-object-card__metadata {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n\tgap: 8px;\n\tmargin-top: 12px;\n\tpadding-top: 12px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-object-card__meta-item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-object-card__meta-label {\n\tfont-size: 11px;\n\tfont-weight: 500;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.3px;\n}\n\n.cn-object-card__actions {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["CnCellRenderer","CnIcon","CnLockIndicator","CnStatusBadge"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnObjectCard.vue2.js","sources":["../../../../src/components/CnObjectCard/CnObjectCard.vue"],"sourcesContent":["<template>\n\t<div\n\t\tclass=\"cn-object-card\"\n\t\t:class=\"[{ 'cn-object-card--selected': selected, 'cn-object-card--board': isBoardLook }, accentVariant && ['cn-object-card--accent', `cn-object-card--accent-${accentVariant}`]]\"\n\t\t@mousedown=\"onCardMouseDown\"\n\t\t@click=\"onCardClick($event)\"\n\t\t@auxclick=\"onCardAuxClick($event)\">\n\t\t<!-- Selection checkbox -->\n\t\t<div v-if=\"selectable\"\n\t\t\tclass=\"cn-object-card__checkbox\"\n\t\t\t@click.stop\n\t\t\t@auxclick.stop>\n\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t:modelValue=\"selected\"\n\t\t\t\t@update:modelValue=\"$emit('select', object)\" />\n\t\t</div>\n\n\t\t<!-- Card content -->\n\t\t<div class=\"cn-object-card__content\">\n\t\t\t<!-- Header: image + title -->\n\t\t\t<div class=\"cn-object-card__header\">\n\t\t\t\t<!-- Board look: the leading element, initials in a circle or an icon on a tint. -->\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"isBoardLook && leadingKind\"\n\t\t\t\t\tclass=\"cn-object-card__leading\"\n\t\t\t\t\t:class=\"`cn-object-card__leading--${leadingKind}`\"\n\t\t\t\t\tdata-testid=\"cn-object-card-leading\"\n\t\t\t\t\taria-hidden=\"true\">\n\t\t\t\t\t<template v-if=\"leadingKind === 'initials'\">{{ leading.initials }}</template>\n\t\t\t\t\t<CnIcon v-else :name=\"leading.icon\" :size=\"20\" />\n\t\t\t\t</span>\n\t\t\t\t<img\n\t\t\t\t\tv-if=\"imageUrl\"\n\t\t\t\t\t:src=\"imageUrl\"\n\t\t\t\t\t:alt=\"title\"\n\t\t\t\t\tclass=\"cn-object-card__image\">\n\n\t\t\t\t<div class=\"cn-object-card__title-area\">\n\t\t\t\t\t<h3 class=\"cn-object-card__title\">\n\t\t\t\t\t\t<!-- Beside the title, not in the badges row: the badges\n\t\t\t\t\t\t slot is the consumer's, and a lock is the library's\n\t\t\t\t\t\t to report. A card whose consumer passes no badges\n\t\t\t\t\t\t would otherwise show no padlock at all. -->\n\t\t\t\t\t\t<CnLockIndicator :object=\"object\" :size=\"16\" />\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-if=\"accentVariant && accent.icon\"\n\t\t\t\t\t\t\tclass=\"cn-object-card__accent-icon\"\n\t\t\t\t\t\t\tdata-testid=\"cn-object-card-accent-icon\"\n\t\t\t\t\t\t\t:role=\"accent.label ? 'img' : null\"\n\t\t\t\t\t\t\t:aria-label=\"accent.label || null\"\n\t\t\t\t\t\t\t:aria-hidden=\"accent.label ? null : 'true'\"\n\t\t\t\t\t\t\t:title=\"accent.label || null\">\n\t\t\t\t\t\t\t<CnIcon :name=\"accent.icon\" :size=\"16\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t{{ title }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<p v-if=\"description\" class=\"cn-object-card__description\">\n\t\t\t\t\t\t{{ truncatedDescription }}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<!-- Board look, end of the head: the status pill, or else the row menu. -->\n\t\t\t\t<template v-if=\"isBoardLook\">\n\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\tv-if=\"statusPill\"\n\t\t\t\t\t\tclass=\"cn-object-card__status\"\n\t\t\t\t\t\t:label=\"statusPill.label\"\n\t\t\t\t\t\t:variant=\"statusPill.variant\"\n\t\t\t\t\t\tdata-testid=\"cn-object-card-status\" />\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-else-if=\"$slots.actions\"\n\t\t\t\t\t\tclass=\"cn-object-card__menu\"\n\t\t\t\t\t\t@click.stop\n\t\t\t\t\t\t@auxclick.stop>\n\t\t\t\t\t\t<slot name=\"actions\" :object=\"object\" />\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</div>\n\n\t\t\t<!-- Badges slot -->\n\t\t\t<div v-if=\"$slots.badges\" class=\"cn-object-card__badges\">\n\t\t\t\t<slot name=\"badges\" :object=\"object\" />\n\t\t\t</div>\n\n\t\t\t<!-- Metadata: visible properties as label:value pairs -->\n\t\t\t<div v-if=\"metadataFields.length > 0\" class=\"cn-object-card__metadata\" :class=\"{ 'cn-object-card__metadata--facts': isBoardLook }\">\n\t\t\t\t<slot name=\"metadata\" :object=\"object\" :fields=\"metadataFields\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-for=\"field in metadataFields\"\n\t\t\t\t\t\t:key=\"field.key\"\n\t\t\t\t\t\tclass=\"cn-object-card__meta-item\">\n\t\t\t\t\t\t<span class=\"cn-object-card__meta-label\">{{ field.label }}</span>\n\t\t\t\t\t\t<CnCellRenderer\n\t\t\t\t\t\t\t:value=\"field.value\"\n\t\t\t\t\t\t\t:property=\"field.property\"\n\t\t\t\t\t\t\t:truncate=\"60\" />\n\t\t\t\t\t</div>\n\t\t\t\t</slot>\n\t\t\t</div>\n\n\t\t\t<!-- Board look: one footer action, with a muted meta text at the start. -->\n\t\t\t<div\n\t\t\t\tv-if=\"isBoardLook && footerAction\"\n\t\t\t\tclass=\"cn-object-card__footer\"\n\t\t\t\tdata-testid=\"cn-object-card-footer\"\n\t\t\t\t@click.stop\n\t\t\t\t@auxclick.stop>\n\t\t\t\t<span v-if=\"footerAction.meta\" class=\"cn-object-card__footer-meta\">{{ footerAction.meta }}</span>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\tclass=\"cn-object-card__footer-action\"\n\t\t\t\t\tdata-testid=\"cn-object-card-footer-action\"\n\t\t\t\t\t:aria-label=\"footerAction.ariaLabel || `${footerAction.label} ${title}`\"\n\t\t\t\t\t@click=\"$emit('footer-action', object, footerAction)\">\n\t\t\t\t\t{{ footerAction.label }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Actions slot -->\n\t\t<div v-if=\"$slots.actions && !isBoardLook\"\n\t\t\tclass=\"cn-object-card__actions\"\n\t\t\t@click.stop\n\t\t\t@auxclick.stop>\n\t\t\t<slot name=\"actions\" :object=\"object\" />\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { NcButton, NcCheckboxRadioSwitch } from '@nextcloud/vue'\nimport { useClickDragGuard } from '../../composables/useClickDragGuard.js'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { resolveImageUrl } from '../../utils/resolveImageUrl.js'\nimport { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'\nimport { formatValue } from '../../utils/schema.js'\nimport { CnCellRenderer } from '../CnCellRenderer/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\nimport { CnLockIndicator } from '../CnLockIndicator/index.js'\nimport { CnStatusBadge } from '../CnStatusBadge/index.js'\n\nconst ACCENT_VARIANTS = ['success', 'warning', 'error', 'info', 'primary']\n\n/**\n * CnObjectCard — Schema-configuration-driven card for object display.\n *\n * Uses `schema.configuration` to determine which fields map to the card title,\n * description, and image. Remaining visible properties are shown as metadata.\n *\n * ```vue\n * <CnObjectCard :object=\"publication\" :schema=\"pubSchema\">\n * <template #actions=\"{ object }\">\n * <NcActions><NcActionButton @click=\"edit(object)\">Edit</NcActionButton></NcActions>\n * </template>\n * </CnObjectCard>\n * ```\n */\nexport default {\n\tname: 'CnObjectCard',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcCheckboxRadioSwitch,\n\t\tCnCellRenderer,\n\t\tCnIcon,\n\t\tCnLockIndicator,\n\t\tCnStatusBadge,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Consumer translation function, provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). Metadata\n\t\t * labels come from schema property titles, authored in English as the\n\t\t * canonical source; the visible label is resolved through this function\n\t\t * so it follows the user's language. Defaults to identity when used\n\t\t * standalone (no CnAppRoot ancestor).\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The look CnAppRoot provides; `board` draws the record card of the screens. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/** The object data */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Schema definition with properties and configuration */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Whether this card is selected */\n\t\tselected: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether to show selection checkbox */\n\t\tselectable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * When true, a body click on a SELECTABLE card emits `click`\n\t\t * (navigation) instead of toggling selection — selection then happens\n\t\t * via the checkbox only. The card counterpart of CnIndexPage's\n\t\t * `rowClickToView` (a table row click already navigates in that mode).\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tclickToView: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the status pill at the end of the head row, as a label\n\t\t * (`\"Open\"`) or `{ label, variant }` with a CnStatusBadge variant\n\t\t * (`success`, `warning`, `error`, `info`, `primary`, `default`).\n\t\t * Without it the head row ends in the row menu, when there is one.\n\t\t *\n\t\t * @type {(string|{label: string, variant?: string})}\n\t\t * @spec openspec/changes/screens-card-parity/specs/card-board-look/spec.md#requirement-a-record-card-has-a-head-facts-and-one-action\n\t\t */\n\t\tstatus: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the leading element of the head row. `{ initials }`\n\t\t * draws the initials in a 36px circle; `{ icon }` a schema icon in a\n\t\t * 36px rounded square on a tint.\n\t\t *\n\t\t * @type {{initials?: string, icon?: string}}\n\t\t */\n\t\tleading: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the card's one footer action. `{ label, meta?,\n\t\t * ariaLabel? }`; `meta` is the 13px muted text at the start of the\n\t\t * footer. Clicking emits `footer-action`.\n\t\t *\n\t\t * @type {{label: string, meta?: string, ariaLabel?: string}}\n\t\t */\n\t\tfooterAction: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * A status accent: a colored border at the start of the card and,\n\t\t * with `icon`, a colored icon before the title. `variant` is one of\n\t\t * `success`, `warning`, `error`, `info` or `primary`; `icon` is a\n\t\t * name registered with CnIcon; `label` names the status for screen\n\t\t * readers and as the icon's tooltip.\n\t\t *\n\t\t * @type {{variant: string, icon?: string, label?: string}}\n\t\t */\n\t\taccent: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The property keys the facts list shows, in order (manifest key\n\t\t * `config.cardFields`). Without it the card shows the first\n\t\t * `maxMetadata` visible properties, as before.\n\t\t *\n\t\t * @type {Array<string>}\n\t\t */\n\t\tcardFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Maximum number of metadata fields to show */\n\t\tmaxMetadata: {\n\t\t\ttype: Number,\n\t\t\tdefault: 4,\n\t\t},\n\t},\n\n\temits: ['click', 'select', 'aux-click', 'footer-action'],\n\n\tsetup() {\n\t\t// Tell a deliberate card click apart from a text-selection drag.\n\t\treturn useClickDragGuard()\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the record card of the board look is drawn.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/**\n\t\t * `initials`, `icon` or '' for the head row's leading element.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tleadingKind() {\n\t\t\tif (this.leading && typeof this.leading.initials === 'string' && this.leading.initials !== '') {\n\t\t\t\treturn 'initials'\n\t\t\t}\n\t\t\treturn this.leading && typeof this.leading.icon === 'string' && this.leading.icon !== '' ? 'icon' : ''\n\t\t},\n\n\t\t/**\n\t\t * The status as `{ label, variant }`, or null.\n\t\t *\n\t\t * @return {?{label: string, variant: string}}\n\t\t */\n\t\tstatusPill() {\n\t\t\tif (typeof this.status === 'string' && this.status !== '') {\n\t\t\t\treturn { label: this.status, variant: 'default' }\n\t\t\t}\n\t\t\tif (this.status && typeof this.status.label === 'string' && this.status.label !== '') {\n\t\t\t\treturn { label: this.status.label, variant: this.status.variant || 'default' }\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/**\n\t\t * The accent variant when it is a known one, else ''.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\taccentVariant() {\n\t\t\tconst variant = this.accent?.variant\n\t\t\treturn ACCENT_VARIANTS.includes(variant) ? variant : ''\n\t\t},\n\n\t\tconfig() {\n\t\t\treturn this.schema?.configuration || {}\n\t\t},\n\n\t\ttitle() {\n\t\t\tconst field = this.config.objectNameField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn String(this.object[field])\n\t\t\t}\n\t\t\treturn this.object.title || this.object.name || this.object.id || '—'\n\t\t},\n\n\t\tdescription() {\n\t\t\tconst field = this.config.objectDescriptionField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn String(this.object[field])\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\ttruncatedDescription() {\n\t\t\tif (!this.description) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (this.description.length > 120) {\n\t\t\t\treturn this.description.substring(0, 120) + '...'\n\t\t\t}\n\t\t\treturn this.description\n\t\t},\n\n\t\timageUrl() {\n\t\t\tconst field = this.config.objectImageField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn resolveImageUrl(this.object[field])\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/** Fields excluded from metadata (already shown as title/desc/image) */\n\t\tconfigFields() {\n\t\t\treturn [\n\t\t\t\tthis.config.objectNameField,\n\t\t\t\tthis.config.objectDescriptionField,\n\t\t\t\tthis.config.objectSummaryField,\n\t\t\t\tthis.config.objectImageField,\n\t\t\t].filter(Boolean)\n\t\t},\n\n\t\t/** Remaining visible properties for the metadata section */\n\t\tmetadataFields() {\n\t\t\tif (!this.schema?.properties) {\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\t// The page names the facts: those properties, in that order.\n\t\t\tif (Array.isArray(this.cardFields)) {\n\t\t\t\treturn this.cardFields\n\t\t\t\t\t.filter((key) => typeof key === 'string' && this.schema.properties[key] && !this.configFields.includes(key))\n\t\t\t\t\t.map((key) => ({\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: this.cnTranslate(this.schema.properties[key].title || key),\n\t\t\t\t\t\tvalue: this.object[key],\n\t\t\t\t\t\tproperty: this.schema.properties[key],\n\t\t\t\t\t}))\n\t\t\t}\n\n\t\t\treturn Object.entries(this.schema.properties)\n\t\t\t\t.filter(([key, prop]) => {\n\t\t\t\t\tif (this.configFields.includes(key)) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.visible === false) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.type === 'object') {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.format === 'markdown') {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t})\n\t\t\t\t.sort(([, a], [, b]) => {\n\t\t\t\t\tconst orderA = typeof a.order === 'number' ? a.order : Infinity\n\t\t\t\t\tconst orderB = typeof b.order === 'number' ? b.order : Infinity\n\t\t\t\t\treturn orderA - orderB\n\t\t\t\t})\n\t\t\t\t.slice(0, this.maxMetadata)\n\t\t\t\t.map(([key, prop]) => ({\n\t\t\t\t\tkey,\n\t\t\t\t\tlabel: this.cnTranslate(prop.title || key),\n\t\t\t\t\tvalue: this.object[key],\n\t\t\t\t\tproperty: prop,\n\t\t\t\t}))\n\t\t},\n\t},\n\n\tmethods: {\n\t\tformatValue,\n\n\t\t/**\n\t\t * Card-body click: emits `select` when `selectable` (ignoring drags),\n\t\t * otherwise emits `click` for navigation. With `clickToView`, a\n\t\t * selectable card's body click navigates too — the checkbox is the\n\t\t * only selection surface, mirroring the table's rowClickToView split.\n\t\t *\n\t\t * @param {MouseEvent} [event] The originating click event.\n\t\t */\n\t\tonCardClick(event) {\n\t\t\tif (this.selectable && !this.clickToView) {\n\t\t\t\tif (this.wasDrag(event)) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event select Emitted when the card toggles selection (clicking the body of a selectable card, or its checkbox).\n\t\t\t\t * @type {object} The card's object.\n\t\t\t\t */\n\t\t\t\tthis.$emit('select', this.object)\n\t\t\t\t// Deprecation: selectable cards used to emit `click`. Keep emitting it\n\t\t\t\t// for listeners that still rely on it, but warn them to migrate to `select`.\n\t\t\t\t// `$.vnode.props`, not `$attrs`: `click` is a declared emit, and\n\t\t\t\t// Vue keeps declared emits out of `$attrs`.\n\t\t\t\tif (this.$.vnode.props?.onClick) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.warn('[CnObjectCard] @click on selectable cards is deprecated; use @select instead.')\n\t\t\t\t\tthis.$emit('click', this.object)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event click Emitted when a non-selectable (or `clickToView`) card is clicked. Payload: `(object, event)` — the card's object and the native click event, for opening it in a new tab on a ctrl/cmd/shift click. A middle click emits `aux-click` instead. Selectable cards emit `select`; they also emit `click` (deprecated) when a `click` listener is present, so migrate selectable consumers to `@select`.\n\t\t\t * @type {object} The card's object.\n\t\t\t */\n\t\t\tthis.$emit('click', this.object, event)\n\t\t},\n\n\t\t/**\n\t\t * Card mousedown: record the press for the drag guard, and on a card a\n\t\t * middle click opens, keep the browser from starting autoscroll.\n\t\t *\n\t\t * @param {MouseEvent} event The mousedown event.\n\t\t */\n\t\tonCardMouseDown(event) {\n\t\t\tthis.onPointerDown(event)\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tpreventMiddleClickAutoscroll(event)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Card-body middle click: emits `aux-click`, not `click`, so an existing\n\t\t * `click` listener that navigates never moves the current tab away.\n\t\t * Ignored on a select-on-click card, on nested controls and on drags.\n\t\t *\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t */\n\t\tonCardAuxClick(event) {\n\t\t\tif (!isRowMiddleClick(event) || this.wasDrag(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.selectable && !this.clickToView) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event aux-click Emitted when a non-selectable (or `clickToView`) card is middle-clicked, for opening it in a new tab (see `openRowTarget`). Payload: `(object, event)` — the card's object and the native auxclick event.\n\t\t\t * @type {object} The card's object.\n\t\t\t */\n\t\t\tthis.$emit('aux-click', this.object, event)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-object-card {\n\tdisplay: flex;\n\tgap: 12px;\n\tpadding: 16px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 10px);\n\tcursor: pointer;\n\ttransition: box-shadow 0.2s ease, border-color 0.2s ease;\n}\n\n.cn-object-card:hover {\n\tborder-color: var(--color-primary-element);\n\tbox-shadow: 0 2px 8px var(--color-box-shadow);\n}\n\n.cn-object-card--selected {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element-light);\n}\n\n/* The 4px start border takes 3px of padding, so the content stays put. */\n.cn-object-card.cn-object-card--accent {\n\tborder-inline-start-width: 4px;\n\tpadding-inline-start: 13px;\n}\n\n.cn-object-card.cn-object-card--accent-success {\n\tborder-inline-start-color: var(--color-element-success);\n}\n\n.cn-object-card.cn-object-card--accent-warning {\n\tborder-inline-start-color: var(--color-element-warning);\n}\n\n.cn-object-card.cn-object-card--accent-error {\n\tborder-inline-start-color: var(--color-element-error);\n}\n\n.cn-object-card.cn-object-card--accent-info {\n\tborder-inline-start-color: var(--color-element-info);\n}\n\n.cn-object-card.cn-object-card--accent-primary {\n\tborder-inline-start-color: var(--color-primary-element);\n}\n\n.cn-object-card__accent-icon {\n\tdisplay: inline-flex;\n\tvertical-align: -2px;\n\tmargin-inline-end: 4px;\n}\n\n.cn-object-card--accent-success .cn-object-card__accent-icon {\n\tcolor: var(--color-text-success);\n}\n\n.cn-object-card--accent-warning .cn-object-card__accent-icon {\n\tcolor: var(--color-text-warning);\n}\n\n.cn-object-card--accent-error .cn-object-card__accent-icon {\n\tcolor: var(--color-text-error);\n}\n\n.cn-object-card--accent-info .cn-object-card__accent-icon {\n\tcolor: var(--color-element-info);\n}\n\n.cn-object-card--accent-primary .cn-object-card__accent-icon {\n\tcolor: var(--color-primary-element);\n}\n\n.cn-object-card__checkbox {\n\tflex-shrink: 0;\n\tpadding-top: 2px;\n}\n\n.cn-object-card__content {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-object-card__header {\n\tdisplay: flex;\n\tgap: 12px;\n\talign-items: flex-start;\n}\n\n.cn-object-card__image {\n\twidth: 48px;\n\theight: 48px;\n\tborder-radius: var(--border-radius);\n\tobject-fit: cover;\n\tflex-shrink: 0;\n}\n\n.cn-object-card__title-area {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-object-card__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: 600;\n\tline-height: 1.3;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-object-card__description {\n\tmargin: 4px 0 0;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n\tline-height: 1.4;\n}\n\n.cn-object-card__badges {\n\tmargin-top: 8px;\n}\n\n.cn-object-card__metadata {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n\tgap: 8px;\n\tmargin-top: 12px;\n\tpadding-top: 12px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-object-card__meta-item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-object-card__meta-label {\n\tfont-size: 11px;\n\tfont-weight: 500;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.3px;\n}\n\n.cn-object-card__actions {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["CnCellRenderer","CnIcon","CnLockIndicator","CnStatusBadge"],"mappings":"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"}
|
|
@@ -5,16 +5,17 @@ const _hoisted_2 = { class: "cn-object-card__header" };
|
|
|
5
5
|
const _hoisted_3 = ["src", "alt"];
|
|
6
6
|
const _hoisted_4 = { class: "cn-object-card__title-area" };
|
|
7
7
|
const _hoisted_5 = { class: "cn-object-card__title" };
|
|
8
|
-
const _hoisted_6 =
|
|
8
|
+
const _hoisted_6 = ["role", "aria-label", "aria-hidden", "title"];
|
|
9
|
+
const _hoisted_7 = {
|
|
9
10
|
key: 0,
|
|
10
11
|
class: "cn-object-card__description"
|
|
11
12
|
};
|
|
12
|
-
const
|
|
13
|
+
const _hoisted_8 = {
|
|
13
14
|
key: 0,
|
|
14
15
|
class: "cn-object-card__badges"
|
|
15
16
|
};
|
|
16
|
-
const
|
|
17
|
-
const
|
|
17
|
+
const _hoisted_9 = { class: "cn-object-card__meta-label" };
|
|
18
|
+
const _hoisted_10 = {
|
|
18
19
|
key: 0,
|
|
19
20
|
class: "cn-object-card__footer-meta"
|
|
20
21
|
};
|
|
@@ -28,7 +29,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
28
29
|
const _component_NcButton = resolveComponent("NcButton");
|
|
29
30
|
|
|
30
31
|
return (openBlock(), createElementBlock("div", {
|
|
31
|
-
class: normalizeClass(["cn-object-card", { 'cn-object-card--selected': $props.selected, 'cn-object-card--board': $options.isBoardLook }]),
|
|
32
|
+
class: normalizeClass(["cn-object-card", [{ 'cn-object-card--selected': $props.selected, 'cn-object-card--board': $options.isBoardLook }, $options.accentVariant && ['cn-object-card--accent', `cn-object-card--accent-${$options.accentVariant}`]]]),
|
|
32
33
|
onMousedown: _cache[10] || (_cache[10] = (...args) => ($options.onCardMouseDown && $options.onCardMouseDown(...args))),
|
|
33
34
|
onClick: _cache[11] || (_cache[11] = $event => ($options.onCardClick($event))),
|
|
34
35
|
onAuxclick: _cache[12] || (_cache[12] = $event => ($options.onCardAuxClick($event)))
|
|
@@ -80,10 +81,26 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
80
81
|
object: $props.object,
|
|
81
82
|
size: 16
|
|
82
83
|
}, null, 8 /* PROPS */, ["object"]),
|
|
84
|
+
($options.accentVariant && $props.accent.icon)
|
|
85
|
+
? (openBlock(), createElementBlock("span", {
|
|
86
|
+
key: 0,
|
|
87
|
+
class: "cn-object-card__accent-icon",
|
|
88
|
+
"data-testid": "cn-object-card-accent-icon",
|
|
89
|
+
role: $props.accent.label ? 'img' : null,
|
|
90
|
+
"aria-label": $props.accent.label || null,
|
|
91
|
+
"aria-hidden": $props.accent.label ? null : 'true',
|
|
92
|
+
title: $props.accent.label || null
|
|
93
|
+
}, [
|
|
94
|
+
createVNode(_component_CnIcon, {
|
|
95
|
+
name: $props.accent.icon,
|
|
96
|
+
size: 16
|
|
97
|
+
}, null, 8 /* PROPS */, ["name"])
|
|
98
|
+
], 8 /* PROPS */, _hoisted_6))
|
|
99
|
+
: createCommentVNode("v-if", true),
|
|
83
100
|
createTextVNode(" " + toDisplayString($options.title), 1 /* TEXT */)
|
|
84
101
|
]),
|
|
85
102
|
($options.description)
|
|
86
|
-
? (openBlock(), createElementBlock("p",
|
|
103
|
+
? (openBlock(), createElementBlock("p", _hoisted_7, toDisplayString($options.truncatedDescription), 1 /* TEXT */))
|
|
87
104
|
: createCommentVNode("v-if", true)
|
|
88
105
|
]),
|
|
89
106
|
($options.isBoardLook)
|
|
@@ -110,7 +127,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
110
127
|
: createCommentVNode("v-if", true)
|
|
111
128
|
]),
|
|
112
129
|
(_ctx.$slots.badges)
|
|
113
|
-
? (openBlock(), createElementBlock("div",
|
|
130
|
+
? (openBlock(), createElementBlock("div", _hoisted_8, [
|
|
114
131
|
renderSlot(_ctx.$slots, "badges", { object: $props.object })
|
|
115
132
|
]))
|
|
116
133
|
: createCommentVNode("v-if", true),
|
|
@@ -128,7 +145,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
128
145
|
key: field.key,
|
|
129
146
|
class: "cn-object-card__meta-item"
|
|
130
147
|
}, [
|
|
131
|
-
createElementVNode("span",
|
|
148
|
+
createElementVNode("span", _hoisted_9, toDisplayString(field.label), 1 /* TEXT */),
|
|
132
149
|
createVNode(_component_CnCellRenderer, {
|
|
133
150
|
value: field.value,
|
|
134
151
|
property: field.property,
|
|
@@ -148,7 +165,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
148
165
|
onAuxclick: _cache[7] || (_cache[7] = withModifiers(() => {}, ["stop"]))
|
|
149
166
|
}, [
|
|
150
167
|
($props.footerAction.meta)
|
|
151
|
-
? (openBlock(), createElementBlock("span",
|
|
168
|
+
? (openBlock(), createElementBlock("span", _hoisted_10, toDisplayString($props.footerAction.meta), 1 /* TEXT */))
|
|
152
169
|
: createCommentVNode("v-if", true),
|
|
153
170
|
createVNode(_component_NcButton, {
|
|
154
171
|
variant: "secondary",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnObjectCard.vue3.js","sources":["../../../../src/components/CnObjectCard/CnObjectCard.vue?vue&type=template&id=6f672c8f&lang.js"],"sourcesContent":["<template>\n\t<div\n\t\tclass=\"cn-object-card\"\n\t\t:class=\"{ 'cn-object-card--selected': selected, 'cn-object-card--board': isBoardLook }\"\n\t\t@mousedown=\"onCardMouseDown\"\n\t\t@click=\"onCardClick($event)\"\n\t\t@auxclick=\"onCardAuxClick($event)\">\n\t\t<!-- Selection checkbox -->\n\t\t<div v-if=\"selectable\"\n\t\t\tclass=\"cn-object-card__checkbox\"\n\t\t\t@click.stop\n\t\t\t@auxclick.stop>\n\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t:modelValue=\"selected\"\n\t\t\t\t@update:modelValue=\"$emit('select', object)\" />\n\t\t</div>\n\n\t\t<!-- Card content -->\n\t\t<div class=\"cn-object-card__content\">\n\t\t\t<!-- Header: image + title -->\n\t\t\t<div class=\"cn-object-card__header\">\n\t\t\t\t<!-- Board look: the leading element, initials in a circle or an icon on a tint. -->\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"isBoardLook && leadingKind\"\n\t\t\t\t\tclass=\"cn-object-card__leading\"\n\t\t\t\t\t:class=\"`cn-object-card__leading--${leadingKind}`\"\n\t\t\t\t\tdata-testid=\"cn-object-card-leading\"\n\t\t\t\t\taria-hidden=\"true\">\n\t\t\t\t\t<template v-if=\"leadingKind === 'initials'\">{{ leading.initials }}</template>\n\t\t\t\t\t<CnIcon v-else :name=\"leading.icon\" :size=\"20\" />\n\t\t\t\t</span>\n\t\t\t\t<img\n\t\t\t\t\tv-if=\"imageUrl\"\n\t\t\t\t\t:src=\"imageUrl\"\n\t\t\t\t\t:alt=\"title\"\n\t\t\t\t\tclass=\"cn-object-card__image\">\n\n\t\t\t\t<div class=\"cn-object-card__title-area\">\n\t\t\t\t\t<h3 class=\"cn-object-card__title\">\n\t\t\t\t\t\t<!-- Beside the title, not in the badges row: the badges\n\t\t\t\t\t\t slot is the consumer's, and a lock is the library's\n\t\t\t\t\t\t to report. A card whose consumer passes no badges\n\t\t\t\t\t\t would otherwise show no padlock at all. -->\n\t\t\t\t\t\t<CnLockIndicator :object=\"object\" :size=\"16\" />\n\t\t\t\t\t\t{{ title }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<p v-if=\"description\" class=\"cn-object-card__description\">\n\t\t\t\t\t\t{{ truncatedDescription }}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<!-- Board look, end of the head: the status pill, or else the row menu. -->\n\t\t\t\t<template v-if=\"isBoardLook\">\n\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\tv-if=\"statusPill\"\n\t\t\t\t\t\tclass=\"cn-object-card__status\"\n\t\t\t\t\t\t:label=\"statusPill.label\"\n\t\t\t\t\t\t:variant=\"statusPill.variant\"\n\t\t\t\t\t\tdata-testid=\"cn-object-card-status\" />\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-else-if=\"$slots.actions\"\n\t\t\t\t\t\tclass=\"cn-object-card__menu\"\n\t\t\t\t\t\t@click.stop\n\t\t\t\t\t\t@auxclick.stop>\n\t\t\t\t\t\t<slot name=\"actions\" :object=\"object\" />\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</div>\n\n\t\t\t<!-- Badges slot -->\n\t\t\t<div v-if=\"$slots.badges\" class=\"cn-object-card__badges\">\n\t\t\t\t<slot name=\"badges\" :object=\"object\" />\n\t\t\t</div>\n\n\t\t\t<!-- Metadata: visible properties as label:value pairs -->\n\t\t\t<div v-if=\"metadataFields.length > 0\" class=\"cn-object-card__metadata\" :class=\"{ 'cn-object-card__metadata--facts': isBoardLook }\">\n\t\t\t\t<slot name=\"metadata\" :object=\"object\" :fields=\"metadataFields\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-for=\"field in metadataFields\"\n\t\t\t\t\t\t:key=\"field.key\"\n\t\t\t\t\t\tclass=\"cn-object-card__meta-item\">\n\t\t\t\t\t\t<span class=\"cn-object-card__meta-label\">{{ field.label }}</span>\n\t\t\t\t\t\t<CnCellRenderer\n\t\t\t\t\t\t\t:value=\"field.value\"\n\t\t\t\t\t\t\t:property=\"field.property\"\n\t\t\t\t\t\t\t:truncate=\"60\" />\n\t\t\t\t\t</div>\n\t\t\t\t</slot>\n\t\t\t</div>\n\n\t\t\t<!-- Board look: one footer action, with a muted meta text at the start. -->\n\t\t\t<div\n\t\t\t\tv-if=\"isBoardLook && footerAction\"\n\t\t\t\tclass=\"cn-object-card__footer\"\n\t\t\t\tdata-testid=\"cn-object-card-footer\"\n\t\t\t\t@click.stop\n\t\t\t\t@auxclick.stop>\n\t\t\t\t<span v-if=\"footerAction.meta\" class=\"cn-object-card__footer-meta\">{{ footerAction.meta }}</span>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\tclass=\"cn-object-card__footer-action\"\n\t\t\t\t\tdata-testid=\"cn-object-card-footer-action\"\n\t\t\t\t\t:aria-label=\"footerAction.ariaLabel || `${footerAction.label} ${title}`\"\n\t\t\t\t\t@click=\"$emit('footer-action', object, footerAction)\">\n\t\t\t\t\t{{ footerAction.label }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Actions slot -->\n\t\t<div v-if=\"$slots.actions && !isBoardLook\"\n\t\t\tclass=\"cn-object-card__actions\"\n\t\t\t@click.stop\n\t\t\t@auxclick.stop>\n\t\t\t<slot name=\"actions\" :object=\"object\" />\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { NcButton, NcCheckboxRadioSwitch } from '@nextcloud/vue'\nimport { useClickDragGuard } from '../../composables/useClickDragGuard.js'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { resolveImageUrl } from '../../utils/resolveImageUrl.js'\nimport { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'\nimport { formatValue } from '../../utils/schema.js'\nimport { CnCellRenderer } from '../CnCellRenderer/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\nimport { CnLockIndicator } from '../CnLockIndicator/index.js'\nimport { CnStatusBadge } from '../CnStatusBadge/index.js'\n\n/**\n * CnObjectCard — Schema-configuration-driven card for object display.\n *\n * Uses `schema.configuration` to determine which fields map to the card title,\n * description, and image. Remaining visible properties are shown as metadata.\n *\n * ```vue\n * <CnObjectCard :object=\"publication\" :schema=\"pubSchema\">\n * <template #actions=\"{ object }\">\n * <NcActions><NcActionButton @click=\"edit(object)\">Edit</NcActionButton></NcActions>\n * </template>\n * </CnObjectCard>\n * ```\n */\nexport default {\n\tname: 'CnObjectCard',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcCheckboxRadioSwitch,\n\t\tCnCellRenderer,\n\t\tCnIcon,\n\t\tCnLockIndicator,\n\t\tCnStatusBadge,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Consumer translation function, provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). Metadata\n\t\t * labels come from schema property titles, authored in English as the\n\t\t * canonical source; the visible label is resolved through this function\n\t\t * so it follows the user's language. Defaults to identity when used\n\t\t * standalone (no CnAppRoot ancestor).\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The look CnAppRoot provides; `board` draws the record card of the screens. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/** The object data */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Schema definition with properties and configuration */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Whether this card is selected */\n\t\tselected: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether to show selection checkbox */\n\t\tselectable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * When true, a body click on a SELECTABLE card emits `click`\n\t\t * (navigation) instead of toggling selection — selection then happens\n\t\t * via the checkbox only. The card counterpart of CnIndexPage's\n\t\t * `rowClickToView` (a table row click already navigates in that mode).\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tclickToView: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the status pill at the end of the head row, as a label\n\t\t * (`\"Open\"`) or `{ label, variant }` with a CnStatusBadge variant\n\t\t * (`success`, `warning`, `error`, `info`, `primary`, `default`).\n\t\t * Without it the head row ends in the row menu, when there is one.\n\t\t *\n\t\t * @type {(string|{label: string, variant?: string})}\n\t\t * @spec openspec/changes/screens-card-parity/specs/card-board-look/spec.md#requirement-a-record-card-has-a-head-facts-and-one-action\n\t\t */\n\t\tstatus: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the leading element of the head row. `{ initials }`\n\t\t * draws the initials in a 36px circle; `{ icon }` a schema icon in a\n\t\t * 36px rounded square on a tint.\n\t\t *\n\t\t * @type {{initials?: string, icon?: string}}\n\t\t */\n\t\tleading: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the card's one footer action. `{ label, meta?,\n\t\t * ariaLabel? }`; `meta` is the 13px muted text at the start of the\n\t\t * footer. Clicking emits `footer-action`.\n\t\t *\n\t\t * @type {{label: string, meta?: string, ariaLabel?: string}}\n\t\t */\n\t\tfooterAction: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The property keys the facts list shows, in order (manifest key\n\t\t * `config.cardFields`). Without it the card shows the first\n\t\t * `maxMetadata` visible properties, as before.\n\t\t *\n\t\t * @type {Array<string>}\n\t\t */\n\t\tcardFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Maximum number of metadata fields to show */\n\t\tmaxMetadata: {\n\t\t\ttype: Number,\n\t\t\tdefault: 4,\n\t\t},\n\t},\n\n\temits: ['click', 'select', 'aux-click', 'footer-action'],\n\n\tsetup() {\n\t\t// Tell a deliberate card click apart from a text-selection drag.\n\t\treturn useClickDragGuard()\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the record card of the board look is drawn.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/**\n\t\t * `initials`, `icon` or '' for the head row's leading element.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tleadingKind() {\n\t\t\tif (this.leading && typeof this.leading.initials === 'string' && this.leading.initials !== '') {\n\t\t\t\treturn 'initials'\n\t\t\t}\n\t\t\treturn this.leading && typeof this.leading.icon === 'string' && this.leading.icon !== '' ? 'icon' : ''\n\t\t},\n\n\t\t/**\n\t\t * The status as `{ label, variant }`, or null.\n\t\t *\n\t\t * @return {?{label: string, variant: string}}\n\t\t */\n\t\tstatusPill() {\n\t\t\tif (typeof this.status === 'string' && this.status !== '') {\n\t\t\t\treturn { label: this.status, variant: 'default' }\n\t\t\t}\n\t\t\tif (this.status && typeof this.status.label === 'string' && this.status.label !== '') {\n\t\t\t\treturn { label: this.status.label, variant: this.status.variant || 'default' }\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\tconfig() {\n\t\t\treturn this.schema?.configuration || {}\n\t\t},\n\n\t\ttitle() {\n\t\t\tconst field = this.config.objectNameField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn String(this.object[field])\n\t\t\t}\n\t\t\treturn this.object.title || this.object.name || this.object.id || '—'\n\t\t},\n\n\t\tdescription() {\n\t\t\tconst field = this.config.objectDescriptionField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn String(this.object[field])\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\ttruncatedDescription() {\n\t\t\tif (!this.description) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (this.description.length > 120) {\n\t\t\t\treturn this.description.substring(0, 120) + '...'\n\t\t\t}\n\t\t\treturn this.description\n\t\t},\n\n\t\timageUrl() {\n\t\t\tconst field = this.config.objectImageField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn resolveImageUrl(this.object[field])\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/** Fields excluded from metadata (already shown as title/desc/image) */\n\t\tconfigFields() {\n\t\t\treturn [\n\t\t\t\tthis.config.objectNameField,\n\t\t\t\tthis.config.objectDescriptionField,\n\t\t\t\tthis.config.objectSummaryField,\n\t\t\t\tthis.config.objectImageField,\n\t\t\t].filter(Boolean)\n\t\t},\n\n\t\t/** Remaining visible properties for the metadata section */\n\t\tmetadataFields() {\n\t\t\tif (!this.schema?.properties) {\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\t// The page names the facts: those properties, in that order.\n\t\t\tif (Array.isArray(this.cardFields)) {\n\t\t\t\treturn this.cardFields\n\t\t\t\t\t.filter((key) => typeof key === 'string' && this.schema.properties[key] && !this.configFields.includes(key))\n\t\t\t\t\t.map((key) => ({\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: this.cnTranslate(this.schema.properties[key].title || key),\n\t\t\t\t\t\tvalue: this.object[key],\n\t\t\t\t\t\tproperty: this.schema.properties[key],\n\t\t\t\t\t}))\n\t\t\t}\n\n\t\t\treturn Object.entries(this.schema.properties)\n\t\t\t\t.filter(([key, prop]) => {\n\t\t\t\t\tif (this.configFields.includes(key)) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.visible === false) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.type === 'object') {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.format === 'markdown') {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t})\n\t\t\t\t.sort(([, a], [, b]) => {\n\t\t\t\t\tconst orderA = typeof a.order === 'number' ? a.order : Infinity\n\t\t\t\t\tconst orderB = typeof b.order === 'number' ? b.order : Infinity\n\t\t\t\t\treturn orderA - orderB\n\t\t\t\t})\n\t\t\t\t.slice(0, this.maxMetadata)\n\t\t\t\t.map(([key, prop]) => ({\n\t\t\t\t\tkey,\n\t\t\t\t\tlabel: this.cnTranslate(prop.title || key),\n\t\t\t\t\tvalue: this.object[key],\n\t\t\t\t\tproperty: prop,\n\t\t\t\t}))\n\t\t},\n\t},\n\n\tmethods: {\n\t\tformatValue,\n\n\t\t/**\n\t\t * Card-body click: emits `select` when `selectable` (ignoring drags),\n\t\t * otherwise emits `click` for navigation. With `clickToView`, a\n\t\t * selectable card's body click navigates too — the checkbox is the\n\t\t * only selection surface, mirroring the table's rowClickToView split.\n\t\t *\n\t\t * @param {MouseEvent} [event] The originating click event.\n\t\t */\n\t\tonCardClick(event) {\n\t\t\tif (this.selectable && !this.clickToView) {\n\t\t\t\tif (this.wasDrag(event)) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event select Emitted when the card toggles selection (clicking the body of a selectable card, or its checkbox).\n\t\t\t\t * @type {object} The card's object.\n\t\t\t\t */\n\t\t\t\tthis.$emit('select', this.object)\n\t\t\t\t// Deprecation: selectable cards used to emit `click`. Keep emitting it\n\t\t\t\t// for listeners that still rely on it, but warn them to migrate to `select`.\n\t\t\t\t// `$.vnode.props`, not `$attrs`: `click` is a declared emit, and\n\t\t\t\t// Vue keeps declared emits out of `$attrs`.\n\t\t\t\tif (this.$.vnode.props?.onClick) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.warn('[CnObjectCard] @click on selectable cards is deprecated; use @select instead.')\n\t\t\t\t\tthis.$emit('click', this.object)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event click Emitted when a non-selectable (or `clickToView`) card is clicked. Payload: `(object, event)` — the card's object and the native click event, for opening it in a new tab on a ctrl/cmd/shift click. A middle click emits `aux-click` instead. Selectable cards emit `select`; they also emit `click` (deprecated) when a `click` listener is present, so migrate selectable consumers to `@select`.\n\t\t\t * @type {object} The card's object.\n\t\t\t */\n\t\t\tthis.$emit('click', this.object, event)\n\t\t},\n\n\t\t/**\n\t\t * Card mousedown: record the press for the drag guard, and on a card a\n\t\t * middle click opens, keep the browser from starting autoscroll.\n\t\t *\n\t\t * @param {MouseEvent} event The mousedown event.\n\t\t */\n\t\tonCardMouseDown(event) {\n\t\t\tthis.onPointerDown(event)\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tpreventMiddleClickAutoscroll(event)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Card-body middle click: emits `aux-click`, not `click`, so an existing\n\t\t * `click` listener that navigates never moves the current tab away.\n\t\t * Ignored on a select-on-click card, on nested controls and on drags.\n\t\t *\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t */\n\t\tonCardAuxClick(event) {\n\t\t\tif (!isRowMiddleClick(event) || this.wasDrag(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.selectable && !this.clickToView) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event aux-click Emitted when a non-selectable (or `clickToView`) card is middle-clicked, for opening it in a new tab (see `openRowTarget`). Payload: `(object, event)` — the card's object and the native auxclick event.\n\t\t\t * @type {object} The card's object.\n\t\t\t */\n\t\t\tthis.$emit('aux-click', this.object, event)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-object-card {\n\tdisplay: flex;\n\tgap: 12px;\n\tpadding: 16px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 10px);\n\tcursor: pointer;\n\ttransition: box-shadow 0.2s ease, border-color 0.2s ease;\n}\n\n.cn-object-card:hover {\n\tborder-color: var(--color-primary-element);\n\tbox-shadow: 0 2px 8px var(--color-box-shadow);\n}\n\n.cn-object-card--selected {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element-light);\n}\n\n.cn-object-card__checkbox {\n\tflex-shrink: 0;\n\tpadding-top: 2px;\n}\n\n.cn-object-card__content {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-object-card__header {\n\tdisplay: flex;\n\tgap: 12px;\n\talign-items: flex-start;\n}\n\n.cn-object-card__image {\n\twidth: 48px;\n\theight: 48px;\n\tborder-radius: var(--border-radius);\n\tobject-fit: cover;\n\tflex-shrink: 0;\n}\n\n.cn-object-card__title-area {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-object-card__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: 600;\n\tline-height: 1.3;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-object-card__description {\n\tmargin: 4px 0 0;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n\tline-height: 1.4;\n}\n\n.cn-object-card__badges {\n\tmargin-top: 8px;\n}\n\n.cn-object-card__metadata {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n\tgap: 8px;\n\tmargin-top: 12px;\n\tpadding-top: 12px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-object-card__meta-item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-object-card__meta-label {\n\tfont-size: 11px;\n\tfont-weight: 500;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.3px;\n}\n\n.cn-object-card__actions {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createVNode","_createElementVNode","_Fragment","_createTextVNode","_toDisplayString","_createBlock","_renderSlot","_openBlock","_renderList"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnObjectCard.vue3.js","sources":["../../../../src/components/CnObjectCard/CnObjectCard.vue?vue&type=template&id=6f672c8f&lang.js"],"sourcesContent":["<template>\n\t<div\n\t\tclass=\"cn-object-card\"\n\t\t:class=\"[{ 'cn-object-card--selected': selected, 'cn-object-card--board': isBoardLook }, accentVariant && ['cn-object-card--accent', `cn-object-card--accent-${accentVariant}`]]\"\n\t\t@mousedown=\"onCardMouseDown\"\n\t\t@click=\"onCardClick($event)\"\n\t\t@auxclick=\"onCardAuxClick($event)\">\n\t\t<!-- Selection checkbox -->\n\t\t<div v-if=\"selectable\"\n\t\t\tclass=\"cn-object-card__checkbox\"\n\t\t\t@click.stop\n\t\t\t@auxclick.stop>\n\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t:modelValue=\"selected\"\n\t\t\t\t@update:modelValue=\"$emit('select', object)\" />\n\t\t</div>\n\n\t\t<!-- Card content -->\n\t\t<div class=\"cn-object-card__content\">\n\t\t\t<!-- Header: image + title -->\n\t\t\t<div class=\"cn-object-card__header\">\n\t\t\t\t<!-- Board look: the leading element, initials in a circle or an icon on a tint. -->\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"isBoardLook && leadingKind\"\n\t\t\t\t\tclass=\"cn-object-card__leading\"\n\t\t\t\t\t:class=\"`cn-object-card__leading--${leadingKind}`\"\n\t\t\t\t\tdata-testid=\"cn-object-card-leading\"\n\t\t\t\t\taria-hidden=\"true\">\n\t\t\t\t\t<template v-if=\"leadingKind === 'initials'\">{{ leading.initials }}</template>\n\t\t\t\t\t<CnIcon v-else :name=\"leading.icon\" :size=\"20\" />\n\t\t\t\t</span>\n\t\t\t\t<img\n\t\t\t\t\tv-if=\"imageUrl\"\n\t\t\t\t\t:src=\"imageUrl\"\n\t\t\t\t\t:alt=\"title\"\n\t\t\t\t\tclass=\"cn-object-card__image\">\n\n\t\t\t\t<div class=\"cn-object-card__title-area\">\n\t\t\t\t\t<h3 class=\"cn-object-card__title\">\n\t\t\t\t\t\t<!-- Beside the title, not in the badges row: the badges\n\t\t\t\t\t\t slot is the consumer's, and a lock is the library's\n\t\t\t\t\t\t to report. A card whose consumer passes no badges\n\t\t\t\t\t\t would otherwise show no padlock at all. -->\n\t\t\t\t\t\t<CnLockIndicator :object=\"object\" :size=\"16\" />\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-if=\"accentVariant && accent.icon\"\n\t\t\t\t\t\t\tclass=\"cn-object-card__accent-icon\"\n\t\t\t\t\t\t\tdata-testid=\"cn-object-card-accent-icon\"\n\t\t\t\t\t\t\t:role=\"accent.label ? 'img' : null\"\n\t\t\t\t\t\t\t:aria-label=\"accent.label || null\"\n\t\t\t\t\t\t\t:aria-hidden=\"accent.label ? null : 'true'\"\n\t\t\t\t\t\t\t:title=\"accent.label || null\">\n\t\t\t\t\t\t\t<CnIcon :name=\"accent.icon\" :size=\"16\" />\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t{{ title }}\n\t\t\t\t\t</h3>\n\t\t\t\t\t<p v-if=\"description\" class=\"cn-object-card__description\">\n\t\t\t\t\t\t{{ truncatedDescription }}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<!-- Board look, end of the head: the status pill, or else the row menu. -->\n\t\t\t\t<template v-if=\"isBoardLook\">\n\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\tv-if=\"statusPill\"\n\t\t\t\t\t\tclass=\"cn-object-card__status\"\n\t\t\t\t\t\t:label=\"statusPill.label\"\n\t\t\t\t\t\t:variant=\"statusPill.variant\"\n\t\t\t\t\t\tdata-testid=\"cn-object-card-status\" />\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-else-if=\"$slots.actions\"\n\t\t\t\t\t\tclass=\"cn-object-card__menu\"\n\t\t\t\t\t\t@click.stop\n\t\t\t\t\t\t@auxclick.stop>\n\t\t\t\t\t\t<slot name=\"actions\" :object=\"object\" />\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</div>\n\n\t\t\t<!-- Badges slot -->\n\t\t\t<div v-if=\"$slots.badges\" class=\"cn-object-card__badges\">\n\t\t\t\t<slot name=\"badges\" :object=\"object\" />\n\t\t\t</div>\n\n\t\t\t<!-- Metadata: visible properties as label:value pairs -->\n\t\t\t<div v-if=\"metadataFields.length > 0\" class=\"cn-object-card__metadata\" :class=\"{ 'cn-object-card__metadata--facts': isBoardLook }\">\n\t\t\t\t<slot name=\"metadata\" :object=\"object\" :fields=\"metadataFields\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-for=\"field in metadataFields\"\n\t\t\t\t\t\t:key=\"field.key\"\n\t\t\t\t\t\tclass=\"cn-object-card__meta-item\">\n\t\t\t\t\t\t<span class=\"cn-object-card__meta-label\">{{ field.label }}</span>\n\t\t\t\t\t\t<CnCellRenderer\n\t\t\t\t\t\t\t:value=\"field.value\"\n\t\t\t\t\t\t\t:property=\"field.property\"\n\t\t\t\t\t\t\t:truncate=\"60\" />\n\t\t\t\t\t</div>\n\t\t\t\t</slot>\n\t\t\t</div>\n\n\t\t\t<!-- Board look: one footer action, with a muted meta text at the start. -->\n\t\t\t<div\n\t\t\t\tv-if=\"isBoardLook && footerAction\"\n\t\t\t\tclass=\"cn-object-card__footer\"\n\t\t\t\tdata-testid=\"cn-object-card-footer\"\n\t\t\t\t@click.stop\n\t\t\t\t@auxclick.stop>\n\t\t\t\t<span v-if=\"footerAction.meta\" class=\"cn-object-card__footer-meta\">{{ footerAction.meta }}</span>\n\t\t\t\t<NcButton\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\tclass=\"cn-object-card__footer-action\"\n\t\t\t\t\tdata-testid=\"cn-object-card-footer-action\"\n\t\t\t\t\t:aria-label=\"footerAction.ariaLabel || `${footerAction.label} ${title}`\"\n\t\t\t\t\t@click=\"$emit('footer-action', object, footerAction)\">\n\t\t\t\t\t{{ footerAction.label }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Actions slot -->\n\t\t<div v-if=\"$slots.actions && !isBoardLook\"\n\t\t\tclass=\"cn-object-card__actions\"\n\t\t\t@click.stop\n\t\t\t@auxclick.stop>\n\t\t\t<slot name=\"actions\" :object=\"object\" />\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { NcButton, NcCheckboxRadioSwitch } from '@nextcloud/vue'\nimport { useClickDragGuard } from '../../composables/useClickDragGuard.js'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { resolveImageUrl } from '../../utils/resolveImageUrl.js'\nimport { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'\nimport { formatValue } from '../../utils/schema.js'\nimport { CnCellRenderer } from '../CnCellRenderer/index.js'\nimport { CnIcon } from '../CnIcon/index.js'\nimport { CnLockIndicator } from '../CnLockIndicator/index.js'\nimport { CnStatusBadge } from '../CnStatusBadge/index.js'\n\nconst ACCENT_VARIANTS = ['success', 'warning', 'error', 'info', 'primary']\n\n/**\n * CnObjectCard — Schema-configuration-driven card for object display.\n *\n * Uses `schema.configuration` to determine which fields map to the card title,\n * description, and image. Remaining visible properties are shown as metadata.\n *\n * ```vue\n * <CnObjectCard :object=\"publication\" :schema=\"pubSchema\">\n * <template #actions=\"{ object }\">\n * <NcActions><NcActionButton @click=\"edit(object)\">Edit</NcActionButton></NcActions>\n * </template>\n * </CnObjectCard>\n * ```\n */\nexport default {\n\tname: 'CnObjectCard',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcCheckboxRadioSwitch,\n\t\tCnCellRenderer,\n\t\tCnIcon,\n\t\tCnLockIndicator,\n\t\tCnStatusBadge,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Consumer translation function, provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). Metadata\n\t\t * labels come from schema property titles, authored in English as the\n\t\t * canonical source; the visible label is resolved through this function\n\t\t * so it follows the user's language. Defaults to identity when used\n\t\t * standalone (no CnAppRoot ancestor).\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The look CnAppRoot provides; `board` draws the record card of the screens. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/** The object data */\n\t\tobject: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Schema definition with properties and configuration */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/** Whether this card is selected */\n\t\tselected: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether to show selection checkbox */\n\t\tselectable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * When true, a body click on a SELECTABLE card emits `click`\n\t\t * (navigation) instead of toggling selection — selection then happens\n\t\t * via the checkbox only. The card counterpart of CnIndexPage's\n\t\t * `rowClickToView` (a table row click already navigates in that mode).\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tclickToView: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the status pill at the end of the head row, as a label\n\t\t * (`\"Open\"`) or `{ label, variant }` with a CnStatusBadge variant\n\t\t * (`success`, `warning`, `error`, `info`, `primary`, `default`).\n\t\t * Without it the head row ends in the row menu, when there is one.\n\t\t *\n\t\t * @type {(string|{label: string, variant?: string})}\n\t\t * @spec openspec/changes/screens-card-parity/specs/card-board-look/spec.md#requirement-a-record-card-has-a-head-facts-and-one-action\n\t\t */\n\t\tstatus: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the leading element of the head row. `{ initials }`\n\t\t * draws the initials in a 36px circle; `{ icon }` a schema icon in a\n\t\t * 36px rounded square on a tint.\n\t\t *\n\t\t * @type {{initials?: string, icon?: string}}\n\t\t */\n\t\tleading: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Board look: the card's one footer action. `{ label, meta?,\n\t\t * ariaLabel? }`; `meta` is the 13px muted text at the start of the\n\t\t * footer. Clicking emits `footer-action`.\n\t\t *\n\t\t * @type {{label: string, meta?: string, ariaLabel?: string}}\n\t\t */\n\t\tfooterAction: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * A status accent: a colored border at the start of the card and,\n\t\t * with `icon`, a colored icon before the title. `variant` is one of\n\t\t * `success`, `warning`, `error`, `info` or `primary`; `icon` is a\n\t\t * name registered with CnIcon; `label` names the status for screen\n\t\t * readers and as the icon's tooltip.\n\t\t *\n\t\t * @type {{variant: string, icon?: string, label?: string}}\n\t\t */\n\t\taccent: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The property keys the facts list shows, in order (manifest key\n\t\t * `config.cardFields`). Without it the card shows the first\n\t\t * `maxMetadata` visible properties, as before.\n\t\t *\n\t\t * @type {Array<string>}\n\t\t */\n\t\tcardFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Maximum number of metadata fields to show */\n\t\tmaxMetadata: {\n\t\t\ttype: Number,\n\t\t\tdefault: 4,\n\t\t},\n\t},\n\n\temits: ['click', 'select', 'aux-click', 'footer-action'],\n\n\tsetup() {\n\t\t// Tell a deliberate card click apart from a text-selection drag.\n\t\treturn useClickDragGuard()\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the record card of the board look is drawn.\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisBoardLook() {\n\t\t\treturn normalizeLook(this.cnLook) === 'board'\n\t\t},\n\n\t\t/**\n\t\t * `initials`, `icon` or '' for the head row's leading element.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tleadingKind() {\n\t\t\tif (this.leading && typeof this.leading.initials === 'string' && this.leading.initials !== '') {\n\t\t\t\treturn 'initials'\n\t\t\t}\n\t\t\treturn this.leading && typeof this.leading.icon === 'string' && this.leading.icon !== '' ? 'icon' : ''\n\t\t},\n\n\t\t/**\n\t\t * The status as `{ label, variant }`, or null.\n\t\t *\n\t\t * @return {?{label: string, variant: string}}\n\t\t */\n\t\tstatusPill() {\n\t\t\tif (typeof this.status === 'string' && this.status !== '') {\n\t\t\t\treturn { label: this.status, variant: 'default' }\n\t\t\t}\n\t\t\tif (this.status && typeof this.status.label === 'string' && this.status.label !== '') {\n\t\t\t\treturn { label: this.status.label, variant: this.status.variant || 'default' }\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/**\n\t\t * The accent variant when it is a known one, else ''.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\taccentVariant() {\n\t\t\tconst variant = this.accent?.variant\n\t\t\treturn ACCENT_VARIANTS.includes(variant) ? variant : ''\n\t\t},\n\n\t\tconfig() {\n\t\t\treturn this.schema?.configuration || {}\n\t\t},\n\n\t\ttitle() {\n\t\t\tconst field = this.config.objectNameField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn String(this.object[field])\n\t\t\t}\n\t\t\treturn this.object.title || this.object.name || this.object.id || '—'\n\t\t},\n\n\t\tdescription() {\n\t\t\tconst field = this.config.objectDescriptionField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn String(this.object[field])\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\ttruncatedDescription() {\n\t\t\tif (!this.description) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tif (this.description.length > 120) {\n\t\t\t\treturn this.description.substring(0, 120) + '...'\n\t\t\t}\n\t\t\treturn this.description\n\t\t},\n\n\t\timageUrl() {\n\t\t\tconst field = this.config.objectImageField\n\t\t\tif (field && this.object[field]) {\n\t\t\t\treturn resolveImageUrl(this.object[field])\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/** Fields excluded from metadata (already shown as title/desc/image) */\n\t\tconfigFields() {\n\t\t\treturn [\n\t\t\t\tthis.config.objectNameField,\n\t\t\t\tthis.config.objectDescriptionField,\n\t\t\t\tthis.config.objectSummaryField,\n\t\t\t\tthis.config.objectImageField,\n\t\t\t].filter(Boolean)\n\t\t},\n\n\t\t/** Remaining visible properties for the metadata section */\n\t\tmetadataFields() {\n\t\t\tif (!this.schema?.properties) {\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\t// The page names the facts: those properties, in that order.\n\t\t\tif (Array.isArray(this.cardFields)) {\n\t\t\t\treturn this.cardFields\n\t\t\t\t\t.filter((key) => typeof key === 'string' && this.schema.properties[key] && !this.configFields.includes(key))\n\t\t\t\t\t.map((key) => ({\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: this.cnTranslate(this.schema.properties[key].title || key),\n\t\t\t\t\t\tvalue: this.object[key],\n\t\t\t\t\t\tproperty: this.schema.properties[key],\n\t\t\t\t\t}))\n\t\t\t}\n\n\t\t\treturn Object.entries(this.schema.properties)\n\t\t\t\t.filter(([key, prop]) => {\n\t\t\t\t\tif (this.configFields.includes(key)) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.visible === false) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.type === 'object') {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tif (prop.format === 'markdown') {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t})\n\t\t\t\t.sort(([, a], [, b]) => {\n\t\t\t\t\tconst orderA = typeof a.order === 'number' ? a.order : Infinity\n\t\t\t\t\tconst orderB = typeof b.order === 'number' ? b.order : Infinity\n\t\t\t\t\treturn orderA - orderB\n\t\t\t\t})\n\t\t\t\t.slice(0, this.maxMetadata)\n\t\t\t\t.map(([key, prop]) => ({\n\t\t\t\t\tkey,\n\t\t\t\t\tlabel: this.cnTranslate(prop.title || key),\n\t\t\t\t\tvalue: this.object[key],\n\t\t\t\t\tproperty: prop,\n\t\t\t\t}))\n\t\t},\n\t},\n\n\tmethods: {\n\t\tformatValue,\n\n\t\t/**\n\t\t * Card-body click: emits `select` when `selectable` (ignoring drags),\n\t\t * otherwise emits `click` for navigation. With `clickToView`, a\n\t\t * selectable card's body click navigates too — the checkbox is the\n\t\t * only selection surface, mirroring the table's rowClickToView split.\n\t\t *\n\t\t * @param {MouseEvent} [event] The originating click event.\n\t\t */\n\t\tonCardClick(event) {\n\t\t\tif (this.selectable && !this.clickToView) {\n\t\t\t\tif (this.wasDrag(event)) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * @event select Emitted when the card toggles selection (clicking the body of a selectable card, or its checkbox).\n\t\t\t\t * @type {object} The card's object.\n\t\t\t\t */\n\t\t\t\tthis.$emit('select', this.object)\n\t\t\t\t// Deprecation: selectable cards used to emit `click`. Keep emitting it\n\t\t\t\t// for listeners that still rely on it, but warn them to migrate to `select`.\n\t\t\t\t// `$.vnode.props`, not `$attrs`: `click` is a declared emit, and\n\t\t\t\t// Vue keeps declared emits out of `$attrs`.\n\t\t\t\tif (this.$.vnode.props?.onClick) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.warn('[CnObjectCard] @click on selectable cards is deprecated; use @select instead.')\n\t\t\t\t\tthis.$emit('click', this.object)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event click Emitted when a non-selectable (or `clickToView`) card is clicked. Payload: `(object, event)` — the card's object and the native click event, for opening it in a new tab on a ctrl/cmd/shift click. A middle click emits `aux-click` instead. Selectable cards emit `select`; they also emit `click` (deprecated) when a `click` listener is present, so migrate selectable consumers to `@select`.\n\t\t\t * @type {object} The card's object.\n\t\t\t */\n\t\t\tthis.$emit('click', this.object, event)\n\t\t},\n\n\t\t/**\n\t\t * Card mousedown: record the press for the drag guard, and on a card a\n\t\t * middle click opens, keep the browser from starting autoscroll.\n\t\t *\n\t\t * @param {MouseEvent} event The mousedown event.\n\t\t */\n\t\tonCardMouseDown(event) {\n\t\t\tthis.onPointerDown(event)\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tpreventMiddleClickAutoscroll(event)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Card-body middle click: emits `aux-click`, not `click`, so an existing\n\t\t * `click` listener that navigates never moves the current tab away.\n\t\t * Ignored on a select-on-click card, on nested controls and on drags.\n\t\t *\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t */\n\t\tonCardAuxClick(event) {\n\t\t\tif (!isRowMiddleClick(event) || this.wasDrag(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.selectable && !this.clickToView) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event aux-click Emitted when a non-selectable (or `clickToView`) card is middle-clicked, for opening it in a new tab (see `openRowTarget`). Payload: `(object, event)` — the card's object and the native auxclick event.\n\t\t\t * @type {object} The card's object.\n\t\t\t */\n\t\t\tthis.$emit('aux-click', this.object, event)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-object-card {\n\tdisplay: flex;\n\tgap: 12px;\n\tpadding: 16px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 10px);\n\tcursor: pointer;\n\ttransition: box-shadow 0.2s ease, border-color 0.2s ease;\n}\n\n.cn-object-card:hover {\n\tborder-color: var(--color-primary-element);\n\tbox-shadow: 0 2px 8px var(--color-box-shadow);\n}\n\n.cn-object-card--selected {\n\tborder-color: var(--color-primary-element);\n\tbackground: var(--color-primary-element-light);\n}\n\n/* The 4px start border takes 3px of padding, so the content stays put. */\n.cn-object-card.cn-object-card--accent {\n\tborder-inline-start-width: 4px;\n\tpadding-inline-start: 13px;\n}\n\n.cn-object-card.cn-object-card--accent-success {\n\tborder-inline-start-color: var(--color-element-success);\n}\n\n.cn-object-card.cn-object-card--accent-warning {\n\tborder-inline-start-color: var(--color-element-warning);\n}\n\n.cn-object-card.cn-object-card--accent-error {\n\tborder-inline-start-color: var(--color-element-error);\n}\n\n.cn-object-card.cn-object-card--accent-info {\n\tborder-inline-start-color: var(--color-element-info);\n}\n\n.cn-object-card.cn-object-card--accent-primary {\n\tborder-inline-start-color: var(--color-primary-element);\n}\n\n.cn-object-card__accent-icon {\n\tdisplay: inline-flex;\n\tvertical-align: -2px;\n\tmargin-inline-end: 4px;\n}\n\n.cn-object-card--accent-success .cn-object-card__accent-icon {\n\tcolor: var(--color-text-success);\n}\n\n.cn-object-card--accent-warning .cn-object-card__accent-icon {\n\tcolor: var(--color-text-warning);\n}\n\n.cn-object-card--accent-error .cn-object-card__accent-icon {\n\tcolor: var(--color-text-error);\n}\n\n.cn-object-card--accent-info .cn-object-card__accent-icon {\n\tcolor: var(--color-element-info);\n}\n\n.cn-object-card--accent-primary .cn-object-card__accent-icon {\n\tcolor: var(--color-primary-element);\n}\n\n.cn-object-card__checkbox {\n\tflex-shrink: 0;\n\tpadding-top: 2px;\n}\n\n.cn-object-card__content {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-object-card__header {\n\tdisplay: flex;\n\tgap: 12px;\n\talign-items: flex-start;\n}\n\n.cn-object-card__image {\n\twidth: 48px;\n\theight: 48px;\n\tborder-radius: var(--border-radius);\n\tobject-fit: cover;\n\tflex-shrink: 0;\n}\n\n.cn-object-card__title-area {\n\tflex: 1;\n\tmin-width: 0;\n}\n\n.cn-object-card__title {\n\tmargin: 0;\n\tfont-size: 16px;\n\tfont-weight: 600;\n\tline-height: 1.3;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n.cn-object-card__description {\n\tmargin: 4px 0 0;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n\tline-height: 1.4;\n}\n\n.cn-object-card__badges {\n\tmargin-top: 8px;\n}\n\n.cn-object-card__metadata {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n\tgap: 8px;\n\tmargin-top: 12px;\n\tpadding-top: 12px;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-object-card__meta-item {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n}\n\n.cn-object-card__meta-label {\n\tfont-size: 11px;\n\tfont-weight: 500;\n\tcolor: var(--color-text-maxcontrast);\n\ttext-transform: uppercase;\n\tletter-spacing: 0.3px;\n}\n\n.cn-object-card__actions {\n\tflex-shrink: 0;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createVNode","_createElementVNode","_Fragment","_createTextVNode","_toDisplayString","_createBlock","_renderSlot","_openBlock","_renderList"],"mappings":"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"}
|
|
@@ -71,7 +71,8 @@ var script = {
|
|
|
71
71
|
* Host translate function provided by CnAppRoot as
|
|
72
72
|
* `cnTranslate: this.translate` (bound to the host app's id). The
|
|
73
73
|
* manifest-authored `content.emptyText` is run through it for this
|
|
74
|
-
* component's OWN empty state, and so
|
|
74
|
+
* component's OWN empty state, and so are `content.prompt` and
|
|
75
|
+
* `content.addLabel`. Defaults to an identity function so
|
|
75
76
|
* an untranslated key renders as itself.
|
|
76
77
|
*/
|
|
77
78
|
cnTranslate: { default: () => (key) => key },
|
|
@@ -806,9 +807,24 @@ var script = {
|
|
|
806
807
|
return (c.formFieldOverrides && typeof c.formFieldOverrides === 'object') ? c.formFieldOverrides : {}
|
|
807
808
|
},
|
|
808
809
|
|
|
809
|
-
/**
|
|
810
|
+
/**
|
|
811
|
+
* The Add label: `content.addLabel` through the app's translate
|
|
812
|
+
* function, else the library's "Add".
|
|
813
|
+
*
|
|
814
|
+
* @spec openspec/changes/r4-object-lock-url-and-credentials-copy/specs/object-list-add-label/spec.md
|
|
815
|
+
* @return {string}
|
|
816
|
+
*/
|
|
810
817
|
addLabel() {
|
|
811
|
-
|
|
818
|
+
// `content.addLabel` is manifest copy in the APP's catalogue, so it
|
|
819
|
+
// goes through the host translate function like `emptyText` and
|
|
820
|
+
// `prompt`. It used to print raw, so a Dutch user read the English
|
|
821
|
+
// manifest string. A label an app already translated has no key
|
|
822
|
+
// of its own in the catalogue and comes back unchanged.
|
|
823
|
+
if (this.content.addLabel) {
|
|
824
|
+
const fn = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k;
|
|
825
|
+
return fn(this.content.addLabel)
|
|
826
|
+
}
|
|
827
|
+
return translate('nextcloud-vue', 'Add')
|
|
812
828
|
},
|
|
813
829
|
|
|
814
830
|
/** Stable signature of the query so the watcher only refetches on real change. */
|