@conduction/nextcloud-vue 2.56.0-beta.59 → 2.56.0-beta.60
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.vue.js +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +18 -0
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +3 -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 +18 -0
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +53 -4
- 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/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js +9 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js.map +1 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js +24 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js.map +1 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js +49 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js.map +1 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +36 -18
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +17 -38
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +53 -2
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +119 -111
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue.js +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +62 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +159 -50
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +121 -98
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +24 -0
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +20 -4
- package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js +22 -4
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +112 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +100 -44
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue.js +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js +67 -4
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js +1 -1
- package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue.js +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +295 -12
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +267 -144
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +4 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +3 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +93 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +322 -246
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue.js +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +122 -7
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js +189 -93
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue.js +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +42 -0
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +38 -19
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue.js +9 -0
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue.js.map +1 -0
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue2.js +258 -0
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue3.js +69 -0
- package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.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 +21 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +21 -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 +803 -453
- package/dist/esm/utils/dueText.js +18 -3
- package/dist/esm/utils/dueText.js.map +1 -1
- package/dist/esm/utils/libraryWidgetKeys.js +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/sortOrder.js +11 -1
- package/dist/esm/utils/sortOrder.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 +8281 -6350
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +803 -453
- package/l10n/en.json +21 -1
- package/l10n/nl.json +21 -1
- package/package.json +1 -1
- package/src/components/CnCardGrid/CnCardGrid.md +2 -0
- package/src/components/CnCardGrid/CnCardGrid.vue +20 -0
- package/src/components/CnCellRenderer/CnCellRenderer.vue +19 -1
- package/src/components/CnDetailPage/CnDetailPage.md +2 -2
- package/src/components/CnDetailPage/CnDetailPage.vue +53 -4
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue +60 -0
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +36 -29
- package/src/components/CnFactsWidget/CnFactsWidget.vue +4 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +70 -4
- package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +62 -1
- package/src/components/CnIndexPage/CnIndexPage.md +7 -5
- package/src/components/CnIndexPage/CnIndexPage.vue +187 -58
- package/src/components/CnIndexPage/CnIndexPageWidgets.vue +36 -1
- package/src/components/CnIndexPage/useSelfFetchList.js +22 -4
- package/src/components/CnIndexSidebar/CnIndexSidebar.md +2 -0
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +148 -1
- package/src/components/CnNcWidgetWidget/CnNcWidgetWidget.vue +68 -5
- package/src/components/CnObjectCard/CnObjectCard.md +1 -0
- package/src/components/CnObjectCard/CnObjectCard.vue +354 -13
- package/src/components/CnObjectSidebar/CnFilesTab.vue +6 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +190 -4
- package/src/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +209 -9
- package/src/components/CnTabs/CnTabs.vue +11 -0
- package/src/components/CnTabsWidget/CnTabsWidget.vue +91 -18
- package/src/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.md +11 -0
- package/src/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue +382 -0
- package/src/components/CnTeamWorkloadWidget/index.js +14 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
- package/src/components/index.js +1 -0
- package/src/css/index-page.css +14 -2
- package/src/css/look-board-detail.css +48 -4
- package/src/css/look-board-index.css +64 -2
- package/src/css/look-board.css +25 -2
- package/src/index.js +1 -1
- package/src/schemas/app-manifest-v2.schema.json +75 -6
- package/src/utils/dueText.js +18 -3
- package/src/utils/libraryWidgetKeys.js +1 -0
- package/src/utils/sortOrder.js +10 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -107,6 +107,24 @@ var script = {
|
|
|
107
107
|
default: null,
|
|
108
108
|
},
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Board look: the record layout each card draws (manifest key
|
|
112
|
+
* `config.cardLayout`), passed through to CnObjectCard `layout`.
|
|
113
|
+
*
|
|
114
|
+
* @type {object}
|
|
115
|
+
* @spec openspec/changes/board-record-card-layout/specs/board-record-card-layout/spec.md#requirement-a-board-card-can-draw-the-record-layout-from-config
|
|
116
|
+
*/
|
|
117
|
+
cardLayout: {
|
|
118
|
+
type: Object,
|
|
119
|
+
default: null,
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
/** The page's columns by key, passed through to CnObjectCard. */
|
|
123
|
+
cardFieldColumns: {
|
|
124
|
+
type: Object,
|
|
125
|
+
default: null,
|
|
126
|
+
},
|
|
127
|
+
|
|
110
128
|
/** Board look: `(object) => status` for each card's status pill. See CnObjectCard `status`. */
|
|
111
129
|
statusOf: {
|
|
112
130
|
type: Function,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnCardGrid.vue2.js","sources":["../../../../src/components/CnCardGrid/CnCardGrid.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-card-grid\">\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-card-grid__loading\">\n\t\t\t<NcLoadingIcon :size=\"32\" />\n\t\t</div>\n\n\t\t<!-- Empty state -->\n\t\t<div v-else-if=\"objects.length === 0\" class=\"cn-card-grid__empty\">\n\t\t\t<slot name=\"empty\">\n\t\t\t\t<CnEmptyContent :name=\"resolvedEmptyText\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ViewGrid :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</CnEmptyContent>\n\t\t\t</slot>\n\t\t</div>\n\n\t\t<!-- Card grid -->\n\t\t<div v-else\n\t\t\tclass=\"cn-card-grid__grid\"\n\t\t\t:class=\"{ 'cn-card-grid__grid--board': isBoardLook }\"\n\t\t\t:style=\"gridStyle\">\n\t\t\t<div\n\t\t\t\tv-for=\"object in objects\"\n\t\t\t\t:key=\"object[rowKey]\"\n\t\t\t\tclass=\"cn-card-grid__item\">\n\t\t\t\t<slot\n\t\t\t\t\tname=\"card\"\n\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t:schema=\"schema\">\n\t\t\t\t\t<CnObjectCard\n\t\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t\t:schema=\"schema\"\n\t\t\t\t\t\t:selectable=\"selectable\"\n\t\t\t\t\t\t:clickToView=\"clickToView\"\n\t\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t\t:cardFields=\"cardFields\"\n\t\t\t\t\t\t:status=\"statusOf ? statusOf(object) : null\"\n\t\t\t\t\t\t:leading=\"leadingOf ? leadingOf(object) : null\"\n\t\t\t\t\t\t:footerAction=\"footerActionOf ? footerActionOf(object) : null\"\n\t\t\t\t\t\t:accent=\"accentOf ? accentOf(object) : null\"\n\t\t\t\t\t\tv-on=\"cardListeners(object)\">\n\t\t\t\t\t\t<template v-if=\"$slots['card-actions']\" #actions=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-actions\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"$slots['card-badges']\" #badges=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-badges\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</CnObjectCard>\n\t\t\t\t</slot>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ViewGrid from 'vue-material-design-icons/ViewGrid.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { CnObjectCard } from '../CnObjectCard/index.js'\n\n/**\n * CnCardGrid — Responsive grid container for CnObjectCard instances.\n *\n * Displays objects in a responsive CSS grid layout using schema-driven cards.\n * Supports selection, loading/empty states, and custom card rendering via slots.\n *\n * ```vue\n * <CnCardGrid\n * :objects=\"publications\"\n * :schema=\"pubSchema\"\n * :selectable=\"true\"\n * :selected-ids=\"selectedIds\"\n * @click=\"openPublication\"\n * @select=\"onSelect\">\n * <template #card-actions=\"{ object }\">\n * <NcActions><NcActionButton>Edit</NcActionButton></NcActions>\n * </template>\n * </CnCardGrid>\n * ```\n */\nexport default {\n\tname: 'CnCardGrid',\n\n\tcomponents: {\n\t\tNcLoadingIcon,\n\t\tCnEmptyContent,\n\t\tViewGrid,\n\t\tCnObjectCard,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). The\n\t\t * manifest-authored empty-state copy is run through it. Defaults to\n\t\t * an identity function so an untranslated key renders as itself.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The look CnAppRoot provides; `board` lays the cards out on the board track. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/** Array of objects to display as cards */\n\t\tobjects: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Schema definition (passed through to CnObjectCard) */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether data is loading */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether cards can be selected */\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) and selection happens via the checkbox only — the\n\t\t * card-grid counterpart of CnIndexPage's `rowClickToView`.\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/** Array of currently selected object IDs */\n\t\tselectedIds: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Property name used as unique identifier */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * The property keys each card's facts list shows (manifest key\n\t\t * `config.cardFields`), passed through to CnObjectCard.\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/** Board look: `(object) => status` for each card's status pill. See CnObjectCard `status`. */\n\t\tstatusOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Board look: `(object) => { initials | icon }` for each card's leading element. See CnObjectCard `leading`. */\n\t\tleadingOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Board look: `(object) => { label, meta? }` for each card's footer action. See CnObjectCard `footerAction`. */\n\t\tfooterActionOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** `(object) => { variant, icon?, label? }` for each card's status accent. See CnObjectCard `accent`. */\n\t\taccentOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Text shown when there are no objects */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No items found'),\n\t\t},\n\t},\n\n\temits: ['click', 'select', 'aux-click'],\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the cards lie on the board look's track.\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 * Under the board look the grid is `minmax(var(--cn-card-grid-min,\n\t\t * 260px), 1fr)` with a `var(--cn-card-grid-gap, 16px)` gap, theme\n\t\t * hooks for both; without it the style is empty and the stylesheet's\n\t\t * 320px track stands.\n\t\t *\n\t\t * @spec openspec/changes/screens-card-parity/specs/card-board-look/spec.md#requirement-the-card-grid-takes-the-board-track\n\t\t * @return {object}\n\t\t */\n\t\tgridStyle() {\n\t\t\tif (!this.isBoardLook) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tgridTemplateColumns: 'repeat(auto-fill, minmax(var(--cn-card-grid-min, 260px), 1fr))',\n\t\t\t\tgap: 'var(--cn-card-grid-gap, 16px)',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The empty-state copy run through the host translate function.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedEmptyText() {\n\t\t\tconst fn = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\treturn this.emptyText ? fn(this.emptyText) : this.emptyText\n\t\t},\n\t},\n\n\tmethods: {\n\t\tisSelected(object) {\n\t\t\treturn this.selectedIds.includes(object[this.rowKey])\n\t\t},\n\n\t\t/**\n\t\t * Listeners bound on each CnObjectCard. Selectable cards select via\n\t\t * `@select` only; the `@click` (navigation) listener is bound for\n\t\t * non-selectable cards — otherwise CnObjectCard's deprecated\n\t\t * click-to-select path fires (a body click would both select AND\n\t\t * navigate, unlike a table row which only selects) — and for\n\t\t * `clickToView` cards, where a body click navigates and the checkbox\n\t\t * is the only selection surface.\n\t\t *\n\t\t * @param {object} object The card's object.\n\t\t * @return {object} Event listeners for the card.\n\t\t */\n\t\tcardListeners(object) {\n\t\t\tconst listeners = { select: () => this.toggleSelect(object) }\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tlisteners.click = (_card, event) => this.$emit('click', object, event)\n\t\t\t\t// A middle click, kept off `click` so a navigating listener stays put.\n\t\t\t\tlisteners['aux-click'] = (_card, event) => {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event aux-click Emitted when a navigating 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\t\t * @type {object} The card's object.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('aux-click', object, event)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn listeners\n\t\t},\n\n\t\ttoggleSelect(object) {\n\t\t\tconst id = object[this.rowKey]\n\t\t\tconst newIds = this.isSelected(object)\n\t\t\t\t? this.selectedIds.filter((i) => i !== id)\n\t\t\t\t: [...this.selectedIds, id]\n\t\t\tthis.$emit('select', newIds)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-card-grid__grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n\tgap: 16px;\n}\n\n/* A card slot whose component renders nothing (e.g. a custom card that hides\n itself with `v-if` for a client-side filter) leaves an empty grid item that\n would still occupy a grid track and a row gap — pushing the visible cards\n down with a large blank gap above them. Collapse those empty cells so the\n remaining cards start at the top. A comment-only node (`v-if` false) still\n matches `:empty` per the CSS spec, so this catches the v-if-hidden case. */\n.cn-card-grid__item:empty {\n\tdisplay: none;\n}\n\n.cn-card-grid__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 40px;\n}\n\n.cn-card-grid__empty {\n\tpadding: 40px 20px;\n\ttext-align: center;\n}\n</style>\n"],"names":["CnEmptyContent","CnObjectCard","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnCardGrid.vue2.js","sources":["../../../../src/components/CnCardGrid/CnCardGrid.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-card-grid\">\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-card-grid__loading\">\n\t\t\t<NcLoadingIcon :size=\"32\" />\n\t\t</div>\n\n\t\t<!-- Empty state -->\n\t\t<div v-else-if=\"objects.length === 0\" class=\"cn-card-grid__empty\">\n\t\t\t<slot name=\"empty\">\n\t\t\t\t<CnEmptyContent :name=\"resolvedEmptyText\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ViewGrid :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</CnEmptyContent>\n\t\t\t</slot>\n\t\t</div>\n\n\t\t<!-- Card grid -->\n\t\t<div v-else\n\t\t\tclass=\"cn-card-grid__grid\"\n\t\t\t:class=\"{ 'cn-card-grid__grid--board': isBoardLook }\"\n\t\t\t:style=\"gridStyle\">\n\t\t\t<div\n\t\t\t\tv-for=\"object in objects\"\n\t\t\t\t:key=\"object[rowKey]\"\n\t\t\t\tclass=\"cn-card-grid__item\">\n\t\t\t\t<slot\n\t\t\t\t\tname=\"card\"\n\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t:schema=\"schema\">\n\t\t\t\t\t<CnObjectCard\n\t\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t\t:schema=\"schema\"\n\t\t\t\t\t\t:selectable=\"selectable\"\n\t\t\t\t\t\t:clickToView=\"clickToView\"\n\t\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t\t:cardFields=\"cardFields\"\n\t\t\t\t\t\t:layout=\"cardLayout\"\n\t\t\t\t\t\t:cardFieldColumns=\"cardFieldColumns\"\n\t\t\t\t\t\t:status=\"statusOf ? statusOf(object) : null\"\n\t\t\t\t\t\t:leading=\"leadingOf ? leadingOf(object) : null\"\n\t\t\t\t\t\t:footerAction=\"footerActionOf ? footerActionOf(object) : null\"\n\t\t\t\t\t\t:accent=\"accentOf ? accentOf(object) : null\"\n\t\t\t\t\t\tv-on=\"cardListeners(object)\">\n\t\t\t\t\t\t<template v-if=\"$slots['card-actions']\" #actions=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-actions\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"$slots['card-badges']\" #badges=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-badges\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</CnObjectCard>\n\t\t\t\t</slot>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ViewGrid from 'vue-material-design-icons/ViewGrid.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { CnObjectCard } from '../CnObjectCard/index.js'\n\n/**\n * CnCardGrid — Responsive grid container for CnObjectCard instances.\n *\n * Displays objects in a responsive CSS grid layout using schema-driven cards.\n * Supports selection, loading/empty states, and custom card rendering via slots.\n *\n * ```vue\n * <CnCardGrid\n * :objects=\"publications\"\n * :schema=\"pubSchema\"\n * :selectable=\"true\"\n * :selected-ids=\"selectedIds\"\n * @click=\"openPublication\"\n * @select=\"onSelect\">\n * <template #card-actions=\"{ object }\">\n * <NcActions><NcActionButton>Edit</NcActionButton></NcActions>\n * </template>\n * </CnCardGrid>\n * ```\n */\nexport default {\n\tname: 'CnCardGrid',\n\n\tcomponents: {\n\t\tNcLoadingIcon,\n\t\tCnEmptyContent,\n\t\tViewGrid,\n\t\tCnObjectCard,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). The\n\t\t * manifest-authored empty-state copy is run through it. Defaults to\n\t\t * an identity function so an untranslated key renders as itself.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The look CnAppRoot provides; `board` lays the cards out on the board track. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/** Array of objects to display as cards */\n\t\tobjects: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Schema definition (passed through to CnObjectCard) */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether data is loading */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether cards can be selected */\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) and selection happens via the checkbox only — the\n\t\t * card-grid counterpart of CnIndexPage's `rowClickToView`.\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/** Array of currently selected object IDs */\n\t\tselectedIds: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Property name used as unique identifier */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * The property keys each card's facts list shows (manifest key\n\t\t * `config.cardFields`), passed through to CnObjectCard.\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/**\n\t\t * Board look: the record layout each card draws (manifest key\n\t\t * `config.cardLayout`), passed through to CnObjectCard `layout`.\n\t\t *\n\t\t * @type {object}\n\t\t * @spec openspec/changes/board-record-card-layout/specs/board-record-card-layout/spec.md#requirement-a-board-card-can-draw-the-record-layout-from-config\n\t\t */\n\t\tcardLayout: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The page's columns by key, passed through to CnObjectCard. */\n\t\tcardFieldColumns: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Board look: `(object) => status` for each card's status pill. See CnObjectCard `status`. */\n\t\tstatusOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Board look: `(object) => { initials | icon }` for each card's leading element. See CnObjectCard `leading`. */\n\t\tleadingOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Board look: `(object) => { label, meta? }` for each card's footer action. See CnObjectCard `footerAction`. */\n\t\tfooterActionOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** `(object) => { variant, icon?, label? }` for each card's status accent. See CnObjectCard `accent`. */\n\t\taccentOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Text shown when there are no objects */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No items found'),\n\t\t},\n\t},\n\n\temits: ['click', 'select', 'aux-click'],\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the cards lie on the board look's track.\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 * Under the board look the grid is `minmax(var(--cn-card-grid-min,\n\t\t * 260px), 1fr)` with a `var(--cn-card-grid-gap, 16px)` gap, theme\n\t\t * hooks for both; without it the style is empty and the stylesheet's\n\t\t * 320px track stands.\n\t\t *\n\t\t * @spec openspec/changes/screens-card-parity/specs/card-board-look/spec.md#requirement-the-card-grid-takes-the-board-track\n\t\t * @return {object}\n\t\t */\n\t\tgridStyle() {\n\t\t\tif (!this.isBoardLook) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tgridTemplateColumns: 'repeat(auto-fill, minmax(var(--cn-card-grid-min, 260px), 1fr))',\n\t\t\t\tgap: 'var(--cn-card-grid-gap, 16px)',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The empty-state copy run through the host translate function.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedEmptyText() {\n\t\t\tconst fn = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\treturn this.emptyText ? fn(this.emptyText) : this.emptyText\n\t\t},\n\t},\n\n\tmethods: {\n\t\tisSelected(object) {\n\t\t\treturn this.selectedIds.includes(object[this.rowKey])\n\t\t},\n\n\t\t/**\n\t\t * Listeners bound on each CnObjectCard. Selectable cards select via\n\t\t * `@select` only; the `@click` (navigation) listener is bound for\n\t\t * non-selectable cards — otherwise CnObjectCard's deprecated\n\t\t * click-to-select path fires (a body click would both select AND\n\t\t * navigate, unlike a table row which only selects) — and for\n\t\t * `clickToView` cards, where a body click navigates and the checkbox\n\t\t * is the only selection surface.\n\t\t *\n\t\t * @param {object} object The card's object.\n\t\t * @return {object} Event listeners for the card.\n\t\t */\n\t\tcardListeners(object) {\n\t\t\tconst listeners = { select: () => this.toggleSelect(object) }\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tlisteners.click = (_card, event) => this.$emit('click', object, event)\n\t\t\t\t// A middle click, kept off `click` so a navigating listener stays put.\n\t\t\t\tlisteners['aux-click'] = (_card, event) => {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event aux-click Emitted when a navigating 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\t\t * @type {object} The card's object.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('aux-click', object, event)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn listeners\n\t\t},\n\n\t\ttoggleSelect(object) {\n\t\t\tconst id = object[this.rowKey]\n\t\t\tconst newIds = this.isSelected(object)\n\t\t\t\t? this.selectedIds.filter((i) => i !== id)\n\t\t\t\t: [...this.selectedIds, id]\n\t\t\tthis.$emit('select', newIds)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-card-grid__grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n\tgap: 16px;\n}\n\n/* A card slot whose component renders nothing (e.g. a custom card that hides\n itself with `v-if` for a client-side filter) leaves an empty grid item that\n would still occupy a grid track and a row gap — pushing the visible cards\n down with a large blank gap above them. Collapse those empty cells so the\n remaining cards start at the top. A comment-only node (`v-if` false) still\n matches `:empty` per the CSS spec, so this catches the v-if-hidden case. */\n.cn-card-grid__item:empty {\n\tdisplay: none;\n}\n\n.cn-card-grid__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 40px;\n}\n\n.cn-card-grid__empty {\n\tpadding: 40px 20px;\n\ttext-align: center;\n}\n</style>\n"],"names":["CnEmptyContent","CnObjectCard","t"],"mappings":"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"}
|
|
@@ -54,6 +54,8 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
54
54
|
clickToView: $props.clickToView,
|
|
55
55
|
selected: $options.isSelected(object),
|
|
56
56
|
cardFields: $props.cardFields,
|
|
57
|
+
layout: $props.cardLayout,
|
|
58
|
+
cardFieldColumns: $props.cardFieldColumns,
|
|
57
59
|
status: $props.statusOf ? $props.statusOf(object) : null,
|
|
58
60
|
leading: $props.leadingOf ? $props.leadingOf(object) : null,
|
|
59
61
|
footerAction: $props.footerActionOf ? $props.footerActionOf(object) : null,
|
|
@@ -77,7 +79,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
77
79
|
key: "1"
|
|
78
80
|
}
|
|
79
81
|
: undefined
|
|
80
|
-
]), 1040 /* FULL_PROPS, DYNAMIC_SLOTS */, ["object", "schema", "selectable", "clickToView", "selected", "cardFields", "status", "leading", "footerAction", "accent"])
|
|
82
|
+
]), 1040 /* FULL_PROPS, DYNAMIC_SLOTS */, ["object", "schema", "selectable", "clickToView", "selected", "cardFields", "layout", "cardFieldColumns", "status", "leading", "footerAction", "accent"])
|
|
81
83
|
])
|
|
82
84
|
]))
|
|
83
85
|
}), 128 /* KEYED_FRAGMENT */))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnCardGrid.vue3.js","sources":["../../../../src/components/CnCardGrid/CnCardGrid.vue?vue&type=template&id=014eda35&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-card-grid\">\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-card-grid__loading\">\n\t\t\t<NcLoadingIcon :size=\"32\" />\n\t\t</div>\n\n\t\t<!-- Empty state -->\n\t\t<div v-else-if=\"objects.length === 0\" class=\"cn-card-grid__empty\">\n\t\t\t<slot name=\"empty\">\n\t\t\t\t<CnEmptyContent :name=\"resolvedEmptyText\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ViewGrid :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</CnEmptyContent>\n\t\t\t</slot>\n\t\t</div>\n\n\t\t<!-- Card grid -->\n\t\t<div v-else\n\t\t\tclass=\"cn-card-grid__grid\"\n\t\t\t:class=\"{ 'cn-card-grid__grid--board': isBoardLook }\"\n\t\t\t:style=\"gridStyle\">\n\t\t\t<div\n\t\t\t\tv-for=\"object in objects\"\n\t\t\t\t:key=\"object[rowKey]\"\n\t\t\t\tclass=\"cn-card-grid__item\">\n\t\t\t\t<slot\n\t\t\t\t\tname=\"card\"\n\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t:schema=\"schema\">\n\t\t\t\t\t<CnObjectCard\n\t\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t\t:schema=\"schema\"\n\t\t\t\t\t\t:selectable=\"selectable\"\n\t\t\t\t\t\t:clickToView=\"clickToView\"\n\t\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t\t:cardFields=\"cardFields\"\n\t\t\t\t\t\t:status=\"statusOf ? statusOf(object) : null\"\n\t\t\t\t\t\t:leading=\"leadingOf ? leadingOf(object) : null\"\n\t\t\t\t\t\t:footerAction=\"footerActionOf ? footerActionOf(object) : null\"\n\t\t\t\t\t\t:accent=\"accentOf ? accentOf(object) : null\"\n\t\t\t\t\t\tv-on=\"cardListeners(object)\">\n\t\t\t\t\t\t<template v-if=\"$slots['card-actions']\" #actions=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-actions\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"$slots['card-badges']\" #badges=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-badges\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</CnObjectCard>\n\t\t\t\t</slot>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ViewGrid from 'vue-material-design-icons/ViewGrid.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { CnObjectCard } from '../CnObjectCard/index.js'\n\n/**\n * CnCardGrid — Responsive grid container for CnObjectCard instances.\n *\n * Displays objects in a responsive CSS grid layout using schema-driven cards.\n * Supports selection, loading/empty states, and custom card rendering via slots.\n *\n * ```vue\n * <CnCardGrid\n * :objects=\"publications\"\n * :schema=\"pubSchema\"\n * :selectable=\"true\"\n * :selected-ids=\"selectedIds\"\n * @click=\"openPublication\"\n * @select=\"onSelect\">\n * <template #card-actions=\"{ object }\">\n * <NcActions><NcActionButton>Edit</NcActionButton></NcActions>\n * </template>\n * </CnCardGrid>\n * ```\n */\nexport default {\n\tname: 'CnCardGrid',\n\n\tcomponents: {\n\t\tNcLoadingIcon,\n\t\tCnEmptyContent,\n\t\tViewGrid,\n\t\tCnObjectCard,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). The\n\t\t * manifest-authored empty-state copy is run through it. Defaults to\n\t\t * an identity function so an untranslated key renders as itself.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The look CnAppRoot provides; `board` lays the cards out on the board track. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/** Array of objects to display as cards */\n\t\tobjects: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Schema definition (passed through to CnObjectCard) */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether data is loading */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether cards can be selected */\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) and selection happens via the checkbox only — the\n\t\t * card-grid counterpart of CnIndexPage's `rowClickToView`.\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/** Array of currently selected object IDs */\n\t\tselectedIds: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Property name used as unique identifier */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * The property keys each card's facts list shows (manifest key\n\t\t * `config.cardFields`), passed through to CnObjectCard.\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/** Board look: `(object) => status` for each card's status pill. See CnObjectCard `status`. */\n\t\tstatusOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Board look: `(object) => { initials | icon }` for each card's leading element. See CnObjectCard `leading`. */\n\t\tleadingOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Board look: `(object) => { label, meta? }` for each card's footer action. See CnObjectCard `footerAction`. */\n\t\tfooterActionOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** `(object) => { variant, icon?, label? }` for each card's status accent. See CnObjectCard `accent`. */\n\t\taccentOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Text shown when there are no objects */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No items found'),\n\t\t},\n\t},\n\n\temits: ['click', 'select', 'aux-click'],\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the cards lie on the board look's track.\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 * Under the board look the grid is `minmax(var(--cn-card-grid-min,\n\t\t * 260px), 1fr)` with a `var(--cn-card-grid-gap, 16px)` gap, theme\n\t\t * hooks for both; without it the style is empty and the stylesheet's\n\t\t * 320px track stands.\n\t\t *\n\t\t * @spec openspec/changes/screens-card-parity/specs/card-board-look/spec.md#requirement-the-card-grid-takes-the-board-track\n\t\t * @return {object}\n\t\t */\n\t\tgridStyle() {\n\t\t\tif (!this.isBoardLook) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tgridTemplateColumns: 'repeat(auto-fill, minmax(var(--cn-card-grid-min, 260px), 1fr))',\n\t\t\t\tgap: 'var(--cn-card-grid-gap, 16px)',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The empty-state copy run through the host translate function.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedEmptyText() {\n\t\t\tconst fn = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\treturn this.emptyText ? fn(this.emptyText) : this.emptyText\n\t\t},\n\t},\n\n\tmethods: {\n\t\tisSelected(object) {\n\t\t\treturn this.selectedIds.includes(object[this.rowKey])\n\t\t},\n\n\t\t/**\n\t\t * Listeners bound on each CnObjectCard. Selectable cards select via\n\t\t * `@select` only; the `@click` (navigation) listener is bound for\n\t\t * non-selectable cards — otherwise CnObjectCard's deprecated\n\t\t * click-to-select path fires (a body click would both select AND\n\t\t * navigate, unlike a table row which only selects) — and for\n\t\t * `clickToView` cards, where a body click navigates and the checkbox\n\t\t * is the only selection surface.\n\t\t *\n\t\t * @param {object} object The card's object.\n\t\t * @return {object} Event listeners for the card.\n\t\t */\n\t\tcardListeners(object) {\n\t\t\tconst listeners = { select: () => this.toggleSelect(object) }\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tlisteners.click = (_card, event) => this.$emit('click', object, event)\n\t\t\t\t// A middle click, kept off `click` so a navigating listener stays put.\n\t\t\t\tlisteners['aux-click'] = (_card, event) => {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event aux-click Emitted when a navigating 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\t\t * @type {object} The card's object.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('aux-click', object, event)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn listeners\n\t\t},\n\n\t\ttoggleSelect(object) {\n\t\t\tconst id = object[this.rowKey]\n\t\t\tconst newIds = this.isSelected(object)\n\t\t\t\t? this.selectedIds.filter((i) => i !== id)\n\t\t\t\t: [...this.selectedIds, id]\n\t\t\tthis.$emit('select', newIds)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-card-grid__grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n\tgap: 16px;\n}\n\n/* A card slot whose component renders nothing (e.g. a custom card that hides\n itself with `v-if` for a client-side filter) leaves an empty grid item that\n would still occupy a grid track and a row gap — pushing the visible cards\n down with a large blank gap above them. Collapse those empty cells so the\n remaining cards start at the top. A comment-only node (`v-if` false) still\n matches `:empty` per the CSS spec, so this catches the v-if-hidden case. */\n.cn-card-grid__item:empty {\n\tdisplay: none;\n}\n\n.cn-card-grid__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 40px;\n}\n\n.cn-card-grid__empty {\n\tpadding: 40px 20px;\n\ttext-align: center;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_renderSlot","_normalizeClass","_Fragment","_renderList","_mergeProps","_toHandlers","_createSlots","_withCtx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnCardGrid.vue3.js","sources":["../../../../src/components/CnCardGrid/CnCardGrid.vue?vue&type=template&id=1c5a3f43&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-card-grid\">\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-card-grid__loading\">\n\t\t\t<NcLoadingIcon :size=\"32\" />\n\t\t</div>\n\n\t\t<!-- Empty state -->\n\t\t<div v-else-if=\"objects.length === 0\" class=\"cn-card-grid__empty\">\n\t\t\t<slot name=\"empty\">\n\t\t\t\t<CnEmptyContent :name=\"resolvedEmptyText\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ViewGrid :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</CnEmptyContent>\n\t\t\t</slot>\n\t\t</div>\n\n\t\t<!-- Card grid -->\n\t\t<div v-else\n\t\t\tclass=\"cn-card-grid__grid\"\n\t\t\t:class=\"{ 'cn-card-grid__grid--board': isBoardLook }\"\n\t\t\t:style=\"gridStyle\">\n\t\t\t<div\n\t\t\t\tv-for=\"object in objects\"\n\t\t\t\t:key=\"object[rowKey]\"\n\t\t\t\tclass=\"cn-card-grid__item\">\n\t\t\t\t<slot\n\t\t\t\t\tname=\"card\"\n\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t:schema=\"schema\">\n\t\t\t\t\t<CnObjectCard\n\t\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t\t:schema=\"schema\"\n\t\t\t\t\t\t:selectable=\"selectable\"\n\t\t\t\t\t\t:clickToView=\"clickToView\"\n\t\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t\t:cardFields=\"cardFields\"\n\t\t\t\t\t\t:layout=\"cardLayout\"\n\t\t\t\t\t\t:cardFieldColumns=\"cardFieldColumns\"\n\t\t\t\t\t\t:status=\"statusOf ? statusOf(object) : null\"\n\t\t\t\t\t\t:leading=\"leadingOf ? leadingOf(object) : null\"\n\t\t\t\t\t\t:footerAction=\"footerActionOf ? footerActionOf(object) : null\"\n\t\t\t\t\t\t:accent=\"accentOf ? accentOf(object) : null\"\n\t\t\t\t\t\tv-on=\"cardListeners(object)\">\n\t\t\t\t\t\t<template v-if=\"$slots['card-actions']\" #actions=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-actions\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"$slots['card-badges']\" #badges=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-badges\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</CnObjectCard>\n\t\t\t\t</slot>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcLoadingIcon } from '@nextcloud/vue'\nimport ViewGrid from 'vue-material-design-icons/ViewGrid.vue'\nimport CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'\nimport { normalizeLook } from '../../composables/useLook.js'\nimport { CnObjectCard } from '../CnObjectCard/index.js'\n\n/**\n * CnCardGrid — Responsive grid container for CnObjectCard instances.\n *\n * Displays objects in a responsive CSS grid layout using schema-driven cards.\n * Supports selection, loading/empty states, and custom card rendering via slots.\n *\n * ```vue\n * <CnCardGrid\n * :objects=\"publications\"\n * :schema=\"pubSchema\"\n * :selectable=\"true\"\n * :selected-ids=\"selectedIds\"\n * @click=\"openPublication\"\n * @select=\"onSelect\">\n * <template #card-actions=\"{ object }\">\n * <NcActions><NcActionButton>Edit</NcActionButton></NcActions>\n * </template>\n * </CnCardGrid>\n * ```\n */\nexport default {\n\tname: 'CnCardGrid',\n\n\tcomponents: {\n\t\tNcLoadingIcon,\n\t\tCnEmptyContent,\n\t\tViewGrid,\n\t\tCnObjectCard,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). The\n\t\t * manifest-authored empty-state copy is run through it. Defaults to\n\t\t * an identity function so an untranslated key renders as itself.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The look CnAppRoot provides; `board` lays the cards out on the board track. */\n\t\tcnLook: { default: 'nextcloud' },\n\t},\n\n\tprops: {\n\t\t/** Array of objects to display as cards */\n\t\tobjects: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Schema definition (passed through to CnObjectCard) */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether data is loading */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether cards can be selected */\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) and selection happens via the checkbox only — the\n\t\t * card-grid counterpart of CnIndexPage's `rowClickToView`.\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/** Array of currently selected object IDs */\n\t\tselectedIds: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Property name used as unique identifier */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * The property keys each card's facts list shows (manifest key\n\t\t * `config.cardFields`), passed through to CnObjectCard.\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/**\n\t\t * Board look: the record layout each card draws (manifest key\n\t\t * `config.cardLayout`), passed through to CnObjectCard `layout`.\n\t\t *\n\t\t * @type {object}\n\t\t * @spec openspec/changes/board-record-card-layout/specs/board-record-card-layout/spec.md#requirement-a-board-card-can-draw-the-record-layout-from-config\n\t\t */\n\t\tcardLayout: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The page's columns by key, passed through to CnObjectCard. */\n\t\tcardFieldColumns: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Board look: `(object) => status` for each card's status pill. See CnObjectCard `status`. */\n\t\tstatusOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Board look: `(object) => { initials | icon }` for each card's leading element. See CnObjectCard `leading`. */\n\t\tleadingOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Board look: `(object) => { label, meta? }` for each card's footer action. See CnObjectCard `footerAction`. */\n\t\tfooterActionOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** `(object) => { variant, icon?, label? }` for each card's status accent. See CnObjectCard `accent`. */\n\t\taccentOf: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Text shown when there are no objects */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No items found'),\n\t\t},\n\t},\n\n\temits: ['click', 'select', 'aux-click'],\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the cards lie on the board look's track.\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 * Under the board look the grid is `minmax(var(--cn-card-grid-min,\n\t\t * 260px), 1fr)` with a `var(--cn-card-grid-gap, 16px)` gap, theme\n\t\t * hooks for both; without it the style is empty and the stylesheet's\n\t\t * 320px track stands.\n\t\t *\n\t\t * @spec openspec/changes/screens-card-parity/specs/card-board-look/spec.md#requirement-the-card-grid-takes-the-board-track\n\t\t * @return {object}\n\t\t */\n\t\tgridStyle() {\n\t\t\tif (!this.isBoardLook) {\n\t\t\t\treturn {}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tgridTemplateColumns: 'repeat(auto-fill, minmax(var(--cn-card-grid-min, 260px), 1fr))',\n\t\t\t\tgap: 'var(--cn-card-grid-gap, 16px)',\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * The empty-state copy run through the host translate function.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedEmptyText() {\n\t\t\tconst fn = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\treturn this.emptyText ? fn(this.emptyText) : this.emptyText\n\t\t},\n\t},\n\n\tmethods: {\n\t\tisSelected(object) {\n\t\t\treturn this.selectedIds.includes(object[this.rowKey])\n\t\t},\n\n\t\t/**\n\t\t * Listeners bound on each CnObjectCard. Selectable cards select via\n\t\t * `@select` only; the `@click` (navigation) listener is bound for\n\t\t * non-selectable cards — otherwise CnObjectCard's deprecated\n\t\t * click-to-select path fires (a body click would both select AND\n\t\t * navigate, unlike a table row which only selects) — and for\n\t\t * `clickToView` cards, where a body click navigates and the checkbox\n\t\t * is the only selection surface.\n\t\t *\n\t\t * @param {object} object The card's object.\n\t\t * @return {object} Event listeners for the card.\n\t\t */\n\t\tcardListeners(object) {\n\t\t\tconst listeners = { select: () => this.toggleSelect(object) }\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tlisteners.click = (_card, event) => this.$emit('click', object, event)\n\t\t\t\t// A middle click, kept off `click` so a navigating listener stays put.\n\t\t\t\tlisteners['aux-click'] = (_card, event) => {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event aux-click Emitted when a navigating 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\t\t * @type {object} The card's object.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('aux-click', object, event)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn listeners\n\t\t},\n\n\t\ttoggleSelect(object) {\n\t\t\tconst id = object[this.rowKey]\n\t\t\tconst newIds = this.isSelected(object)\n\t\t\t\t? this.selectedIds.filter((i) => i !== id)\n\t\t\t\t: [...this.selectedIds, id]\n\t\t\tthis.$emit('select', newIds)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-card-grid__grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n\tgap: 16px;\n}\n\n/* A card slot whose component renders nothing (e.g. a custom card that hides\n itself with `v-if` for a client-side filter) leaves an empty grid item that\n would still occupy a grid track and a row gap — pushing the visible cards\n down with a large blank gap above them. Collapse those empty cells so the\n remaining cards start at the top. A comment-only node (`v-if` false) still\n matches `:empty` per the CSS spec, so this catches the v-if-hidden case. */\n.cn-card-grid__item:empty {\n\tdisplay: none;\n}\n\n.cn-card-grid__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 40px;\n}\n\n.cn-card-grid__empty {\n\tpadding: 40px 20px;\n\ttext-align: center;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_renderSlot","_normalizeClass","_Fragment","_renderList","_mergeProps","_toHandlers","_createSlots","_withCtx"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import script from './CnCellRenderer.vue2.js';
|
|
|
2
2
|
import { render } from './CnCellRenderer.vue3.js';
|
|
3
3
|
|
|
4
4
|
script.render = render;
|
|
5
|
-
script.__scopeId = "data-v-
|
|
5
|
+
script.__scopeId = "data-v-2d766cc2";
|
|
6
6
|
const __sfc_main = script;
|
|
7
7
|
|
|
8
8
|
export { __sfc_main as default };
|
|
@@ -411,6 +411,24 @@ var script = {
|
|
|
411
411
|
return Number.isFinite(size) && size > 0 ? size : (this.isBoardLook ? 32 : 24)
|
|
412
412
|
},
|
|
413
413
|
|
|
414
|
+
/**
|
|
415
|
+
* `date` widget with `widgetProps.due`: the colour class. The
|
|
416
|
+
* `variantWhen` rules win when they match; else an overdue date reads
|
|
417
|
+
* as an error and a soon one (`due.soonWithin`) as a warning.
|
|
418
|
+
*
|
|
419
|
+
* @return {string|null} The class, or null for the plain colour.
|
|
420
|
+
* @spec openspec/changes/due-text-soon/specs/due-text-soon/spec.md#requirement-a-due-date-within-a-few-days-reads-as-soon
|
|
421
|
+
*/
|
|
422
|
+
dueClass() {
|
|
423
|
+
if (this.dateVariant) {
|
|
424
|
+
return 'cn-cell-renderer__date--' + this.dateVariant
|
|
425
|
+
}
|
|
426
|
+
if (this.dueResult && this.dueResult.tone === 'danger') {
|
|
427
|
+
return 'cn-cell-renderer__date--error'
|
|
428
|
+
}
|
|
429
|
+
return this.dueResult && this.dueResult.soon ? 'cn-cell-renderer__date--warning' : null
|
|
430
|
+
},
|
|
431
|
+
|
|
414
432
|
/**
|
|
415
433
|
* `date` widget: the variant of the first `widgetProps.variantWhen`
|
|
416
434
|
* rule the date matches, or '' for none.
|