@conduction/nextcloud-vue 0.1.0-beta.1 → 0.1.0-beta.11

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.
Files changed (208) hide show
  1. package/README.md +226 -0
  2. package/css/index.css +5 -0
  3. package/dist/nextcloud-vue.cjs.js +79416 -7715
  4. package/dist/nextcloud-vue.cjs.js.map +1 -1
  5. package/dist/nextcloud-vue.css +3583 -504
  6. package/dist/nextcloud-vue.esm.js +79343 -7692
  7. package/dist/nextcloud-vue.esm.js.map +1 -1
  8. package/l10n/en.json +164 -0
  9. package/l10n/nl.json +164 -0
  10. package/package.json +104 -63
  11. package/src/components/CnActionsBar/CnActionsBar.vue +254 -0
  12. package/src/components/CnActionsBar/index.js +1 -0
  13. package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +570 -0
  14. package/src/components/CnAdvancedFormDialog/CnDataTab.vue +217 -0
  15. package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +121 -0
  16. package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +422 -0
  17. package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +247 -0
  18. package/src/components/CnAdvancedFormDialog/index.js +1 -0
  19. package/src/components/CnCard/CnCard.vue +415 -0
  20. package/src/components/CnCard/index.js +1 -0
  21. package/src/components/CnCardGrid/CnCardGrid.vue +156 -152
  22. package/src/components/CnCardGrid/index.js +1 -1
  23. package/src/components/CnCellRenderer/CnCellRenderer.vue +132 -132
  24. package/src/components/CnCellRenderer/index.js +1 -1
  25. package/src/components/CnChartWidget/CnChartWidget.vue +346 -0
  26. package/src/components/CnChartWidget/index.js +1 -0
  27. package/src/components/CnConfigurationCard/CnConfigurationCard.vue +77 -77
  28. package/src/components/CnConfigurationCard/index.js +1 -1
  29. package/src/components/CnContextMenu/CnContextMenu.vue +142 -0
  30. package/src/components/CnContextMenu/index.js +1 -0
  31. package/src/components/CnCopyDialog/CnCopyDialog.vue +266 -0
  32. package/src/components/CnCopyDialog/index.js +1 -0
  33. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +229 -0
  34. package/src/components/CnDashboardGrid/index.js +1 -0
  35. package/src/components/CnDashboardPage/CnDashboardPage.vue +397 -0
  36. package/src/components/CnDashboardPage/index.js +1 -0
  37. package/src/components/CnDataTable/CnDataTable.vue +362 -354
  38. package/src/components/CnDataTable/index.js +1 -1
  39. package/src/components/CnDeleteDialog/CnDeleteDialog.vue +177 -0
  40. package/src/components/CnDeleteDialog/index.js +1 -0
  41. package/src/components/CnDetailCard/CnDetailCard.vue +225 -0
  42. package/src/components/CnDetailCard/index.js +1 -0
  43. package/src/components/CnDetailGrid/CnDetailGrid.vue +256 -0
  44. package/src/components/CnDetailGrid/index.js +1 -0
  45. package/src/components/CnDetailPage/CnDetailPage.vue +432 -0
  46. package/src/components/CnDetailPage/index.js +1 -0
  47. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +234 -223
  48. package/src/components/CnFacetSidebar/index.js +1 -1
  49. package/src/components/CnFilterBar/CnFilterBar.vue +153 -152
  50. package/src/components/CnFilterBar/index.js +1 -1
  51. package/src/components/CnFormDialog/CnFormDialog.vue +1047 -0
  52. package/src/components/CnFormDialog/index.js +1 -0
  53. package/src/components/CnIcon/CnIcon.vue +89 -0
  54. package/src/components/CnIcon/index.js +1 -0
  55. package/src/components/CnIndexPage/CnIndexPage.vue +980 -682
  56. package/src/components/CnIndexPage/index.js +1 -1
  57. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +536 -0
  58. package/src/components/CnIndexSidebar/index.js +1 -0
  59. package/src/components/CnInfoWidget/CnInfoWidget.vue +219 -0
  60. package/src/components/CnInfoWidget/index.js +1 -0
  61. package/src/components/CnItemCard/CnItemCard.vue +134 -0
  62. package/src/components/CnItemCard/index.js +1 -0
  63. package/src/components/CnJsonViewer/CnJsonViewer.vue +312 -0
  64. package/src/components/CnJsonViewer/index.js +1 -0
  65. package/src/components/CnKpiGrid/CnKpiGrid.vue +93 -89
  66. package/src/components/CnKpiGrid/index.js +1 -1
  67. package/src/components/CnMassActionBar/CnMassActionBar.vue +161 -160
  68. package/src/components/CnMassActionBar/index.js +1 -1
  69. package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +327 -320
  70. package/src/components/CnMassCopyDialog/index.js +1 -1
  71. package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +245 -238
  72. package/src/components/CnMassDeleteDialog/index.js +1 -1
  73. package/src/components/CnMassExportDialog/CnMassExportDialog.vue +191 -190
  74. package/src/components/CnMassExportDialog/index.js +1 -1
  75. package/src/components/CnMassImportDialog/CnMassImportDialog.vue +494 -491
  76. package/src/components/CnMassImportDialog/index.js +1 -1
  77. package/src/components/CnNoteCard/CnNoteCard.vue +149 -0
  78. package/src/components/CnNoteCard/index.js +1 -0
  79. package/src/components/CnNotesCard/CnNotesCard.vue +416 -0
  80. package/src/components/CnNotesCard/index.js +1 -0
  81. package/src/components/CnObjectCard/CnObjectCard.vue +294 -292
  82. package/src/components/CnObjectCard/index.js +1 -1
  83. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +854 -0
  84. package/src/components/CnObjectDataWidget/index.js +1 -0
  85. package/src/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue +289 -0
  86. package/src/components/CnObjectMetadataWidget/index.js +1 -0
  87. package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +369 -0
  88. package/src/components/CnObjectSidebar/CnFilesTab.vue +287 -0
  89. package/src/components/CnObjectSidebar/CnNotesTab.vue +250 -0
  90. package/src/components/CnObjectSidebar/CnObjectSidebar.vue +255 -0
  91. package/src/components/CnObjectSidebar/CnTagsTab.vue +259 -0
  92. package/src/components/CnObjectSidebar/CnTasksTab.vue +483 -0
  93. package/src/components/CnObjectSidebar/index.js +6 -0
  94. package/src/components/CnPageHeader/CnPageHeader.vue +61 -0
  95. package/src/components/CnPageHeader/index.js +1 -0
  96. package/src/components/CnPagination/CnPagination.vue +253 -252
  97. package/src/components/CnPagination/index.js +1 -1
  98. package/src/components/CnProgressBar/CnProgressBar.vue +262 -0
  99. package/src/components/CnProgressBar/index.js +1 -0
  100. package/src/components/CnRegisterMapping/CnRegisterMapping.vue +793 -0
  101. package/src/components/CnRegisterMapping/index.js +1 -0
  102. package/src/components/CnRowActions/CnRowActions.vue +95 -73
  103. package/src/components/CnRowActions/index.js +1 -1
  104. package/src/components/CnSchemaFormDialog/CnSchemaConfigurationTab.vue +226 -0
  105. package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +788 -0
  106. package/src/components/CnSchemaFormDialog/CnSchemaPropertiesTab.vue +305 -0
  107. package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +1398 -0
  108. package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +236 -0
  109. package/src/components/CnSchemaFormDialog/index.js +1 -0
  110. package/src/components/CnSettingsCard/CnSettingsCard.vue +92 -92
  111. package/src/components/CnSettingsCard/index.js +1 -1
  112. package/src/components/CnSettingsSection/CnSettingsSection.vue +267 -266
  113. package/src/components/CnSettingsSection/index.js +1 -1
  114. package/src/components/CnStatsBlock/CnStatsBlock.vue +437 -366
  115. package/src/components/CnStatsBlock/index.js +1 -1
  116. package/src/components/CnStatsPanel/CnStatsPanel.vue +321 -0
  117. package/src/components/CnStatsPanel/index.js +1 -0
  118. package/src/components/CnStatusBadge/CnStatusBadge.vue +90 -77
  119. package/src/components/CnStatusBadge/index.js +1 -1
  120. package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +545 -0
  121. package/src/components/CnTabbedFormDialog/index.js +1 -0
  122. package/src/components/CnTableWidget/CnTableWidget.vue +333 -0
  123. package/src/components/CnTableWidget/index.js +1 -0
  124. package/src/components/CnTasksCard/CnTasksCard.vue +374 -0
  125. package/src/components/CnTasksCard/index.js +1 -0
  126. package/src/components/CnTileWidget/CnTileWidget.vue +159 -0
  127. package/src/components/CnTileWidget/index.js +1 -0
  128. package/src/components/CnTimelineStages/CnTimelineStages.vue +294 -0
  129. package/src/components/CnTimelineStages/index.js +1 -0
  130. package/src/components/CnUserActionMenu/CnUserActionMenu.vue +436 -0
  131. package/src/components/CnUserActionMenu/index.js +1 -0
  132. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +313 -312
  133. package/src/components/CnVersionInfoCard/index.js +1 -1
  134. package/src/components/CnWidgetRenderer/CnWidgetRenderer.vue +180 -0
  135. package/src/components/CnWidgetRenderer/index.js +1 -0
  136. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +248 -0
  137. package/src/components/CnWidgetWrapper/index.js +1 -0
  138. package/src/components/index.js +57 -25
  139. package/src/composables/index.js +5 -3
  140. package/src/composables/useContextMenu.js +126 -0
  141. package/src/composables/useDashboardView.js +286 -0
  142. package/src/composables/useDetailView.js +290 -132
  143. package/src/composables/useListView.js +364 -153
  144. package/src/composables/useSubResource.js +142 -142
  145. package/src/constants/metadata.js +30 -0
  146. package/src/css/CnSchemaFormDialog.css +546 -0
  147. package/src/css/__sample_nextcloud_tokens.css +110 -0
  148. package/src/css/actions-bar.css +54 -0
  149. package/src/css/badge.css +83 -51
  150. package/src/css/card.css +129 -128
  151. package/src/css/context-menu.css +20 -0
  152. package/src/css/dashboard.css +70 -0
  153. package/src/css/detail-page.css +235 -0
  154. package/src/css/detail.css +68 -68
  155. package/src/css/index-page.css +44 -0
  156. package/src/css/index-sidebar.css +193 -0
  157. package/src/css/index.css +17 -8
  158. package/src/css/layout.css +90 -90
  159. package/src/css/page-header.css +35 -0
  160. package/src/css/pagination.css +72 -72
  161. package/src/css/table.css +142 -143
  162. package/src/css/timeline-stages.css +220 -0
  163. package/src/css/utilities.css +46 -46
  164. package/src/index.js +95 -50
  165. package/src/l10n/index.js +12 -0
  166. package/src/mixins/gridLayout.js +118 -0
  167. package/src/store/createCrudStore.d.ts +350 -0
  168. package/src/store/createCrudStore.js +413 -0
  169. package/src/store/createSubResourcePlugin.js +125 -135
  170. package/src/store/index.js +4 -3
  171. package/src/store/pluginMerge.js +55 -0
  172. package/src/store/plugins/auditTrails.js +357 -17
  173. package/src/store/plugins/files.js +250 -186
  174. package/src/store/plugins/index.js +8 -4
  175. package/src/store/plugins/lifecycle.js +180 -180
  176. package/src/store/plugins/logs.d.ts +22 -0
  177. package/src/store/plugins/logs.js +172 -0
  178. package/src/store/plugins/registerMapping.js +195 -0
  179. package/src/store/plugins/relations.js +68 -68
  180. package/src/store/plugins/search.js +385 -0
  181. package/src/store/plugins/selection.js +104 -0
  182. package/src/store/useObjectStore.js +793 -625
  183. package/src/types/auditTrail.d.ts +32 -32
  184. package/src/types/file.d.ts +23 -23
  185. package/src/types/index.d.ts +67 -35
  186. package/src/types/notification.d.ts +36 -36
  187. package/src/types/object.d.ts +40 -40
  188. package/src/types/organisation.d.ts +41 -41
  189. package/src/types/register.d.ts +25 -25
  190. package/src/types/schema.d.ts +39 -39
  191. package/src/types/shared.d.ts +79 -79
  192. package/src/types/source.d.ts +14 -14
  193. package/src/types/task.d.ts +31 -31
  194. package/src/utils/errors.js +96 -96
  195. package/src/utils/getTheme.js +9 -0
  196. package/src/utils/headers.js +80 -44
  197. package/src/utils/id.js +13 -0
  198. package/src/utils/index.js +4 -3
  199. package/src/utils/schema.js +423 -287
  200. package/src/utils/widgetVisibility.js +162 -0
  201. package/src/components/CnDetailViewLayout/CnDetailViewLayout.vue +0 -88
  202. package/src/components/CnDetailViewLayout/index.js +0 -1
  203. package/src/components/CnEmptyState/CnEmptyState.vue +0 -78
  204. package/src/components/CnEmptyState/index.js +0 -1
  205. package/src/components/CnListViewLayout/CnListViewLayout.vue +0 -80
  206. package/src/components/CnListViewLayout/index.js +0 -1
  207. package/src/components/CnViewModeToggle/CnViewModeToggle.vue +0 -77
  208. package/src/components/CnViewModeToggle/index.js +0 -1
@@ -1,292 +1,294 @@
1
- <template>
2
- <div
3
- class="cn-object-card"
4
- :class="{ 'cn-object-card--selected': selected }"
5
- @click="$emit('click', object)">
6
- <!-- Selection checkbox -->
7
- <div v-if="selectable" class="cn-object-card__checkbox" @click.stop>
8
- <NcCheckboxRadioSwitch
9
- :checked="selected"
10
- @update:checked="$emit('select', object)" />
11
- </div>
12
-
13
- <!-- Card content -->
14
- <div class="cn-object-card__content">
15
- <!-- Header: image + title -->
16
- <div class="cn-object-card__header">
17
- <img
18
- v-if="imageUrl"
19
- :src="imageUrl"
20
- :alt="title"
21
- class="cn-object-card__image">
22
-
23
- <div class="cn-object-card__title-area">
24
- <h3 class="cn-object-card__title">{{ title }}</h3>
25
- <p v-if="description" class="cn-object-card__description">
26
- {{ truncatedDescription }}
27
- </p>
28
- </div>
29
- </div>
30
-
31
- <!-- Badges slot -->
32
- <div v-if="$scopedSlots.badges" class="cn-object-card__badges">
33
- <slot name="badges" :object="object" />
34
- </div>
35
-
36
- <!-- Metadata: visible properties as label:value pairs -->
37
- <div v-if="metadataFields.length > 0" class="cn-object-card__metadata">
38
- <slot name="metadata" :object="object" :fields="metadataFields">
39
- <div
40
- v-for="field in metadataFields"
41
- :key="field.key"
42
- class="cn-object-card__meta-item">
43
- <span class="cn-object-card__meta-label">{{ field.label }}</span>
44
- <CnCellRenderer
45
- :value="field.value"
46
- :property="field.property"
47
- :truncate="60" />
48
- </div>
49
- </slot>
50
- </div>
51
- </div>
52
-
53
- <!-- Actions slot -->
54
- <div v-if="$scopedSlots.actions" class="cn-object-card__actions" @click.stop>
55
- <slot name="actions" :object="object" />
56
- </div>
57
- </div>
58
- </template>
59
-
60
- <script>
61
- import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
62
- import { CnCellRenderer } from '../CnCellRenderer/index.js'
63
- import { formatValue } from '../../utils/schema.js'
64
-
65
- /**
66
- * CnObjectCard — Schema-configuration-driven card for object display.
67
- *
68
- * Uses `schema.configuration` to determine which fields map to the card title,
69
- * description, and image. Remaining visible properties are shown as metadata.
70
- *
71
- * @example
72
- * <CnObjectCard :object="publication" :schema="pubSchema">
73
- * <template #actions="{ object }">
74
- * <NcActions><NcActionButton @click="edit(object)">Edit</NcActionButton></NcActions>
75
- * </template>
76
- * </CnObjectCard>
77
- */
78
- export default {
79
- name: 'CnObjectCard',
80
-
81
- components: {
82
- NcCheckboxRadioSwitch,
83
- CnCellRenderer,
84
- },
85
-
86
- props: {
87
- /** The object data */
88
- object: {
89
- type: Object,
90
- required: true,
91
- },
92
- /** Schema definition with properties and configuration */
93
- schema: {
94
- type: Object,
95
- required: true,
96
- },
97
- /** Whether this card is selected */
98
- selected: {
99
- type: Boolean,
100
- default: false,
101
- },
102
- /** Whether to show selection checkbox */
103
- selectable: {
104
- type: Boolean,
105
- default: false,
106
- },
107
- /** Maximum number of metadata fields to show */
108
- maxMetadata: {
109
- type: Number,
110
- default: 4,
111
- },
112
- },
113
-
114
- computed: {
115
- config() {
116
- return this.schema?.configuration || {}
117
- },
118
-
119
- title() {
120
- const field = this.config.objectNameField
121
- if (field && this.object[field]) {
122
- return String(this.object[field])
123
- }
124
- return this.object.title || this.object.name || this.object.id || '—'
125
- },
126
-
127
- description() {
128
- const field = this.config.objectDescriptionField
129
- if (field && this.object[field]) {
130
- return String(this.object[field])
131
- }
132
- return null
133
- },
134
-
135
- truncatedDescription() {
136
- if (!this.description) return null
137
- if (this.description.length > 120) {
138
- return this.description.substring(0, 120) + '...'
139
- }
140
- return this.description
141
- },
142
-
143
- imageUrl() {
144
- const field = this.config.objectImageField
145
- if (field && this.object[field]) {
146
- return this.object[field]
147
- }
148
- return null
149
- },
150
-
151
- /** Fields excluded from metadata (already shown as title/desc/image) */
152
- configFields() {
153
- return [
154
- this.config.objectNameField,
155
- this.config.objectDescriptionField,
156
- this.config.objectSummaryField,
157
- this.config.objectImageField,
158
- ].filter(Boolean)
159
- },
160
-
161
- /** Remaining visible properties for the metadata section */
162
- metadataFields() {
163
- if (!this.schema?.properties) return []
164
-
165
- return Object.entries(this.schema.properties)
166
- .filter(([key, prop]) => {
167
- if (this.configFields.includes(key)) return false
168
- if (prop.visible === false) return false
169
- if (prop.type === 'object') return false
170
- if (prop.format === 'markdown') return false
171
- return true
172
- })
173
- .sort(([, a], [, b]) => {
174
- const orderA = typeof a.order === 'number' ? a.order : Infinity
175
- const orderB = typeof b.order === 'number' ? b.order : Infinity
176
- return orderA - orderB
177
- })
178
- .slice(0, this.maxMetadata)
179
- .map(([key, prop]) => ({
180
- key,
181
- label: prop.title || key,
182
- value: this.object[key],
183
- property: prop,
184
- }))
185
- },
186
- },
187
-
188
- methods: {
189
- formatValue,
190
- },
191
- }
192
- </script>
193
-
194
- <style scoped>
195
- .cn-object-card {
196
- display: flex;
197
- gap: 12px;
198
- padding: 16px;
199
- background: var(--color-main-background);
200
- border: 1px solid var(--color-border);
201
- border-radius: var(--border-radius-large, 10px);
202
- cursor: pointer;
203
- transition: box-shadow 0.2s ease, border-color 0.2s ease;
204
- }
205
-
206
- .cn-object-card:hover {
207
- border-color: var(--color-primary-element);
208
- box-shadow: 0 2px 8px var(--color-box-shadow);
209
- }
210
-
211
- .cn-object-card--selected {
212
- border-color: var(--color-primary-element);
213
- background: var(--color-primary-element-light);
214
- }
215
-
216
- .cn-object-card__checkbox {
217
- flex-shrink: 0;
218
- padding-top: 2px;
219
- }
220
-
221
- .cn-object-card__content {
222
- flex: 1;
223
- min-width: 0;
224
- }
225
-
226
- .cn-object-card__header {
227
- display: flex;
228
- gap: 12px;
229
- align-items: flex-start;
230
- }
231
-
232
- .cn-object-card__image {
233
- width: 48px;
234
- height: 48px;
235
- border-radius: var(--border-radius);
236
- object-fit: cover;
237
- flex-shrink: 0;
238
- }
239
-
240
- .cn-object-card__title-area {
241
- flex: 1;
242
- min-width: 0;
243
- }
244
-
245
- .cn-object-card__title {
246
- margin: 0;
247
- font-size: 16px;
248
- font-weight: 600;
249
- line-height: 1.3;
250
- overflow: hidden;
251
- text-overflow: ellipsis;
252
- white-space: nowrap;
253
- }
254
-
255
- .cn-object-card__description {
256
- margin: 4px 0 0;
257
- font-size: 13px;
258
- color: var(--color-text-maxcontrast);
259
- line-height: 1.4;
260
- }
261
-
262
- .cn-object-card__badges {
263
- margin-top: 8px;
264
- }
265
-
266
- .cn-object-card__metadata {
267
- display: grid;
268
- grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
269
- gap: 8px;
270
- margin-top: 12px;
271
- padding-top: 12px;
272
- border-top: 1px solid var(--color-border);
273
- }
274
-
275
- .cn-object-card__meta-item {
276
- display: flex;
277
- flex-direction: column;
278
- gap: 2px;
279
- }
280
-
281
- .cn-object-card__meta-label {
282
- font-size: 11px;
283
- font-weight: 500;
284
- color: var(--color-text-maxcontrast);
285
- text-transform: uppercase;
286
- letter-spacing: 0.3px;
287
- }
288
-
289
- .cn-object-card__actions {
290
- flex-shrink: 0;
291
- }
292
- </style>
1
+ <template>
2
+ <div
3
+ class="cn-object-card"
4
+ :class="{ 'cn-object-card--selected': selected }"
5
+ @click="$emit('click', object)">
6
+ <!-- Selection checkbox -->
7
+ <div v-if="selectable" class="cn-object-card__checkbox" @click.stop>
8
+ <NcCheckboxRadioSwitch
9
+ :checked="selected"
10
+ @update:checked="$emit('select', object)" />
11
+ </div>
12
+
13
+ <!-- Card content -->
14
+ <div class="cn-object-card__content">
15
+ <!-- Header: image + title -->
16
+ <div class="cn-object-card__header">
17
+ <img
18
+ v-if="imageUrl"
19
+ :src="imageUrl"
20
+ :alt="title"
21
+ class="cn-object-card__image">
22
+
23
+ <div class="cn-object-card__title-area">
24
+ <h3 class="cn-object-card__title">
25
+ {{ title }}
26
+ </h3>
27
+ <p v-if="description" class="cn-object-card__description">
28
+ {{ truncatedDescription }}
29
+ </p>
30
+ </div>
31
+ </div>
32
+
33
+ <!-- Badges slot -->
34
+ <div v-if="$scopedSlots.badges" class="cn-object-card__badges">
35
+ <slot name="badges" :object="object" />
36
+ </div>
37
+
38
+ <!-- Metadata: visible properties as label:value pairs -->
39
+ <div v-if="metadataFields.length > 0" class="cn-object-card__metadata">
40
+ <slot name="metadata" :object="object" :fields="metadataFields">
41
+ <div
42
+ v-for="field in metadataFields"
43
+ :key="field.key"
44
+ class="cn-object-card__meta-item">
45
+ <span class="cn-object-card__meta-label">{{ field.label }}</span>
46
+ <CnCellRenderer
47
+ :value="field.value"
48
+ :property="field.property"
49
+ :truncate="60" />
50
+ </div>
51
+ </slot>
52
+ </div>
53
+ </div>
54
+
55
+ <!-- Actions slot -->
56
+ <div v-if="$scopedSlots.actions" class="cn-object-card__actions" @click.stop>
57
+ <slot name="actions" :object="object" />
58
+ </div>
59
+ </div>
60
+ </template>
61
+
62
+ <script>
63
+ import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
64
+ import { CnCellRenderer } from '../CnCellRenderer/index.js'
65
+ import { formatValue } from '../../utils/schema.js'
66
+
67
+ /**
68
+ * CnObjectCard — Schema-configuration-driven card for object display.
69
+ *
70
+ * Uses `schema.configuration` to determine which fields map to the card title,
71
+ * description, and image. Remaining visible properties are shown as metadata.
72
+ *
73
+ * @example
74
+ * <CnObjectCard :object="publication" :schema="pubSchema">
75
+ * <template #actions="{ object }">
76
+ * <NcActions><NcActionButton @click="edit(object)">Edit</NcActionButton></NcActions>
77
+ * </template>
78
+ * </CnObjectCard>
79
+ */
80
+ export default {
81
+ name: 'CnObjectCard',
82
+
83
+ components: {
84
+ NcCheckboxRadioSwitch,
85
+ CnCellRenderer,
86
+ },
87
+
88
+ props: {
89
+ /** The object data */
90
+ object: {
91
+ type: Object,
92
+ required: true,
93
+ },
94
+ /** Schema definition with properties and configuration */
95
+ schema: {
96
+ type: Object,
97
+ required: true,
98
+ },
99
+ /** Whether this card is selected */
100
+ selected: {
101
+ type: Boolean,
102
+ default: false,
103
+ },
104
+ /** Whether to show selection checkbox */
105
+ selectable: {
106
+ type: Boolean,
107
+ default: false,
108
+ },
109
+ /** Maximum number of metadata fields to show */
110
+ maxMetadata: {
111
+ type: Number,
112
+ default: 4,
113
+ },
114
+ },
115
+
116
+ computed: {
117
+ config() {
118
+ return this.schema?.configuration || {}
119
+ },
120
+
121
+ title() {
122
+ const field = this.config.objectNameField
123
+ if (field && this.object[field]) {
124
+ return String(this.object[field])
125
+ }
126
+ return this.object.title || this.object.name || this.object.id || '—'
127
+ },
128
+
129
+ description() {
130
+ const field = this.config.objectDescriptionField
131
+ if (field && this.object[field]) {
132
+ return String(this.object[field])
133
+ }
134
+ return null
135
+ },
136
+
137
+ truncatedDescription() {
138
+ if (!this.description) return null
139
+ if (this.description.length > 120) {
140
+ return this.description.substring(0, 120) + '...'
141
+ }
142
+ return this.description
143
+ },
144
+
145
+ imageUrl() {
146
+ const field = this.config.objectImageField
147
+ if (field && this.object[field]) {
148
+ return this.object[field]
149
+ }
150
+ return null
151
+ },
152
+
153
+ /** Fields excluded from metadata (already shown as title/desc/image) */
154
+ configFields() {
155
+ return [
156
+ this.config.objectNameField,
157
+ this.config.objectDescriptionField,
158
+ this.config.objectSummaryField,
159
+ this.config.objectImageField,
160
+ ].filter(Boolean)
161
+ },
162
+
163
+ /** Remaining visible properties for the metadata section */
164
+ metadataFields() {
165
+ if (!this.schema?.properties) return []
166
+
167
+ return Object.entries(this.schema.properties)
168
+ .filter(([key, prop]) => {
169
+ if (this.configFields.includes(key)) return false
170
+ if (prop.visible === false) return false
171
+ if (prop.type === 'object') return false
172
+ if (prop.format === 'markdown') return false
173
+ return true
174
+ })
175
+ .sort(([, a], [, b]) => {
176
+ const orderA = typeof a.order === 'number' ? a.order : Infinity
177
+ const orderB = typeof b.order === 'number' ? b.order : Infinity
178
+ return orderA - orderB
179
+ })
180
+ .slice(0, this.maxMetadata)
181
+ .map(([key, prop]) => ({
182
+ key,
183
+ label: prop.title || key,
184
+ value: this.object[key],
185
+ property: prop,
186
+ }))
187
+ },
188
+ },
189
+
190
+ methods: {
191
+ formatValue,
192
+ },
193
+ }
194
+ </script>
195
+
196
+ <style scoped>
197
+ .cn-object-card {
198
+ display: flex;
199
+ gap: 12px;
200
+ padding: 16px;
201
+ background: var(--color-main-background);
202
+ border: 1px solid var(--color-border);
203
+ border-radius: var(--border-radius-large, 10px);
204
+ cursor: pointer;
205
+ transition: box-shadow 0.2s ease, border-color 0.2s ease;
206
+ }
207
+
208
+ .cn-object-card:hover {
209
+ border-color: var(--color-primary-element);
210
+ box-shadow: 0 2px 8px var(--color-box-shadow);
211
+ }
212
+
213
+ .cn-object-card--selected {
214
+ border-color: var(--color-primary-element);
215
+ background: var(--color-primary-element-light);
216
+ }
217
+
218
+ .cn-object-card__checkbox {
219
+ flex-shrink: 0;
220
+ padding-top: 2px;
221
+ }
222
+
223
+ .cn-object-card__content {
224
+ flex: 1;
225
+ min-width: 0;
226
+ }
227
+
228
+ .cn-object-card__header {
229
+ display: flex;
230
+ gap: 12px;
231
+ align-items: flex-start;
232
+ }
233
+
234
+ .cn-object-card__image {
235
+ width: 48px;
236
+ height: 48px;
237
+ border-radius: var(--border-radius);
238
+ object-fit: cover;
239
+ flex-shrink: 0;
240
+ }
241
+
242
+ .cn-object-card__title-area {
243
+ flex: 1;
244
+ min-width: 0;
245
+ }
246
+
247
+ .cn-object-card__title {
248
+ margin: 0;
249
+ font-size: 16px;
250
+ font-weight: 600;
251
+ line-height: 1.3;
252
+ overflow: hidden;
253
+ text-overflow: ellipsis;
254
+ white-space: nowrap;
255
+ }
256
+
257
+ .cn-object-card__description {
258
+ margin: 4px 0 0;
259
+ font-size: 13px;
260
+ color: var(--color-text-maxcontrast);
261
+ line-height: 1.4;
262
+ }
263
+
264
+ .cn-object-card__badges {
265
+ margin-top: 8px;
266
+ }
267
+
268
+ .cn-object-card__metadata {
269
+ display: grid;
270
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
271
+ gap: 8px;
272
+ margin-top: 12px;
273
+ padding-top: 12px;
274
+ border-top: 1px solid var(--color-border);
275
+ }
276
+
277
+ .cn-object-card__meta-item {
278
+ display: flex;
279
+ flex-direction: column;
280
+ gap: 2px;
281
+ }
282
+
283
+ .cn-object-card__meta-label {
284
+ font-size: 11px;
285
+ font-weight: 500;
286
+ color: var(--color-text-maxcontrast);
287
+ text-transform: uppercase;
288
+ letter-spacing: 0.3px;
289
+ }
290
+
291
+ .cn-object-card__actions {
292
+ flex-shrink: 0;
293
+ }
294
+ </style>
@@ -1 +1 @@
1
- export { default as CnObjectCard } from './CnObjectCard.vue'
1
+ export { default as CnObjectCard } from './CnObjectCard.vue'